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