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