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