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