]> git.ipfire.org Git - thirdparty/cups.git/blob - backend/ipp.c
Load cups into easysw/current.
[thirdparty/cups.git] / backend / ipp.c
1 /*
2 * "$Id: ipp.c 6482 2007-04-30 17:05:59Z mike $"
3 *
4 * IPP backend for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
13 * at:
14 *
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636 USA
19 *
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * This file is subject to the Apple OS-Developed Software exception.
25 *
26 * Contents:
27 *
28 * main() - Send a file to the printer or server.
29 * cancel_job() - Cancel a print job.
30 * check_printer_state() - Check the printer state...
31 * compress_files() - Compress print files...
32 * password_cb() - Disable the password prompt for
33 * cupsDoFileRequest().
34 * report_printer_state() - Report the printer state.
35 * run_pictwps_filter() - Convert PICT files to PostScript when printing
36 * remotely.
37 * sigterm_handler() - Handle 'terminate' signals that stop the backend.
38 */
39
40 /*
41 * Include necessary headers.
42 */
43
44 #include <cups/http-private.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <errno.h>
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #include <cups/backend.h>
51 #include <cups/cups.h>
52 #include <cups/language.h>
53 #include <cups/i18n.h>
54 #include <cups/string.h>
55 #include <signal.h>
56 #include <sys/wait.h>
57
58 /*
59 * Globals...
60 */
61
62 static char *password = NULL; /* Password for device URI */
63 #ifdef __APPLE__
64 static char pstmpname[1024] = ""; /* Temporary PostScript file name */
65 #endif /* __APPLE__ */
66 static char tmpfilename[1024] = ""; /* Temporary spool file name */
67 static int job_cancelled = 0; /* Job cancelled? */
68
69
70 /*
71 * Local functions...
72 */
73
74 static void cancel_job(http_t *http, const char *uri, int id,
75 const char *resource, const char *user, int version);
76 static void check_printer_state(http_t *http, const char *uri,
77 const char *resource, const char *user,
78 int version);
79 #ifdef HAVE_LIBZ
80 static void compress_files(int num_files, char **files);
81 #endif /* HAVE_LIBZ */
82 static const char *password_cb(const char *);
83 static int report_printer_state(ipp_t *ipp);
84
85 #ifdef __APPLE__
86 static int run_pictwps_filter(char **argv, const char *filename);
87 #endif /* __APPLE__ */
88 static void sigterm_handler(int sig);
89
90
91 /*
92 * 'main()' - Send a file to the printer or server.
93 *
94 * Usage:
95 *
96 * printer-uri job-id user title copies options [file]
97 */
98
99 int /* O - Exit status */
100 main(int argc, /* I - Number of command-line args */
101 char *argv[]) /* I - Command-line arguments */
102 {
103 int i; /* Looping var */
104 int send_options; /* Send job options? */
105 int num_options; /* Number of printer options */
106 cups_option_t *options; /* Printer options */
107 char method[255], /* Method in URI */
108 hostname[1024], /* Hostname */
109 username[255], /* Username info */
110 resource[1024], /* Resource info (printer name) */
111 addrname[256], /* Address name */
112 *optptr, /* Pointer to URI options */
113 name[255], /* Name of option */
114 value[255], /* Value of option */
115 *ptr; /* Pointer into name or value */
116 int num_files; /* Number of files to print */
117 char **files, /* Files to print */
118 *filename; /* Pointer to single filename */
119 int port; /* Port number (not used) */
120 char uri[HTTP_MAX_URI]; /* Updated URI without user/pass */
121 ipp_status_t ipp_status; /* Status of IPP request */
122 http_t *http; /* HTTP connection */
123 ipp_t *request, /* IPP request */
124 *response, /* IPP response */
125 *supported; /* get-printer-attributes response */
126 time_t start_time; /* Time of first connect */
127 int recoverable; /* Recoverable error shown? */
128 int contimeout; /* Connection timeout */
129 int delay; /* Delay for retries... */
130 int compression, /* Do compression of the job data? */
131 waitjob, /* Wait for job complete? */
132 waitprinter; /* Wait for printer ready? */
133 ipp_attribute_t *job_id_attr; /* job-id attribute */
134 int job_id; /* job-id value */
135 ipp_attribute_t *job_sheets; /* job-media-sheets-completed */
136 ipp_attribute_t *job_state; /* job-state */
137 ipp_attribute_t *copies_sup; /* copies-supported */
138 ipp_attribute_t *format_sup; /* document-format-supported */
139 ipp_attribute_t *printer_state; /* printer-state attribute */
140 ipp_attribute_t *printer_accepting; /* printer-is-accepting-jobs */
141 int copies, /* Number of copies for job */
142 copies_remaining; /* Number of copies remaining */
143 const char *content_type; /* CONTENT_TYPE environment variable */
144 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
145 struct sigaction action; /* Actions for POSIX signals */
146 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
147 int version; /* IPP version */
148 static const char * const pattrs[] =
149 { /* Printer attributes we want */
150 "copies-supported",
151 "document-format-supported",
152 "printer-is-accepting-jobs",
153 "printer-state",
154 "printer-state-message",
155 "printer-state-reasons",
156 };
157 static const char * const jattrs[] =
158 { /* Job attributes we want */
159 "job-media-sheets-completed",
160 "job-state"
161 };
162
163
164 /*
165 * Make sure status messages are not buffered...
166 */
167
168 setbuf(stderr, NULL);
169
170 /*
171 * Ignore SIGPIPE and catch SIGTERM signals...
172 */
173
174 #ifdef HAVE_SIGSET
175 sigset(SIGPIPE, SIG_IGN);
176 sigset(SIGTERM, sigterm_handler);
177 #elif defined(HAVE_SIGACTION)
178 memset(&action, 0, sizeof(action));
179 action.sa_handler = SIG_IGN;
180 sigaction(SIGPIPE, &action, NULL);
181
182 sigemptyset(&action.sa_mask);
183 sigaddset(&action.sa_mask, SIGTERM);
184 action.sa_handler = sigterm_handler;
185 sigaction(SIGTERM, &action, NULL);
186 #else
187 signal(SIGPIPE, SIG_IGN);
188 signal(SIGTERM, sigterm_handler);
189 #endif /* HAVE_SIGSET */
190
191 /*
192 * Check command-line...
193 */
194
195 if (argc == 1)
196 {
197 char *s;
198
199 if ((s = strrchr(argv[0], '/')) != NULL)
200 s ++;
201 else
202 s = argv[0];
203
204 printf("network %s \"Unknown\" \"Internet Printing Protocol (%s)\"\n",
205 s, s);
206 return (CUPS_BACKEND_OK);
207 }
208 else if (argc < 6)
209 {
210 fprintf(stderr, _("Usage: %s job-id user title copies options [file]\n"),
211 argv[0]);
212 return (CUPS_BACKEND_STOP);
213 }
214
215 /*
216 * Get the (final) content type...
217 */
218
219 if ((content_type = getenv("FINAL_CONTENT_TYPE")) == NULL)
220 if ((content_type = getenv("CONTENT_TYPE")) == NULL)
221 content_type = "application/octet-stream";
222
223 if (!strncmp(content_type, "printer/", 8))
224 content_type = "application/vnd.cups-raw";
225
226 /*
227 * Extract the hostname and printer name from the URI...
228 */
229
230 if (httpSeparateURI(HTTP_URI_CODING_ALL, cupsBackendDeviceURI(argv),
231 method, sizeof(method), username, sizeof(username),
232 hostname, sizeof(hostname), &port,
233 resource, sizeof(resource)) < HTTP_URI_OK)
234 {
235 fputs(_("ERROR: Missing device URI on command-line and no "
236 "DEVICE_URI environment variable!\n"), stderr);
237 return (CUPS_BACKEND_STOP);
238 }
239
240 if (!strcmp(method, "https"))
241 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
242 else
243 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
244
245 /*
246 * See if there are any options...
247 */
248
249 compression = 0;
250 version = 1;
251 waitjob = 1;
252 waitprinter = 1;
253 contimeout = 7 * 24 * 60 * 60;
254
255 if ((optptr = strchr(resource, '?')) != NULL)
256 {
257 /*
258 * Yup, terminate the device name string and move to the first
259 * character of the optptr...
260 */
261
262 *optptr++ = '\0';
263
264 /*
265 * Then parse the optptr...
266 */
267
268 while (*optptr)
269 {
270 /*
271 * Get the name...
272 */
273
274 for (ptr = name; *optptr && *optptr != '=';)
275 if (ptr < (name + sizeof(name) - 1))
276 *ptr++ = *optptr++;
277 *ptr = '\0';
278
279 if (*optptr == '=')
280 {
281 /*
282 * Get the value...
283 */
284
285 optptr ++;
286
287 for (ptr = value; *optptr && *optptr != '+' && *optptr != '&';)
288 if (ptr < (value + sizeof(value) - 1))
289 *ptr++ = *optptr++;
290 *ptr = '\0';
291
292 if (*optptr == '+' || *optptr == '&')
293 optptr ++;
294 }
295 else
296 value[0] = '\0';
297
298 /*
299 * Process the option...
300 */
301
302 if (!strcasecmp(name, "waitjob"))
303 {
304 /*
305 * Wait for job completion?
306 */
307
308 waitjob = !strcasecmp(value, "on") ||
309 !strcasecmp(value, "yes") ||
310 !strcasecmp(value, "true");
311 }
312 else if (!strcasecmp(name, "waitprinter"))
313 {
314 /*
315 * Wait for printer idle?
316 */
317
318 waitprinter = !strcasecmp(value, "on") ||
319 !strcasecmp(value, "yes") ||
320 !strcasecmp(value, "true");
321 }
322 else if (!strcasecmp(name, "encryption"))
323 {
324 /*
325 * Enable/disable encryption?
326 */
327
328 if (!strcasecmp(value, "always"))
329 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
330 else if (!strcasecmp(value, "required"))
331 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
332 else if (!strcasecmp(value, "never"))
333 cupsSetEncryption(HTTP_ENCRYPT_NEVER);
334 else if (!strcasecmp(value, "ifrequested"))
335 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
336 else
337 {
338 fprintf(stderr,
339 _("ERROR: Unknown encryption option value \"%s\"!\n"),
340 value);
341 }
342 }
343 else if (!strcasecmp(name, "version"))
344 {
345 if (!strcmp(value, "1.0"))
346 version = 0;
347 else if (!strcmp(value, "1.1"))
348 version = 1;
349 else
350 {
351 fprintf(stderr,
352 _("ERROR: Unknown version option value \"%s\"!\n"),
353 value);
354 }
355 }
356 #ifdef HAVE_LIBZ
357 else if (!strcasecmp(name, "compression"))
358 {
359 compression = !strcasecmp(value, "true") ||
360 !strcasecmp(value, "yes") ||
361 !strcasecmp(value, "on") ||
362 !strcasecmp(value, "gzip");
363 }
364 #endif /* HAVE_LIBZ */
365 else if (!strcasecmp(name, "contimeout"))
366 {
367 /*
368 * Set the connection timeout...
369 */
370
371 if (atoi(value) > 0)
372 contimeout = atoi(value);
373 }
374 else
375 {
376 /*
377 * Unknown option...
378 */
379
380 fprintf(stderr, _("ERROR: Unknown option \"%s\" with value \"%s\"!\n"),
381 name, value);
382 }
383 }
384 }
385
386 /*
387 * If we have 7 arguments, print the file named on the command-line.
388 * Otherwise, copy stdin to a temporary file and print the temporary
389 * file.
390 */
391
392 if (argc == 6)
393 {
394 /*
395 * Copy stdin to a temporary file...
396 */
397
398 int fd; /* File descriptor */
399 cups_file_t *fp; /* Temporary file */
400 char buffer[8192]; /* Buffer for copying */
401 int bytes; /* Number of bytes read */
402
403
404 if ((fd = cupsTempFd(tmpfilename, sizeof(tmpfilename))) < 0)
405 {
406 perror("ERROR: unable to create temporary file");
407 return (CUPS_BACKEND_FAILED);
408 }
409
410 if ((fp = cupsFileOpenFd(fd, compression ? "w9" : "w")) == NULL)
411 {
412 perror("ERROR: unable to open temporary file");
413 close(fd);
414 unlink(tmpfilename);
415 return (CUPS_BACKEND_FAILED);
416 }
417
418 while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
419 if (cupsFileWrite(fp, buffer, bytes) < bytes)
420 {
421 perror("ERROR: unable to write to temporary file");
422 cupsFileClose(fp);
423 unlink(tmpfilename);
424 return (CUPS_BACKEND_FAILED);
425 }
426
427 cupsFileClose(fp);
428
429 /*
430 * Point to the single file from stdin...
431 */
432
433 filename = tmpfilename;
434 files = &filename;
435 num_files = 1;
436
437 send_options = 0;
438 }
439 else
440 {
441 /*
442 * Point to the files on the command-line...
443 */
444
445 num_files = argc - 6;
446 files = argv + 6;
447
448 send_options = strncasecmp(content_type, "application/vnd.cups-", 21) != 0;
449
450 #ifdef HAVE_LIBZ
451 if (compression)
452 compress_files(num_files, files);
453 #endif /* HAVE_LIBZ */
454 }
455
456 fprintf(stderr, "DEBUG: %d files to send in job...\n", num_files);
457
458 /*
459 * Set the authentication info, if any...
460 */
461
462 cupsSetPasswordCB(password_cb);
463
464 if (username[0])
465 {
466 /*
467 * Use authenticaion information in the device URI...
468 */
469
470 if ((password = strchr(username, ':')) != NULL)
471 *password++ = '\0';
472
473 cupsSetUser(username);
474 }
475 else if (!getuid())
476 {
477 /*
478 * Try loading authentication information from the a##### file.
479 */
480
481 const char *request_root; /* CUPS_REQUESTROOT env var */
482 char afilename[1024], /* a##### filename */
483 aline[2048]; /* Line from file */
484 FILE *fp; /* File pointer */
485
486
487 if ((request_root = getenv("CUPS_REQUESTROOT")) != NULL)
488 {
489 /*
490 * Try opening authentication cache file...
491 */
492
493 snprintf(afilename, sizeof(afilename), "%s/a%05d", request_root,
494 atoi(argv[1]));
495 if ((fp = fopen(afilename, "r")) != NULL)
496 {
497 /*
498 * Read username...
499 */
500
501 if (fgets(aline, sizeof(aline), fp))
502 {
503 /*
504 * Decode username...
505 */
506
507 i = sizeof(username);
508 httpDecode64_2(username, &i, aline);
509
510 /*
511 * Read password...
512 */
513
514 if (fgets(aline, sizeof(aline), fp))
515 {
516 /*
517 * Decode password...
518 */
519
520 i = sizeof(password);
521 httpDecode64_2(password, &i, aline);
522 }
523 }
524
525 /*
526 * Close the file...
527 */
528
529 fclose(fp);
530 }
531 }
532 }
533
534 /*
535 * Try connecting to the remote server...
536 */
537
538 delay = 5;
539 recoverable = 0;
540 start_time = time(NULL);
541
542 fputs("STATE: +connecting-to-device\n", stderr);
543
544 do
545 {
546 fprintf(stderr, _("INFO: Connecting to %s on port %d...\n"),
547 hostname, port);
548
549 if ((http = httpConnectEncrypt(hostname, port, cupsEncryption())) == NULL)
550 {
551 if (job_cancelled)
552 break;
553
554 if (getenv("CLASS") != NULL)
555 {
556 /*
557 * If the CLASS environment variable is set, the job was submitted
558 * to a class and not to a specific queue. In this case, we want
559 * to abort immediately so that the job can be requeued on the next
560 * available printer in the class.
561 */
562
563 fputs(_("INFO: Unable to contact printer, queuing on next "
564 "printer in class...\n"), stderr);
565
566 if (argc == 6 || strcmp(filename, argv[6]))
567 unlink(filename);
568
569 /*
570 * Sleep 5 seconds to keep the job from requeuing too rapidly...
571 */
572
573 sleep(5);
574
575 return (CUPS_BACKEND_FAILED);
576 }
577
578 if (errno == ECONNREFUSED || errno == EHOSTDOWN ||
579 errno == EHOSTUNREACH)
580 {
581 if (contimeout && (time(NULL) - start_time) > contimeout)
582 {
583 fputs(_("ERROR: Printer not responding!\n"), stderr);
584 return (CUPS_BACKEND_FAILED);
585 }
586
587 recoverable = 1;
588
589 fprintf(stderr,
590 _("WARNING: recoverable: Network host \'%s\' is busy; will "
591 "retry in %d seconds...\n"),
592 hostname, delay);
593
594 sleep(delay);
595
596 if (delay < 30)
597 delay += 5;
598 }
599 else if (h_errno)
600 {
601 fprintf(stderr, _("ERROR: Unable to locate printer \'%s\'!\n"),
602 hostname);
603 return (CUPS_BACKEND_STOP);
604 }
605 else
606 {
607 recoverable = 1;
608
609 fprintf(stderr, "DEBUG: Connection error: %s\n", strerror(errno));
610 fputs(_("ERROR: recoverable: Unable to connect to printer; will "
611 "retry in 30 seconds...\n"), stderr);
612 sleep(30);
613 }
614
615 if (job_cancelled)
616 break;
617 }
618 }
619 while (http == NULL);
620
621 if (job_cancelled)
622 {
623 if (argc == 6 || strcmp(filename, argv[6]))
624 unlink(filename);
625
626 return (CUPS_BACKEND_FAILED);
627 }
628
629 fputs("STATE: -connecting-to-device\n", stderr);
630 fprintf(stderr, _("INFO: Connected to %s...\n"), hostname);
631
632 #ifdef AF_INET6
633 if (http->hostaddr->addr.sa_family == AF_INET6)
634 fprintf(stderr, "DEBUG: Connected to [%s]:%d (IPv6)...\n",
635 httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
636 ntohs(http->hostaddr->ipv6.sin6_port));
637 else
638 #endif /* AF_INET6 */
639 if (http->hostaddr->addr.sa_family == AF_INET)
640 fprintf(stderr, "DEBUG: Connected to %s:%d (IPv4)...\n",
641 httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
642 ntohs(http->hostaddr->ipv4.sin_port));
643
644 /*
645 * Build a URI for the printer and fill the standard IPP attributes for
646 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
647 * might contain username:password information...
648 */
649
650 snprintf(uri, sizeof(uri), "%s://%s:%d%s", method, hostname, port, resource);
651
652 /*
653 * First validate the destination and see if the device supports multiple
654 * copies. We have to do this because some IPP servers (e.g. HP JetDirect)
655 * don't support the copies attribute...
656 */
657
658 copies_sup = NULL;
659 format_sup = NULL;
660 supported = NULL;
661
662 do
663 {
664 /*
665 * Build the IPP request...
666 */
667
668 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
669 request->request.op.version[1] = version;
670
671 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
672 NULL, uri);
673
674 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
675 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
676 NULL, pattrs);
677
678 /*
679 * Do the request...
680 */
681
682 fputs("DEBUG: Getting supported attributes...\n", stderr);
683
684 if ((supported = cupsDoRequest(http, request, resource)) == NULL)
685 ipp_status = cupsLastError();
686 else
687 ipp_status = supported->request.status.status_code;
688
689 if (ipp_status > IPP_OK_CONFLICT)
690 {
691 if (ipp_status == IPP_PRINTER_BUSY ||
692 ipp_status == IPP_SERVICE_UNAVAILABLE)
693 {
694 if (contimeout && (time(NULL) - start_time) > contimeout)
695 {
696 fputs(_("ERROR: Printer not responding!\n"), stderr);
697 return (CUPS_BACKEND_FAILED);
698 }
699
700 recoverable = 1;
701
702 fprintf(stderr,
703 _("WARNING: recoverable: Network host \'%s\' is busy; will "
704 "retry in %d seconds...\n"),
705 hostname, delay);
706
707 report_printer_state(supported);
708
709 sleep(delay);
710
711 if (delay < 30)
712 delay += 5;
713 }
714 else if ((ipp_status == IPP_BAD_REQUEST ||
715 ipp_status == IPP_VERSION_NOT_SUPPORTED) && version == 1)
716 {
717 /*
718 * Switch to IPP/1.0...
719 */
720
721 fputs(_("INFO: Printer does not support IPP/1.1, trying IPP/1.0...\n"),
722 stderr);
723 version = 0;
724 httpReconnect(http);
725 }
726 else if (ipp_status == IPP_NOT_FOUND)
727 {
728 fputs(_("ERROR: Destination printer does not exist!\n"), stderr);
729
730 if (supported)
731 ippDelete(supported);
732
733 return (CUPS_BACKEND_STOP);
734 }
735 else
736 {
737 fprintf(stderr, _("ERROR: Unable to get printer status (%s)!\n"),
738 cupsLastErrorString());
739 sleep(10);
740 }
741
742 if (supported)
743 ippDelete(supported);
744
745 continue;
746 }
747 else if ((copies_sup = ippFindAttribute(supported, "copies-supported",
748 IPP_TAG_RANGE)) != NULL)
749 {
750 /*
751 * Has the "copies-supported" attribute - does it have an upper
752 * bound > 1?
753 */
754
755 if (copies_sup->values[0].range.upper <= 1)
756 copies_sup = NULL; /* No */
757 }
758
759 format_sup = ippFindAttribute(supported, "document-format-supported",
760 IPP_TAG_MIMETYPE);
761
762 if (format_sup)
763 {
764 fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
765 format_sup->num_values);
766 for (i = 0; i < format_sup->num_values; i ++)
767 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
768 format_sup->values[i].string.text);
769 }
770
771 report_printer_state(supported);
772 }
773 while (ipp_status > IPP_OK_CONFLICT);
774
775 /*
776 * See if the printer is accepting jobs and is not stopped; if either
777 * condition is true and we are printing to a class, requeue the job...
778 */
779
780 if (getenv("CLASS") != NULL)
781 {
782 printer_state = ippFindAttribute(supported, "printer-state",
783 IPP_TAG_ENUM);
784 printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs",
785 IPP_TAG_BOOLEAN);
786
787 if (printer_state == NULL ||
788 (printer_state->values[0].integer > IPP_PRINTER_PROCESSING &&
789 waitprinter) ||
790 printer_accepting == NULL ||
791 !printer_accepting->values[0].boolean)
792 {
793 /*
794 * If the CLASS environment variable is set, the job was submitted
795 * to a class and not to a specific queue. In this case, we want
796 * to abort immediately so that the job can be requeued on the next
797 * available printer in the class.
798 */
799
800 fputs(_("INFO: Unable to contact printer, queuing on next "
801 "printer in class...\n"), stderr);
802
803 ippDelete(supported);
804 httpClose(http);
805
806 if (argc == 6 || strcmp(filename, argv[6]))
807 unlink(filename);
808
809 /*
810 * Sleep 5 seconds to keep the job from requeuing too rapidly...
811 */
812
813 sleep(5);
814
815 return (CUPS_BACKEND_FAILED);
816 }
817 }
818
819 if (recoverable)
820 {
821 /*
822 * If we've shown a recoverable error make sure the printer proxies
823 * have a chance to see the recovered message. Not pretty but
824 * necessary for now...
825 */
826
827 fputs("INFO: recovered: \n", stderr);
828 sleep(5);
829 }
830
831 /*
832 * See if the printer supports multiple copies...
833 */
834
835 copies = atoi(argv[4]);
836
837 if (copies_sup || argc < 7)
838 {
839 copies_remaining = 1;
840
841 if (argc < 7)
842 copies = 1;
843 }
844 else
845 copies_remaining = copies;
846
847 /*
848 * Then issue the print-job request...
849 */
850
851 job_id = 0;
852
853 while (copies_remaining > 0)
854 {
855 /*
856 * Build the IPP request...
857 */
858
859 if (job_cancelled)
860 break;
861
862 if (num_files > 1)
863 request = ippNewRequest(IPP_CREATE_JOB);
864 else
865 request = ippNewRequest(IPP_PRINT_JOB);
866
867 request->request.op.version[1] = version;
868
869 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
870 NULL, uri);
871
872 fprintf(stderr, "DEBUG: printer-uri = \"%s\"\n", uri);
873
874 if (argv[2][0])
875 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
876 "requesting-user-name", NULL, argv[2]);
877
878 fprintf(stderr, "DEBUG: requesting-user-name = \"%s\"\n", argv[2]);
879
880 /*
881 * Only add a "job-name" attribute if the remote server supports
882 * copy generation - some IPP implementations like HP's don't seem
883 * to like UTF-8 job names (STR #1837)...
884 */
885
886 if (argv[3][0] && copies_sup)
887 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
888 argv[3]);
889
890 fprintf(stderr, "DEBUG: job-name = \"%s\"\n", argv[3]);
891
892 #ifdef HAVE_LIBZ
893 if (compression)
894 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
895 "compression", NULL, "gzip");
896 #endif /* HAVE_LIBZ */
897
898 /*
899 * Handle options on the command-line...
900 */
901
902 options = NULL;
903 num_options = cupsParseOptions(argv[5], 0, &options);
904
905 #ifdef __APPLE__
906 if (content_type != NULL &&
907 !strcasecmp(content_type, "application/pictwps") && num_files == 1)
908 {
909 if (format_sup != NULL)
910 {
911 for (i = 0; i < format_sup->num_values; i ++)
912 if (!strcasecmp(content_type, format_sup->values[i].string.text))
913 break;
914 }
915
916 if (format_sup == NULL || i >= format_sup->num_values)
917 {
918 /*
919 * Remote doesn't support "application/pictwps" (i.e. it's not MacOS X)
920 * so convert the document to PostScript...
921 */
922
923 if (run_pictwps_filter(argv, filename))
924 return (CUPS_BACKEND_FAILED);
925
926 filename = pstmpname;
927
928 /*
929 * Change the MIME type to application/postscript and change the
930 * number of copies to 1...
931 */
932
933 content_type = "application/postscript";
934 copies = 1;
935 copies_remaining = 1;
936 send_options = 0;
937 }
938 }
939 #endif /* __APPLE__ */
940
941 if (content_type != NULL && format_sup != NULL)
942 {
943 for (i = 0; i < format_sup->num_values; i ++)
944 if (!strcasecmp(content_type, format_sup->values[i].string.text))
945 break;
946
947 if (i < format_sup->num_values)
948 num_options = cupsAddOption("document-format", content_type,
949 num_options, &options);
950 }
951
952 if (copies_sup && version > 0 && send_options)
953 {
954 /*
955 * Only send options if the destination printer supports the copies
956 * attribute and IPP/1.1. This is a hack for the HP and Lexmark
957 * implementations of IPP, which do not accept extension attributes
958 * and incorrectly report a client-error-bad-request error instead of
959 * the successful-ok-unsupported-attributes status. In short, at least
960 * some HP and Lexmark implementations of IPP are non-compliant.
961 */
962
963 cupsEncodeOptions(request, num_options, options);
964
965 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies",
966 copies);
967 }
968
969 cupsFreeOptions(num_options, options);
970
971 /*
972 * If copies aren't supported, then we are likely dealing with an HP
973 * JetDirect. The HP IPP implementation seems to close the connection
974 * after every request - that is, it does *not* implement HTTP Keep-
975 * Alive, which is REQUIRED by HTTP/1.1...
976 */
977
978 if (!copies_sup)
979 httpReconnect(http);
980
981 /*
982 * Do the request...
983 */
984
985 if (num_files > 1)
986 response = cupsDoRequest(http, request, resource);
987 else
988 response = cupsDoFileRequest(http, request, resource, files[0]);
989
990 ipp_status = cupsLastError();
991
992 if (ipp_status > IPP_OK_CONFLICT)
993 {
994 job_id = 0;
995
996 if (job_cancelled)
997 break;
998
999 if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1000 ipp_status == IPP_PRINTER_BUSY)
1001 {
1002 fputs(_("INFO: Printer busy; will retry in 10 seconds...\n"), stderr);
1003 sleep(10);
1004 }
1005 else if ((ipp_status == IPP_BAD_REQUEST ||
1006 ipp_status == IPP_VERSION_NOT_SUPPORTED) && version == 1)
1007 {
1008 /*
1009 * Switch to IPP/1.0...
1010 */
1011
1012 fputs(_("INFO: Printer does not support IPP/1.1, trying IPP/1.0...\n"),
1013 stderr);
1014 version = 0;
1015 httpReconnect(http);
1016 }
1017 else
1018 fprintf(stderr, _("ERROR: Print file was not accepted (%s)!\n"),
1019 cupsLastErrorString());
1020 }
1021 else if ((job_id_attr = ippFindAttribute(response, "job-id",
1022 IPP_TAG_INTEGER)) == NULL)
1023 {
1024 fputs(_("NOTICE: Print file accepted - job ID unknown.\n"), stderr);
1025 job_id = 0;
1026 }
1027 else
1028 {
1029 job_id = job_id_attr->values[0].integer;
1030 fprintf(stderr, _("NOTICE: Print file accepted - job ID %d.\n"), job_id);
1031 }
1032
1033 ippDelete(response);
1034
1035 if (job_cancelled)
1036 break;
1037
1038 if (job_id && num_files > 1)
1039 {
1040 for (i = 0; i < num_files; i ++)
1041 {
1042 request = ippNewRequest(IPP_SEND_DOCUMENT);
1043
1044 request->request.op.version[1] = version;
1045
1046 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1047 NULL, uri);
1048
1049 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1050 job_id);
1051
1052 if (argv[2][0])
1053 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1054 "requesting-user-name", NULL, argv[2]);
1055
1056 if ((i + 1) == num_files)
1057 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
1058
1059 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
1060 "document-format", NULL, content_type);
1061
1062 ippDelete(cupsDoFileRequest(http, request, resource, files[i]));
1063
1064 if (cupsLastError() > IPP_OK_CONFLICT)
1065 {
1066 ipp_status = cupsLastError();
1067
1068 fprintf(stderr, _("ERROR: Unable to add file %d to job: %s\n"),
1069 job_id, cupsLastErrorString());
1070 break;
1071 }
1072 }
1073 }
1074
1075 if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
1076 {
1077 fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
1078 copies_remaining --;
1079 }
1080 else if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1081 ipp_status == IPP_PRINTER_BUSY)
1082 break;
1083 else
1084 copies_remaining --;
1085
1086 /*
1087 * Wait for the job to complete...
1088 */
1089
1090 if (!job_id || !waitjob)
1091 continue;
1092
1093 fputs(_("INFO: Waiting for job to complete...\n"), stderr);
1094
1095 for (; !job_cancelled;)
1096 {
1097 /*
1098 * Build an IPP_GET_JOB_ATTRIBUTES request...
1099 */
1100
1101 request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
1102 request->request.op.version[1] = version;
1103
1104 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1105 NULL, uri);
1106
1107 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1108 job_id);
1109
1110 if (argv[2][0])
1111 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1112 "requesting-user-name", NULL, argv[2]);
1113
1114 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1115 "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
1116 NULL, jattrs);
1117
1118 /*
1119 * Do the request...
1120 */
1121
1122 if (!copies_sup)
1123 httpReconnect(http);
1124
1125 response = cupsDoRequest(http, request, resource);
1126 ipp_status = cupsLastError();
1127
1128 if (ipp_status == IPP_NOT_FOUND)
1129 {
1130 /*
1131 * Job has gone away and/or the server has no job history...
1132 */
1133
1134 ippDelete(response);
1135
1136 ipp_status = IPP_OK;
1137 break;
1138 }
1139
1140 if (ipp_status > IPP_OK_CONFLICT)
1141 {
1142 if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
1143 ipp_status != IPP_PRINTER_BUSY)
1144 {
1145 ippDelete(response);
1146
1147 fprintf(stderr, _("ERROR: Unable to get job %d attributes (%s)!\n"),
1148 job_id, cupsLastErrorString());
1149 break;
1150 }
1151 }
1152
1153 if (response)
1154 {
1155 if ((job_state = ippFindAttribute(response, "job-state",
1156 IPP_TAG_ENUM)) != NULL)
1157 {
1158 /*
1159 * Stop polling if the job is finished or pending-held...
1160 */
1161
1162 if (job_state->values[0].integer > IPP_JOB_STOPPED)
1163 {
1164 if ((job_sheets = ippFindAttribute(response,
1165 "job-media-sheets-completed",
1166 IPP_TAG_INTEGER)) != NULL)
1167 fprintf(stderr, "PAGE: total %d\n",
1168 job_sheets->values[0].integer);
1169
1170 ippDelete(response);
1171 break;
1172 }
1173 }
1174 }
1175
1176 ippDelete(response);
1177
1178 /*
1179 * Check the printer state and report it if necessary...
1180 */
1181
1182 check_printer_state(http, uri, resource, argv[2], version);
1183
1184 /*
1185 * Wait 10 seconds before polling again...
1186 */
1187
1188 sleep(10);
1189 }
1190 }
1191
1192 /*
1193 * Cancel the job as needed...
1194 */
1195
1196 if (job_cancelled && job_id)
1197 cancel_job(http, uri, job_id, resource, argv[2], version);
1198
1199 /*
1200 * Check the printer state and report it if necessary...
1201 */
1202
1203 check_printer_state(http, uri, resource, argv[2], version);
1204
1205 /*
1206 * Free memory...
1207 */
1208
1209 httpClose(http);
1210
1211 ippDelete(supported);
1212
1213 /*
1214 * Remove the temporary file(s) if necessary...
1215 */
1216
1217 if (tmpfilename[0])
1218 unlink(tmpfilename);
1219
1220 #ifdef HAVE_LIBZ
1221 if (compression)
1222 {
1223 for (i = 0; i < num_files; i ++)
1224 unlink(files[i]);
1225 }
1226 #endif /* HAVE_LIBZ */
1227
1228 #ifdef __APPLE__
1229 if (pstmpname[0])
1230 unlink(pstmpname);
1231 #endif /* __APPLE__ */
1232
1233 /*
1234 * Return the queue status...
1235 */
1236
1237 return (ipp_status > IPP_OK_CONFLICT ? CUPS_BACKEND_FAILED : CUPS_BACKEND_OK);
1238 }
1239
1240
1241 /*
1242 * 'cancel_job()' - Cancel a print job.
1243 */
1244
1245 static void
1246 cancel_job(http_t *http, /* I - HTTP connection */
1247 const char *uri, /* I - printer-uri */
1248 int id, /* I - job-id */
1249 const char *resource, /* I - Resource path */
1250 const char *user, /* I - requesting-user-name */
1251 int version) /* I - IPP version */
1252 {
1253 ipp_t *request; /* Cancel-Job request */
1254
1255
1256 fputs(_("INFO: Canceling print job...\n"), stderr);
1257
1258 request = ippNewRequest(IPP_CANCEL_JOB);
1259 request->request.op.version[1] = version;
1260
1261 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1262 NULL, uri);
1263 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
1264
1265 if (user && user[0])
1266 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1267 "requesting-user-name", NULL, user);
1268
1269 /*
1270 * Do the request...
1271 */
1272
1273 ippDelete(cupsDoRequest(http, request, resource));
1274
1275 if (cupsLastError() > IPP_OK_CONFLICT)
1276 fprintf(stderr, _("ERROR: Unable to cancel job %d: %s\n"), id,
1277 cupsLastErrorString());
1278 }
1279
1280
1281 /*
1282 * 'check_printer_state()' - Check the printer state...
1283 */
1284
1285 static void
1286 check_printer_state(
1287 http_t *http, /* I - HTTP connection */
1288 const char *uri, /* I - Printer URI */
1289 const char *resource, /* I - Resource path */
1290 const char *user, /* I - Username, if any */
1291 int version) /* I - IPP version */
1292 {
1293 ipp_t *request, /* IPP request */
1294 *response; /* IPP response */
1295 static const char * const attrs[] = /* Attributes we want */
1296 {
1297 "printer-state-message",
1298 "printer-state-reasons"
1299 };
1300
1301
1302 /*
1303 * Check on the printer state...
1304 */
1305
1306 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
1307 request->request.op.version[1] = version;
1308
1309 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1310 NULL, uri);
1311
1312 if (user && user[0])
1313 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1314 "requesting-user-name", NULL, user);
1315
1316 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1317 "requested-attributes",
1318 (int)(sizeof(attrs) / sizeof(attrs[0])), NULL, attrs);
1319
1320 /*
1321 * Do the request...
1322 */
1323
1324 if ((response = cupsDoRequest(http, request, resource)) != NULL)
1325 {
1326 report_printer_state(response);
1327 ippDelete(response);
1328 }
1329 }
1330
1331
1332 #ifdef HAVE_LIBZ
1333 /*
1334 * 'compress_files()' - Compress print files...
1335 */
1336
1337 static void
1338 compress_files(int num_files, /* I - Number of files */
1339 char **files) /* I - Files */
1340 {
1341 int i, /* Looping var */
1342 fd; /* Temporary file descriptor */
1343 ssize_t bytes; /* Bytes read/written */
1344 size_t total; /* Total bytes read */
1345 cups_file_t *in, /* Input file */
1346 *out; /* Output file */
1347 struct stat outinfo; /* Output file information */
1348 char filename[1024], /* Temporary filename */
1349 buffer[32768]; /* Copy buffer */
1350
1351
1352 fprintf(stderr, "DEBUG: Compressing %d job files...\n", num_files);
1353 for (i = 0; i < num_files; i ++)
1354 {
1355 if ((fd = cupsTempFd(filename, sizeof(filename))) < 0)
1356 {
1357 fprintf(stderr,
1358 _("ERROR: Unable to create temporary compressed print file: "
1359 "%s\n"),
1360 strerror(errno));
1361 exit(CUPS_BACKEND_FAILED);
1362 }
1363
1364 if ((out = cupsFileOpenFd(fd, "w9")) == NULL)
1365 {
1366 fprintf(stderr,
1367 _("ERROR: Unable to open temporary compressed print file: %s\n"),
1368 strerror(errno));
1369 exit(CUPS_BACKEND_FAILED);
1370 }
1371
1372 if ((in = cupsFileOpen(files[i], "r")) == NULL)
1373 {
1374 fprintf(stderr, _("ERROR: Unable to open print file \"%s\": %s\n"),
1375 files[i], strerror(errno));
1376 cupsFileClose(out);
1377 exit(CUPS_BACKEND_FAILED);
1378 }
1379
1380 total = 0;
1381 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
1382 if (cupsFileWrite(out, buffer, bytes) < bytes)
1383 {
1384 fprintf(stderr, _("ERROR: Unable to write %d bytes to \"%s\": %s\n"),
1385 (int)bytes, filename, strerror(errno));
1386 cupsFileClose(in);
1387 cupsFileClose(out);
1388 exit(CUPS_BACKEND_FAILED);
1389 }
1390 else
1391 total += bytes;
1392
1393 cupsFileClose(out);
1394 cupsFileClose(in);
1395
1396 files[i] = strdup(filename);
1397
1398 if (!stat(filename, &outinfo))
1399 fprintf(stderr,
1400 "DEBUG: File %d compressed to %.1f%% of original size, "
1401 CUPS_LLFMT " bytes...\n",
1402 i + 1, 100.0 * outinfo.st_size / total,
1403 CUPS_LLCAST outinfo.st_size);
1404 }
1405 }
1406 #endif /* HAVE_LIBZ */
1407
1408
1409 /*
1410 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
1411 */
1412
1413 static const char * /* O - Password */
1414 password_cb(const char *prompt) /* I - Prompt (not used) */
1415 {
1416 (void)prompt;
1417
1418 if (password)
1419 return (password);
1420 else
1421 {
1422 /*
1423 * If there is no password set in the device URI, return the
1424 * "authentication required" exit code...
1425 */
1426
1427 if (tmpfilename[0])
1428 unlink(tmpfilename);
1429
1430 #ifdef __APPLE__
1431 if (pstmpname[0])
1432 unlink(pstmpname);
1433 #endif /* __APPLE__ */
1434
1435 fputs("ATTR: auth-info-required=username,password\n", stderr);
1436
1437 exit(CUPS_BACKEND_AUTH_REQUIRED);
1438
1439 return (NULL); /* Eliminate compiler warning */
1440 }
1441 }
1442
1443
1444 /*
1445 * 'report_printer_state()' - Report the printer state.
1446 */
1447
1448 static int /* O - Number of reasons shown */
1449 report_printer_state(ipp_t *ipp) /* I - IPP response */
1450 {
1451 int i; /* Looping var */
1452 int count; /* Count of reasons shown... */
1453 ipp_attribute_t *psm, /* pritner-state-message */
1454 *reasons; /* printer-state-reasons */
1455 const char *reason; /* Current reason */
1456 const char *message; /* Message to show */
1457 char unknown[1024]; /* Unknown message string */
1458 const char *prefix; /* Prefix for STATE: line */
1459 char state[1024]; /* State string */
1460
1461
1462 if ((psm = ippFindAttribute(ipp, "printer-state-message",
1463 IPP_TAG_TEXT)) != NULL)
1464 fprintf(stderr, "INFO: %s\n", psm->values[0].string.text);
1465
1466 if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
1467 IPP_TAG_KEYWORD)) == NULL)
1468 return (0);
1469
1470 state[0] = '\0';
1471 prefix = "STATE: ";
1472
1473 for (i = 0, count = 0; i < reasons->num_values; i ++)
1474 {
1475 reason = reasons->values[i].string.text;
1476
1477 strlcat(state, prefix, sizeof(state));
1478 strlcat(state, reason, sizeof(state));
1479
1480 prefix = ",";
1481 message = "";
1482
1483 if (!strncmp(reason, "media-needed", 12))
1484 message = _("Media tray needs to be filled.");
1485 else if (!strncmp(reason, "media-jam", 9))
1486 message = _("Media jam!");
1487 else if (!strncmp(reason, "moving-to-paused", 16) ||
1488 !strncmp(reason, "paused", 6) ||
1489 !strncmp(reason, "shutdown", 8))
1490 message = _("Printer off-line.");
1491 else if (!strncmp(reason, "toner-low", 9))
1492 message = _("Toner low.");
1493 else if (!strncmp(reason, "toner-empty", 11))
1494 message = _("Out of toner!");
1495 else if (!strncmp(reason, "cover-open", 10))
1496 message = _("Cover open.");
1497 else if (!strncmp(reason, "interlock-open", 14))
1498 message = _("Interlock open.");
1499 else if (!strncmp(reason, "door-open", 9))
1500 message = _("Door open.");
1501 else if (!strncmp(reason, "input-tray-missing", 18))
1502 message = _("Media tray missing!");
1503 else if (!strncmp(reason, "media-low", 9))
1504 message = _("Media tray almost empty.");
1505 else if (!strncmp(reason, "media-empty", 11))
1506 message = _("Media tray empty!");
1507 else if (!strncmp(reason, "output-tray-missing", 19))
1508 message = _("Output tray missing!");
1509 else if (!strncmp(reason, "output-area-almost-full", 23))
1510 message = _("Output bin almost full.");
1511 else if (!strncmp(reason, "output-area-full", 16))
1512 message = _("Output bin full!");
1513 else if (!strncmp(reason, "marker-supply-low", 17))
1514 message = _("Ink/toner almost empty.");
1515 else if (!strncmp(reason, "marker-supply-empty", 19))
1516 message = _("Ink/toner empty!");
1517 else if (!strncmp(reason, "marker-waste-almost-full", 24))
1518 message = _("Ink/toner waste bin almost full.");
1519 else if (!strncmp(reason, "marker-waste-full", 17))
1520 message = _("Ink/toner waste bin full!");
1521 else if (!strncmp(reason, "fuser-over-temp", 15))
1522 message = _("Fuser temperature high!");
1523 else if (!strncmp(reason, "fuser-under-temp", 16))
1524 message = _("Fuser temperature low!");
1525 else if (!strncmp(reason, "opc-near-eol", 12))
1526 message = _("OPC almost at end-of-life.");
1527 else if (!strncmp(reason, "opc-life-over", 13))
1528 message = _("OPC at end-of-life!");
1529 else if (!strncmp(reason, "developer-low", 13))
1530 message = _("Developer almost empty.");
1531 else if (!strncmp(reason, "developer-empty", 15))
1532 message = _("Developer empty!");
1533 else if (strstr(reason, "error") != NULL)
1534 {
1535 message = unknown;
1536
1537 snprintf(unknown, sizeof(unknown), _("Unknown printer error (%s)!"),
1538 reason);
1539 }
1540
1541 if (message)
1542 {
1543 count ++;
1544 if (strstr(reasons->values[i].string.text, "error"))
1545 fprintf(stderr, "ERROR: %s\n", message);
1546 else if (strstr(reasons->values[i].string.text, "warning"))
1547 fprintf(stderr, "WARNING: %s\n", message);
1548 else
1549 fprintf(stderr, "INFO: %s\n", message);
1550 }
1551 }
1552
1553 fprintf(stderr, "%s\n", state);
1554
1555 return (count);
1556 }
1557
1558
1559 #ifdef __APPLE__
1560 /*
1561 * 'run_pictwps_filter()' - Convert PICT files to PostScript when printing
1562 * remotely.
1563 *
1564 * This step is required because the PICT format is not documented and
1565 * subject to change, so developing a filter for other OS's is infeasible.
1566 * Also, fonts required by the PICT file need to be embedded on the
1567 * client side (which has the fonts), so we run the filter to get a
1568 * PostScript file for printing...
1569 */
1570
1571 static int /* O - Exit status of filter */
1572 run_pictwps_filter(char **argv, /* I - Command-line arguments */
1573 const char *filename)/* I - Filename */
1574 {
1575 struct stat fileinfo; /* Print file information */
1576 const char *ppdfile; /* PPD file for destination printer */
1577 int pid; /* Child process ID */
1578 int fd; /* Temporary file descriptor */
1579 int status; /* Exit status of filter */
1580 const char *printer; /* PRINTER env var */
1581 static char ppdenv[1024]; /* PPD environment variable */
1582
1583
1584 /*
1585 * First get the PPD file for the printer...
1586 */
1587
1588 printer = getenv("PRINTER");
1589 if (!printer)
1590 {
1591 fputs(_("ERROR: PRINTER environment variable not defined!\n"), stderr);
1592 return (-1);
1593 }
1594
1595 if ((ppdfile = cupsGetPPD(printer)) == NULL)
1596 {
1597 fprintf(stderr,
1598 _("ERROR: Unable to get PPD file for printer \"%s\" - %s.\n"),
1599 printer, cupsLastErrorString());
1600 }
1601 else
1602 {
1603 snprintf(ppdenv, sizeof(ppdenv), "PPD=%s", ppdfile);
1604 putenv(ppdenv);
1605 }
1606
1607 /*
1608 * Then create a temporary file for printing...
1609 */
1610
1611 if ((fd = cupsTempFd(pstmpname, sizeof(pstmpname))) < 0)
1612 {
1613 fprintf(stderr, _("ERROR: Unable to create temporary file - %s.\n"),
1614 strerror(errno));
1615 if (ppdfile)
1616 unlink(ppdfile);
1617 return (-1);
1618 }
1619
1620 /*
1621 * Get the owner of the spool file - it is owned by the user we want to run
1622 * as...
1623 */
1624
1625 if (argv[6])
1626 stat(argv[6], &fileinfo);
1627 else
1628 {
1629 /*
1630 * Use the OSX defaults, as an up-stream filter created the PICT
1631 * file...
1632 */
1633
1634 fileinfo.st_uid = 1;
1635 fileinfo.st_gid = 80;
1636 }
1637
1638 if (ppdfile)
1639 chown(ppdfile, fileinfo.st_uid, fileinfo.st_gid);
1640
1641 fchown(fd, fileinfo.st_uid, fileinfo.st_gid);
1642
1643 /*
1644 * Finally, run the filter to convert the file...
1645 */
1646
1647 if ((pid = fork()) == 0)
1648 {
1649 /*
1650 * Child process for pictwpstops... Redirect output of pictwpstops to a
1651 * file...
1652 */
1653
1654 close(1);
1655 dup(fd);
1656 close(fd);
1657
1658 if (!getuid())
1659 {
1660 /*
1661 * Change to an unpriviledged user...
1662 */
1663
1664 setgid(fileinfo.st_gid);
1665 setuid(fileinfo.st_uid);
1666 }
1667
1668 execlp("pictwpstops", printer, argv[1], argv[2], argv[3], argv[4], argv[5],
1669 filename, NULL);
1670 fprintf(stderr, _("ERROR: Unable to exec pictwpstops: %s\n"),
1671 strerror(errno));
1672 return (errno);
1673 }
1674
1675 close(fd);
1676
1677 if (pid < 0)
1678 {
1679 /*
1680 * Error!
1681 */
1682
1683 fprintf(stderr, _("ERROR: Unable to fork pictwpstops: %s\n"),
1684 strerror(errno));
1685 unlink(filename);
1686 if (ppdfile)
1687 unlink(ppdfile);
1688 return (-1);
1689 }
1690
1691 /*
1692 * Now wait for the filter to complete...
1693 */
1694
1695 if (wait(&status) < 0)
1696 {
1697 fprintf(stderr, _("ERROR: Unable to wait for pictwpstops: %s\n"),
1698 strerror(errno));
1699 close(fd);
1700 unlink(filename);
1701 if (ppdfile)
1702 unlink(ppdfile);
1703 return (-1);
1704 }
1705
1706 if (ppdfile)
1707 unlink(ppdfile);
1708
1709 close(fd);
1710
1711 if (status)
1712 {
1713 if (status >= 256)
1714 fprintf(stderr, _("ERROR: pictwpstops exited with status %d!\n"),
1715 status / 256);
1716 else
1717 fprintf(stderr, _("ERROR: pictwpstops exited on signal %d!\n"),
1718 status);
1719
1720 unlink(filename);
1721 return (status);
1722 }
1723
1724 /*
1725 * Return with no errors..
1726 */
1727
1728 return (0);
1729 }
1730 #endif /* __APPLE__ */
1731
1732
1733 /*
1734 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
1735 */
1736
1737 static void
1738 sigterm_handler(int sig) /* I - Signal */
1739 {
1740 (void)sig; /* remove compiler warnings... */
1741
1742 if (!job_cancelled)
1743 {
1744 /*
1745 * Flag that the job should be cancelled...
1746 */
1747
1748 job_cancelled = 1;
1749 return;
1750 }
1751
1752 /*
1753 * The scheduler already tried to cancel us once, now just terminate
1754 * after removing our temp files!
1755 */
1756
1757 if (tmpfilename[0])
1758 unlink(tmpfilename);
1759
1760 #ifdef __APPLE__
1761 if (pstmpname[0])
1762 unlink(pstmpname);
1763 #endif /* __APPLE__ */
1764
1765 exit(1);
1766 }
1767
1768
1769 /*
1770 * End of "$Id: ipp.c 6482 2007-04-30 17:05:59Z mike $".
1771 */