1 /***************************************************************************
2  * LPRng - An Extended Print Spooler System
3  *
4  * Copyright 1988-2003, Patrick Powell, San Diego, CA
5  *     papowell@lprng.com
6  * See LICENSE for conditions of use.
7  *
8  ***************************************************************************/
9 
10 /***************************************************************************
11  *  Filter template and frontend.
12  *
13  *	A filter is invoked with the following parameters,
14  *  which can be in any order, and perhaps some missing.
15  *
16  *  filtername arguments \   <- from PRINTCAP entry
17  *      -PPrinter -wwidth -llength -xwidth -ylength [-c] \
18  *      -Kcontrolfilename -Lbnrname \
19  *      [-iindent] \
20  *		[-Zoptions] [-Cclass] [-Jjob] [-Raccntname] -nlogin -hHost  \
21  *      -Fformat [-Tcrlf] [-Tdebug] [affile]
22  *
23  *  1. Parameters can be in different order than the above.
24  *  2. Optional parameters can be missing
25  *  3. Values specified for the width, length, etc., are from PRINTCAP
26  *     or from the overridding user specified options.
27  *
28  *  This program provides a common front end for most of the necessary
29  *  grunt work.  This falls into the following classes:
30  * 1. Parameter extraction.
31  * 2. Suspension when used as the "of" filter.
32  * 3. Termination and accounting
33  *  The front end will extract parameters,  then call the filter_pgm()
34  *  routine,  which is responsible for carrying out the required filter
35  *  actions. filter_pgm() is invoked with the printer device on fd 1,
36  *	and error log on fd 2.  The npages variable is used to record the
37  *  number of pages that were used.
38  *  The "halt string", which is a sequence of characters that
39  *  should cause the filter to suspend itself, is passed to filter.
40  *  When these characters are detected,  the "suspend_ofilter()" routine should be
41  *  called.
42  *
43  *  On successful termination,  the accounting file will be updated.
44  *
45  *  The filter_pgm() routine should return 0 (success), 1 (retry) or 2 (abort).
46  *
47  * Parameter Extraction
48  *	The main() routine will extract parameters
49  *  whose values are placed in the appropriate variables.  This is done
50  *  by using the ParmTable[], which has entries for each valid letter
51  *  parmeter, such as the letter flag, the type of variable,
52  *  and the address of the variable.
53  *  The following variables are provided as a default set.
54  *      -PPrinter -wwidth -llength -xwidth -ylength [-c] [-iindent] \
55  *		[-Zoptions] [-Cclass] [-Jjob] [-Raccntname] -nlogin -hHost  \
56  *      -Fformat [affile]
57  * VARIABLE  FLAG          TYPE    PURPOSE / PRINTCAP ENTRTY
58  * name     name of filter char*    argv[0], program identification
59  * width    -wwidth	       int      PW, width in chars
60  * length   -llength	   int      PL, length in lines
61  * xwidth   -xwidth        int      PX, width in pixels
62  * xlength  -xlength       int      PY, length in pixels
63  * literal  -c	           int      if set, ignore control chars
64  * controlfile -kcontrolfile char*  control file name
65  * bnrname  -Lbnrname      char*    banner name
66  * indent   -iindent       int      indent amount (depends on device)
67  * zopts    -Zoptions      char*    extra options for printer
68  * comment  -Scomment      char*    printer name in comment field
69  * class    -Cclass        char*    classname
70  * job      -Jjob          char*    jobname
71  * accntname -Raccntname   char*    account for billing purposes
72  * login    -nlogin        char*    login name
73  * host     -hhost         char*    host name
74  * format   -Fformat       char*    format
75  * statusfile  -sstatusfile   char*    statusfile
76  * accntfile file          char*    AF, accounting file
77  *
78  * npages    - number of pages for accounting
79  *
80  * -Tdebug - increment debug level
81  * -Tcrlf  - turn LF to CRLF translation off
82  *
83  *	The functions logerr(), and logerr_die() can be used to report
84  *	status. The variable errorcode can be set by the user before calling
85  *	these functions, and will be the exit value of the program. Its default
86  *	value will be 2 (abort status).
87  *	logerr() reports a message, appends information indicated by errno
88  *	(see perror(2) for details), and then returns.
89  *	logerr_die() will call logerr(), and then will exit with errorcode
90  *	status.
91  *
92  * DEBUGGING:  a simple minded debugging version can be enabled by
93  * compiling with the -DDEBUG option.
94  */
95 
96 #include <config.h>
97 #include "portable.h"
98 #include "plp_snprintf.h"
99 
100 /* VARARGS2 */
101 #ifdef HAVE_STDARGS
102  static void safefprintf (int fd, const char *format,...) PRINTFATTR(2,3)
103 ;
104 #else
105  static void safefprintf ();
106 #endif
107 #ifdef HAVE_STDARGS
108 static void logerr(const char *msg, ...) PRINTFATTR(1,2)
109 #else
110 static void logerr( va_alist ) va_dcl
111 #endif
112 ;
113 #ifdef HAVE_STDARGS
114 static void logerr_die(const char *msg, ...) PRINTFATTR(1,2)
115 #else
116 static void logerr_die( va_alist ) va_dcl
117 #endif
118 ;
119 
120 #include <sys/types.h>
121 #include <stdio.h>
122 #include <signal.h>
123 #include <string.h>
124 #include <ctype.h>
125 #include <errno.h>
126 #include <time.h>
127 
128 #ifdef HAVE_STDLIB_H
129 #include <stdlib.h>
130 #endif
131 
132 #ifdef HAVE_UNISTD_H
133 #include <unistd.h>
134 #endif
135 
136 #ifdef HAVE_SYS_FILE_H
137 # include <sys/file.h>
138 #endif
139 
140 #ifdef HAVE_SYS_FCNTL_H
141 # include <sys/fcntl.h>
142 #endif
143 #ifdef HAVE_FCNTL_H
144 # include <fcntl.h>
145 #endif
146 
147 /* varargs declarations: */
148 
149 #if defined(HAVE_STDARG_H)
150 # include <stdarg.h>
151 # define HAVE_STDARGS    /* let's hope that works everywhere (mj) */
152 # define VA_LOCAL_DECL   va_list ap;
153 # define VA_START(f)     va_start(ap, f)
154 # define VA_SHIFT(v,t)	;	/* no-op for ANSI */
155 # define VA_END          va_end(ap)
156 #else
157 # if defined(HAVE_VARARGS_H)
158 #  include <varargs.h>
159 #  undef HAVE_STDARGS
160 #  define VA_LOCAL_DECL   va_list ap;
161 #  define VA_START(f)     va_start(ap)		/* f is ignored! */
162 #  define VA_SHIFT(v,t)	v = va_arg(ap,t)
163 #  define VA_END		va_end(ap)
164 # else
165 XX ** NO VARARGS ** XX
166 # endif
167 #endif
168 
169 static char *lpf_time_str(void);
170 static void filter_pgm(const char *stop);
171 
172 /*
173  * default exit status, causes abort
174  */
175 static int errorcode = 2;
176 static const char *name;		/* name of filter */
177 /* set from flags */
178 static int debug = 0;
179 static int width = 80, length = 72, xwidth = 1440, ylength = -1;
180 static int literal = 0, indent = 0;
181 static const char *zopts = NULL, *class = NULL, *job = NULL, *login = NULL;
182 static const char *accntname = NULL, *host = NULL, *accntfile= NULL;
183 static const char *format = NULL, *printer = NULL, *controlfile = NULL;
184 static const char *bnrname = NULL, *comment = NULL, *queuename = NULL;
185 static const char *errorfile = NULL, *statusfile = NULL;
186 static int npages;	/* number of pages */
187 static int accounting_fd;
188 static int crlf = 0;	/* change lf to CRLF */
189 
190 static void getargs( int argc, char *argv[], char *envp[] );
191 static int of_filter;
192 
main(int argc,char * argv[],char * envp[])193 int main( int argc, char *argv[], char *envp[] )
194 {
195 
196 	/* check to see if you have the accounting fd */
197 	accounting_fd = dup(3);
198 	/* if this works, then you have one */
199 	if( accounting_fd >= 0 ){
200 		(void)close( accounting_fd );
201 		accounting_fd = 3;
202 	} else {
203 		accounting_fd = -1;
204 	}
205 	if( fcntl(0,F_GETFL,0) == -1 ){
206 		FPRINTF(STDERR,"BAD FD 0\n");
207 		exit(2);
208 	}
209 	if( fcntl(1,F_GETFL,0) == -1 ){
210 		FPRINTF(STDERR,"BAD FD 1\n");
211 		exit(2);
212 	}
213 	if( fcntl(2,F_GETFL,0) == -1 ){
214 		FPRINTF(STDERR,"BAD FD 2\n");
215 		exit(2);
216 	}
217 	getargs( argc, argv, envp );
218 	/*
219 	 * Turn off SIGPIPE
220 	 */
221 	(void)signal( SIGPIPE, SIG_IGN );
222 	(void)signal( SIGINT,  SIG_DFL );
223 	(void)signal( SIGHUP,  SIG_DFL );
224 	(void)signal( SIGQUIT, SIG_DFL );
225 	(void)signal( SIGCHLD, SIG_DFL );
226 	if( of_filter || (format && format[0] == 'o') ){
227 		filter_pgm( "\031\001" );
228 	} else {
229 		filter_pgm( (char *)0 );
230 	}
231 	return(0);
232 }
233 
Write_fd_str(int fd,const char * msg)234 static int Write_fd_str( int fd, const char *msg )
235 {
236 	int n;
237 	n = strlen(msg);
238 	return write(fd,msg,n);
239 }
240 
241 /* VARARGS2 */
242 #ifdef HAVE_STDARGS
safefprintf(int fd,const char * format,...)243  void safefprintf (int fd, const char *format,...)
244 #else
245  void safefprintf (va_alist) va_dcl
246 #endif
247 {
248 #ifndef HAVE_STDARGS
249 	int fd;
250     char *format;
251 #endif
252 	char buf[1024];
253     VA_LOCAL_DECL
254 
255     VA_START (format);
256     VA_SHIFT (fd, int);
257     VA_SHIFT (format, char *);
258 
259 	buf[0] = 0;
260 	(void) plp_vsnprintf(buf, sizeof(buf), format, ap);
261 	Write_fd_str(fd,buf);
262 }
263 
264 /****************************************************************************
265  * Extract the necessary definitions for error message reporting
266  ****************************************************************************/
267 
268 #ifdef HAVE_STRERROR
269 #define Errormsg strerror
270 #else
Errormsg(int err)271 static const char * Errormsg ( int err )
272 {
273 	if( err == 0 ){
274 		return "No Error";
275 	} else {
276 		static char msgbuf[32];     /* holds "errno=%d". */
277 		(void) plp_snprintf(msgbuf, sizeof(msgbuf), "errno=%d", err);
278 		return msgbuf;
279 	}
280 }
281 #endif
282 
283 #ifdef HAVE_STDARGS
logerr(const char * msg,...)284 static void logerr(const char *msg, ...)
285 #else
286 static void logerr( va_alist ) va_dcl
287 #endif
288 {
289 #ifndef HAVE_STDARGS
290 	char *msg;
291 #endif
292 	int err = errno;
293 	int n;
294 	char buf[1024];
295 	VA_LOCAL_DECL
296 	VA_START(msg);
297 	VA_SHIFT(msg, char *);
298 
299 	(void)plp_snprintf(buf,sizeof(buf), "%s: ", name);
300 	n = strlen(buf);
301 	(void)plp_vsnprintf(buf+n,sizeof(buf)-n, msg, ap);
302 	n = strlen(buf);
303 	(void)plp_snprintf(buf+n,sizeof(buf)-n, "- %s\n", Errormsg(err) );
304 	Write_fd_str(2,buf);
305 	VA_END;
306 }
307 
308 #ifdef HAVE_STDARGS
logerr_die(const char * msg,...)309 static void logerr_die(const char *msg, ...)
310 #else
311 static void logerr_die( va_alist ) va_dcl
312 #endif
313 {
314 #ifndef HAVE_STDARGS
315 	char *msg;
316 #endif
317 	int err = errno;
318 	int n;
319 	char buf[1024];
320 	VA_LOCAL_DECL
321 	VA_START(msg);
322 	VA_SHIFT(msg, char *);
323 
324 	(void)plp_snprintf(buf,sizeof(buf), "%s: ", name);
325 	n = strlen(buf);
326 	(void)plp_vsnprintf(buf+n,sizeof(buf)-n, msg, ap);
327 	n = strlen(buf);
328 	(void)plp_snprintf(buf+n,sizeof(buf)-n, "- %s\n", Errormsg(err) );
329 	Write_fd_str(2,buf);
330 	VA_END;
331 	exit(errorcode);
332 }
333 
334 /*
335  *	doaccnt()
336  *	writes the accounting information to the accounting file
337  *  This has the format: user host printer pages format date
338  */
doaccnt(void)339 static void doaccnt(void)
340 {
341 	char buffer[256];
342 	FILE *f;
343 	int l, len, c;
344 
345 	plp_snprintf(buffer, sizeof(buffer), "%s\t%s\t%s\t%7d\t%s\t%s\n",
346 		login? login: "NULL",
347 		host? host: "NULL",
348 		printer? printer: "NULL",
349 		npages,
350 		format? format: "NULL",
351 		lpf_time_str());
352 	len = strlen( buffer );
353 	if( accounting_fd < 0 ){
354 		if(accntfile && (f = fopen(accntfile, "a" )) != NULL ) {
355 			accounting_fd = fileno( f );
356 		}
357 	}
358 	if( accounting_fd >= 0 ){
359 		for( c = l = 0; c >= 0 && l < len; l += c ){
360 			c = write( accounting_fd, &buffer[l], len-l );
361 		}
362 		if( c < 0 ){
363 			logerr( "bad write to accounting file" );
364 		}
365 	}
366 }
367 
getargs(int argc,char * argv[],char * envp[])368 static void getargs( int argc, char *argv[], char *envp[] )
369 {
370 	int i, c;		/* argument index */
371 	char *arg, *optargv;	/* argument */
372 	char *s, *end;
373 	const char *n;
374 
375 	if( (name = argv[0]) == 0 ) name = "FILTER";
376 	if( (n = strrchr( name, '/' )) ){
377 		++n;
378 	} else {
379 		n = name;
380 	}
381 	of_filter =  (strstr( n, "of" ) != 0);
382 	for( i = 1; i < argc && (arg = argv[i])[0] == '-'; ++i ){
383 		if( (c = arg[1]) == 0 ){
384 			FPRINTF( STDERR, "missing option flag");
385 			i = argc;
386 			break;
387 		}
388 		if( c == 'c' ){
389 			literal = 1;
390 			continue;
391 		}
392 		optargv = &arg[2];
393 		if( arg[2] == 0 ){
394 			optargv = argv[++i];
395 			if( optargv == 0 ){
396 				FPRINTF( STDERR, "missing option '%c' value", c );
397 				i = argc;
398 				break;
399 			}
400 		}
401 		switch(c){
402 			case 'C': class = optargv; break;
403 			case 'E': errorfile = optargv; break;
404 			case 'T':
405 					for( s = optargv; s && *s; s = end ){
406 						end = strchr( s, ',' );
407 						if( end ){
408 							*end++ = 0;
409 						}
410 						if( !strcasecmp( s, "crlf" ) ){
411 							crlf = 1;
412 						}
413 						if( !strcasecmp( s, "debug" ) ){
414 							++debug;
415 						}
416 					}
417 					break;
418 			case 'F': format = optargv; break;
419 			case 'J': job = optargv; break;
420 			case 'K': controlfile = optargv; break;
421 			case 'L': bnrname = optargv; break;
422 			case 'P': printer = optargv; break;
423 			case 'Q': queuename = optargv; break;
424 			case 'R': accntname = optargv; break;
425 			case 'S': comment = optargv; break;
426 			case 'Z': zopts = optargv; break;
427 			case 'h': host = optargv; break;
428 			case 'i': indent = atoi( optargv ); break;
429 			case 'l': length = atoi( optargv ); break;
430 			case 'n': login = optargv; break;
431 			case 's': statusfile = optargv; break;
432 			case 'w': width = atoi( optargv ); break;
433 			case 'x': xwidth = atoi( optargv ); break;
434 			case 'y': ylength = atoi( optargv ); break;
435 			default: break;
436 		}
437 	}
438 	if( i < argc ){
439 		accntfile = argv[i];
440 	}
441 	if( errorfile ){
442 		int fd;
443 		fd = open( errorfile, O_APPEND | O_WRONLY, 0600 );
444 		if( fd < 0 ){
445 			FPRINTF( STDERR, "cannot open error log file '%s'", errorfile );
446 		} else {
447 			FPRINTF( STDERR, "using error log file '%s'", errorfile );
448 			if( fd != 2 ){
449 				dup2(fd, 2 );
450 				close(fd);
451 			}
452 		}
453 	}
454 	if( debug ){
455 		FPRINTF(STDERR, "%s command: ", name );
456 		for( i = 0; i < argc; ++i ){
457 			FPRINTF(STDERR, "%s ", argv[i] );
458 		}
459 		FPRINTF( STDERR, "\n" );
460 	}
461 	if( debug ){
462 		FPRINTF(STDERR, "FILTER decoded options: " );
463 		FPRINTF(STDERR,"accntfile '%s'\n", accntfile? accntfile : "null" );
464 		FPRINTF(STDERR,"accntname '%s'\n", accntname? accntname : "null" );
465 		FPRINTF(STDERR,"class '%s'\n", class? class : "null" );
466 		FPRINTF(STDERR,"errorfile '%s'\n", errorfile? errorfile : "null" );
467 		FPRINTF(STDERR,"format '%s'\n", format? format : "null" );
468 		FPRINTF(STDERR,"host '%s'\n", host? host : "null" );
469 		FPRINTF(STDERR,"indent, %d\n", indent);
470 		FPRINTF(STDERR,"job '%s'\n", job? job : "null" );
471 		FPRINTF(STDERR,"length, %d\n", length);
472 		FPRINTF(STDERR,"literal, %d\n", literal);
473 		FPRINTF(STDERR,"login '%s'\n", login? login : "null" );
474 		FPRINTF(STDERR,"printer '%s'\n", printer? printer : "null" );
475 		FPRINTF(STDERR,"queuename '%s'\n", queuename? queuename : "null" );
476 		FPRINTF(STDERR,"statusfile '%s'\n", statusfile? statusfile : "null" );
477 		FPRINTF(STDERR,"width, %d\n", width);
478 		FPRINTF(STDERR,"xwidth, %d\n", xwidth);
479 		FPRINTF(STDERR,"ylength, %d\n", ylength);
480 		FPRINTF(STDERR,"zopts '%s'\n", zopts? zopts : "null" );
481 
482 		FPRINTF(STDERR, "FILTER environment: " );
483 		for( i = 0; (arg = envp[i]); ++i ){
484 			FPRINTF(STDERR,"%s\n", arg );
485 		}
486 		FPRINTF(STDERR, "RUID: %d, EUID: %d\n", (int)getuid(), (int)geteuid() );
487 	}
488 }
489 
490 /*
491  * suspend_ofilter():  suspends the output filter, waits for a signal
492  */
suspend_ofilter(void)493 static void suspend_ofilter(void)
494 {
495 	fflush(stdout);
496 	fflush(stderr);
497 	if(debug)FPRINTF(STDERR,"FILTER suspending\n");
498 	kill(getpid(), SIGSTOP);
499 	if(debug)FPRINTF(STDERR,"FILTER awake\n");
500 }
501 /******************************************
502  * prototype filter()
503  * filter will scan the input looking for the suspend string
504  * if any.
505  ******************************************/
506 
filter_pgm(const char * stop)507 static void filter_pgm(const char *stop)
508 {
509 	int c;
510 	int state, i, xout, lastc;
511 	int lines = 0;
512 	char inputline[1024];
513 	int inputcount = 0;
514 
515 	/*
516 	 * do whatever initializations are needed
517 	 */
518 	/* FPRINTF(STDERR, "filter ('%s')\n", stop ? stop : "NULL" ); */
519 	/*
520 	 * now scan the input string, looking for the stop string
521 	 */
522 	lastc = xout = state = 0;
523 	npages = 1;
524 
525 	inputcount = 0;
526 	while( (c = getchar()) != EOF ){
527 		if( inputcount < (int)sizeof(inputline) - 3 ) inputline[inputcount++] = c;
528 		if( c == '\n' ){
529 			inputline[inputcount-1] = 0;
530 			if(debug)FPRINTF(STDERR,"INPUTLINE count %d '%s'\n", inputcount, inputline );
531 			inputcount = 0;
532 			++lines;
533 			if( lines > length ){
534 				lines -= length;
535 				++npages;
536 			}
537 			if( !literal && crlf == 0 && lastc != '\r' ){
538 				putchar( '\r' );
539 			}
540 		}
541 		if( c == '\014' ){
542 			++npages;
543 			lines = 0;
544 			if( !literal && crlf == 0 ){
545 				putchar( '\r' );
546 			}
547 		}
548 		if( stop ){
549 			if( c == stop[state] ){
550 				++state;
551 				if( stop[state] == 0 ){
552 					state = 0;
553 					suspend_ofilter();
554 				}
555 			} else if( state ){
556 				for( i = 0; i < state; ++i ){
557 					putchar( stop[i] );
558 				}
559 				state = 0;
560 				putchar( c );
561 			} else {
562 				putchar( c );
563 			}
564 		} else {
565 			putchar( c );
566 		}
567 		lastc = c;
568 	}
569 	if( ferror( stdin ) ){
570 		logerr( "error on STDIN");
571 	}
572 	for( i = 0; i < state; ++i ){
573 		putchar( stop[i] );
574 	}
575 	if( lines > 0 ){
576 		++npages;
577 	}
578 	doaccnt();
579 }
580 
581 /*
582  * Time_str: return "cleaned up" ctime() string...
583  *
584  * in YY/MO/DY/hr:mn:sc
585  * Thu Aug 4 12:34:17 BST 1994 -> 12:34:17
586  */
587 
lpf_time_str(void)588 static char *lpf_time_str(void)
589 {
590     static char buffer[99];
591 	struct tm *tmptr;
592 	struct timeval tv;
593 
594 	tv.tv_usec = 0;
595 	if( gettimeofday( &tv, 0 ) == -1 ){
596 		logerr_die( "Time_str: gettimeofday failed");
597 	}
598 	tmptr = localtime( &tv.tv_sec );
599 	plp_snprintf( buffer, sizeof(buffer),
600 		"%d-%02d-%02d-%02d:%02d:%02d.%03d",
601 		tmptr->tm_year+1900, tmptr->tm_mon+1, tmptr->tm_mday,
602 		tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec,
603 		(int)(tv.tv_usec/1000) );
604 	return( buffer );
605 }
606