/* * Filtering program for CUPS. * * Copyright © 2021-2022 by OpenPrinting * Copyright © 2007-2016 by Apple Inc. * Copyright © 1997-2006 by Easy Software Products, all rights reserved. * * Licensed under Apache License v2.0. See the file "LICENSE" for more information. */ /* * Include necessary headers... */ #include #include #include #include "mime.h" #include #include #include #include #include #if defined(__APPLE__) # include #endif /* __APPLE__ */ /* * Local globals... */ static char *DataDir = NULL;/* CUPS_DATADIR environment variable */ static mime_filter_t GZIPFilter = /* gziptoany filter */ { NULL, /* Source type */ NULL, /* Destination type */ 0, /* Cost */ "gziptoany" /* Filter program to run */ }; static char *Path = NULL; /* PATH environment variable */ static char *ServerBin = NULL; /* CUPS_SERVERBIN environment variable */ static char *ServerRoot = NULL; /* CUPS_SERVERROOT environment variable */ static char TempFile[1024] = ""; /* Temporary file */ /* * Local functions... */ static void add_printer_filter(const char *command, mime_t *mime, mime_type_t *printer_type, const char *filter); static mime_type_t *add_printer_filters(const char *command, mime_t *mime, const char *printer, const char *ppdfile, mime_type_t **prefilter_type); static void check_cb(void *context, _cups_fc_result_t result, const char *message); static int compare_pids(mime_filter_t *a, mime_filter_t *b); static char *escape_options(int num_options, cups_option_t *options); static int exec_filter(const char *filter, char **argv, char **envp, int infd, int outfd); static int exec_filters(mime_type_t *srctype, cups_array_t *filters, const char *infile, const char *outfile, const char *ppdfile, const char *printer, const char *user, const char *title, int num_options, cups_option_t *options); static void get_job_file(const char *job); static int open_pipe(int *fds); static int read_cups_files_conf(const char *filename); static void set_string(char **s, const char *val); static void sighandler(int sig); static void usage(const char *opt) _CUPS_NORETURN; /* * 'main()' - Main entry for the test program. */ int /* O - Exit status */ main(int argc, /* I - Number of command-line args */ char *argv[]) /* I - Command-line arguments */ { int i, /* Looping vars */ list_filters = 0; /* Just list the filters? */ const char *command, /* Command name */ *opt, /* Current option */ *printer; /* Printer name */ mime_type_t *printer_type, /* Printer MIME type */ *prefilter_type; /* Printer prefilter MIME type */ char *srctype, /* Source type */ *dsttype, /* Destination type */ super[MIME_MAX_SUPER], /* Super-type name */ type[MIME_MAX_TYPE]; /* Type name */ int compression; /* Compression of file */ int cost; /* Cost of filters */ mime_t *mime; /* MIME database */ char mimedir[1024]; /* MIME directory */ char *infile, /* File to filter */ *outfile; /* File to create */ char cupsfilesconf[1024]; /* cups-files.conf file */ const char *server_root; /* CUPS_SERVERROOT environment variable */ mime_type_t *src, /* Source type */ *dst; /* Destination type */ cups_array_t *filters; /* Filters for the file */ int num_options; /* Number of options */ cups_option_t *options; /* Options */ const char *ppdfile; /* PPD file */ const char *title, /* Title string */ *user; /* Username */ int all_filters, /* Use all filters */ removeppd, /* Remove PPD file */ removeinfile; /* Remove input file */ int status; /* Execution status */ /* * Setup defaults... */ if ((command = strrchr(argv[0], '/')) != NULL) command ++; else command = argv[0]; printer = !strcmp(command, "convert") ? "tofile" : "cupsfilter"; mime = NULL; srctype = NULL; compression = 0; dsttype = "application/pdf"; infile = NULL; outfile = NULL; num_options = 0; options = NULL; ppdfile = NULL; title = NULL; user = cupsUser(); all_filters = 0; removeppd = 0; removeinfile = 0; if ((server_root = getenv("CUPS_SERVERROOT")) == NULL) server_root = CUPS_SERVERROOT; snprintf(cupsfilesconf, sizeof(cupsfilesconf), "%s/cups-files.conf", server_root); /* * Process command-line arguments... */ _cupsSetLocale(argv); for (i = 1; i < argc; i ++) { if (argv[i][0] == '-') { if (!strcmp(argv[i], "--list-filters")) { list_filters = 1; } else if (!strcmp(argv[i], "--")) { i ++; if (i < argc && !infile) infile = argv[i]; else usage(NULL); } else { for (opt = argv[i] + 1; *opt; opt ++) { switch (*opt) { case 'a' : /* Specify option... */ i ++; if (i < argc) { num_options = cupsParseOptions(argv[i], num_options, &options); } else { _cupsLangPrintf(stderr, _("%s: Error - expected name=value after \"-a\" option."), argv[0]); usage(opt); } break; case 'c' : /* Specify cups-files.conf file location... */ i ++; if (i < argc) { if (!strcmp(command, "convert")) num_options = cupsAddOption("copies", argv[i], num_options, &options); else strlcpy(cupsfilesconf, argv[i], sizeof(cupsfilesconf)); } else { _cupsLangPrintf(stderr, _("%s: Error - expected filename after \"-c\" option."), argv[0]); usage(NULL); } break; case 'd' : /* Specify the real printer name */ i ++; if (i < argc) { printer = argv[i]; } else { _cupsLangPrintf(stderr, _("%s: Error - expected printer name after \"-d\" option."), argv[0]); usage(NULL); } break; case 'D' : /* Delete input file after conversion */ removeinfile = 1; break; case 'e' : /* Use every filter from the PPD file */ all_filters = 1; break; case 'f' : /* Specify input file... */ i ++; if (i < argc && !infile) { infile = argv[i]; } else { _cupsLangPrintf(stderr, _("%s: Error - expected input file after \"-f\" option."), argv[0]); usage(NULL); } break; case 'i' : /* Specify source MIME type... */ i ++; if (i < argc) { if (sscanf(argv[i], "%15[^/]/%255s", super, type) != 2) usage(opt); srctype = argv[i]; } else { _cupsLangPrintf(stderr, _("%s: Error - expected source MIME type after \"-i\" option."), argv[0]); usage(NULL); } break; case 'j' : /* Get job file or specify destination MIME type... */ if (strcmp(command, "convert")) { i ++; if (i < argc) { get_job_file(argv[i]); infile = TempFile; } else { _cupsLangPrintf(stdout, _("%s: Error - expected job-id after \"-j\" option."), argv[0]); usage(NULL); } break; } case 'm' : /* Specify destination MIME type... */ i ++; if (i < argc) { if (sscanf(argv[i], "%15[^/]/%255s", super, type) != 2) usage(opt); dsttype = argv[i]; } else { _cupsLangPrintf(stderr, _("%s: Error - expected destination MIME type after \"-m\" option."), argv[0]); usage(NULL); } break; case 'n' : /* Specify number of copies... */ i ++; if (i < argc) { num_options = cupsAddOption("copies", argv[i], num_options, &options); } else { _cupsLangPrintf(stderr, _("%s: Error - expected number of copies after \"-n\" option."), argv[0]); usage(NULL); } break; case 'o' : /* Specify option(s) or output filename */ i ++; if (i < argc) { if (!strcmp(command, "convert")) { if (outfile) usage(NULL); else outfile = argv[i]; } else { num_options = cupsParseOptions(argv[i], num_options, &options); } } else { _cupsLangPrintf(stderr, _("%s: Error - expected name=value after \"-o\" option."), argv[0]); usage(NULL); } break; case 'p' : /* Specify PPD file... */ case 'P' : /* Specify PPD file... */ i ++; if (i < argc) { ppdfile = argv[i]; } else { _cupsLangPrintf(stderr, _("%s: Error - expected PPD file after \"-%c\" option."), argv[0], *opt); usage(NULL); } break; case 't' : /* Specify title... */ case 'J' : /* Specify title... */ i ++; if (i < argc) { title = argv[i]; } else { _cupsLangPrintf(stderr, _("%s: Error - expected title after \"-%c\" option."), argv[0], *opt); usage(NULL); } break; case 'u' : /* Delete PPD file after conversion */ removeppd = 1; break; case 'U' : /* Specify username... */ i ++; if (i < argc) { user = argv[i]; } else { _cupsLangPrintf(stderr, _("%s: Error - expected \"username\" after \"-U\" option."), argv[0]); usage(NULL); } break; default : /* Something we don't understand... */ _cupsLangPrintf(stderr, _("%s: Error - unknown option \"-%c\"."), argv[0], *opt); usage(NULL); } } } } else if (!infile) { if (strcmp(command, "convert")) infile = argv[i]; else usage(NULL); } else { _cupsLangPuts(stderr, _("cupsfilter: Only one filename can be specified.")); usage(NULL); } } if (!infile && !srctype) usage(NULL); if (!title) { if (!infile) title = "(stdin)"; else if ((title = strrchr(infile, '/')) != NULL) title ++; else title = infile; } /* * Load the cups-files.conf file and create the MIME database... */ if (read_cups_files_conf(cupsfilesconf)) return (1); snprintf(mimedir, sizeof(mimedir), "%s/mime", DataDir); mime = mimeLoadTypes(NULL, mimedir); mime = mimeLoadTypes(mime, ServerRoot); mime = mimeLoadFilters(mime, mimedir, Path); mime = mimeLoadFilters(mime, ServerRoot, Path); if (!mime) { _cupsLangPrintf(stderr, _("%s: Unable to read MIME database from \"%s\" or \"%s\"."), command, mimedir, ServerRoot); return (1); } prefilter_type = NULL; if (all_filters) printer_type = add_printer_filters(command, mime, printer, ppdfile, &prefilter_type); else printer_type = mimeType(mime, "application", "vnd.cups-postscript"); /* * Get the source and destination types... */ if (srctype) { /* sscanf return value already checked above */ sscanf(srctype, "%15[^/]/%255s", super, type); if ((src = mimeType(mime, super, type)) == NULL) { _cupsLangPrintf(stderr, _("%s: Unknown source MIME type %s/%s."), command, super, type); return (1); } } else if ((src = mimeFileType(mime, infile, infile, &compression)) == NULL) { _cupsLangPrintf(stderr, _("%s: Unable to determine MIME type of \"%s\"."), command, infile); return (1); } /* sscanf return value already checked above */ sscanf(dsttype, "%15[^/]/%255s", super, type); if (!_cups_strcasecmp(super, "printer")) dst = printer_type; else if ((dst = mimeType(mime, super, type)) == NULL) { _cupsLangPrintf(stderr, _("%s: Unknown destination MIME type %s/%s."), command, super, type); return (1); } /* * Figure out how to filter the file... */ if (src == dst) { /* * Special case - no filtering needed... */ filters = cupsArrayNew(NULL, NULL); cupsArrayAdd(filters, &GZIPFilter); GZIPFilter.src = src; GZIPFilter.dst = dst; } else if ((filters = mimeFilter(mime, src, dst, &cost)) == NULL) { _cupsLangPrintf(stderr, _("%s: No filter to convert from %s/%s to %s/%s."), command, src->super, src->type, dst->super, dst->type); return (1); } else if (compression) cupsArrayInsert(filters, &GZIPFilter); if (prefilter_type) { /* * Add pre-filters... */ mime_filter_t *filter, /* Current filter */ *prefilter; /* Current pre-filter */ cups_array_t *prefilters = cupsArrayNew(NULL, NULL); /* New filters array */ for (filter = (mime_filter_t *)cupsArrayFirst(filters); filter; filter = (mime_filter_t *)cupsArrayNext(filters)) { if ((prefilter = mimeFilterLookup(mime, filter->src, prefilter_type)) != NULL) cupsArrayAdd(prefilters, prefilter); cupsArrayAdd(prefilters, filter); } cupsArrayDelete(filters); filters = prefilters; } if (list_filters) { /* * List filters... */ mime_filter_t *filter; /* Current filter */ for (filter = (mime_filter_t *)cupsArrayFirst(filters); filter; filter = (mime_filter_t *)cupsArrayNext(filters)) if (strcmp(filter->filter, "-")) _cupsLangPuts(stdout, filter->filter); status = 0; } else { /* * Run filters... */ status = exec_filters(src, filters, infile, outfile, ppdfile, printer, user, title, num_options, options); } /* * Remove files as needed, then exit... */ if (TempFile[0]) unlink(TempFile); if (removeppd && ppdfile) unlink(ppdfile); if (removeinfile && infile) unlink(infile); return (status); } /* * 'add_printer_filter()' - Add a single filters from a PPD file. */ static void add_printer_filter( const char *command, /* I - Command name */ mime_t *mime, /* I - MIME database */ mime_type_t *filtertype, /* I - Printer or prefilter MIME type */ const char *filter) /* I - Filter to add */ { char super[MIME_MAX_SUPER], /* Super-type for filter */ type[MIME_MAX_TYPE], /* Type for filter */ dsuper[MIME_MAX_SUPER], /* Destination super-type for filter */ dtype[MIME_MAX_TYPE], /* Destination type for filter */ dest[MIME_MAX_SUPER + MIME_MAX_TYPE + 2], /* Destination super/type */ program[1024]; /* Program/filter name */ int cost; /* Cost of filter */ size_t maxsize = 0; /* Maximum supported file size */ mime_type_t *temptype, /* MIME type looping var */ *desttype; /* Destination MIME type */ mime_filter_t *filterptr; /* MIME filter */ /* * Parse the filter string; it should be in one of the following formats: * * source/type cost program * source/type cost maxsize(nnnn) program * source/type dest/type cost program * source/type dest/type cost maxsize(nnnn) program */ if (sscanf(filter, "%15[^/]/%255s%*[ \t]%15[^/]/%255s%d%*[ \t]%1023[^\n]", super, type, dsuper, dtype, &cost, program) == 6) { snprintf(dest, sizeof(dest), "%s/%s/%s", filtertype->type, dsuper, dtype); if ((desttype = mimeType(mime, "printer", dest)) == NULL) desttype = mimeAddType(mime, "printer", dest); } else { if (sscanf(filter, "%15[^/]/%255s%d%*[ \t]%1023[^\n]", super, type, &cost, program) == 4) { desttype = filtertype; } else { _cupsLangPrintf(stderr, _("%s: Invalid filter string \"%s\"."), command, filter); return; } } if (!strncmp(program, "maxsize(", 8)) { char *ptr; /* Pointer into maxsize(nnnn) program */ maxsize = (size_t)strtoll(program + 8, &ptr, 10); if (*ptr != ')') { printf("testmime: Invalid filter string \"%s\".\n", filter); return; } ptr ++; while (_cups_isspace(*ptr)) ptr ++; _cups_strcpy(program, ptr); } /* * See if the filter program exists; if not, stop the printer and flag * the error! */ if (strcmp(program, "-")) { char filename[1024]; /* Full path to program */ if (program[0] == '/') strlcpy(filename, program, sizeof(filename)); else snprintf(filename, sizeof(filename), "%s/filter/%s", ServerBin, program); if (_cupsFileCheck(filename, _CUPS_FILE_CHECK_PROGRAM, !geteuid(), check_cb, (void *)command)) return; } /* * Add the filter to the MIME database, supporting wildcards as needed... */ for (temptype = mimeFirstType(mime); temptype; temptype = mimeNextType(mime)) if (((super[0] == '*' && _cups_strcasecmp(temptype->super, "printer")) || !_cups_strcasecmp(temptype->super, super)) && (type[0] == '*' || !_cups_strcasecmp(temptype->type, type))) { if (desttype != filtertype) { filterptr = mimeAddFilter(mime, temptype, desttype, cost, program); if (!mimeFilterLookup(mime, desttype, filtertype)) mimeAddFilter(mime, desttype, filtertype, 0, "-"); } else filterptr = mimeAddFilter(mime, temptype, filtertype, cost, program); if (filterptr) filterptr->maxsize = maxsize; } } /* * 'add_printer_filters()' - Add filters from a PPD file. */ static mime_type_t * /* O - Printer type or NULL on error */ add_printer_filters( const char *command, /* I - Command name */ mime_t *mime, /* I - MIME database */ const char *printer, /* I - Printer name */ const char *ppdfile, /* I - PPD file */ mime_type_t **prefilter_type) /* O - Prefilter type */ { ppd_file_t *ppd; /* PPD file data */ _ppd_cache_t *pc; /* Cache data for PPD */ const char *value; /* Filter definition value */ mime_type_t *printer_type; /* Printer filter type */ if ((ppd = _ppdOpenFile(ppdfile, _PPD_LOCALIZATION_NONE)) == NULL) { ppd_status_t status; /* PPD load status */ int linenum; /* Line number */ status = ppdLastError(&linenum); _cupsLangPrintf(stderr, _("%s: Unable to open PPD file: %s on line %d."), command, ppdErrorString(status), linenum); return (NULL); } pc = _ppdCacheCreateWithPPD(ppd); if (!pc) return (NULL); printer_type = mimeAddType(mime, "printer", printer); *prefilter_type = NULL; if (pc->filters) { for (value = (const char *)cupsArrayFirst(pc->filters); value; value = (const char *)cupsArrayNext(pc->filters)) add_printer_filter(command, mime, printer_type, value); } else { add_printer_filter(command, mime, printer_type, "application/vnd.cups-raw 0 -"); add_printer_filter(command, mime, printer_type, "application/vnd.cups-postscript 0 -"); } if (pc->prefilters) { *prefilter_type = mimeAddType(mime, "prefilter", printer); for (value = (const char *)cupsArrayFirst(pc->prefilters); value; value = (const char *)cupsArrayNext(pc->prefilters)) add_printer_filter(command, mime, *prefilter_type, value); } return (printer_type); } /* * 'check_cb()' - Callback function for _cupsFileCheck. */ static void check_cb(void *context, /* I - Context (command name) */ _cups_fc_result_t result, /* I - Result of check */ const char *message) /* I - Localized message */ { (void)result; _cupsLangPrintf(stderr, _("%s: %s"), (char *)context, message); } /* * 'compare_pids()' - Compare two filter PIDs... */ static int /* O - Result of comparison */ compare_pids(mime_filter_t *a, /* I - First filter */ mime_filter_t *b) /* I - Second filter */ { /* * Because we're particularly lazy, we store the process ID in the "cost" * variable... */ return (a->cost - b->cost); } /* * 'escape_options()' - Convert an options array to a string. */ static char * /* O - Option string */ escape_options( int num_options, /* I - Number of options */ cups_option_t *options) /* I - Options */ { int i; /* Looping var */ cups_option_t *option; /* Current option */ size_t bytes; /* Number of bytes needed */ char *s, /* Option string */ *sptr, /* Pointer into string */ *vptr; /* Pointer into value */ /* * Figure out the worst-case number of bytes we need for the option string. */ for (i = num_options, option = options, bytes = 1; i > 0; i --, option ++) bytes += 2 * (strlen(option->name) + strlen(option->value)) + 2; if ((s = malloc(bytes)) == NULL) return (NULL); /* * Copy the options to the string... */ for (i = num_options, option = options, sptr = s; i > 0; i --, option ++) { if (!strcmp(option->name, "copies")) continue; if (sptr > s) *sptr++ = ' '; strlcpy(sptr, option->name, bytes - (size_t)(sptr - s)); sptr += strlen(sptr); *sptr++ = '='; for (vptr = option->value; *vptr;) { if (strchr("\\ \t\n", *vptr)) *sptr++ = '\\'; *sptr++ = *vptr++; } } *sptr = '\0'; return (s); } /* * 'exec_filter()' - Execute a single filter. */ static int /* O - Process ID or -1 on error */ exec_filter(const char *filter, /* I - Filter to execute */ char **argv, /* I - Argument list */ char **envp, /* I - Environment list */ int infd, /* I - Stdin file descriptor */ int outfd) /* I - Stdout file descriptor */ { int pid, /* Process ID */ fd; /* Temporary file descriptor */ #if defined(__APPLE__) char processPath[1024], /* CFProcessPath environment variable */ linkpath[1024]; /* Link path for symlinks... */ int linkbytes; /* Bytes for link path */ /* * Add special voodoo magic for macOS - this allows macOS * programs to access their bundle resources properly... */ if ((linkbytes = readlink(filter, linkpath, sizeof(linkpath) - 1)) > 0) { /* * Yes, this is a symlink to the actual program, nul-terminate and * use it... */ linkpath[linkbytes] = '\0'; if (linkpath[0] == '/') snprintf(processPath, sizeof(processPath), "CFProcessPath=%s", linkpath); else snprintf(processPath, sizeof(processPath), "CFProcessPath=%s/%s", dirname((char *)filter), linkpath); } else snprintf(processPath, sizeof(processPath), "CFProcessPath=%s", filter); envp[0] = processPath; /* Replace string */ #endif /* __APPLE__ */ if ((pid = fork()) == 0) { /* * Child process goes here... * * Update stdin/stdout/stderr as needed... */ if (infd != 0) { if (infd < 0) infd = open("/dev/null", O_RDONLY); if (infd > 0) { dup2(infd, 0); close(infd); } } if (outfd != 1) { if (outfd < 0) outfd = open("/dev/null", O_WRONLY); if (outfd > 1) { dup2(outfd, 1); close(outfd); } } if ((fd = open("/dev/null", O_RDWR)) > 3) { dup2(fd, 3); close(fd); } fcntl(3, F_SETFL, O_NDELAY); if ((fd = open("/dev/null", O_RDWR)) > 4) { dup2(fd, 4); close(fd); } fcntl(4, F_SETFL, O_NDELAY); /* * Execute command... */ execve(filter, argv, envp); perror(filter); exit(errno); } return (pid); } /* * 'exec_filters()' - Execute filters for the given file and options. */ static int /* O - 0 on success, 1 on error */ exec_filters(mime_type_t *srctype, /* I - Source type */ cups_array_t *filters, /* I - Array of filters to run */ const char *infile, /* I - File to filter */ const char *outfile, /* I - File to create */ const char *ppdfile, /* I - PPD file, if any */ const char *printer, /* I - Printer name */ const char *user, /* I - Username */ const char *title, /* I - Job title */ int num_options, /* I - Number of filter options */ cups_option_t *options) /* I - Filter options */ { int i; /* Looping var */ const char *argv[8], /* Command-line arguments */ *envp[21], /* Environment variables */ *temp; /* Temporary string */ char *optstr, /* Filter options */ content_type[1024], /* CONTENT_TYPE */ cups_datadir[1024], /* CUPS_DATADIR */ cups_fontpath[1024], /* CUPS_FONTPATH */ cups_serverbin[1024], /* CUPS_SERVERBIN */ cups_serverroot[1024], /* CUPS_SERVERROOT */ final_content_type[1024] = "", /* FINAL_CONTENT_TYPE */ lang[1024], /* LANG */ path[1024], /* PATH */ ppd[1024], /* PPD */ printer_info[255], /* PRINTER_INFO env variable */ printer_location[255], /* PRINTER_LOCATION env variable */ printer_name[255], /* PRINTER env variable */ userenv[1024], /* USER */ #if CUPS_SNAP fontconfig_file[1024], /* FONTCONFIG_FILE */ fontconfig_path[1024], /* FONTCONFIG_PATH */ fontconfig_sysroot[1024], /* FONTCONFIG_SYSROOT */ ld_library_path[2048], /* LD_LIBRARY_PATH */ #endif /* CUPS_SNAP */ program[1024]; /* Program to run */ mime_filter_t *filter, /* Current filter */ *next; /* Next filter */ int current, /* Current filter */ filterfds[2][2], /* Pipes for filters */ pid, /* Process ID of filter */ status, /* Exit status */ retval; /* Return value */ cups_array_t *pids; /* Executed filters array */ mime_filter_t key; /* Search key for filters */ cups_lang_t *language; /* Current language */ cups_dest_t *dest; /* Destination information */ /* * Figure out the final content type... */ for (filter = (mime_filter_t *)cupsArrayLast(filters); filter && filter->dst; filter = (mime_filter_t *)cupsArrayPrev(filters)) if (strcmp(filter->dst->super, "printer")) break; if (filter && filter->dst) { const char *ptr; /* Pointer in type name */ if ((ptr = strchr(filter->dst->type, '/')) != NULL) snprintf(final_content_type, sizeof(final_content_type), "FINAL_CONTENT_TYPE=%s", ptr + 1); else snprintf(final_content_type, sizeof(final_content_type), "FINAL_CONTENT_TYPE=%s/%s", filter->dst->super, filter->dst->type); } /* * Remove NULL ("-") filters... */ for (filter = (mime_filter_t *)cupsArrayFirst(filters); filter; filter = (mime_filter_t *)cupsArrayNext(filters)) if (!strcmp(filter->filter, "-")) cupsArrayRemove(filters, filter); /* * Setup the filter environment and command-line... */ optstr = escape_options(num_options, options); snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s/%s", srctype->super, srctype->type); snprintf(cups_datadir, sizeof(cups_datadir), "CUPS_DATADIR=%s", DataDir); snprintf(cups_serverbin, sizeof(cups_serverbin), "CUPS_SERVERBIN=%s", ServerBin); snprintf(cups_serverroot, sizeof(cups_serverroot), "CUPS_SERVERROOT=%s", ServerRoot); language = cupsLangDefault(); snprintf(lang, sizeof(lang), "LANG=%s.UTF8", language->language); snprintf(path, sizeof(path), "PATH=%s", Path); if (ppdfile) snprintf(ppd, sizeof(ppd), "PPD=%s", ppdfile); else if ((temp = getenv("PPD")) != NULL) snprintf(ppd, sizeof(ppd), "PPD=%s", temp); else #ifdef __APPLE__ if (!access("/System/Library/Frameworks/ApplicationServices.framework/" "Versions/A/Frameworks/PrintCore.framework/Versions/A/" "Resources/English.lproj/Generic.ppd", 0)) strlcpy(ppd, "PPD=/System/Library/Frameworks/ApplicationServices.framework/" "Versions/A/Frameworks/PrintCore.framework/Versions/A/" "Resources/English.lproj/Generic.ppd", sizeof(ppd)); else strlcpy(ppd, "PPD=/System/Library/Frameworks/ApplicationServices.framework/" "Versions/A/Frameworks/PrintCore.framework/Versions/A/" "Resources/Generic.ppd", sizeof(ppd)); #else snprintf(ppd, sizeof(ppd), "PPD=%s/model/laserjet.ppd", DataDir); #endif /* __APPLE__ */ snprintf(userenv, sizeof(userenv), "USER=%s", user); if (printer && (dest = cupsGetNamedDest(CUPS_HTTP_DEFAULT, printer, NULL)) != NULL) { if ((temp = cupsGetOption("printer-info", dest->num_options, dest->options)) != NULL) snprintf(printer_info, sizeof(printer_info), "PRINTER_INFO=%s", temp); else snprintf(printer_info, sizeof(printer_info), "PRINTER_INFO=%s", printer); if ((temp = cupsGetOption("printer-location", dest->num_options, dest->options)) != NULL) snprintf(printer_location, sizeof(printer_location), "PRINTER_LOCATION=%s", temp); else strlcpy(printer_location, "PRINTER_LOCATION=Unknown", sizeof(printer_location)); } else { snprintf(printer_info, sizeof(printer_info), "PRINTER_INFO=%s", printer ? printer : "Unknown"); strlcpy(printer_location, "PRINTER_LOCATION=Unknown", sizeof(printer_location)); } snprintf(printer_name, sizeof(printer_name), "PRINTER=%s", printer ? printer : "Unknown"); argv[0] = (char *)printer; argv[1] = "1"; argv[2] = user; argv[3] = title; argv[4] = cupsGetOption("copies", num_options, options); argv[5] = optstr; argv[6] = infile; argv[7] = NULL; if (!argv[4]) argv[4] = "1"; for (i = 0; argv[i]; i ++) fprintf(stderr, "DEBUG: argv[%d]=\"%s\"\n", i, argv[i]); i = 0; #ifdef __APPLE__ envp[i ++] = ""; #endif /* __APPLE__ */ envp[i ++] = content_type; envp[i ++] = cups_datadir; envp[i ++] = cups_fontpath; envp[i ++] = cups_serverbin; envp[i ++] = cups_serverroot; envp[i ++] = lang; envp[i ++] = path; envp[i ++] = ppd; envp[i ++] = printer_info; envp[i ++] = printer_location; envp[i ++] = printer_name; envp[i ++] = userenv; envp[i ++] = "CHARSET=utf-8"; if (final_content_type[0]) envp[i ++] = final_content_type; #if CUPS_SNAP if ((temp = getenv("FONTCONFIG_FILE")) != NULL) { snprintf(fontconfig_file, sizeof(fontconfig_file), "FONTCONFIG_FILE=%s", temp); envp[i ++] = fontconfig_file; } if ((temp = getenv("FONTCONFIG_PATH")) != NULL) { snprintf(fontconfig_path, sizeof(fontconfig_path), "FONTCONFIG_PATH=%s", temp); envp[i ++] = fontconfig_path; } if ((temp = getenv("FONTCONFIG_SYSROOT")) != NULL) { snprintf(fontconfig_sysroot, sizeof(fontconfig_sysroot), "FONTCONFIG_SYSROOT=%s", temp); envp[i ++] = fontconfig_sysroot; } if ((temp = getenv("LD_LIBRARY_PATH")) != NULL) { snprintf(ld_library_path, sizeof(ld_library_path), "LD_LIBRARY_PATH=%s", temp); envp[i ++] = ld_library_path; } #endif /* CUPS_SNAP */ envp[i] = NULL; for (i = 0; envp[i]; i ++) fprintf(stderr, "DEBUG: envp[%d]=\"%s\"\n", i, envp[i]); /* * Execute all of the filters... */ pids = cupsArrayNew((cups_array_func_t)compare_pids, NULL); current = 0; filterfds[0][0] = -1; filterfds[0][1] = -1; filterfds[1][0] = -1; filterfds[1][1] = -1; if (!infile) filterfds[0][0] = 0; for (filter = (mime_filter_t *)cupsArrayFirst(filters); filter; filter = next, current = 1 - current) { next = (mime_filter_t *)cupsArrayNext(filters); if (filter->filter[0] == '/') strlcpy(program, filter->filter, sizeof(program)); else snprintf(program, sizeof(program), "%s/filter/%s", ServerBin, filter->filter); if (filterfds[!current][1] > 1) { close(filterfds[1 - current][0]); close(filterfds[1 - current][1]); filterfds[1 - current][0] = -1; filterfds[1 - current][0] = -1; } if (next) open_pipe(filterfds[1 - current]); else if (outfile) { filterfds[1 - current][1] = open(outfile, O_CREAT | O_TRUNC | O_WRONLY, 0666); if (filterfds[1 - current][1] < 0) fprintf(stderr, "ERROR: Unable to create \"%s\" - %s\n", outfile, strerror(errno)); } else filterfds[1 - current][1] = 1; pid = exec_filter(program, (char **)argv, (char **)envp, filterfds[current][0], filterfds[1 - current][1]); if (pid > 0) { fprintf(stderr, "INFO: %s (PID %d) started.\n", filter->filter, pid); filter->cost = pid; cupsArrayAdd(pids, filter); } else break; argv[6] = NULL; } /* * Close remaining pipes... */ if (filterfds[0][1] > 1) { close(filterfds[0][0]); close(filterfds[0][1]); } if (filterfds[1][1] > 1) { close(filterfds[1][0]); close(filterfds[1][1]); } /* * Wait for the children to exit... */ retval = 0; while (cupsArrayCount(pids) > 0) { if ((pid = wait(&status)) < 0) continue; key.cost = pid; if ((filter = (mime_filter_t *)cupsArrayFind(pids, &key)) != NULL) { cupsArrayRemove(pids, filter); if (status) { if (WIFEXITED(status)) fprintf(stderr, "ERROR: %s (PID %d) stopped with status %d\n", filter->filter, pid, WEXITSTATUS(status)); else fprintf(stderr, "ERROR: %s (PID %d) crashed on signal %d\n", filter->filter, pid, WTERMSIG(status)); retval = 1; } else fprintf(stderr, "INFO: %s (PID %d) exited with no errors.\n", filter->filter, pid); } } cupsArrayDelete(pids); return (retval); } /* * 'get_job_file()' - Get the specified job file. */ static void get_job_file(const char *job) /* I - Job ID */ { long jobid, /* Job ID */ docnum; /* Document number */ const char *jobptr; /* Pointer into job ID string */ char uri[1024]; /* job-uri */ http_t *http; /* Connection to server */ ipp_t *request; /* Request data */ int tempfd; /* Temporary file */ /* * Get the job ID and document number, if any... */ if ((jobptr = strrchr(job, '-')) != NULL) jobptr ++; else jobptr = job; jobid = strtol(jobptr, (char **)&jobptr, 10); if (*jobptr == ',') docnum = strtol(jobptr + 1, NULL, 10); else docnum = 1; if (jobid < 1 || jobid > INT_MAX) { _cupsLangPrintf(stderr, _("cupsfilter: Invalid job ID %d."), (int)jobid); exit(1); } if (docnum < 1 || docnum > INT_MAX) { _cupsLangPrintf(stderr, _("cupsfilter: Invalid document number %d."), (int)docnum); exit(1); } /* * Ask the server for the document file... */ if ((http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption())) == NULL) { _cupsLangPrintf(stderr, _("%s: Unable to connect to server."), "cupsfilter"); exit(1); } request = ippNewRequest(CUPS_GET_DOCUMENT); snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", (int)jobid); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri); ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "document-number", (int)docnum); if ((tempfd = cupsTempFd(TempFile, sizeof(TempFile))) == -1) { _cupsLangPrintError("ERROR", _("Unable to create temporary file")); httpClose(http); exit(1); } signal(SIGTERM, sighandler); ippDelete(cupsDoIORequest(http, request, "/", -1, tempfd)); close(tempfd); httpClose(http); if (cupsLastError() != IPP_OK) { _cupsLangPrintf(stderr, _("cupsfilter: Unable to get job file - %s"), cupsLastErrorString()); unlink(TempFile); exit(1); } } /* * 'open_pipe()' - Create a pipe which is closed on exec. */ static int /* O - 0 on success, -1 on error */ open_pipe(int *fds) /* O - Pipe file descriptors (2) */ { /* * Create the pipe... */ if (pipe(fds)) { fds[0] = -1; fds[1] = -1; return (-1); } /* * Set the "close on exec" flag on each end of the pipe... */ if (fcntl(fds[0], F_SETFD, fcntl(fds[0], F_GETFD) | FD_CLOEXEC)) { close(fds[0]); close(fds[1]); fds[0] = -1; fds[1] = -1; return (-1); } if (fcntl(fds[1], F_SETFD, fcntl(fds[1], F_GETFD) | FD_CLOEXEC)) { close(fds[0]); close(fds[1]); fds[0] = -1; fds[1] = -1; return (-1); } /* * Return 0 indicating success... */ return (0); } /* * 'read_cups_files_conf()' - Read the cups-files.conf file to get the filter settings. */ static int /* O - 0 on success, 1 on error */ read_cups_files_conf( const char *filename) /* I - File to read */ { cups_file_t *fp; /* cups-files.conf file */ const char *temp; /* Temporary string */ char line[1024], /* Line from file */ *ptr; /* Pointer into line */ int linenum; /* Current line number */ if ((temp = getenv("CUPS_DATADIR")) != NULL) set_string(&DataDir, temp); else set_string(&DataDir, CUPS_DATADIR); if ((temp = getenv("CUPS_SERVERBIN")) != NULL) set_string(&ServerBin, temp); else set_string(&ServerBin, CUPS_SERVERBIN); strlcpy(line, filename, sizeof(line)); if ((ptr = strrchr(line, '/')) != NULL) *ptr = '\0'; else getcwd(line, sizeof(line)); set_string(&ServerRoot, line); if ((fp = cupsFileOpen(filename, "r")) != NULL) { linenum = 0; while (cupsFileGetConf(fp, line, sizeof(line), &ptr, &linenum)) { if (!_cups_strcasecmp(line, "DataDir")) set_string(&DataDir, ptr); else if (!_cups_strcasecmp(line, "ServerBin")) set_string(&ServerBin, ptr); else if (!_cups_strcasecmp(line, "ServerRoot")) set_string(&ServerRoot, ptr); } cupsFileClose(fp); } #if CUPS_SNAP if ((temp = getenv("PATH")) != NULL) snprintf(line, sizeof(line), "%s/filter:" CUPS_BINDIR ":" CUPS_SBINDIR ":%s", ServerBin, temp); else #endif /* CUPS_SNAP */ snprintf(line, sizeof(line), "%s/filter:" CUPS_BINDIR ":" CUPS_SBINDIR ":/bin:/usr/bin", ServerBin); set_string(&Path, line); return (0); } /* * 'set_string()' - Copy and set a string. */ static void set_string(char **s, /* O - Copy of string */ const char *val) /* I - String to copy */ { if (*s) free(*s); *s = strdup(val); } /* * 'sighandler()' - Signal catcher for when we print from stdin... */ static void sighandler(int s) /* I - Signal number */ { /* * Remove the temporary file we're using to print a job file... */ if (TempFile[0]) unlink(TempFile); /* * Exit... */ exit(s); } /* * 'usage()' - Show program usage... */ static void usage(const char *opt) /* I - Incorrect option, if any */ { if (opt) _cupsLangPrintf(stderr, _("%s: Unknown option \"%c\"."), "cupsfilter", *opt); _cupsLangPuts(stdout, _("Usage: cupsfilter [ options ] [ -- ] filename")); _cupsLangPuts(stdout, _("Options:")); _cupsLangPuts(stdout, _(" --list-filters List filters that will be used.")); _cupsLangPuts(stdout, _(" -D Remove the input file when finished.")); _cupsLangPuts(stdout, _(" -P filename.ppd Set PPD file.")); _cupsLangPuts(stdout, _(" -U username Specify username.")); _cupsLangPuts(stdout, _(" -c cups-files.conf Set cups-files.conf file to use.")); _cupsLangPuts(stdout, _(" -d printer Use the named printer.")); _cupsLangPuts(stdout, _(" -e Use every filter from the PPD file.")); _cupsLangPuts(stdout, _(" -i mime/type Set input MIME type (otherwise auto-typed).")); _cupsLangPuts(stdout, _(" -j job-id[,N] Filter file N from the specified job (default is file 1).")); _cupsLangPuts(stdout, _(" -m mime/type Set output MIME type (otherwise application/pdf).")); _cupsLangPuts(stdout, _(" -n copies Set number of copies.")); _cupsLangPuts(stdout, _(" -o name=value Set option(s).")); _cupsLangPuts(stdout, _(" -p filename.ppd Set PPD file.")); _cupsLangPuts(stdout, _(" -t title Set title.")); _cupsLangPuts(stdout, _(" -u Remove the PPD file when finished.")); exit(1); }