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