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