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