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