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