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