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