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