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