• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Filtering program for CUPS.
3  *
4  * Copyright © 2021-2023 by OpenPrinting
5  * Copyright © 2007-2016 by Apple Inc.
6  * Copyright © 1997-2006 by Easy Software Products, all rights reserved.
7  *
8  * Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
9  */
10 
11 /*
12  * Include necessary headers...
13  */
14 
15 #include <cups/cups-private.h>
16 #include <cups/file-private.h>
17 #include <cups/ppd-private.h>
18 #include "mime.h"
19 #include <limits.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <signal.h>
23 #include <sys/wait.h>
24 #if defined(__APPLE__)
25 #  include <libgen.h>
26 #endif /* __APPLE__ */
27 
28 
29 /*
30  * Local globals...
31  */
32 
33 static char		*DataDir = NULL;/* CUPS_DATADIR environment variable */
34 static mime_filter_t	GZIPFilter =	/* gziptoany filter */
35 {
36   NULL,					/* Source type */
37   NULL,					/* Destination type */
38   0,					/* Cost */
39   "gziptoany"				/* Filter program to run */
40 };
41 static char		*Path = NULL;	/* PATH environment variable */
42 static char		*ServerBin = NULL;
43 					/* CUPS_SERVERBIN environment variable */
44 static char		*ServerRoot = NULL;
45 					/* CUPS_SERVERROOT environment variable */
46 static char		TempFile[1024] = "";
47 					/* Temporary file */
48 
49 
50 /*
51  * Local functions...
52  */
53 
54 static void		add_printer_filter(const char *command, mime_t *mime,
55 			                   mime_type_t *printer_type,
56 		                           const char  *filter);
57 static mime_type_t	*add_printer_filters(const char *command,
58 					     mime_t *mime, const char *printer,
59 			                     const char *ppdfile,
60 					     mime_type_t **prefilter_type);
61 static void		check_cb(void *context, _cups_fc_result_t result,
62 				 const char *message);
63 static int		compare_pids(mime_filter_t *a, mime_filter_t *b);
64 static char		*escape_options(int num_options, cups_option_t *options);
65 static int		exec_filter(const char *filter, char **argv,
66 			            char **envp, int infd, int outfd);
67 static int		exec_filters(mime_type_t *srctype,
68 			             cups_array_t *filters, const char *infile,
69 		                     const char *outfile, const char *ppdfile,
70 			             const char *printer, const char *user,
71 				     const char *title, int num_options,
72 			             cups_option_t *options);
73 static void		get_job_file(const char *job);
74 static int		open_pipe(int *fds);
75 static int		read_cups_files_conf(const char *filename);
76 static void		set_string(char **s, const char *val);
77 static void		sighandler(int sig);
78 static void		usage(const char *opt) _CUPS_NORETURN;
79 
80 
81 /*
82  * 'main()' - Main entry for the test program.
83  */
84 
85 int					/* O - Exit status */
main(int argc,char * argv[])86 main(int  argc,				/* I - Number of command-line args */
87      char *argv[])			/* I - Command-line arguments */
88 {
89   int		i,			/* Looping vars */
90 		list_filters = 0;	/* Just list the filters? */
91   const char	*command,		/* Command name */
92 		*opt,			/* Current option */
93 		*printer;		/* Printer name */
94   mime_type_t	*printer_type,		/* Printer MIME type */
95 		*prefilter_type;	/* Printer prefilter MIME type */
96   char		*srctype,		/* Source type */
97 		*dsttype,		/* Destination type */
98 		super[MIME_MAX_SUPER],	/* Super-type name */
99 		type[MIME_MAX_TYPE];	/* Type name */
100   int		compression;		/* Compression of file */
101   int		cost;			/* Cost of filters */
102   mime_t	*mime;			/* MIME database */
103   char		mimedir[1024];		/* MIME directory */
104   char		*infile,		/* File to filter */
105 		*outfile;		/* File to create */
106   char		cupsfilesconf[1024];	/* cups-files.conf file */
107   const char	*server_root;		/* CUPS_SERVERROOT environment variable */
108   mime_type_t	*src,			/* Source type */
109 		*dst;			/* Destination type */
110   cups_array_t	*filters;		/* Filters for the file */
111   int		num_options;		/* Number of options */
112   cups_option_t	*options;		/* Options */
113   const char	*ppdfile;		/* PPD file */
114   const char	*title,			/* Title string */
115 		*user;			/* Username */
116   int		all_filters,		/* Use all filters */
117 		removeppd,		/* Remove PPD file */
118 		removeinfile;		/* Remove input file */
119   int		status;			/* Execution status */
120 
121 
122  /*
123   * Setup defaults...
124   */
125 
126   if ((command = strrchr(argv[0], '/')) != NULL)
127     command ++;
128   else
129     command = argv[0];
130 
131   printer      = !strcmp(command, "convert") ? "tofile" : "cupsfilter";
132   mime         = NULL;
133   srctype      = NULL;
134   compression  = 0;
135   dsttype      = "application/pdf";
136   infile       = NULL;
137   outfile      = NULL;
138   num_options  = 0;
139   options      = NULL;
140   ppdfile      = NULL;
141   title        = NULL;
142   user         = cupsUser();
143   all_filters  = 0;
144   removeppd    = 0;
145   removeinfile = 0;
146 
147   if ((server_root = getenv("CUPS_SERVERROOT")) == NULL)
148     server_root = CUPS_SERVERROOT;
149 
150   snprintf(cupsfilesconf, sizeof(cupsfilesconf), "%s/cups-files.conf", server_root);
151 
152  /*
153   * Process command-line arguments...
154   */
155 
156   _cupsSetLocale(argv);
157 
158   for (i = 1; i < argc; i ++)
159   {
160     if (argv[i][0] == '-')
161     {
162       if (!strcmp(argv[i], "--list-filters"))
163       {
164         list_filters = 1;
165       }
166       else if (!strcmp(argv[i], "--"))
167       {
168 	i ++;
169 	if (i < argc && !infile)
170 	  infile = argv[i];
171 	else
172 	  usage(NULL);
173       }
174       else
175       {
176 	for (opt = argv[i] + 1; *opt; opt ++)
177 	{
178 	  switch (*opt)
179 	  {
180 	    case 'a' : /* Specify option... */
181 		i ++;
182 		if (i < argc)
183 		{
184 		  num_options = cupsParseOptions(argv[i], num_options, &options);
185 		}
186 		else
187 		{
188 		  _cupsLangPrintf(stderr, _("%s: Error - expected name=value after \"-a\" option."), argv[0]);
189 		  usage(opt);
190 		}
191 		break;
192 
193 	    case 'c' : /* Specify cups-files.conf file location... */
194 		i ++;
195 		if (i < argc)
196 		{
197 		  if (!strcmp(command, "convert"))
198 		    num_options = cupsAddOption("copies", argv[i], num_options, &options);
199 		  else
200 		    strlcpy(cupsfilesconf, argv[i], sizeof(cupsfilesconf));
201 		}
202 		else
203 		{
204 		  _cupsLangPrintf(stderr, _("%s: Error - expected filename after \"-c\" option."), argv[0]);
205 		  usage(NULL);
206 		}
207 		break;
208 
209 	    case 'd' : /* Specify the real printer name */
210 		i ++;
211 		if (i < argc)
212 		{
213 		  printer = argv[i];
214 		}
215 		else
216 		{
217 		  _cupsLangPrintf(stderr, _("%s: Error - expected printer name after \"-d\" option."), argv[0]);
218 		  usage(NULL);
219 		}
220 		break;
221 
222 	    case 'D' : /* Delete input file after conversion */
223 		removeinfile = 1;
224 		break;
225 
226 	    case 'e' : /* Use every filter from the PPD file */
227 		all_filters = 1;
228 		break;
229 
230 	    case 'f' : /* Specify input file... */
231 		i ++;
232 		if (i < argc && !infile)
233 		{
234 		  infile = argv[i];
235 		}
236 		else
237 		{
238 		  _cupsLangPrintf(stderr, _("%s: Error - expected input file after \"-f\" option."), argv[0]);
239 		  usage(NULL);
240 		}
241 		break;
242 
243 	    case 'i' : /* Specify source MIME type... */
244 		i ++;
245 		if (i < argc)
246 		{
247 		  if (sscanf(argv[i], "%15[^/]/%255s", super, type) != 2)
248 		    usage(opt);
249 
250 		  srctype = argv[i];
251 		}
252 		else
253 		{
254 		  _cupsLangPrintf(stderr, _("%s: Error - expected source MIME type after \"-i\" option."), argv[0]);
255 		  usage(NULL);
256 		}
257 		break;
258 
259 	    case 'j' : /* Get job file or specify destination MIME type... */
260 		if (strcmp(command, "convert"))
261 		{
262 		  i ++;
263 		  if (i < argc)
264 		  {
265 		    get_job_file(argv[i]);
266 		    infile = TempFile;
267 		  }
268 		  else
269 		  {
270 		    _cupsLangPrintf(stdout, _("%s: Error - expected job-id after \"-j\" option."), argv[0]);
271 		    usage(NULL);
272 		  }
273 		  break;
274 		}
275 
276 	    case 'm' : /* Specify destination MIME type... */
277 		i ++;
278 		if (i < argc)
279 		{
280 		  if (sscanf(argv[i], "%15[^/]/%255s", super, type) != 2)
281 		    usage(opt);
282 
283 		  dsttype = argv[i];
284 		}
285 		else
286 		{
287 		  _cupsLangPrintf(stderr, _("%s: Error - expected destination MIME type after \"-m\" option."), argv[0]);
288 		  usage(NULL);
289 		}
290 		break;
291 
292 	    case 'n' : /* Specify number of copies... */
293 		i ++;
294 		if (i < argc)
295 		{
296 		  num_options = cupsAddOption("copies", argv[i], num_options, &options);
297 		}
298 		else
299 		{
300 		  _cupsLangPrintf(stderr, _("%s: Error - expected number of copies after \"-n\" option."), argv[0]);
301 		  usage(NULL);
302 		}
303 		break;
304 
305 	    case 'o' : /* Specify option(s) or output filename */
306 		i ++;
307 		if (i < argc)
308 		{
309 		  if (!strcmp(command, "convert"))
310 		  {
311 		    if (outfile)
312 		      usage(NULL);
313 		    else
314 		      outfile = argv[i];
315 		  }
316 		  else
317 		  {
318 		    num_options = cupsParseOptions(argv[i], num_options, &options);
319 		  }
320 		}
321 		else
322 		{
323 		  _cupsLangPrintf(stderr, _("%s: Error - expected name=value after \"-o\" option."), argv[0]);
324 		  usage(NULL);
325 		}
326 		break;
327 
328 	    case 'p' : /* Specify PPD file... */
329 	    case 'P' : /* Specify PPD file... */
330 		i ++;
331 		if (i < argc)
332 		{
333 		  ppdfile = argv[i];
334 		}
335 		else
336 		{
337 		  _cupsLangPrintf(stderr, _("%s: Error - expected PPD file after \"-%c\" option."), argv[0], *opt);
338 		  usage(NULL);
339 		}
340 		break;
341 
342 	    case 't' : /* Specify title... */
343 	    case 'J' : /* Specify title... */
344 		i ++;
345 		if (i < argc)
346 		{
347 		  title = argv[i];
348 		}
349 		else
350 		{
351 		  _cupsLangPrintf(stderr, _("%s: Error - expected title after \"-%c\" option."), argv[0], *opt);
352 		  usage(NULL);
353 		}
354 		break;
355 
356 	    case 'u' : /* Delete PPD file after conversion */
357 		removeppd = 1;
358 		break;
359 
360 	    case 'U' : /* Specify username... */
361 		i ++;
362 		if (i < argc)
363 		{
364 		  user = argv[i];
365 		}
366 		else
367 		{
368 		  _cupsLangPrintf(stderr, _("%s: Error - expected \"username\" after \"-U\" option."), argv[0]);
369 		  usage(NULL);
370 		}
371 		break;
372 
373 	    default : /* Something we don't understand... */
374 		_cupsLangPrintf(stderr, _("%s: Error - unknown option \"-%c\"."), argv[0], *opt);
375 		usage(NULL);
376 	  }
377 	}
378       }
379     }
380     else if (!infile)
381     {
382       if (strcmp(command, "convert"))
383 	infile = argv[i];
384       else
385 	usage(NULL);
386     }
387     else
388     {
389       _cupsLangPuts(stderr, _("cupsfilter: Only one filename can be specified."));
390       usage(NULL);
391     }
392   }
393 
394   if (!infile && !srctype)
395     usage(NULL);
396 
397   if (!title)
398   {
399     if (!infile)
400       title = "(stdin)";
401     else if ((title = strrchr(infile, '/')) != NULL)
402       title ++;
403     else
404       title = infile;
405   }
406 
407  /*
408   * Load the cups-files.conf file and create the MIME database...
409   */
410 
411   if (read_cups_files_conf(cupsfilesconf))
412     return (1);
413 
414   snprintf(mimedir, sizeof(mimedir), "%s/mime", DataDir);
415 
416   mime = mimeLoadTypes(NULL, mimedir);
417   mime = mimeLoadTypes(mime, ServerRoot);
418   mime = mimeLoadFilters(mime, mimedir, Path);
419   mime = mimeLoadFilters(mime, ServerRoot, Path);
420 
421   if (!mime)
422   {
423     _cupsLangPrintf(stderr, _("%s: Unable to read MIME database from \"%s\" or \"%s\"."), command, mimedir, ServerRoot);
424     return (1);
425   }
426 
427   prefilter_type = NULL;
428 
429   if (all_filters)
430     printer_type = add_printer_filters(command, mime, printer, ppdfile,
431 				       &prefilter_type);
432   else
433     printer_type   = mimeType(mime, "application", "vnd.cups-postscript");
434 
435  /*
436   * Get the source and destination types...
437   */
438 
439   if (srctype)
440   {
441    /* sscanf return value already checked above */
442     sscanf(srctype, "%15[^/]/%255s", super, type);
443     if ((src = mimeType(mime, super, type)) == NULL)
444     {
445       _cupsLangPrintf(stderr,
446 		      _("%s: Unknown source MIME type %s/%s."),
447 		      command, super, type);
448       return (1);
449     }
450   }
451   else if ((src = mimeFileType(mime, infile, infile, &compression)) == NULL)
452   {
453     _cupsLangPrintf(stderr,
454                     _("%s: Unable to determine MIME type of \"%s\"."),
455 		    command, infile);
456     return (1);
457   }
458 
459  /* sscanf return value already checked above */
460   sscanf(dsttype, "%15[^/]/%255s", super, type);
461   if (!_cups_strcasecmp(super, "printer"))
462     dst = printer_type;
463   else if ((dst = mimeType(mime, super, type)) == NULL)
464   {
465     _cupsLangPrintf(stderr,
466                     _("%s: Unknown destination MIME type %s/%s."),
467 		    command, super, type);
468     return (1);
469   }
470 
471  /*
472   * Figure out how to filter the file...
473   */
474 
475   if (src == dst)
476   {
477    /*
478     * Special case - no filtering needed...
479     */
480 
481     filters = cupsArrayNew(NULL, NULL);
482     cupsArrayAdd(filters, &GZIPFilter);
483     GZIPFilter.src = src;
484     GZIPFilter.dst = dst;
485   }
486   else if ((filters = mimeFilter(mime, src, dst, &cost)) == NULL)
487   {
488     _cupsLangPrintf(stderr,
489                     _("%s: No filter to convert from %s/%s to %s/%s."),
490 		    command, src->super, src->type, dst->super, dst->type);
491     return (1);
492   }
493   else if (compression)
494     cupsArrayInsert(filters, &GZIPFilter);
495 
496   if (prefilter_type)
497   {
498    /*
499     * Add pre-filters...
500     */
501 
502     mime_filter_t	*filter,	/* Current filter */
503 			*prefilter;	/* Current pre-filter */
504     cups_array_t	*prefilters = cupsArrayNew(NULL, NULL);
505 					/* New filters array */
506 
507 
508     for (filter = (mime_filter_t *)cupsArrayFirst(filters);
509 	 filter;
510 	 filter = (mime_filter_t *)cupsArrayNext(filters))
511     {
512       if ((prefilter = mimeFilterLookup(mime, filter->src,
513                                         prefilter_type)) != NULL)
514 	cupsArrayAdd(prefilters, prefilter);
515 
516       cupsArrayAdd(prefilters, filter);
517     }
518 
519     cupsArrayDelete(filters);
520     filters = prefilters;
521   }
522 
523   if (list_filters)
524   {
525    /*
526     * List filters...
527     */
528 
529     mime_filter_t	*filter;	/* Current filter */
530 
531     for (filter = (mime_filter_t *)cupsArrayFirst(filters);
532 	 filter;
533 	 filter = (mime_filter_t *)cupsArrayNext(filters))
534       if (strcmp(filter->filter, "-"))
535         _cupsLangPuts(stdout, filter->filter);
536 
537     status = 0;
538   }
539   else
540   {
541    /*
542     * Run filters...
543     */
544 
545     status = exec_filters(src, filters, infile, outfile, ppdfile, printer, user,
546 			  title, num_options, options);
547   }
548 
549  /*
550   * Remove files as needed, then exit...
551   */
552 
553   if (TempFile[0])
554     unlink(TempFile);
555 
556   if (removeppd && ppdfile)
557     unlink(ppdfile);
558 
559   if (removeinfile && infile)
560     unlink(infile);
561 
562   return (status);
563 }
564 
565 
566 /*
567  * 'add_printer_filter()' - Add a single filters from a PPD file.
568  */
569 
570 static void
add_printer_filter(const char * command,mime_t * mime,mime_type_t * filtertype,const char * filter)571 add_printer_filter(
572     const char  *command,		/* I - Command name */
573     mime_t      *mime,			/* I - MIME database */
574     mime_type_t *filtertype,		/* I - Printer or prefilter MIME type */
575     const char  *filter)		/* I - Filter to add */
576 {
577   char		super[MIME_MAX_SUPER],	/* Super-type for filter */
578 		type[MIME_MAX_TYPE],	/* Type for filter */
579 		dsuper[MIME_MAX_SUPER],	/* Destination super-type for filter */
580 		dtype[MIME_MAX_TYPE],	/* Destination type for filter */
581 		dest[MIME_MAX_SUPER + MIME_MAX_TYPE + 2],
582 					/* Destination super/type */
583 		program[1024];		/* Program/filter name */
584   int		cost;			/* Cost of filter */
585   size_t	maxsize = 0;		/* Maximum supported file size */
586   mime_type_t	*temptype,		/* MIME type looping var */
587 		*desttype;		/* Destination MIME type */
588   mime_filter_t	*filterptr;		/* MIME filter */
589 
590 
591  /*
592   * Parse the filter string; it should be in one of the following formats:
593   *
594   *     source/type cost program
595   *     source/type cost maxsize(nnnn) program
596   *     source/type dest/type cost program
597   *     source/type dest/type cost maxsize(nnnn) program
598   */
599 
600   if (sscanf(filter, "%15[^/]/%255s%*[ \t]%15[^/]/%255s%d%*[ \t]%1023[^\n]",
601              super, type, dsuper, dtype, &cost, program) == 6)
602   {
603     snprintf(dest, sizeof(dest), "%s/%s/%s", filtertype->type, dsuper, dtype);
604 
605     if ((desttype = mimeType(mime, "printer", dest)) == NULL)
606       desttype = mimeAddType(mime, "printer", dest);
607   }
608   else
609   {
610     if (sscanf(filter, "%15[^/]/%255s%d%*[ \t]%1023[^\n]", super, type, &cost,
611                program) == 4)
612     {
613       desttype = filtertype;
614     }
615     else
616     {
617       _cupsLangPrintf(stderr, _("%s: Invalid filter string \"%s\"."), command,
618 		      filter);
619       return;
620     }
621   }
622 
623   if (!strncmp(program, "maxsize(", 8))
624   {
625     char	*ptr;			/* Pointer into maxsize(nnnn) program */
626 
627     maxsize = (size_t)strtoll(program + 8, &ptr, 10);
628 
629     if (*ptr != ')')
630     {
631       printf("testmime: Invalid filter string \"%s\".\n", filter);
632       return;
633     }
634 
635     ptr ++;
636     while (_cups_isspace(*ptr))
637       ptr ++;
638 
639     _cups_strcpy(program, ptr);
640   }
641 
642  /*
643   * See if the filter program exists; if not, stop the printer and flag
644   * the error!
645   */
646 
647   if (strcmp(program, "-"))
648   {
649     char filename[1024];		/* Full path to program */
650 
651     if (program[0] == '/')
652       strlcpy(filename, program, sizeof(filename));
653     else
654       snprintf(filename, sizeof(filename), "%s/filter/%s", ServerBin, program);
655 
656     if (_cupsFileCheck(filename, _CUPS_FILE_CHECK_PROGRAM, !geteuid(), check_cb,
657                        (void *)command))
658       return;
659   }
660 
661  /*
662   * Add the filter to the MIME database, supporting wildcards as needed...
663   */
664 
665   for (temptype = mimeFirstType(mime);
666        temptype;
667        temptype = mimeNextType(mime))
668     if (((super[0] == '*' && _cups_strcasecmp(temptype->super, "printer")) ||
669          !_cups_strcasecmp(temptype->super, super)) &&
670         (type[0] == '*' || !_cups_strcasecmp(temptype->type, type)))
671     {
672       if (desttype != filtertype)
673       {
674         filterptr = mimeAddFilter(mime, temptype, desttype, cost, program);
675 
676         if (!mimeFilterLookup(mime, desttype, filtertype))
677           mimeAddFilter(mime, desttype, filtertype, 0, "-");
678       }
679       else
680         filterptr = mimeAddFilter(mime, temptype, filtertype, cost, program);
681 
682       if (filterptr)
683 	filterptr->maxsize = maxsize;
684     }
685 }
686 
687 
688 /*
689  * 'add_printer_filters()' - Add filters from a PPD file.
690  */
691 
692 static mime_type_t *			/* O - Printer type or NULL on error */
add_printer_filters(const char * command,mime_t * mime,const char * printer,const char * ppdfile,mime_type_t ** prefilter_type)693 add_printer_filters(
694     const char  *command,		/* I - Command name */
695     mime_t      *mime,			/* I - MIME database */
696     const char  *printer,		/* I - Printer name */
697     const char  *ppdfile,		/* I - PPD file */
698     mime_type_t **prefilter_type)	/* O - Prefilter type */
699 {
700   ppd_file_t	*ppd;			/* PPD file data */
701   _ppd_cache_t	*pc;			/* Cache data for PPD */
702   const char	*value;			/* Filter definition value */
703   mime_type_t	*printer_type;		/* Printer filter type */
704 
705 
706   if ((ppd = _ppdOpenFile(ppdfile, _PPD_LOCALIZATION_NONE)) == NULL)
707   {
708     ppd_status_t	status;		/* PPD load status */
709     int			linenum;	/* Line number */
710 
711     status = ppdLastError(&linenum);
712     _cupsLangPrintf(stderr, _("%s: Unable to open PPD file: %s on line %d."),
713                     command, ppdErrorString(status), linenum);
714     return (NULL);
715   }
716 
717   pc = _ppdCacheCreateWithPPD(ppd);
718   if (!pc)
719     return (NULL);
720 
721   printer_type    = mimeAddType(mime, "printer", printer);
722   *prefilter_type = NULL;
723 
724   if (pc->filters)
725   {
726     for (value = (const char *)cupsArrayFirst(pc->filters);
727          value;
728          value = (const char *)cupsArrayNext(pc->filters))
729       add_printer_filter(command, mime, printer_type, value);
730   }
731   else
732   {
733     add_printer_filter(command, mime, printer_type,
734                        "application/vnd.cups-raw 0 -");
735     add_printer_filter(command, mime, printer_type,
736                        "application/vnd.cups-postscript 0 -");
737   }
738 
739   if (pc->prefilters)
740   {
741     *prefilter_type = mimeAddType(mime, "prefilter", printer);
742 
743     for (value = (const char *)cupsArrayFirst(pc->prefilters);
744          value;
745          value = (const char *)cupsArrayNext(pc->prefilters))
746       add_printer_filter(command, mime, *prefilter_type, value);
747   }
748 
749   return (printer_type);
750 }
751 
752 
753 /*
754  * 'check_cb()' - Callback function for _cupsFileCheck.
755  */
756 
757 static void
check_cb(void * context,_cups_fc_result_t result,const char * message)758 check_cb(void              *context,	/* I - Context (command name) */
759          _cups_fc_result_t result,	/* I - Result of check */
760 	 const char        *message)	/* I - Localized message */
761 {
762   (void)result;
763 
764   _cupsLangPrintf(stderr, _("%s: %s"), (char *)context, message);
765 }
766 
767 
768 /*
769  * 'compare_pids()' - Compare two filter PIDs...
770  */
771 
772 static int				/* O - Result of comparison */
compare_pids(mime_filter_t * a,mime_filter_t * b)773 compare_pids(mime_filter_t *a,		/* I - First filter */
774              mime_filter_t *b)		/* I - Second filter */
775 {
776  /*
777   * Because we're particularly lazy, we store the process ID in the "cost"
778   * variable...
779   */
780 
781   return (a->cost - b->cost);
782 }
783 
784 
785 /*
786  * 'escape_options()' - Convert an options array to a string.
787  */
788 
789 static char *				/* O - Option string */
escape_options(int num_options,cups_option_t * options)790 escape_options(
791     int           num_options,		/* I - Number of options */
792     cups_option_t *options)		/* I - Options */
793 {
794   int		i;			/* Looping var */
795   cups_option_t	*option;		/* Current option */
796   size_t	bytes;			/* Number of bytes needed */
797   char		*s,			/* Option string */
798 		*sptr,			/* Pointer into string */
799 		*vptr;			/* Pointer into value */
800 
801 
802  /*
803   * Figure out the worst-case number of bytes we need for the option string.
804   */
805 
806   for (i = num_options, option = options, bytes = 1; i > 0; i --, option ++)
807     bytes += 2 * (strlen(option->name) + strlen(option->value)) + 2;
808 
809   if ((s = malloc(bytes)) == NULL)
810     return (NULL);
811 
812  /*
813   * Copy the options to the string...
814   */
815 
816   for (i = num_options, option = options, sptr = s; i > 0; i --, option ++)
817   {
818     if (!strcmp(option->name, "copies"))
819       continue;
820 
821     if (sptr > s)
822       *sptr++ = ' ';
823 
824     strlcpy(sptr, option->name, bytes - (size_t)(sptr - s));
825     sptr += strlen(sptr);
826     *sptr++ = '=';
827 
828     for (vptr = option->value; *vptr;)
829     {
830       if (strchr("\\ \t\n", *vptr))
831         *sptr++ = '\\';
832 
833       *sptr++ = *vptr++;
834     }
835   }
836 
837   *sptr = '\0';
838 
839   return (s);
840 }
841 
842 
843 /*
844  * 'exec_filter()' - Execute a single filter.
845  */
846 
847 static int				/* O - Process ID or -1 on error */
exec_filter(const char * filter,char ** argv,char ** envp,int infd,int outfd)848 exec_filter(const char *filter,		/* I - Filter to execute */
849             char       **argv,		/* I - Argument list */
850 	    char       **envp,		/* I - Environment list */
851 	    int        infd,		/* I - Stdin file descriptor */
852 	    int        outfd)		/* I - Stdout file descriptor */
853 {
854   int		pid,			/* Process ID */
855 		fd;			/* Temporary file descriptor */
856 #if defined(__APPLE__)
857   char		processPath[1024],	/* CFProcessPath environment variable */
858 		linkpath[1024];		/* Link path for symlinks... */
859   int		linkbytes;		/* Bytes for link path */
860 
861 
862  /*
863   * Add special voodoo magic for macOS - this allows macOS
864   * programs to access their bundle resources properly...
865   */
866 
867   if ((linkbytes = readlink(filter, linkpath, sizeof(linkpath) - 1)) > 0)
868   {
869    /*
870     * Yes, this is a symlink to the actual program, nul-terminate and
871     * use it...
872     */
873 
874     linkpath[linkbytes] = '\0';
875 
876     if (linkpath[0] == '/')
877       snprintf(processPath, sizeof(processPath), "CFProcessPath=%s",
878 	       linkpath);
879     else
880       snprintf(processPath, sizeof(processPath), "CFProcessPath=%s/%s",
881 	       dirname((char *)filter), linkpath);
882   }
883   else
884     snprintf(processPath, sizeof(processPath), "CFProcessPath=%s", filter);
885 
886   envp[0] = processPath;		/* Replace <CFProcessPath> string */
887 #endif	/* __APPLE__ */
888 
889   if ((pid = fork()) == 0)
890   {
891    /*
892     * Child process goes here...
893     *
894     * Update stdin/stdout/stderr as needed...
895     */
896 
897     if (infd != 0)
898     {
899       if (infd < 0)
900         infd = open("/dev/null", O_RDONLY);
901 
902       if (infd > 0)
903       {
904         dup2(infd, 0);
905 	close(infd);
906       }
907     }
908 
909     if (outfd != 1)
910     {
911       if (outfd < 0)
912         outfd = open("/dev/null", O_WRONLY);
913 
914       if (outfd > 1)
915       {
916 	dup2(outfd, 1);
917 	close(outfd);
918       }
919     }
920 
921     if ((fd = open("/dev/null", O_RDWR)) > 3)
922     {
923       dup2(fd, 3);
924       close(fd);
925     }
926     fcntl(3, F_SETFL, O_NDELAY);
927 
928     if ((fd = open("/dev/null", O_RDWR)) > 4)
929     {
930       dup2(fd, 4);
931       close(fd);
932     }
933     fcntl(4, F_SETFL, O_NDELAY);
934 
935    /*
936     * Execute command...
937     */
938 
939     execve(filter, argv, envp);
940 
941     perror(filter);
942 
943     exit(errno);
944   }
945 
946   return (pid);
947 }
948 
949 
950 /*
951  * 'exec_filters()' - Execute filters for the given file and options.
952  */
953 
954 static int				/* O - 0 on success, 1 on error */
exec_filters(mime_type_t * srctype,cups_array_t * filters,const char * infile,const char * outfile,const char * ppdfile,const char * printer,const char * user,const char * title,int num_options,cups_option_t * options)955 exec_filters(mime_type_t   *srctype,	/* I - Source type */
956              cups_array_t  *filters,	/* I - Array of filters to run */
957              const char    *infile,	/* I - File to filter */
958 	     const char    *outfile,	/* I - File to create */
959 	     const char    *ppdfile,	/* I - PPD file, if any */
960 	     const char    *printer,	/* I - Printer name */
961 	     const char    *user,	/* I - Username */
962 	     const char    *title,	/* I - Job title */
963              int           num_options,	/* I - Number of filter options */
964 	     cups_option_t *options)	/* I - Filter options */
965 {
966   int		i;			/* Looping var */
967   const char	*argv[8],		/* Command-line arguments */
968 		*envp[21],		/* Environment variables */
969 		*temp;			/* Temporary string */
970   char		*optstr,		/* Filter options */
971 		content_type[1024],	/* CONTENT_TYPE */
972 		cups_datadir[1024],	/* CUPS_DATADIR */
973 		cups_fontpath[1024],	/* CUPS_FONTPATH */
974 		cups_serverbin[1024],	/* CUPS_SERVERBIN */
975 		cups_serverroot[1024],	/* CUPS_SERVERROOT */
976 		final_content_type[1024] = "",
977 					/* FINAL_CONTENT_TYPE */
978 		lang[1024],		/* LANG */
979 		path[1024],		/* PATH */
980 		ppd[1024],		/* PPD */
981 		printer_info[255],	/* PRINTER_INFO env variable */
982 		printer_location[255],	/* PRINTER_LOCATION env variable */
983 		printer_name[255],	/* PRINTER env variable */
984 		userenv[1024],		/* USER */
985 #if CUPS_SNAP
986 		fontconfig_file[1024],	/* FONTCONFIG_FILE */
987 		fontconfig_path[1024],	/* FONTCONFIG_PATH */
988 		fontconfig_sysroot[1024],
989 					/* FONTCONFIG_SYSROOT */
990 		ld_library_path[2048],	/* LD_LIBRARY_PATH */
991 #endif /* CUPS_SNAP */
992 		program[1024];		/* Program to run */
993   mime_filter_t	*filter,		/* Current filter */
994 		*next;			/* Next filter */
995   int		current,		/* Current filter */
996 		filterfds[2][2],	/* Pipes for filters */
997 		pid,			/* Process ID of filter */
998 		status,			/* Exit status */
999 		retval;			/* Return value */
1000   cups_array_t	*pids;			/* Executed filters array */
1001   mime_filter_t	key;			/* Search key for filters */
1002   cups_lang_t	*language;		/* Current language */
1003   cups_dest_t	*dest;			/* Destination information */
1004 
1005 
1006  /*
1007   * Figure out the final content type...
1008   */
1009 
1010   for (filter = (mime_filter_t *)cupsArrayLast(filters);
1011        filter && filter->dst;
1012        filter = (mime_filter_t *)cupsArrayPrev(filters))
1013     if (strcmp(filter->dst->super, "printer"))
1014       break;
1015 
1016   if (filter && filter->dst)
1017   {
1018     const char *ptr;			/* Pointer in type name */
1019 
1020     if ((ptr = strchr(filter->dst->type, '/')) != NULL)
1021       snprintf(final_content_type, sizeof(final_content_type),
1022 	       "FINAL_CONTENT_TYPE=%s", ptr + 1);
1023     else
1024       snprintf(final_content_type, sizeof(final_content_type),
1025 	       "FINAL_CONTENT_TYPE=%s/%s", filter->dst->super,
1026 	       filter->dst->type);
1027   }
1028 
1029  /*
1030   * Remove NULL ("-") filters...
1031   */
1032 
1033   for (filter = (mime_filter_t *)cupsArrayFirst(filters);
1034        filter;
1035        filter = (mime_filter_t *)cupsArrayNext(filters))
1036     if (!strcmp(filter->filter, "-"))
1037       cupsArrayRemove(filters, filter);
1038 
1039  /*
1040   * Setup the filter environment and command-line...
1041   */
1042 
1043   optstr = escape_options(num_options, options);
1044 
1045   snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s/%s",
1046            srctype->super, srctype->type);
1047   snprintf(cups_datadir, sizeof(cups_datadir), "CUPS_DATADIR=%s", DataDir);
1048   snprintf(cups_serverbin, sizeof(cups_serverbin), "CUPS_SERVERBIN=%s",
1049            ServerBin);
1050   snprintf(cups_serverroot, sizeof(cups_serverroot), "CUPS_SERVERROOT=%s",
1051            ServerRoot);
1052   language = cupsLangDefault();
1053   snprintf(lang, sizeof(lang), "LANG=%s.UTF8", language->language);
1054   snprintf(path, sizeof(path), "PATH=%s", Path);
1055   if (ppdfile)
1056     snprintf(ppd, sizeof(ppd), "PPD=%s", ppdfile);
1057   else if ((temp = getenv("PPD")) != NULL)
1058     snprintf(ppd, sizeof(ppd), "PPD=%s", temp);
1059   else
1060 #ifdef __APPLE__
1061   if (!access("/System/Library/Frameworks/ApplicationServices.framework/"
1062 	      "Versions/A/Frameworks/PrintCore.framework/Versions/A/"
1063 	      "Resources/English.lproj/Generic.ppd", 0))
1064     strlcpy(ppd, "PPD=/System/Library/Frameworks/ApplicationServices.framework/"
1065                  "Versions/A/Frameworks/PrintCore.framework/Versions/A/"
1066 		 "Resources/English.lproj/Generic.ppd", sizeof(ppd));
1067   else
1068     strlcpy(ppd, "PPD=/System/Library/Frameworks/ApplicationServices.framework/"
1069                  "Versions/A/Frameworks/PrintCore.framework/Versions/A/"
1070 		 "Resources/Generic.ppd", sizeof(ppd));
1071 #else
1072   snprintf(ppd, sizeof(ppd), "PPD=%s/model/laserjet.ppd", DataDir);
1073 #endif /* __APPLE__ */
1074 
1075   snprintf(userenv, sizeof(userenv), "USER=%s", user);
1076 
1077   if (printer &&
1078       (dest = cupsGetNamedDest(CUPS_HTTP_DEFAULT, printer, NULL)) != NULL)
1079   {
1080     if ((temp = cupsGetOption("printer-info", dest->num_options,
1081                               dest->options)) != NULL)
1082       snprintf(printer_info, sizeof(printer_info), "PRINTER_INFO=%s", temp);
1083     else
1084       snprintf(printer_info, sizeof(printer_info), "PRINTER_INFO=%s", printer);
1085 
1086     if ((temp = cupsGetOption("printer-location", dest->num_options,
1087                               dest->options)) != NULL)
1088       snprintf(printer_location, sizeof(printer_location),
1089                "PRINTER_LOCATION=%s", temp);
1090     else
1091       strlcpy(printer_location, "PRINTER_LOCATION=Unknown",
1092               sizeof(printer_location));
1093   }
1094   else
1095   {
1096     snprintf(printer_info, sizeof(printer_info), "PRINTER_INFO=%s",
1097              printer ? printer : "Unknown");
1098     strlcpy(printer_location, "PRINTER_LOCATION=Unknown",
1099             sizeof(printer_location));
1100   }
1101 
1102   snprintf(printer_name, sizeof(printer_name), "PRINTER=%s",
1103 	   printer ? printer : "Unknown");
1104 
1105   argv[0] = (char *)printer;
1106   argv[1] = "1";
1107   argv[2] = user;
1108   argv[3] = title;
1109   argv[4] = cupsGetOption("copies", num_options, options);
1110   argv[5] = optstr;
1111   argv[6] = infile;
1112   argv[7] = NULL;
1113 
1114   if (!argv[4])
1115     argv[4] = "1";
1116 
1117   for (i = 0; argv[i]; i ++)
1118     fprintf(stderr, "DEBUG: argv[%d]=\"%s\"\n", i, argv[i]);
1119 
1120   i = 0;
1121 #ifdef __APPLE__
1122   envp[i ++] = "<CFProcessPath>";
1123 #endif /* __APPLE__ */
1124   envp[i ++] = content_type;
1125   envp[i ++] = cups_datadir;
1126   envp[i ++] = cups_fontpath;
1127   envp[i ++] = cups_serverbin;
1128   envp[i ++] = cups_serverroot;
1129   envp[i ++] = lang;
1130   envp[i ++] = path;
1131   envp[i ++] = ppd;
1132   envp[i ++] = printer_info;
1133   envp[i ++] = printer_location;
1134   envp[i ++] = printer_name;
1135   envp[i ++] = userenv;
1136   envp[i ++] = "CHARSET=utf-8";
1137   if (final_content_type[0])
1138     envp[i ++] = final_content_type;
1139 
1140 #if CUPS_SNAP
1141   if ((temp = getenv("FONTCONFIG_FILE")) != NULL)
1142   {
1143     snprintf(fontconfig_file, sizeof(fontconfig_file), "FONTCONFIG_FILE=%s", temp);
1144     envp[i ++] = fontconfig_file;
1145   }
1146   if ((temp = getenv("FONTCONFIG_PATH")) != NULL)
1147   {
1148     snprintf(fontconfig_path, sizeof(fontconfig_path), "FONTCONFIG_PATH=%s", temp);
1149     envp[i ++] = fontconfig_path;
1150   }
1151   if ((temp = getenv("FONTCONFIG_SYSROOT")) != NULL)
1152   {
1153     snprintf(fontconfig_sysroot, sizeof(fontconfig_sysroot), "FONTCONFIG_SYSROOT=%s", temp);
1154     envp[i ++] = fontconfig_sysroot;
1155   }
1156   if ((temp = getenv("LD_LIBRARY_PATH")) != NULL)
1157   {
1158     snprintf(ld_library_path, sizeof(ld_library_path), "LD_LIBRARY_PATH=%s", temp);
1159     envp[i ++] = ld_library_path;
1160   }
1161 #endif /* CUPS_SNAP */
1162 
1163   envp[i] = NULL;
1164 
1165   for (i = 0; envp[i]; i ++)
1166     fprintf(stderr, "DEBUG: envp[%d]=\"%s\"\n", i, envp[i]);
1167 
1168  /*
1169   * Execute all of the filters...
1170   */
1171 
1172   pids            =  cupsArrayNew((cups_array_func_t)compare_pids, NULL);
1173   current         =  1;
1174   filterfds[0][0] =  infile ? -1 : 0;
1175   filterfds[0][1] = -1;
1176   filterfds[1][0] = -1;
1177   filterfds[1][1] = -1;
1178 
1179   for (filter = (mime_filter_t *)cupsArrayFirst(filters);
1180        filter;
1181        filter = next, current ^= 1)
1182   {
1183     next = (mime_filter_t *)cupsArrayNext(filters);
1184 
1185     if (filter->filter[0] == '/')
1186       strlcpy(program, filter->filter, sizeof(program));
1187     else
1188       snprintf(program, sizeof(program), "%s/filter/%s", ServerBin,
1189 	       filter->filter);
1190 
1191     if (filterfds[current][1] > 1)
1192     {
1193       close(filterfds[current][0]);
1194       close(filterfds[current][1]);
1195 
1196       filterfds[current][0] = -1;
1197       filterfds[current][1] = -1;
1198     }
1199 
1200     if (next)
1201       open_pipe(filterfds[current]);
1202     else if (outfile)
1203     {
1204       filterfds[current][1] = open(outfile, O_CREAT | O_TRUNC | O_WRONLY, 0666);
1205 
1206       if (filterfds[current][1] < 0)
1207         fprintf(stderr, "ERROR: Unable to create \"%s\" - %s\n", outfile,
1208 	        strerror(errno));
1209     }
1210     else
1211       filterfds[current][1] = 1;
1212 
1213     pid = exec_filter(program, (char **)argv, (char **)envp,
1214                       filterfds[current ^ 1][0], filterfds[current][1]);
1215 
1216     if (pid > 0)
1217     {
1218       fprintf(stderr, "INFO: %s (PID %d) started.\n", filter->filter, pid);
1219 
1220       filter->cost = pid;
1221       cupsArrayAdd(pids, filter);
1222     }
1223     else
1224       break;
1225 
1226     argv[6] = NULL;
1227   }
1228 
1229  /*
1230   * Close remaining pipes...
1231   */
1232 
1233   if (filterfds[0][1] > 1)
1234   {
1235     close(filterfds[0][0]);
1236     close(filterfds[0][1]);
1237   }
1238 
1239   if (filterfds[1][1] > 1)
1240   {
1241     close(filterfds[1][0]);
1242     close(filterfds[1][1]);
1243   }
1244 
1245  /*
1246   * Wait for the children to exit...
1247   */
1248 
1249   retval = 0;
1250 
1251   while (cupsArrayCount(pids) > 0)
1252   {
1253     if ((pid = wait(&status)) < 0)
1254       continue;
1255 
1256     key.cost = pid;
1257     if ((filter = (mime_filter_t *)cupsArrayFind(pids, &key)) != NULL)
1258     {
1259       cupsArrayRemove(pids, filter);
1260 
1261       if (status)
1262       {
1263 	if (WIFEXITED(status))
1264 	  fprintf(stderr, "ERROR: %s (PID %d) stopped with status %d\n",
1265 		  filter->filter, pid, WEXITSTATUS(status));
1266 	else
1267 	  fprintf(stderr, "ERROR: %s (PID %d) crashed on signal %d\n",
1268 		  filter->filter, pid, WTERMSIG(status));
1269 
1270         retval = 1;
1271       }
1272       else
1273         fprintf(stderr, "INFO: %s (PID %d) exited with no errors.\n",
1274 	        filter->filter, pid);
1275     }
1276   }
1277 
1278   cupsArrayDelete(pids);
1279 
1280   return (retval);
1281 }
1282 
1283 
1284 /*
1285  * 'get_job_file()' - Get the specified job file.
1286  */
1287 
1288 static void
get_job_file(const char * job)1289 get_job_file(const char *job)		/* I - Job ID */
1290 {
1291   long		jobid,			/* Job ID */
1292 		docnum;			/* Document number */
1293   const char	*jobptr;		/* Pointer into job ID string */
1294   char		uri[1024];		/* job-uri */
1295   http_t	*http;			/* Connection to server */
1296   ipp_t		*request;		/* Request data */
1297   int		tempfd;			/* Temporary file */
1298 
1299 
1300  /*
1301   * Get the job ID and document number, if any...
1302   */
1303 
1304   if ((jobptr = strrchr(job, '-')) != NULL)
1305     jobptr ++;
1306   else
1307     jobptr = job;
1308 
1309   jobid = strtol(jobptr, (char **)&jobptr, 10);
1310 
1311   if (*jobptr == ',')
1312     docnum = strtol(jobptr + 1, NULL, 10);
1313   else
1314     docnum = 1;
1315 
1316   if (jobid < 1 || jobid > INT_MAX)
1317   {
1318     _cupsLangPrintf(stderr, _("cupsfilter: Invalid job ID %d."), (int)jobid);
1319     exit(1);
1320   }
1321 
1322   if (docnum < 1 || docnum > INT_MAX)
1323   {
1324     _cupsLangPrintf(stderr, _("cupsfilter: Invalid document number %d."),
1325                     (int)docnum);
1326     exit(1);
1327   }
1328 
1329  /*
1330   * Ask the server for the document file...
1331   */
1332 
1333   if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
1334                                  cupsEncryption())) == NULL)
1335   {
1336     _cupsLangPrintf(stderr, _("%s: Unable to connect to server."),
1337                     "cupsfilter");
1338     exit(1);
1339   }
1340 
1341   request = ippNewRequest(CUPS_GET_DOCUMENT);
1342 
1343   snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", (int)jobid);
1344 
1345   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
1346   ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "document-number",
1347                 (int)docnum);
1348 
1349   if ((tempfd = cupsTempFd(TempFile, sizeof(TempFile))) == -1)
1350   {
1351     _cupsLangPrintError("ERROR", _("Unable to create temporary file"));
1352     httpClose(http);
1353     exit(1);
1354   }
1355 
1356   signal(SIGTERM, sighandler);
1357 
1358   ippDelete(cupsDoIORequest(http, request, "/", -1, tempfd));
1359 
1360   close(tempfd);
1361 
1362   httpClose(http);
1363 
1364   if (cupsLastError() != IPP_OK)
1365   {
1366     _cupsLangPrintf(stderr, _("cupsfilter: Unable to get job file - %s"),
1367                     cupsLastErrorString());
1368     unlink(TempFile);
1369     exit(1);
1370   }
1371 }
1372 
1373 
1374 /*
1375  * 'open_pipe()' - Create a pipe which is closed on exec.
1376  */
1377 
1378 static int				/* O - 0 on success, -1 on error */
open_pipe(int * fds)1379 open_pipe(int *fds)			/* O - Pipe file descriptors (2) */
1380 {
1381  /*
1382   * Create the pipe...
1383   */
1384 
1385   if (pipe(fds))
1386   {
1387     fds[0] = -1;
1388     fds[1] = -1;
1389 
1390     return (-1);
1391   }
1392 
1393  /*
1394   * Set the "close on exec" flag on each end of the pipe...
1395   */
1396 
1397   if (fcntl(fds[0], F_SETFD, fcntl(fds[0], F_GETFD) | FD_CLOEXEC))
1398   {
1399     close(fds[0]);
1400     close(fds[1]);
1401 
1402     fds[0] = -1;
1403     fds[1] = -1;
1404 
1405     return (-1);
1406   }
1407 
1408   if (fcntl(fds[1], F_SETFD, fcntl(fds[1], F_GETFD) | FD_CLOEXEC))
1409   {
1410     close(fds[0]);
1411     close(fds[1]);
1412 
1413     fds[0] = -1;
1414     fds[1] = -1;
1415 
1416     return (-1);
1417   }
1418 
1419  /*
1420   * Return 0 indicating success...
1421   */
1422 
1423   return (0);
1424 }
1425 
1426 
1427 /*
1428  * 'read_cups_files_conf()' - Read the cups-files.conf file to get the filter settings.
1429  */
1430 
1431 static int				/* O - 0 on success, 1 on error */
read_cups_files_conf(const char * filename)1432 read_cups_files_conf(
1433     const char *filename)		/* I - File to read */
1434 {
1435   cups_file_t	*fp;			/* cups-files.conf file */
1436   const char	*temp;			/* Temporary string */
1437   char		line[1024],		/* Line from file */
1438 		*ptr;			/* Pointer into line */
1439   int		linenum;		/* Current line number */
1440 
1441 
1442   if ((temp = getenv("CUPS_DATADIR")) != NULL)
1443     set_string(&DataDir, temp);
1444   else
1445     set_string(&DataDir, CUPS_DATADIR);
1446 
1447   if ((temp = getenv("CUPS_SERVERBIN")) != NULL)
1448     set_string(&ServerBin, temp);
1449   else
1450     set_string(&ServerBin, CUPS_SERVERBIN);
1451 
1452   strlcpy(line, filename, sizeof(line));
1453   if ((ptr = strrchr(line, '/')) != NULL)
1454     *ptr = '\0';
1455   else
1456     getcwd(line, sizeof(line));
1457 
1458   set_string(&ServerRoot, line);
1459 
1460   if ((fp = cupsFileOpen(filename, "r")) != NULL)
1461   {
1462     linenum = 0;
1463 
1464     while (cupsFileGetConf(fp, line, sizeof(line), &ptr, &linenum))
1465     {
1466       if (!_cups_strcasecmp(line, "DataDir"))
1467         set_string(&DataDir, ptr);
1468       else if (!_cups_strcasecmp(line, "ServerBin"))
1469         set_string(&ServerBin, ptr);
1470       else if (!_cups_strcasecmp(line, "ServerRoot"))
1471         set_string(&ServerRoot, ptr);
1472     }
1473 
1474     cupsFileClose(fp);
1475   }
1476 
1477 #if CUPS_SNAP
1478   if ((temp = getenv("PATH")) != NULL)
1479     snprintf(line, sizeof(line), "%s/filter:" CUPS_BINDIR ":" CUPS_SBINDIR ":%s", ServerBin, temp);
1480   else
1481 #endif /* CUPS_SNAP */
1482   snprintf(line, sizeof(line), "%s/filter:" CUPS_BINDIR ":" CUPS_SBINDIR ":/bin:/usr/bin", ServerBin);
1483   set_string(&Path, line);
1484 
1485   return (0);
1486 }
1487 
1488 
1489 /*
1490  * 'set_string()' - Copy and set a string.
1491  */
1492 
1493 static void
set_string(char ** s,const char * val)1494 set_string(char       **s,		/* O - Copy of string */
1495            const char *val)		/* I - String to copy */
1496 {
1497   if (*s)
1498     free(*s);
1499 
1500   *s = strdup(val);
1501 }
1502 
1503 
1504 /*
1505  * 'sighandler()' - Signal catcher for when we print from stdin...
1506  */
1507 
1508 static void
sighandler(int s)1509 sighandler(int s)			/* I - Signal number */
1510 {
1511  /*
1512   * Remove the temporary file we're using to print a job file...
1513   */
1514 
1515   if (TempFile[0])
1516     unlink(TempFile);
1517 
1518  /*
1519   * Exit...
1520   */
1521 
1522   exit(s);
1523 }
1524 
1525 
1526 /*
1527  * 'usage()' - Show program usage...
1528  */
1529 
1530 static void
usage(const char * opt)1531 usage(const char *opt)			/* I - Incorrect option, if any */
1532 {
1533   if (opt)
1534     _cupsLangPrintf(stderr, _("%s: Unknown option \"%c\"."), "cupsfilter", *opt);
1535 
1536   _cupsLangPuts(stdout, _("Usage: cupsfilter [ options ] [ -- ] filename"));
1537   _cupsLangPuts(stdout, _("Options:"));
1538   _cupsLangPuts(stdout, _("  --list-filters          List filters that will be used."));
1539   _cupsLangPuts(stdout, _("  -D                      Remove the input file when finished."));
1540   _cupsLangPuts(stdout, _("  -P filename.ppd         Set PPD file."));
1541   _cupsLangPuts(stdout, _("  -U username             Specify username."));
1542   _cupsLangPuts(stdout, _("  -c cups-files.conf      Set cups-files.conf file to use."));
1543   _cupsLangPuts(stdout, _("  -d printer              Use the named printer."));
1544   _cupsLangPuts(stdout, _("  -e                      Use every filter from the PPD file."));
1545   _cupsLangPuts(stdout, _("  -i mime/type            Set input MIME type (otherwise auto-typed)."));
1546   _cupsLangPuts(stdout, _("  -j job-id[,N]           Filter file N from the specified job (default is file 1)."));
1547   _cupsLangPuts(stdout, _("  -m mime/type            Set output MIME type (otherwise application/pdf)."));
1548   _cupsLangPuts(stdout, _("  -n copies               Set number of copies."));
1549   _cupsLangPuts(stdout, _("  -o name=value           Set option(s)."));
1550   _cupsLangPuts(stdout, _("  -p filename.ppd         Set PPD file."));
1551   _cupsLangPuts(stdout, _("  -t title                Set title."));
1552   _cupsLangPuts(stdout, _("  -u                      Remove the PPD file when finished."));
1553 
1554   exit(1);
1555 }
1556