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