• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* foomaticrip.c
2  *
3  * Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com>
4  * Copyright (C) 2008 Lars Karlitski (formerly Uebernickel) <lars@karlitski.net>
5  *
6  * This file is part of foomatic-rip.
7  *
8  * Foomatic-rip is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * Foomatic-rip is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23 
24 #include "foomaticrip.h"
25 #include "util.h"
26 #include "options.h"
27 #include "pdf.h"
28 #include "postscript.h"
29 #include "process.h"
30 #include "spooler.h"
31 #include "renderer.h"
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <strings.h>
36 #include <errno.h>
37 #include <memory.h>
38 #include <ctype.h>
39 #include <stdarg.h>
40 #include <assert.h>
41 #include <unistd.h>
42 #include <sys/wait.h>
43 #include <math.h>
44 #include <signal.h>
45 #include <pwd.h>
46 #include <cupsfilters/colormanager.h>
47 
48 /* Logging */
49 FILE* logh = NULL;
50 
_logv(const char * msg,va_list ap)51 void _logv(const char *msg, va_list ap)
52 {
53     if (!logh)
54         return;
55     vfprintf(logh, msg, ap);
56     fflush(logh);
57 }
58 
_log(const char * msg,...)59 void _log(const char* msg, ...)
60 {
61     va_list ap;
62     va_start(ap, msg);
63     _logv(msg, ap);
64     va_end(ap);
65 }
66 
close_log()67 void close_log()
68 {
69     if (logh && logh != stderr)
70         fclose(logh);
71 }
72 
redirect_log_to_stderr()73 int redirect_log_to_stderr()
74 {
75     if (dup2(fileno(logh), fileno(stderr)) < 0) {
76         _log("Could not dup logh to stderr\n");
77         return 0;
78     }
79     return 1;
80 }
81 
rip_die(int status,const char * msg,...)82 void rip_die(int status, const char *msg, ...)
83 {
84     va_list ap;
85 
86     _log("Process is dying with \"");
87     va_start(ap, msg);
88     _logv(msg, ap);
89     va_end(ap);
90     _log("\", exit stat %d\n", status);
91 
92     _log("Cleaning up...\n");
93     kill_all_processes();
94 
95     exit(status);
96 }
97 
98 
99 jobparams_t  *job = NULL;
100 
get_current_job()101 jobparams_t * get_current_job()
102 {
103     assert(job);
104     return job;
105 }
106 
107 
108 dstr_t *postpipe = NULL;  /* command into which the output of this filter should be piped */
109 FILE *postpipe_fh = NULL;
110 
open_postpipe()111 FILE * open_postpipe()
112 {
113     const char *p;
114 
115     if (postpipe_fh)
116         return postpipe_fh;
117 
118     if (isempty(postpipe->data))
119         return stdout;
120 
121     /* Delete possible '|' symbol in the beginning */
122     p = skip_whitespace(postpipe->data);
123     if (*p && *p == '|')
124         p += 1;
125 
126     if (start_system_process("postpipe", p, &postpipe_fh, NULL) < 0)
127         rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS,
128                 "Cannot execute postpipe %s\n", postpipe->data);
129 
130     return postpipe_fh;
131 }
132 
133 
134 char printer_model[256] = "";
135 char attrpath[256] = "";
136 
137 
138 int spooler = SPOOLER_DIRECT;
139 int dontparse = 0;
140 int jobhasjcl;
141 int pdfconvertedtops;
142 
143 
144 /* cm-calibration flag */
145 int cm_calibrate = 0;
146 
147 int cm_disabled = 0;
148 
149 /* These variables were in 'dat' before */
150 char colorprofile [128];
151 char cupsfilter[256];
152 char **jclprepend = NULL;
153 dstr_t *jclappend;
154 
155 /* Set debug to 1 to enable the debug logfile for this filter; it will
156  * appear as defined by LOG_FILE. It will contain status from this
157  * filter, plus the renderer's stderr output. You can also add a line
158  * "debug: 1" to your /etc/cups/foomatic-rip.conf or
159  * /etc/foomatic/filter.conf to get all your Foomatic filters into
160  * debug mode.  WARNING: This logfile is a security hole; do not use
161  * in production. */
162 int debug = 0;
163 
164 /* Path to the GhostScript which foomatic-rip shall use */
165 char gspath[PATH_MAX] = "gs";
166 
167 /* What 'echo' program to use.  It needs -e and -n.  Linux's builtin
168 and regular echo work fine; non-GNU platforms may need to install
169 gnu echo and put gecho here or something. */
170 char echopath[PATH_MAX] = "echo";
171 
172 /* CUPS raster drivers are searched here */
173 char cupsfilterpath[PATH_MAX] = "/usr/local/lib/cups/filter:"
174                                 "/usr/local/libexec/cups/filter:"
175                                 "/opt/cups/filter:"
176                                 "/usr/lib/cups/filter";
177 
178 char modern_shell[] = SHELL;
179 
config_set_option(const char * key,const char * value)180 void config_set_option(const char *key, const char *value)
181 {
182     if (strcmp(key, "debug") == 0)
183         debug = atoi(value);
184 
185     /* What path to use for filter programs and such
186      *
187      * Your printer driver must be in the path, as must be the renderer,
188      * and possibly other stuff. The default path is often fine on Linux,
189      * but may not be on other systems. */
190     else if (strcmp(key, "execpath") == 0 && !isempty(value))
191         setenv("PATH", value, 1);
192 
193     else if (strcmp(key, "cupsfilterpath") == 0)
194         strlcpy(cupsfilterpath, value, PATH_MAX);
195     else if (strcmp(key, "preferred_shell") == 0)
196         strlcpy(modern_shell, value, 32);
197     else if (strcmp(key, "gspath") == 0)
198         strlcpy(gspath, value, PATH_MAX);
199     else if (strcmp(key, "echo") == 0)
200         strlcpy(echopath, value, PATH_MAX);
201 }
202 
config_from_file(const char * filename)203 int config_from_file(const char *filename)
204 {
205     FILE *fh;
206     char line[256];
207     char *key, *value;
208 
209     fh = fopen(filename, "r");
210     if (fh == NULL)
211         return 0;
212 
213     while (fgets(line, 256, fh) != NULL)
214     {
215         key = strtok(line, " :\t\r\n");
216         if (key == NULL || key[0] == '#')
217             continue;
218         value = strtok(NULL, " \t\r\n#");
219         config_set_option(key, value);
220     }
221     fclose(fh);
222 
223     return 1;
224 }
225 
get_modern_shell()226 const char * get_modern_shell()
227 {
228     return modern_shell;
229 }
230 
231 /* returns position in 'str' after the option */
extract_next_option(char * str,char ** pagerange,char ** key,char ** value)232 char * extract_next_option(char *str, char **pagerange, char **key, char **value)
233 {
234     char *p = str;
235     char quotechar;
236 
237     *pagerange = NULL;
238     *key = NULL;
239     *value = NULL;
240 
241     if (!str)
242         return NULL;
243 
244     /* skip whitespace */
245     while (*p && isspace(*p)) p++;
246 
247     if (!*p)
248         return NULL;
249 
250     /* read the pagerange if we have one */
251     if (prefixcmp(p, "even:") == 0 || prefixcmp(p, "odd:") == 0 || isdigit(*p)) {
252         *pagerange = p;
253         p = strchr(p, ':');
254         if (!p)
255             return NULL;
256         *p = '\0';
257         p++;
258     }
259 
260     /* read the key */
261     if (*p == '\'' || *p == '\"') {
262         quotechar = *p;
263         *key = p +1;
264         p = strchr(*key, quotechar);
265         if (!p)
266             return NULL;
267     }
268     else {
269         *key = p;
270         while (*p && *p != ':' && *p != '=' && !isspace(*p)) p++;
271     }
272 
273     if (*p != ':' && *p != '=') { /* no value for this option */
274         if (!*p)
275             return NULL;
276         else if (isspace(*p)) {
277             *p = '\0';
278             return p +1;
279         }
280         return p;
281     }
282 
283     *p++ = '\0'; /* remove the separator sign */
284 
285     if (*p == '\"' || *p == '\'') {
286         quotechar = *p;
287         *value = p +1;
288         p = strchr(*value, quotechar);
289         if (!p)
290             return NULL;
291         *p = '\0';
292         p++;
293     }
294     else {
295         *value = p;
296         while (*p && !isspace(*p)) p++;
297         if (*p == '\0')
298             return NULL;
299         *p = '\0';
300         p++;
301     }
302 
303     return *p ? p : NULL;
304 }
305 
306 /* processes job->optstr */
process_cmdline_options()307 void process_cmdline_options()
308 {
309     char *p, *cmdlineopts, *nextopt, *pagerange, *key, *value;
310     option_t *opt, *opt2;
311     int optset;
312     char tmp [256];
313 
314     _log("Printing system options:\n");
315     cmdlineopts = strdup(job->optstr->data);
316     for (nextopt = extract_next_option(cmdlineopts, &pagerange, &key, &value);
317         key;
318         nextopt = extract_next_option(nextopt, &pagerange, &key, &value))
319     {
320         /* Consider only options which are not in the PPD file here */
321         if ((opt = find_option(key)) != NULL) continue;
322         if (value)
323             _log("Pondering option '%s=%s'\n", key, value);
324         else
325             _log("Pondering option '%s'\n", key);
326 
327         /* "profile" option to supply a color correction profile to a CUPS raster driver */
328         if (!strcmp(key, "profile")) {
329             strlcpy(colorprofile, value, 128);
330             continue;
331         }
332         /* option to set color calibration mode */
333         if (!strcmp(key, "cm-calibration")) {
334             cm_calibrate = 1;
335             continue;
336         }
337         /* Solaris options that have no reason to be */
338         if (!strcmp(key, "nobanner") || !strcmp(key, "dest") || !strcmp(key, "protocol"))
339             continue;
340 
341         if (pagerange) {
342             snprintf(tmp, 256, "pages:%s", pagerange);
343             optset = optionset(tmp);
344         }
345         else
346             optset = optionset("userval");
347 
348         if (value) {
349             if (strcasecmp(key, "media") == 0) {
350                 /*  Standard arguments?
351                     media=x,y,z
352                     sides=one|two-sided-long|short-edge
353 
354                     Rummage around in the media= option for known media, source,
355                     etc types.
356                     We ought to do something sensible to make the common manual
357                     boolean option work when specified as a media= tray thing.
358 
359                     Note that this fails miserably when the option value is in
360                     fact a number; they all look alike.  It's unclear how many
361                     drivers do that.  We may have to standardize the verbose
362                     names to make them work as selections, too. */
363 
364 	        if (value[0] == '\0')
365 		    continue;
366                 p = strtok(value, ",");
367                 do {
368                     if ((opt = find_option("PageSize")) && option_accepts_value(opt, p))
369                         option_set_value(opt, optset, p);
370                     else if ((opt = find_option("MediaType")) && option_has_choice(opt, p))
371                         option_set_value(opt, optset, p);
372                     else if ((opt = find_option("InputSlot")) && option_has_choice(opt, p))
373                         option_set_value(opt, optset, p);
374                     else if (!strcasecmp(p, "manualfeed")) {
375                         /* Special case for our typical boolean manual
376                            feeder option if we didn't match an InputSlot above */
377                         if ((opt = find_option("ManualFeed")))
378                             option_set_value(opt, optset, "1");
379                     }
380                     else
381                         _log("Unknown \"media\" component: \"%s\".\n", p);
382 
383                 } while ((p = strtok(NULL, ",")));
384             }
385             else if (!strcasecmp(key, "sides")) {
386                 /* Handle the standard duplex option, mostly */
387                 if (!prefixcasecmp(value, "two-sided")) {
388                     if ((opt = find_option("Duplex"))) {
389                         /* Default to long-edge binding here, for the case that
390                            there is no binding setting */
391                         option_set_value(opt, optset, "DuplexNoTumble");
392 
393                         /* Check the binding: "long edge" or "short edge" */
394                         if (strcasestr(value, "long-edge")) {
395                             if ((opt2 = find_option("Binding")))
396                                 option_set_value(opt2, optset, "LongEdge");
397                             else
398                                 option_set_value(opt, optset, "DuplexNoTumble");
399                         }
400                         else if (strcasestr(value, "short-edge")) {
401                             if ((opt2 = find_option("Binding")))
402                                 option_set_value(opt2, optset, "ShortEdge");
403                             else
404                                 option_set_value(opt, optset, "DuplexTumble");
405                         }
406                     }
407                 }
408                 else if (!prefixcasecmp(value, "one-sided")) {
409                     if ((opt = find_option("Duplex")))
410                         option_set_value(opt, optset, "0");
411                 }
412 
413                 /*  TODO
414                     We should handle the other half of this option - the
415                     BindEdge bit.  Also, are there well-known ipp/cups options
416                     for Collate and StapleLocation?  These may be here...
417                 */
418             }
419 	    else
420 	        _log("Unknown option %s=%s.\n", key, value);
421         }
422         /* Custom paper size */
423         else if ((opt = find_option("PageSize")) && option_set_value(opt, optset, key)) {
424             /* do nothing, if the value could be set, it has been set */
425         }
426         else
427             _log("Unknown boolean option \"%s\".\n", key);
428     }
429     free(cmdlineopts);
430 
431     /* We 'clear' the profile if cm-calibration mode was specified */
432     if (cm_calibrate) {
433         colorprofile[0] = '\0';
434         cm_disabled = 1;
435     }
436 
437     _log("CM Color Calibration Mode in CUPS: %s\n", cm_calibrate ?
438          "Activated" : "Off");
439 
440     _log("Options from the PPD file:\n");
441     cmdlineopts = strdup(job->optstr->data);
442     for (nextopt = extract_next_option(cmdlineopts, &pagerange, &key, &value);
443         key;
444         nextopt = extract_next_option(nextopt, &pagerange, &key, &value))
445     {
446         /* Consider only PPD file options here */
447         if ((opt = find_option(key)) == NULL) continue;
448         if (value)
449             _log("Pondering option '%s=%s'\n", key, value);
450         else
451             _log("Pondering option '%s'\n", key);
452 
453         if (pagerange) {
454             snprintf(tmp, 256, "pages:%s", pagerange);
455             optset = optionset(tmp);
456 
457             if (opt && (option_get_section(opt) != SECTION_ANYSETUP &&
458                         option_get_section(opt) != SECTION_PAGESETUP)) {
459                 _log("This option (%s) is not a \"PageSetup\" or \"AnySetup\" option, so it cannot be restricted to a page range.\n", key);
460                 continue;
461             }
462         }
463         else
464             optset = optionset("userval");
465 
466         if (value) {
467 	    /* Various non-standard printer-specific options */
468 	    if (!option_set_value(opt, optset, value)) {
469 	        _log("  invalid choice \"%s\", using \"%s\" instead\n",
470 		     value, option_get_value(opt, optset));
471 	    }
472         }
473         /* Standard bool args:
474            landscape; what to do here?
475            duplex; we should just handle this one OK now? */
476         else if (!prefixcasecmp(key, "no"))
477             option_set_value(opt, optset, "0");
478         else
479             option_set_value(opt, optset, "1");
480     }
481     free(cmdlineopts);
482 }
483 
484 /*  Functions to let foomatic-rip fork to do several tasks in parallel.
485 
486 To do the filtering without loading the whole file into memory we work
487 on a data stream, we read the data line by line analyse it to decide what
488 filters to use and start the filters if we have found out which we need.
489 We buffer the data only as long as we didn't determing which filters to
490 use for this piece of data and with which options. There are no temporary
491 files used.
492 
493 foomatic-rip splits into up to 3 parallel processes to do the whole
494 filtering (listed in the order of the data flow):
495 
496    MAIN: Prepare the job auto-detecting the spooler, reading the PPD,
497          extracting the options from the command line, and parsing
498          the job data itself. It analyses the job data to check
499          whether it is PostScript or PDF, it also stuffs PostScript
500          code from option settings into the PostScript data stream.
501          It starts the renderer (KID3/KID4) as soon as it knows its
502          command line and restarts it when page-specific option
503          settings need another command line or different JCL commands.
504    KID3: The rendering process. In most cases Ghostscript, "cat"
505          for native PostScript printers with their manufacturer's
506          PPD files.
507    KID4: Put together the JCL commands and the renderer's output
508          and send all that either to STDOUT or pipe it into the
509          command line defined with $postpipe. */
510 
511 
512 
write_output(void * data,size_t len)513 void write_output(void *data, size_t len)
514 {
515     const char *p = (const char *)data;
516     size_t left = len;
517     FILE *postpipe = open_postpipe();
518 
519     /* Remove leading whitespace */
520     while (isspace(*p++) && left-- > 0)
521         ;
522 
523     fwrite_or_die((void *)p, left, 1, postpipe);
524     fflush(postpipe);
525 }
526 
527 enum FileType {
528     UNKNOWN_FILE,
529     PDF_FILE,
530     PS_FILE
531 };
532 
guess_file_type(const char * begin,size_t len,int * startpos)533 int guess_file_type(const char *begin, size_t len, int *startpos)
534 {
535     const char * p, * end;
536     p = begin;
537     end = begin + len;
538 
539     while (p < end)
540     {
541         p = memchr(p, '%', end - p);
542 	if (!p)
543 	    return UNKNOWN_FILE;
544 	*startpos = p - begin;
545 	if ((end - p) >= 2 && !memcmp(p, "%!", 2))
546 	    return PS_FILE;
547 	else if ((end - p) > 7 && !memcmp(p, "%PDF-1.", 7))
548 	    return PDF_FILE;
549 	++ p;
550     }
551     *startpos = 0;
552     return UNKNOWN_FILE;
553 }
554 
555 /*
556  * Prints 'filename'. If 'convert' is true, the file will be converted if it is
557  * not postscript or pdf
558  */
print_file(const char * filename,int convert)559 int print_file(const char *filename, int convert)
560 {
561     FILE *file;
562     char buf[8192];
563     char tmpfilename[PATH_MAX] = "";
564     int type;
565     int startpos;
566     int pagecount;
567     size_t n;
568     int ret;
569 
570     if (!strcasecmp(filename, "<STDIN>"))
571         file = stdin;
572     else {
573         file = fopen(filename, "r");
574         if (!file) {
575             _log("Could not open \"%s\" for reading\n", filename);
576             return 0;
577         }
578     }
579 
580     n = fread_or_die(buf, 1, sizeof(buf) - 1, file);
581     if (!n){
582         _log("Input is empty, outputting empty file.\n");
583         return 1;
584     }
585     buf[n] = '\0';
586     type = guess_file_type(buf, n, &startpos);
587     /* We do not use any JCL preceeded to the input data, as it is simply
588        the PJL commands from the PPD file, and these commands we can also
589        generate, end we even merge them with PJl from the driver */
590     /*if (startpos > 0) {
591         jobhasjcl = 1;
592         write_output(buf, startpos);
593     }*/
594     if (file != stdin)
595         rewind(file);
596 
597     if (convert) pdfconvertedtops = 0;
598 
599     switch (type) {
600         case PDF_FILE:
601             _log("Filetype: PDF\n");
602 
603             if (!ppd_supports_pdf())
604             {
605                 char pdf2ps_cmd[CMDLINE_MAX];
606                 FILE *out, *in;
607                 int renderer_pid;
608 
609                 _log("Driver does not understand PDF input, "
610                      "converting to PostScript\n");
611 
612 		pdfconvertedtops = 1;
613 
614 		/* If reading from stdin, write everything into a temporary file */
615 		if (file == stdin)
616                 {
617 		    int fd;
618 		    FILE *tmpfile;
619 
620 		    snprintf(tmpfilename, PATH_MAX, "%s/foomatic-XXXXXX", temp_dir());
621 		    fd = mkstemp(tmpfilename);
622 		    if (fd < 0) {
623 		        _log("Could not create temporary file: %s\n", strerror(errno));
624 		        return EXIT_PRNERR_NORETRY_BAD_SETTINGS;
625 		    }
626 		    tmpfile = fdopen(fd, "r+");
627 		    copy_file(tmpfile, stdin, buf, n);
628 		    fclose(tmpfile);
629 
630 		    filename = tmpfilename;
631 		}
632 
633                 pagecount = pdf_count_pages(filename);
634                 _log("File contains %d pages.\n", pagecount);
635                 if (pagecount < 0) {
636                     _log("Unexpected page_count\n");
637                     return 0;
638                 }
639                 if (pagecount == 0) {
640                   _log("No pages left, outputting empty file.\n");
641                   return 1;
642                 }
643 
644 		/* If the spooler is CUPS we use the pdftops filter of CUPS,
645 		   to have always the same PDF->PostScript conversion method
646 		   in the whole printing environment, including incompatibility
647 		   workarounds in the CUPS filter (so this way we also have to
648 		   maintain all these quirks only once).
649 
650 		   The "-dNOINTERPOLATE" makes Ghostscript rendering
651 		   significantly faster.
652 
653 		   The "-dNOMEDIAATTRS" makes Ghostscript not checking the
654 		   page sizes against a list of known sizes and try to
655 		   correct them.
656 
657 		   Note that Ghostscript's "pswrite" output device turns text
658 		   into bitmaps and therefore produces huge PostScript files.
659 		   In addition, this output device is deprecated. Therefore
660 		   we use "ps2write".
661 
662 		   We give priority to Ghostscript here and use Poppler if
663 		   Ghostscript is not available. */
664 		if (spooler == SPOOLER_CUPS)
665 		  snprintf(pdf2ps_cmd, CMDLINE_MAX,
666 			   "pdftops '%s' '%s' '%s' '%s' '%s' '%s'",
667 			   job->id, job->user, job->title, "1", job->optstr->data,
668 			   filename);
669 		else
670 		  snprintf(pdf2ps_cmd, CMDLINE_MAX,
671 			   "gs -q -sstdout=%%stderr -sDEVICE=ps2write -sOutputFile=- "
672 			   "-dBATCH -dNOPAUSE -dSAFER -dNOINTERPOLATE -dNOMEDIAATTRS -dShowAcroForm %s 2>/dev/null || "
673 			   "pdftops -level2 -origpagesizes %s - 2>/dev/null",
674 			   filename, filename);
675 
676                 renderer_pid = start_system_process("pdf-to-ps", pdf2ps_cmd, &in, &out);
677 
678                 if (dup2(fileno(out), fileno(stdin)) < 0)
679                     rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS,
680                             "Couldn't dup stdout of pdf-to-ps\n");
681 
682                 clearerr(stdin);
683 
684                 ret = print_file("<STDIN>", 0);
685 
686                 wait_for_process(renderer_pid);
687                 if (in != NULL)
688                   fclose(in);
689                 if (out != NULL)
690                   fclose(out);
691 
692                 // Delete temp file if we created one
693                 if ( *tmpfilename )
694                     unlink(tmpfilename);
695 
696                 return ret;
697             }
698 
699             if (file == stdin)
700                 return print_pdf(stdin, buf, n, filename, startpos);
701             else
702                 return print_pdf(file, NULL, 0, filename, startpos);
703 
704         case PS_FILE:
705             _log("Filetype: PostScript\n");
706             if (file == stdin)
707             {
708                 if (convert)
709                 {
710                     int fd;
711                     FILE *tmpfile;
712 
713                     snprintf(tmpfilename, PATH_MAX, "%s/foomatic-XXXXXX", temp_dir());
714                     fd = mkstemp(tmpfilename);
715                     if (fd < 0) {
716                         _log("Could not create temporary file: %s\n", strerror(errno));
717                         return EXIT_PRNERR_NORETRY_BAD_SETTINGS;
718                     }
719 
720                     if ((tmpfile = fdopen(fd,"r+")) == 0) {
721                         _log("ERROR: Can't fdopen temporary file\n");
722                         close(fd);
723                         return 0;
724                     }
725 
726                     /* Copy stdin to the tmp file */
727                     copy_file(tmpfile, stdin, buf, n);
728                     if (fflush(tmpfile) == EOF)
729                       _log("ERROR: Cannot flush buffer: %s\n", strerror(errno));
730                     rewind(tmpfile);
731 
732                     ret = print_ps(tmpfile, NULL, 0, tmpfilename);
733                     fclose(tmpfile);
734                     unlink(tmpfilename);
735                     return ret;
736                 }
737                 else
738                     return print_ps(stdin, buf, n, filename);
739             }
740             else
741                 return print_ps(file, NULL, 0, filename);
742 
743         case UNKNOWN_FILE:
744 	    _log("Cannot process \"%s\": Unknown filetype.\n", filename);
745 	    if (file != NULL)
746 	      fclose(file);
747 	    return 0;
748     }
749 
750     fclose(file);
751     return 1;
752 }
753 
signal_terminate(int signal)754 void signal_terminate(int signal)
755 {
756     rip_die(EXIT_PRINTED, "Caught termination signal: Job canceled\n");
757 }
758 
create_job()759 jobparams_t * create_job()
760 {
761     jobparams_t *job = calloc(1, sizeof(jobparams_t));
762     struct passwd *passwd;
763 
764     job->optstr = create_dstr();
765     job->time = time(NULL);
766     strcpy(job->copies, "1");
767     gethostname(job->host, 128);
768     passwd = getpwuid(getuid());
769     if (passwd)
770         strlcpy(job->user, passwd->pw_name, 128);
771     snprintf(job->title, 2048, "%s@%s", job->user, job->host);
772 
773     return job;
774 }
775 
free_job(jobparams_t * job)776 void free_job(jobparams_t *job)
777 {
778     free_dstr(job->optstr);
779     free(job);
780 }
781 
main(int argc,char ** argv)782 int main(int argc, char** argv)
783 {
784     int i;
785     int verbose = 0, quiet = 0;
786     const char* str;
787     char *p, *filename;
788     const char *path;
789     char tmp[1024], profile_arg[256], gstoraster[512];
790     int havefilter, havegstoraster;
791     dstr_t *filelist;
792     list_t * arglist;
793 
794     arglist = list_create_from_array(argc -1, (void**)&argv[1]);
795 
796     if (argc == 2 && (arglist_find(arglist, "--version") || arglist_find(arglist, "--help") ||
797                 arglist_find(arglist, "-v") || arglist_find(arglist, "-h"))) {
798         printf("foomatic-rip of cups-filters version "VERSION"\n");
799         printf("\"man foomatic-rip\" for help.\n");
800         list_free(arglist);
801         return 0;
802     }
803 
804     filelist = create_dstr();
805     job = create_job();
806 
807     jclprepend = NULL;
808     jclappend = create_dstr();
809     postpipe = create_dstr();
810 
811     options_init();
812 
813     signal(SIGTERM, signal_terminate);
814     signal(SIGINT, signal_terminate);
815     signal(SIGPIPE, SIG_IGN);
816 
817     /* First try to find a config file in the CUS config directory, like
818        /etc/cups/foomatic-rip.conf */
819     i = 0;
820     if ((str = getenv("CUPS_SERVERROOT")) != NULL) {
821 	snprintf(tmp, sizeof(tmp), "%s/foomatic-rip.conf", str);
822 	i = config_from_file(tmp);
823     }
824     /* If there is none, fall back to /etc/foomatic/filter.conf */
825     if (i == 0)
826         i = config_from_file(CONFIG_PATH "/filter.conf");
827 
828     /* Command line options for verbosity */
829     if (arglist_remove_flag(arglist, "-v"))
830         verbose = 1;
831     if (arglist_remove_flag(arglist, "-q"))
832         quiet = 1;
833     if (arglist_remove_flag(arglist, "--debug"))
834         debug = 1;
835 
836     if (debug) {
837 #if defined(__UCLIBC__) || defined(__NetBSD__)
838 	sprintf(tmp, "%s-log-XXXXXX", LOG_FILE);
839 	int fd = mkstemp (tmp);
840 #else
841 	sprintf(tmp, "%s-XXXXXX.log", LOG_FILE);
842 	int fd = mkstemps (tmp, 4);
843 #endif
844 	if (fd != -1)
845 	    logh = fdopen(fd, "w");
846 	else
847 	    logh = stderr;
848     } else if (quiet && !verbose)
849         logh = NULL; /* Quiet mode, do not log */
850     else
851         logh = stderr; /* Default: log to stderr */
852 
853     /* Start debug logging */
854     if (debug) {
855         /* If we are not in debug mode, we do this later, as we must find out at
856         first which spooler is used. When printing without spooler we
857         suppress logging because foomatic-rip is called directly on the
858         command line and so we avoid logging onto the console. */
859         _log("foomatic-rip version "VERSION" running...\n");
860 
861         /* Print the command line only in debug mode, Mac OS X adds very many
862         options so that CUPS cannot handle the output of the command line
863         in its log files. If CUPS encounters a line with more than 1024
864         characters sent into its log files, it aborts the job with an error. */
865         if (spooler != SPOOLER_CUPS) {
866             _log("called with arguments: ");
867             for (i = 1; i < argc -1; i++)
868                 _log("\'%s\', ", argv[i]);
869             _log("\'%s\'\n", argv[i]);
870         }
871     }
872 
873     if (getenv("PPD")) {
874         strncpy(job->ppdfile, getenv("PPD"), sizeof(job->ppdfile) - 1);
875         if (strlen(getenv("PPD")) > 2047)
876           job->ppdfile[2047] = '\0';
877         spooler = SPOOLER_CUPS;
878     if (getenv("CUPS_SERVERBIN")) {
879         strncpy(cupsfilterpath, getenv("CUPS_SERVERBIN"),
880 		sizeof(cupsfilterpath) - 1);
881         if (strlen(getenv("CUPS_SERVERBIN")) > PATH_MAX-1)
882           cupsfilterpath[PATH_MAX-1] = '\0';
883         }
884     }
885 
886     /* Check status of printer color management from the color manager */
887     cm_disabled = cmIsPrinterCmDisabled(getenv("PRINTER"));
888 
889     /* CUPS calls foomatic-rip only with 5 or 6 positional parameters,
890        not with named options, like for example "-p <string>". */
891     if (spooler != SPOOLER_CUPS) {
892 
893         if ((str = arglist_get_value(arglist, "-j")) || (str = arglist_get_value(arglist, "-J"))) {
894             strncpy_omit(job->title, str, 2048, omit_shellescapes);
895           if (!arglist_remove(arglist, "-j"))
896         	arglist_remove(arglist, "-J");
897 	}
898 
899         /* PPD file name given via the command line
900            allow duplicates, and use the last specified one */
901             while ((str = arglist_get_value(arglist, "-p"))) {
902                 strncpy(job->ppdfile, str, sizeof(job->ppdfile) - 1);
903                 if (strlen(str) > 2047)
904                   job->ppdfile[2047] = '\0';
905                 arglist_remove(arglist, "-p");
906             }
907 	    while ((str = arglist_get_value(arglist, "--ppd"))) {
908 	        strncpy(job->ppdfile, str, sizeof(job->ppdfile) - 1);
909 	        if (strlen(str) > 2047)
910 	          job->ppdfile[2047] = '\0';
911 	        arglist_remove(arglist, "--ppd");
912 	    }
913 
914         /* Options for spooler-less printing */
915         while ((str = arglist_get_value(arglist, "-o"))) {
916             strncpy_omit(tmp, str, 1024, omit_shellescapes);
917             dstrcatf(job->optstr, "%s ", tmp);
918             /* if "-o cm-calibration" was passed, we raise a flag */
919             if (!strcmp(tmp, "cm-calibration")) {
920                 cm_calibrate = 1;
921                 cm_disabled = 1;
922             }
923             arglist_remove(arglist, "-o");
924 	    /* We print without spooler */
925 	    spooler = SPOOLER_DIRECT;
926         }
927 
928         /* Printer for spooler-less printing */
929         if ((str = arglist_get_value(arglist, "-d"))) {
930             strncpy_omit(job->printer, str, 256, omit_shellescapes);
931             arglist_remove(arglist, "-d");
932         }
933 
934         /* Printer for spooler-less printing */
935         if ((str = arglist_get_value(arglist, "-P"))) {
936             strncpy_omit(job->printer, str, 256, omit_shellescapes);
937             arglist_remove(arglist, "-P");
938         }
939 
940     }
941 
942     _log("'CM Color Calibration' Mode in SPOOLER-LESS: %s\n", cm_calibrate ?
943          "Activated" : "Off");
944 
945     /* spooler specific initialization */
946     switch (spooler) {
947 
948         case SPOOLER_CUPS:
949             init_cups(arglist, filelist, job);
950             break;
951 
952         case SPOOLER_DIRECT:
953             init_direct(arglist, filelist, job);
954             break;
955     }
956 
957     /* Files to be printed (can be more than one for spooler-less printing) */
958     /* Empty file list -> print STDIN */
959     dstrtrim(filelist);
960     if (filelist->len == 0)
961         dstrcpyf(filelist, "<STDIN>");
962 
963     /* Check filelist */
964     p = strtok(strdup(filelist->data), " ");
965     while (p) {
966         if (strcmp(p, "<STDIN>") != 0) {
967             if (p[0] == '-')
968                 rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Invalid argument: %s", p);
969             else if (access(p, R_OK) != 0) {
970                 _log("File %s does not exist/is not readable\n", p);
971             strclr(p);
972             }
973         }
974         p = strtok(NULL, " ");
975     }
976 
977     /* When we print without spooler do not log onto STDERR unless
978        the "-v" ('Verbose') is set or the debug mode is used */
979     if (spooler == SPOOLER_DIRECT && !verbose && !debug) {
980         if (logh && logh != stderr)
981             fclose(logh);
982         logh = NULL;
983     }
984 
985     /* If we are in debug mode, we do this earlier. */
986     if (!debug) {
987         _log("foomatic-rip version " VERSION " running...\n");
988         /* Print the command line only in debug mode, Mac OS X adds very many
989         options so that CUPS cannot handle the output of the command line
990         in its log files. If CUPS encounters a line with more than 1024
991         characters sent into its log files, it aborts the job with an error. */
992         if (spooler != SPOOLER_CUPS) {
993             _log("called with arguments: ");
994             for (i = 1; i < argc -1; i++)
995                 _log("\'%s\', ", argv[i]);
996             _log("\'%s\'\n", argv[i]);
997         }
998     }
999 
1000     /* PPD File */
1001     /* Load the PPD file and build a data structure for the renderer's
1002        command line and the options */
1003     if (spooler == SPOOLER_CUPS && job->printer && strlen(job->printer) > 0) {
1004       str = cupsGetPPD(job->printer);
1005       if (str) {
1006         read_ppd_file(str);
1007         unlink(str);
1008       } else
1009         read_ppd_file(job->ppdfile);
1010     } else
1011       read_ppd_file(job->ppdfile);
1012 
1013     /* We do not need to parse the PostScript job when we don't have
1014        any options. If we have options, we must check whether the
1015        default settings from the PPD file are valid and correct them
1016        if nexessary. */
1017     if (option_count() == 0) {
1018         /* We don't have any options, so we do not need to parse the
1019            PostScript data */
1020         dontparse = 1;
1021     }
1022 
1023     /* Is our PPD for a CUPS raster driver */
1024     if (!isempty(cupsfilter)) {
1025         /* Search the filter in cupsfilterpath
1026            The %Y is a placeholder for the option settings */
1027         havefilter = 0;
1028         path = cupsfilterpath;
1029         while ((path = strncpy_tochar(tmp, path, 1024, ":"))) {
1030             strlcat(tmp, "/", 1024);
1031             strlcat(tmp, cupsfilter, 1024);
1032             if (access(tmp, X_OK) == 0) {
1033                 havefilter = 1;
1034                 strlcpy(cupsfilter, tmp, 256);
1035                 strlcat(cupsfilter, " 0 '' '' 0 '%Y%X'", 256);
1036                 break;
1037             }
1038         }
1039 
1040         if (!havefilter) {
1041             /* We do not have the required filter, so we assume that
1042                rendering this job is supposed to be done on a remote
1043                server. So we do not define a renderer command line and
1044                embed only the option settings (as we had a PostScript
1045                printer). This way the settings are taken into account
1046                when the job is rendered on the server.*/
1047             _log("CUPS filter for this PPD file not found - assuming that job will "
1048                  "be rendered on a remote server. Only the PostScript of the options"
1049                  "will be inserted into the PostScript data stream.\n");
1050         }
1051         else {
1052             /* use gstoraster filter if available, otherwise run Ghostscript
1053 	       directly */
1054             havegstoraster = 0;
1055             path = cupsfilterpath;
1056             while ((path = strncpy_tochar(tmp, path, 1024, ":"))) {
1057                 strlcat(tmp, "/gstoraster", 1024);
1058                 if (access(tmp, X_OK) == 0) {
1059                     havegstoraster = 1;
1060                     strlcpy(gstoraster, tmp, 256);
1061                     strlcat(gstoraster, " 0 '' '' 0 '%X'", 256);
1062                     break;
1063                 }
1064             }
1065             if (!havegstoraster) {
1066                 const char **qualifier = NULL;
1067                 char *icc_profile = NULL;
1068 
1069                 if (!cm_disabled) {
1070                   qualifier = get_ppd_qualifier();
1071                   _log("INFO: Using qualifer: '%s.%s.%s'\n",
1072                         qualifier[0], qualifier[1], qualifier[2]);
1073 
1074                   cmGetPrinterIccProfile(getenv("PRINTER"),
1075 					 (char **)&icc_profile, 0);
1076 
1077                   /* fall back to PPD */
1078                   if (icc_profile == NULL) {
1079                     _log("INFO: need to look in PPD for matching qualifer\n");
1080                     icc_profile = get_icc_profile_for_qualifier(qualifier);
1081                   }
1082                 }
1083 
1084                 /* ICC profile is specified for Ghostscript unless
1085                    "cm-calibration" option was passed in foomatic-rip */
1086                 if (icc_profile != NULL)
1087                   snprintf(profile_arg, sizeof(profile_arg),
1088                            "-sOutputICCProfile='%s'", icc_profile);
1089                 else
1090                   profile_arg[0] = '\0';
1091 
1092                 snprintf(gstoraster, sizeof(gstoraster), "gs -dQUIET -dDEBUG -dSAFER -dNOPAUSE -dBATCH -dNOINTERPOLATE -dNOMEDIAATTRS -sDEVICE=cups -dShowAcroForm %s -sOutputFile=- -", profile_arg);
1093                 free(icc_profile);
1094             }
1095 
1096             /* build Ghostscript/CUPS driver command line */
1097             snprintf(cmd, 1024, "%s | %s", gstoraster, cupsfilter);
1098             _log("INFO: Using command line: %s\n", cmd);
1099 
1100             /* Set environment variables */
1101             setenv("PPD", job->ppdfile, 1);
1102         }
1103     }
1104 
1105     /* Was the RIP command line defined in the PPD file? If not, we assume a PostScript printer
1106        and do not render/translate the input data */
1107     if (isempty(cmd)) {
1108         strcpy(cmd, "cat%A%B%C%D%E%F%G%H%I%J%K%L%M%Z");
1109         if (dontparse) {
1110             /* No command line, no options, we have a raw queue, don't check
1111                whether the input is PostScript, simply pass the input data to
1112                the backend.*/
1113             dontparse = 2;
1114             strcpy(printer_model, "Raw queue");
1115         }
1116     }
1117 
1118     /* Summary for debugging */
1119     _log("\nParameter Summary\n"
1120          "-----------------\n\n"
1121          "Spooler: %s\n"
1122          "Printer: %s\n"
1123          "Shell: %s\n"
1124          "PPD file: %s\n"
1125          "ATTR file: %s\n"
1126          "Printer model: %s\n",
1127         spooler_name(spooler), job->printer, get_modern_shell(), job->ppdfile, attrpath, printer_model);
1128     /* Print the options string only in debug mode, Mac OS X adds very many
1129        options so that CUPS cannot handle the output of the option string
1130        in its log files. If CUPS encounters a line with more than 1024 characters
1131        sent into its log files, it aborts the job with an error.*/
1132     if (debug || spooler != SPOOLER_CUPS)
1133         _log("Options: %s\n", job->optstr->data);
1134     _log("Job title: %s\n", job->title);
1135     _log("File(s) to be printed:\n");
1136     _log("%s\n\n", filelist->data);
1137     if (getenv("GS_LIB"))
1138         _log("Ghostscript extra search path ('GS_LIB'): %s\n", getenv("GS_LIB"));
1139 
1140     /* Process options from command line */
1141     optionset_copy_values(optionset("default"), optionset("userval"));
1142     process_cmdline_options();
1143 
1144     /* no postpipe for CUPS , even if one is defined in the PPD file */
1145     if (spooler == SPOOLER_CUPS )
1146         dstrclear(postpipe);
1147 
1148     if (postpipe->len)
1149         _log("Output will be redirected to:\n%s\n", postpipe);
1150 
1151 
1152     filename = strtok_r(filelist->data, " ", &p);
1153     while (filename) {
1154         _log("\n================================================\n\n"
1155              "File: %s\n\n"
1156              "================================================\n\n", filename);
1157 
1158         /* Do we have a raw queue? */
1159         if (dontparse == 2) {
1160             /* Raw queue, simply pass the input into the postpipe (or to STDOUT
1161                when there is no postpipe) */
1162             _log("Raw printing, executing \"cat %%s\"\n\n");
1163             snprintf(tmp, 1024, "cat %s", postpipe->data);
1164             if (strcasecmp(filename, "<STDIN>")) {
1165               FILE *fh = fopen(filename, "r");
1166               if (!fh) {
1167                 _log("Failed to open \"%s\".\n", filename);
1168                 fclose(stdin);
1169               } else {
1170                 dup2(fileno(fh), 0);
1171                 fclose(fh);
1172               }
1173             }
1174             run_system_process("raw-printer", tmp);
1175             filename = strtok_r(NULL, " ", &p);
1176             continue;
1177         }
1178 
1179         /* First, for arguments with a default, stick the default in as
1180            the initial value for the "header" option set, this option set
1181            consists of the PPD defaults, the options specified on the
1182            command line, and the options set in the header part of the
1183            PostScript file (all before the first page begins). */
1184         optionset_copy_values(optionset("userval"), optionset("header"));
1185 
1186         if (!print_file(filename, 1))
1187 	    rip_die(EXIT_PRNERR_NORETRY, "Could not print file %s\n", filename);
1188         filename = strtok_r(NULL, " ", &p);
1189     }
1190 
1191     /* Close the last input file */
1192     fclose(stdin);
1193 
1194     /* TODO dump everything in $dat when debug is turned on (necessary?) */
1195 
1196     _log("\nClosing foomatic-rip.\n");
1197 
1198 
1199     /* Cleanup */
1200     free_job(job);
1201     free_dstr(filelist);
1202     options_free();
1203     close_log();
1204 
1205     argv_free(jclprepend);
1206     free_dstr(jclappend);
1207 
1208     list_free(arglist);
1209 
1210     return EXIT_PRINTED;
1211 }
1212 
1213