]> git.ipfire.org Git - thirdparty/cups.git/blob - backend/ipp.c
Merge changes from CUPS 1.5svn-r9717.
[thirdparty/cups.git] / backend / ipp.c
1 /*
2 * "$Id: ipp.c 7948 2008-09-17 00:04:12Z mike $"
3 *
4 * IPP backend for CUPS.
5 *
6 * Copyright 2007-2011 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 * monitor_printer() - Monitor the printer state...
24 * new_request() - Create a new print creation or validation request.
25 * password_cb() - Disable the password prompt for
26 * cupsDoFileRequest().
27 * report_attr() - Report an IPP attribute value.
28 * report_printer_state() - Report the printer state.
29 * sigterm_handler() - Handle 'terminate' signals that stop the backend.
30 */
31
32 /*
33 * Include necessary headers.
34 */
35
36 #include "backend-private.h"
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/wait.h>
40
41
42 /*
43 * Types...
44 */
45
46 typedef struct _cups_monitor_s /**** Monitoring data ****/
47 {
48 const char *uri, /* Printer URI */
49 *hostname, /* Hostname */
50 *user, /* Username */
51 *resource; /* Resource path */
52 int port, /* Port number */
53 version, /* IPP version */
54 job_id; /* Job ID for submitted job */
55 http_encryption_t encryption; /* Use encryption? */
56 ipp_jstate_t job_state; /* Current job state */
57 ipp_pstate_t printer_state; /* Current printer state */
58 } _cups_monitor_t;
59
60
61 /*
62 * Globals...
63 */
64
65 static const char *auth_info_required = "none";
66 /* New auth-info-required value */
67 static const char * const jattrs[] = /* Job attributes we want */
68 {
69 "job-media-sheets-completed",
70 "job-state",
71 "job-state-reasons"
72 };
73 static int job_canceled = 0; /* Job cancelled? */
74 static char *password = NULL; /* Password for device URI */
75 static int password_tries = 0; /* Password tries */
76 static const char * const pattrs[] = /* Printer attributes we want */
77 {
78 "copies-supported",
79 "cups-version",
80 "document-format-supported",
81 "marker-colors",
82 "marker-high-levels",
83 "marker-levels",
84 "marker-low-levels",
85 "marker-message",
86 "marker-names",
87 "marker-types",
88 "media-col-supported",
89 "multiple-document-handling-supported",
90 "operations-supported",
91 "printer-alert",
92 "printer-alert-description",
93 "printer-is-accepting-jobs",
94 "printer-state",
95 "printer-state-message",
96 "printer-state-reasons",
97 };
98 static const char * const remote_job_states[] =
99 { /* Remote job state keywords */
100 "cups-remote-pending",
101 "cups-remote-pending-held",
102 "cups-remote-processing",
103 "cups-remote-stopped",
104 "cups-remote-canceled",
105 "cups-remote-aborted",
106 "cups-remote-completed"
107 };
108 static char tmpfilename[1024] = ""; /* Temporary spool file name */
109
110
111 /*
112 * Local functions...
113 */
114
115 static void cancel_job(http_t *http, const char *uri, int id,
116 const char *resource, const char *user,
117 int version);
118 static ipp_pstate_t check_printer_state(http_t *http, const char *uri,
119 const char *resource,
120 const char *user, int version,
121 int job_id);
122 #ifdef HAVE_LIBZ
123 static void compress_files(int num_files, char **files);
124 #endif /* HAVE_LIBZ */
125 static void *monitor_printer(_cups_monitor_t *monitor);
126 static ipp_t *new_request(ipp_op_t op, int version, const char *uri,
127 const char *user, const char *title,
128 int num_options, cups_option_t *options,
129 const char *compression, int copies,
130 const char *format, _ppd_cache_t *pc,
131 ipp_attribute_t *media_col_sup,
132 ipp_attribute_t *doc_handling_sup);
133 static const char *password_cb(const char *);
134 static void report_attr(ipp_attribute_t *attr);
135 static int report_printer_state(ipp_t *ipp, int job_id);
136 static void sigterm_handler(int sig);
137
138
139 /*
140 * 'main()' - Send a file to the printer or server.
141 *
142 * Usage:
143 *
144 * printer-uri job-id user title copies options [file]
145 */
146
147 int /* O - Exit status */
148 main(int argc, /* I - Number of command-line args */
149 char *argv[]) /* I - Command-line arguments */
150 {
151 int i; /* Looping var */
152 int send_options; /* Send job options? */
153 int num_options; /* Number of printer options */
154 cups_option_t *options; /* Printer options */
155 const char *device_uri; /* Device URI */
156 char scheme[255], /* Scheme in URI */
157 hostname[1024], /* Hostname */
158 username[255], /* Username info */
159 resource[1024], /* Resource info (printer name) */
160 addrname[256], /* Address name */
161 *optptr, /* Pointer to URI options */
162 *name, /* Name of option */
163 *value, /* Value of option */
164 sep; /* Separator character */
165 http_addrlist_t *addrlist; /* Address of printer */
166 int snmp_fd, /* SNMP socket */
167 start_count, /* Page count via SNMP at start */
168 page_count, /* Page count via SNMP */
169 have_supplies; /* Printer supports supply levels? */
170 int num_files; /* Number of files to print */
171 char **files, /* Files to print */
172 *compatfile = NULL; /* Compatibility filename */
173 off_t compatsize = 0; /* Size of compatibility file */
174 int port; /* Port number (not used) */
175 char portname[255]; /* Port name */
176 char uri[HTTP_MAX_URI]; /* Updated URI without user/pass */
177 http_status_t http_status; /* Status of HTTP request */
178 ipp_status_t ipp_status; /* Status of IPP request */
179 http_t *http; /* HTTP connection */
180 ipp_t *request, /* IPP request */
181 *response, /* IPP response */
182 *supported; /* get-printer-attributes response */
183 time_t start_time; /* Time of first connect */
184 int contimeout; /* Connection timeout */
185 int delay, /* Delay for retries */
186 prev_delay; /* Previous delay */
187 const char *compression; /* Compression mode */
188 int waitjob, /* Wait for job complete? */
189 waitprinter; /* Wait for printer ready? */
190 _cups_monitor_t monitor; /* Monitoring data */
191 ipp_attribute_t *job_id_attr; /* job-id attribute */
192 int job_id; /* job-id value */
193 ipp_attribute_t *job_sheets; /* job-media-sheets-completed */
194 ipp_attribute_t *job_state; /* job-state */
195 ipp_attribute_t *copies_sup; /* copies-supported */
196 ipp_attribute_t *cups_version; /* cups-version */
197 ipp_attribute_t *format_sup; /* document-format-supported */
198 ipp_attribute_t *media_col_sup; /* media-col-supported */
199 ipp_attribute_t *operations_sup; /* operations-supported */
200 ipp_attribute_t *doc_handling_sup; /* multiple-document-handling-supported */
201 ipp_attribute_t *printer_state; /* printer-state attribute */
202 ipp_attribute_t *printer_accepting; /* printer-is-accepting-jobs */
203 int validate_job; /* Does printer support Validate-Job? */
204 int copies, /* Number of copies for job */
205 copies_remaining; /* Number of copies remaining */
206 const char *content_type, /* CONTENT_TYPE environment variable */
207 *final_content_type, /* FINAL_CONTENT_TYPE environment var */
208 *document_format; /* document-format value */
209 int fd; /* File descriptor */
210 off_t bytes; /* Bytes copied */
211 char buffer[16384]; /* Copy buffer */
212 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
213 struct sigaction action; /* Actions for POSIX signals */
214 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
215 int version; /* IPP version */
216 ppd_file_t *ppd; /* PPD file */
217 _ppd_cache_t *pc; /* PPD cache and mapping data */
218
219
220 /*
221 * Make sure status messages are not buffered...
222 */
223
224 setbuf(stderr, NULL);
225
226 /*
227 * Ignore SIGPIPE and catch SIGTERM signals...
228 */
229
230 #ifdef HAVE_SIGSET
231 sigset(SIGPIPE, SIG_IGN);
232 sigset(SIGTERM, sigterm_handler);
233 #elif defined(HAVE_SIGACTION)
234 memset(&action, 0, sizeof(action));
235 action.sa_handler = SIG_IGN;
236 sigaction(SIGPIPE, &action, NULL);
237
238 sigemptyset(&action.sa_mask);
239 sigaddset(&action.sa_mask, SIGTERM);
240 action.sa_handler = sigterm_handler;
241 sigaction(SIGTERM, &action, NULL);
242 #else
243 signal(SIGPIPE, SIG_IGN);
244 signal(SIGTERM, sigterm_handler);
245 #endif /* HAVE_SIGSET */
246
247 /*
248 * Check command-line...
249 */
250
251 if (argc == 1)
252 {
253 char *s;
254
255 if ((s = strrchr(argv[0], '/')) != NULL)
256 s ++;
257 else
258 s = argv[0];
259
260 printf("network %s \"Unknown\" \"%s (%s)\"\n",
261 s, _cupsLangString(cupsLangDefault(),
262 _("Internet Printing Protocol")), s);
263 return (CUPS_BACKEND_OK);
264 }
265 else if (argc < 6)
266 {
267 _cupsLangPrintf(stderr,
268 _("Usage: %s job-id user title copies options [file]"),
269 argv[0]);
270 return (CUPS_BACKEND_STOP);
271 }
272
273 /*
274 * Get the (final) content type...
275 */
276
277 if ((content_type = getenv("CONTENT_TYPE")) == NULL)
278 content_type = "application/octet-stream";
279
280 if ((final_content_type = getenv("FINAL_CONTENT_TYPE")) == NULL)
281 {
282 final_content_type = content_type;
283
284 if (!strncmp(final_content_type, "printer/", 8))
285 final_content_type = "application/vnd.cups-raw";
286 }
287
288 /*
289 * Extract the hostname and printer name from the URI...
290 */
291
292 while ((device_uri = cupsBackendDeviceURI(argv)) == NULL)
293 {
294 _cupsLangPrintFilter(stderr, "INFO", _("Unable to locate printer."));
295 sleep(10);
296
297 if (getenv("CLASS") != NULL)
298 return (CUPS_BACKEND_FAILED);
299 }
300
301 httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme),
302 username, sizeof(username), hostname, sizeof(hostname), &port,
303 resource, sizeof(resource));
304
305 if (!port)
306 port = IPP_PORT; /* Default to port 631 */
307
308 if (!strcmp(scheme, "https"))
309 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
310 else
311 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
312
313 /*
314 * See if there are any options...
315 */
316
317 compression = NULL;
318 version = 20;
319 waitjob = 1;
320 waitprinter = 1;
321 contimeout = 7 * 24 * 60 * 60;
322
323 if ((optptr = strchr(resource, '?')) != NULL)
324 {
325 /*
326 * Yup, terminate the device name string and move to the first
327 * character of the optptr...
328 */
329
330 *optptr++ = '\0';
331
332 /*
333 * Then parse the optptr...
334 */
335
336 while (*optptr)
337 {
338 /*
339 * Get the name...
340 */
341
342 name = optptr;
343
344 while (*optptr && *optptr != '=' && *optptr != '+' && *optptr != '&')
345 optptr ++;
346
347 if ((sep = *optptr) != '\0')
348 *optptr++ = '\0';
349
350 if (sep == '=')
351 {
352 /*
353 * Get the value...
354 */
355
356 value = optptr;
357
358 while (*optptr && *optptr != '+' && *optptr != '&')
359 optptr ++;
360
361 if (*optptr)
362 *optptr++ = '\0';
363 }
364 else
365 value = (char *)"";
366
367 /*
368 * Process the option...
369 */
370
371 if (!strcasecmp(name, "waitjob"))
372 {
373 /*
374 * Wait for job completion?
375 */
376
377 waitjob = !strcasecmp(value, "on") ||
378 !strcasecmp(value, "yes") ||
379 !strcasecmp(value, "true");
380 }
381 else if (!strcasecmp(name, "waitprinter"))
382 {
383 /*
384 * Wait for printer idle?
385 */
386
387 waitprinter = !strcasecmp(value, "on") ||
388 !strcasecmp(value, "yes") ||
389 !strcasecmp(value, "true");
390 }
391 else if (!strcasecmp(name, "encryption"))
392 {
393 /*
394 * Enable/disable encryption?
395 */
396
397 if (!strcasecmp(value, "always"))
398 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
399 else if (!strcasecmp(value, "required"))
400 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
401 else if (!strcasecmp(value, "never"))
402 cupsSetEncryption(HTTP_ENCRYPT_NEVER);
403 else if (!strcasecmp(value, "ifrequested"))
404 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
405 else
406 {
407 _cupsLangPrintFilter(stderr, "ERROR",
408 _("Unknown encryption option value: \"%s\"."),
409 value);
410 }
411 }
412 else if (!strcasecmp(name, "version"))
413 {
414 if (!strcmp(value, "1.0"))
415 version = 10;
416 else if (!strcmp(value, "1.1"))
417 version = 11;
418 else if (!strcmp(value, "2.0"))
419 version = 20;
420 else if (!strcmp(value, "2.1"))
421 version = 21;
422 else if (!strcmp(value, "2.2"))
423 version = 22;
424 else
425 {
426 _cupsLangPrintFilter(stderr, "ERROR",
427 _("Unknown version option value: \"%s\"."),
428 value);
429 }
430 }
431 #ifdef HAVE_LIBZ
432 else if (!strcasecmp(name, "compression"))
433 {
434 if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") ||
435 !strcasecmp(value, "on") || !strcasecmp(value, "gzip"))
436 compression = "gzip";
437 }
438 #endif /* HAVE_LIBZ */
439 else if (!strcasecmp(name, "contimeout"))
440 {
441 /*
442 * Set the connection timeout...
443 */
444
445 if (atoi(value) > 0)
446 contimeout = atoi(value);
447 }
448 else
449 {
450 /*
451 * Unknown option...
452 */
453
454 _cupsLangPrintFilter(stderr, "ERROR",
455 _("Unknown option \"%s\" with value \"%s\"."),
456 name, value);
457 }
458 }
459 }
460
461 /*
462 * If we have 7 arguments, print the file named on the command-line.
463 * Otherwise, copy stdin to a temporary file and print the temporary
464 * file.
465 */
466
467 if (argc == 6)
468 {
469 num_files = 0;
470 send_options = !strcasecmp(final_content_type, "application/pdf") ||
471 !strcasecmp(final_content_type, "application/vnd.cups-pdf") ||
472 !strncasecmp(final_content_type, "image/", 6);
473
474 fputs("DEBUG: Sending stdin for job...\n", stderr);
475 }
476 else
477 {
478 /*
479 * Point to the files on the command-line...
480 */
481
482 num_files = argc - 6;
483 files = argv + 6;
484 send_options = 1;
485
486 #ifdef HAVE_LIBZ
487 if (compression)
488 compress_files(num_files, files);
489 #endif /* HAVE_LIBZ */
490
491 fprintf(stderr, "DEBUG: %d files to send in job...\n", num_files);
492 }
493
494 /*
495 * Set the authentication info, if any...
496 */
497
498 cupsSetPasswordCB(password_cb);
499
500 if (username[0])
501 {
502 /*
503 * Use authenticaion information in the device URI...
504 */
505
506 if ((password = strchr(username, ':')) != NULL)
507 *password++ = '\0';
508
509 cupsSetUser(username);
510 }
511 else
512 {
513 /*
514 * Try loading authentication information from the environment.
515 */
516
517 const char *ptr = getenv("AUTH_USERNAME");
518
519 if (ptr)
520 cupsSetUser(ptr);
521
522 password = getenv("AUTH_PASSWORD");
523 }
524
525 #ifdef HAVE_GSSAPI
526 /*
527 * For Kerberos, become the printing user (if we can) to get the credentials
528 * that way.
529 */
530
531 if (!getuid() && (value = getenv("AUTH_UID")) != NULL)
532 seteuid(atoi(value));
533 #endif /* HAVE_GSSAPI */
534
535 /*
536 * Try finding the remote server...
537 */
538
539 start_time = time(NULL);
540
541 sprintf(portname, "%d", port);
542
543 fputs("STATE: +connecting-to-device\n", stderr);
544 fprintf(stderr, "DEBUG: Looking up \"%s\"...\n", hostname);
545
546 while ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portname)) == NULL)
547 {
548 _cupsLangPrintFilter(stderr, "INFO",
549 _("Unable to locate printer \"%s\"."), hostname);
550 sleep(10);
551
552 if (getenv("CLASS") != NULL)
553 {
554 fputs("STATE: -connecting-to-device\n", stderr);
555 return (CUPS_BACKEND_STOP);
556 }
557 }
558
559 http = _httpCreate(hostname, port, addrlist, cupsEncryption(), AF_UNSPEC);
560
561 /*
562 * See if the printer supports SNMP...
563 */
564
565 if ((snmp_fd = _cupsSNMPOpen(addrlist->addr.addr.sa_family)) >= 0)
566 {
567 have_supplies = !backendSNMPSupplies(snmp_fd, &(addrlist->addr),
568 &start_count, NULL);
569 }
570 else
571 have_supplies = start_count = 0;
572
573 /*
574 * Wait for data from the filter...
575 */
576
577 if (num_files == 0)
578 if (!backendWaitLoop(snmp_fd, &(addrlist->addr), 0, backendNetworkSideCB))
579 return (CUPS_BACKEND_OK);
580
581 /*
582 * Try connecting to the remote server...
583 */
584
585 delay = _cupsNextDelay(0, &prev_delay);
586
587 do
588 {
589 fprintf(stderr, "DEBUG: Connecting to %s:%d\n", hostname, port);
590 _cupsLangPrintFilter(stderr, "INFO", _("Connecting to printer."));
591
592 if (httpReconnect(http))
593 {
594 int error = errno; /* Connection error */
595
596 if (http->status == HTTP_PKI_ERROR)
597 fputs("STATE: +cups-certificate-error\n", stderr);
598
599 if (job_canceled)
600 break;
601
602 if (getenv("CLASS") != NULL)
603 {
604 /*
605 * If the CLASS environment variable is set, the job was submitted
606 * to a class and not to a specific queue. In this case, we want
607 * to abort immediately so that the job can be requeued on the next
608 * available printer in the class.
609 */
610
611 _cupsLangPrintFilter(stderr, "INFO",
612 _("Unable to contact printer, queuing on next "
613 "printer in class."));
614
615 /*
616 * Sleep 5 seconds to keep the job from requeuing too rapidly...
617 */
618
619 sleep(5);
620
621 fputs("STATE: -connecting-to-device\n", stderr);
622
623 return (CUPS_BACKEND_FAILED);
624 }
625
626 fprintf(stderr, "DEBUG: Connection error: %s\n", strerror(errno));
627
628 if (errno == ECONNREFUSED || errno == EHOSTDOWN ||
629 errno == EHOSTUNREACH)
630 {
631 if (contimeout && (time(NULL) - start_time) > contimeout)
632 {
633 _cupsLangPrintFilter(stderr, "ERROR",
634 _("The printer is not responding."));
635 fputs("STATE: -connecting-to-device\n", stderr);
636 return (CUPS_BACKEND_FAILED);
637 }
638
639 switch (error)
640 {
641 case EHOSTDOWN :
642 _cupsLangPrintFilter(stderr, "WARNING",
643 _("The printer may not exist or "
644 "is unavailable at this time."));
645 break;
646
647 case EHOSTUNREACH :
648 _cupsLangPrintFilter(stderr, "WARNING",
649 _("The printer is unreachable at this "
650 "time."));
651 break;
652
653 case ECONNREFUSED :
654 default :
655 _cupsLangPrintFilter(stderr, "WARNING",
656 _("The printer is busy."));
657 break;
658 }
659
660 sleep(delay);
661
662 delay = _cupsNextDelay(delay, &prev_delay);
663 }
664 else
665 {
666 _cupsLangPrintFilter(stderr, "ERROR",
667 _("The printer is not responding."));
668 sleep(30);
669 }
670
671 if (job_canceled)
672 break;
673 }
674 else
675 fputs("STATE: -cups-certificate-error\n", stderr);
676 }
677 while (http->fd < 0);
678
679 if (job_canceled || !http)
680 return (CUPS_BACKEND_FAILED);
681
682 fputs("STATE: -connecting-to-device\n", stderr);
683 _cupsLangPrintFilter(stderr, "INFO", _("Connected to printer."));
684
685 fprintf(stderr, "DEBUG: Connected to %s:%d...\n",
686 httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
687 _httpAddrPort(http->hostaddr));
688
689 /*
690 * Build a URI for the printer and fill the standard IPP attributes for
691 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
692 * might contain username:password information...
693 */
694
695 httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), scheme, NULL, hostname,
696 port, resource);
697
698 /*
699 * First validate the destination and see if the device supports multiple
700 * copies...
701 */
702
703 copies_sup = NULL;
704 cups_version = NULL;
705 format_sup = NULL;
706 media_col_sup = NULL;
707 supported = NULL;
708 operations_sup = NULL;
709 doc_handling_sup = NULL;
710 validate_job = 0;
711
712 do
713 {
714 /*
715 * Check for side-channel requests...
716 */
717
718 backendCheckSideChannel(snmp_fd, http->hostaddr);
719
720 /*
721 * Build the IPP request...
722 */
723
724 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
725 request->request.op.version[0] = version / 10;
726 request->request.op.version[1] = version % 10;
727
728 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
729 NULL, uri);
730
731 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
732 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
733 NULL, pattrs);
734
735 /*
736 * Do the request...
737 */
738
739 fputs("DEBUG: Getting supported attributes...\n", stderr);
740
741 if (http->version < HTTP_1_1)
742 {
743 fprintf(stderr, "DEBUG: Printer responded with HTTP version %d.%d.\n",
744 http->version / 100, http->version % 100);
745
746 _cupsLangPrintFilter(stderr, "ERROR",
747 _("This printer does not conform to the IPP "
748 "standard. Please contact the manufacturer of "
749 "your printer for assistance."));
750 }
751
752 supported = cupsDoRequest(http, request, resource);
753 ipp_status = cupsLastError();
754
755 fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
756 ippErrorString(ipp_status), cupsLastErrorString());
757
758 if (ipp_status > IPP_OK_CONFLICT)
759 {
760 fprintf(stderr, "DEBUG: Get-Printer-Attributes returned %s.\n",
761 ippErrorString(ipp_status));
762
763 if (ipp_status == IPP_PRINTER_BUSY ||
764 ipp_status == IPP_SERVICE_UNAVAILABLE)
765 {
766 if (contimeout && (time(NULL) - start_time) > contimeout)
767 {
768 _cupsLangPrintFilter(stderr, "ERROR",
769 _("The printer is not responding."));
770 return (CUPS_BACKEND_FAILED);
771 }
772
773 _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
774
775 report_printer_state(supported, 0);
776
777 sleep(delay);
778
779 delay = _cupsNextDelay(delay, &prev_delay);
780 }
781 else if ((ipp_status == IPP_BAD_REQUEST ||
782 ipp_status == IPP_VERSION_NOT_SUPPORTED) && version > 10)
783 {
784 /*
785 * Switch to IPP/1.1 or IPP/1.0...
786 */
787
788 if (version >= 20)
789 {
790 _cupsLangPrintFilter(stderr, "INFO",
791 _("Printer does not support IPP/%d.%d, trying "
792 "IPP/%s."), version / 10, version % 10, "1.1");
793 version = 11;
794 }
795 else
796 {
797 _cupsLangPrintFilter(stderr, "INFO",
798 _("Printer does not support IPP/%d.%d, trying "
799 "IPP/%s."), version / 10, version % 10, "1.0");
800 version = 10;
801 }
802
803 httpReconnect(http);
804 }
805 else if (ipp_status == IPP_NOT_FOUND)
806 {
807 _cupsLangPrintFilter(stderr, "ERROR",
808 _("The printer URI is incorrect or no longer "
809 "exists."));
810
811 ippDelete(supported);
812
813 return (CUPS_BACKEND_STOP);
814 }
815 else if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
816 {
817 if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
818 "Negotiate", 9))
819 auth_info_required = "negotiate";
820
821 fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
822 return (CUPS_BACKEND_AUTH_REQUIRED);
823 }
824 else
825 {
826 _cupsLangPrintFilter(stderr, "ERROR",
827 _("Unable to get printer status."));
828 sleep(10);
829 }
830
831 ippDelete(supported);
832 supported = NULL;
833 continue;
834 }
835
836 /*
837 * Check printer-state-reasons for the "spool-area-full" keyword...
838 */
839
840 if ((printer_state = ippFindAttribute(supported, "printer-state-reasons",
841 IPP_TAG_KEYWORD)) != NULL)
842 {
843 for (i = 0; i < printer_state->num_values; i ++)
844 if (!strcmp(printer_state->values[0].string.text, "spool-area-full") ||
845 !strncmp(printer_state->values[0].string.text, "spool-area-full-",
846 16))
847 break;
848
849 if (i < printer_state->num_values)
850 {
851 _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
852
853 report_printer_state(supported, 0);
854
855 sleep(delay);
856
857 delay = _cupsNextDelay(delay, &prev_delay);
858
859 ippDelete(supported);
860 supported = NULL;
861 continue;
862 }
863 }
864 else
865 _cupsLangPrintFilter(stderr, "ERROR",
866 _("This printer does not conform to the IPP "
867 "standard. Please contact the manufacturer of "
868 "your printer for assistance."));
869
870 /*
871 * Check for supported attributes...
872 */
873
874 if ((copies_sup = ippFindAttribute(supported, "copies-supported",
875 IPP_TAG_RANGE)) != NULL)
876 {
877 /*
878 * Has the "copies-supported" attribute - does it have an upper
879 * bound > 1?
880 */
881
882 fprintf(stderr, "DEBUG: copies-supported=%d-%d\n",
883 copies_sup->values[0].range.lower,
884 copies_sup->values[0].range.upper);
885
886 if (copies_sup->values[0].range.upper <= 1)
887 copies_sup = NULL; /* No */
888 }
889
890 cups_version = ippFindAttribute(supported, "cups-version", IPP_TAG_TEXT);
891
892 if ((format_sup = ippFindAttribute(supported, "document-format-supported",
893 IPP_TAG_MIMETYPE)) != NULL)
894 {
895 fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
896 format_sup->num_values);
897 for (i = 0; i < format_sup->num_values; i ++)
898 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
899 format_sup->values[i].string.text);
900 }
901
902 if ((media_col_sup = ippFindAttribute(supported, "media-col-supported",
903 IPP_TAG_KEYWORD)) != NULL)
904 {
905 fprintf(stderr, "DEBUG: media-col-supported (%d values)\n",
906 media_col_sup->num_values);
907 for (i = 0; i < media_col_sup->num_values; i ++)
908 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
909 media_col_sup->values[i].string.text);
910 }
911
912 if ((operations_sup = ippFindAttribute(supported, "operations-supported",
913 IPP_TAG_ENUM)) != NULL)
914 {
915 for (i = 0; i < operations_sup->num_values; i ++)
916 if (operations_sup->values[i].integer == IPP_VALIDATE_JOB)
917 {
918 validate_job = 1;
919 break;
920 }
921
922 if (!validate_job)
923 {
924 _cupsLangPrintFilter(stderr, "WARNING",
925 _("This printer does not conform to the IPP "
926 "standard and may not work."));
927 fputs("DEBUG: operations-supported does not list Validate-Job.\n",
928 stderr);
929 }
930 }
931 else
932 {
933 _cupsLangPrintFilter(stderr, "WARNING",
934 _("This printer does not conform to the IPP "
935 "standard and may not work."));
936 fputs("DEBUG: operations-supported not returned in "
937 "Get-Printer-Attributes request.\n", stderr);
938 }
939
940 doc_handling_sup = ippFindAttribute(supported,
941 "multiple-document-handling-supported",
942 IPP_TAG_KEYWORD);
943
944 report_printer_state(supported, 0);
945 }
946 while (ipp_status > IPP_OK_CONFLICT);
947
948 /*
949 * See if the printer is accepting jobs and is not stopped; if either
950 * condition is true and we are printing to a class, requeue the job...
951 */
952
953 if (getenv("CLASS") != NULL)
954 {
955 printer_state = ippFindAttribute(supported, "printer-state",
956 IPP_TAG_ENUM);
957 printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs",
958 IPP_TAG_BOOLEAN);
959
960 if (printer_state == NULL ||
961 (printer_state->values[0].integer > IPP_PRINTER_PROCESSING &&
962 waitprinter) ||
963 printer_accepting == NULL ||
964 !printer_accepting->values[0].boolean)
965 {
966 /*
967 * If the CLASS environment variable is set, the job was submitted
968 * to a class and not to a specific queue. In this case, we want
969 * to abort immediately so that the job can be requeued on the next
970 * available printer in the class.
971 */
972
973 _cupsLangPrintFilter(stderr, "INFO",
974 _("Unable to contact printer, queuing on next "
975 "printer in class."));
976
977 ippDelete(supported);
978 httpClose(http);
979
980 /*
981 * Sleep 5 seconds to keep the job from requeuing too rapidly...
982 */
983
984 sleep(5);
985
986 return (CUPS_BACKEND_FAILED);
987 }
988 }
989
990 /*
991 * See if the printer supports multiple copies...
992 */
993
994 copies = atoi(argv[4]);
995
996 if (copies_sup || argc < 7)
997 {
998 copies_remaining = 1;
999
1000 if (argc < 7 && !send_options)
1001 copies = 1;
1002 }
1003 else
1004 copies_remaining = copies;
1005
1006 /*
1007 * Prepare remaining printing options...
1008 */
1009
1010 options = NULL;
1011 pc = NULL;
1012
1013 if (send_options)
1014 {
1015 num_options = cupsParseOptions(argv[5], 0, &options);
1016
1017 if (!cups_version && media_col_sup)
1018 {
1019 /*
1020 * Load the PPD file and generate PWG attribute mapping information...
1021 */
1022
1023 ppd = ppdOpenFile(getenv("PPD"));
1024 pc = _ppdCacheCreateWithPPD(ppd);
1025
1026 ppdClose(ppd);
1027 }
1028 }
1029 else
1030 num_options = 0;
1031
1032 document_format = NULL;
1033
1034 if (format_sup != NULL)
1035 {
1036 for (i = 0; i < format_sup->num_values; i ++)
1037 if (!strcasecmp(final_content_type, format_sup->values[i].string.text))
1038 {
1039 document_format = final_content_type;
1040 break;
1041 }
1042
1043 if (!document_format)
1044 {
1045 for (i = 0; i < format_sup->num_values; i ++)
1046 if (!strcasecmp("application/octet-stream",
1047 format_sup->values[i].string.text))
1048 {
1049 document_format = "application/octet-stream";
1050 break;
1051 }
1052 }
1053 }
1054
1055 /*
1056 * If the printer does not support HTTP/1.1 (which IPP requires), copy stdin
1057 * to a temporary file so that we can do a HTTP/1.0 submission...
1058 *
1059 * (I hate compatibility hacks!)
1060 */
1061
1062 if (http->version < HTTP_1_1 && num_files == 0)
1063 {
1064 if ((fd = cupsTempFd(tmpfilename, sizeof(tmpfilename))) < 0)
1065 {
1066 perror("DEBUG: Unable to create temporary file");
1067 return (CUPS_BACKEND_FAILED);
1068 }
1069
1070 _cupsLangPrintFilter(stderr, "INFO", _("Copying print data."));
1071
1072 compatsize = backendRunLoop(-1, fd, snmp_fd, &(addrlist->addr), 0, 0,
1073 backendNetworkSideCB);
1074
1075 close(fd);
1076
1077 compatfile = tmpfilename;
1078 files = &compatfile;
1079 num_files = 1;
1080 }
1081 else if (http->version < HTTP_1_1 && num_files == 1)
1082 {
1083 struct stat fileinfo; /* File information */
1084
1085 if (!stat(files[0], &fileinfo))
1086 compatsize = fileinfo.st_size;
1087 }
1088
1089 /*
1090 * Start monitoring the printer in the background...
1091 */
1092
1093 monitor.uri = uri;
1094 monitor.hostname = hostname;
1095 monitor.user = argv[2];
1096 monitor.resource = resource;
1097 monitor.port = port;
1098 monitor.version = version;
1099 monitor.job_id = 0;
1100 monitor.encryption = cupsEncryption();
1101 monitor.job_state = IPP_JOB_PENDING;
1102 monitor.printer_state = IPP_PRINTER_IDLE;
1103
1104 _cupsThreadCreate((_cups_thread_func_t)monitor_printer, &monitor);
1105
1106 /*
1107 * Validate access to the printer...
1108 */
1109
1110 while (!job_canceled && validate_job)
1111 {
1112 request = new_request(IPP_VALIDATE_JOB, version, uri, argv[2], argv[3],
1113 num_options, options, compression,
1114 copies_sup ? copies : 1, document_format, pc,
1115 media_col_sup, doc_handling_sup);
1116
1117 ippDelete(cupsDoRequest(http, request, resource));
1118
1119 ipp_status = cupsLastError();
1120
1121 fprintf(stderr, "DEBUG: Validate-Job: %s (%s)\n",
1122 ippErrorString(ipp_status), cupsLastErrorString());
1123
1124 if (job_canceled)
1125 break;
1126
1127 if (ipp_status == IPP_SERVICE_UNAVAILABLE || ipp_status == IPP_PRINTER_BUSY)
1128 {
1129 _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
1130 sleep(10);
1131 }
1132 else if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
1133 {
1134 /*
1135 * Update auth-info-required as needed...
1136 */
1137
1138 fprintf(stderr, "DEBUG: WWW-Authenticate=\"%s\"\n",
1139 httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE));
1140
1141 /*
1142 * Normal authentication goes through the password callback, which sets
1143 * auth_info_required to "username,password". Kerberos goes directly
1144 * through GSSAPI, so look for Negotiate in the WWW-Authenticate header
1145 * here and set auth_info_required as needed...
1146 */
1147
1148 if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
1149 "Negotiate", 9))
1150 auth_info_required = "negotiate";
1151
1152 goto cleanup;
1153 }
1154 else if (ipp_status == IPP_OPERATION_NOT_SUPPORTED)
1155 {
1156 _cupsLangPrintFilter(stderr, "WARNING",
1157 _("This printer does not conform to the IPP "
1158 "standard and may not work."));
1159 break;
1160 }
1161 else if (ipp_status < IPP_REDIRECTION_OTHER_SITE)
1162 break;
1163 }
1164
1165 /*
1166 * Then issue the print-job request...
1167 */
1168
1169 job_id = 0;
1170
1171 while (!job_canceled && copies_remaining > 0)
1172 {
1173 /*
1174 * Check for side-channel requests...
1175 */
1176
1177 backendCheckSideChannel(snmp_fd, http->hostaddr);
1178
1179 /*
1180 * Build the IPP job creation request...
1181 */
1182
1183 if (job_canceled)
1184 break;
1185
1186 request = new_request(num_files > 1 ? IPP_CREATE_JOB : IPP_PRINT_JOB,
1187 version, uri, argv[2], argv[3], num_options, options,
1188 compression, copies_sup ? copies : 1, document_format,
1189 pc, media_col_sup, doc_handling_sup);
1190
1191 /*
1192 * Do the request...
1193 */
1194
1195 if (num_files > 1)
1196 response = cupsDoRequest(http, request, resource);
1197 else
1198 {
1199 size_t length = 0; /* Length of request */
1200
1201 if (compatsize > 0)
1202 {
1203 fputs("DEBUG: Sending file using HTTP/1.0 Content-Length...\n", stderr);
1204 length = ippLength(request) + (size_t)compatsize;
1205 }
1206 else
1207 fputs("DEBUG: Sending file using HTTP/1.1 chunking...\n", stderr);
1208
1209 http_status = cupsSendRequest(http, request, resource, length);
1210 if (http_status == HTTP_CONTINUE && request->state == IPP_DATA)
1211 {
1212 if (num_files == 1)
1213 fd = open(files[0], O_RDONLY);
1214 else
1215 fd = 0;
1216
1217 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
1218 {
1219 fprintf(stderr, "DEBUG: Read %d bytes...\n", (int)bytes);
1220
1221 if (cupsWriteRequestData(http, buffer, bytes) != HTTP_CONTINUE)
1222 break;
1223 else
1224 {
1225 /*
1226 * Check for side-channel requests...
1227 */
1228
1229 backendCheckSideChannel(snmp_fd, http->hostaddr);
1230 }
1231 }
1232
1233 if (num_files == 1)
1234 close(fd);
1235 }
1236
1237 response = cupsGetResponse(http, resource);
1238 ippDelete(request);
1239 }
1240
1241 ipp_status = cupsLastError();
1242
1243 fprintf(stderr, "DEBUG: %s: %s (%s)\n",
1244 num_files > 1 ? "Create-Job" : "Print-Job",
1245 ippErrorString(ipp_status), cupsLastErrorString());
1246
1247 if (ipp_status > IPP_OK_CONFLICT)
1248 {
1249 job_id = 0;
1250
1251 if (job_canceled)
1252 break;
1253
1254 if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1255 ipp_status == IPP_PRINTER_BUSY)
1256 {
1257 _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
1258 sleep(10);
1259
1260 if (num_files == 0)
1261 {
1262 /*
1263 * We can't re-submit when we have no files to print, so exit
1264 * immediately with the right status code...
1265 */
1266
1267 goto cleanup;
1268 }
1269 }
1270 else
1271 {
1272 /*
1273 * Update auth-info-required as needed...
1274 */
1275
1276 _cupsLangPrintFilter(stderr, "ERROR",
1277 _("Print file was not accepted."));
1278
1279 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
1280 {
1281 fprintf(stderr, "DEBUG: WWW-Authenticate=\"%s\"\n",
1282 httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE));
1283
1284 /*
1285 * Normal authentication goes through the password callback, which sets
1286 * auth_info_required to "username,password". Kerberos goes directly
1287 * through GSSAPI, so look for Negotiate in the WWW-Authenticate header
1288 * here and set auth_info_required as needed...
1289 */
1290
1291 if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
1292 "Negotiate", 9))
1293 auth_info_required = "negotiate";
1294 }
1295 else
1296 sleep(10);
1297
1298 if (num_files == 0)
1299 {
1300 /*
1301 * We can't re-submit when we have no files to print, so exit
1302 * immediately with the right status code...
1303 */
1304
1305 goto cleanup;
1306 }
1307 }
1308 }
1309 else if ((job_id_attr = ippFindAttribute(response, "job-id",
1310 IPP_TAG_INTEGER)) == NULL)
1311 {
1312 _cupsLangPrintFilter(stderr, "INFO",
1313 _("Print file accepted - job ID unknown."));
1314 job_id = 0;
1315 }
1316 else
1317 {
1318 monitor.job_id = job_id = job_id_attr->values[0].integer;
1319 _cupsLangPrintFilter(stderr, "INFO",
1320 _("Print file accepted - job ID %d."), job_id);
1321 }
1322
1323 ippDelete(response);
1324
1325 if (job_canceled)
1326 break;
1327
1328 if (job_id && num_files > 1)
1329 {
1330 for (i = 0; i < num_files; i ++)
1331 {
1332 /*
1333 * Check for side-channel requests...
1334 */
1335
1336 backendCheckSideChannel(snmp_fd, http->hostaddr);
1337
1338 /*
1339 * Send the next file in the job...
1340 */
1341
1342 request = ippNewRequest(IPP_SEND_DOCUMENT);
1343 request->request.op.version[0] = version / 10;
1344 request->request.op.version[1] = version % 10;
1345
1346 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1347 NULL, uri);
1348
1349 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1350 job_id);
1351
1352 if (argv[2][0])
1353 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1354 "requesting-user-name", NULL, argv[2]);
1355
1356 if ((i + 1) == num_files)
1357 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
1358
1359 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
1360 "document-format", NULL, content_type);
1361
1362 fprintf(stderr, "DEBUG: Sending file %d using chunking...\n", i + 1);
1363 http_status = cupsSendRequest(http, request, resource, 0);
1364 if (http_status == HTTP_CONTINUE && request->state == IPP_DATA &&
1365 (fd = open(files[i], O_RDONLY)) >= 0)
1366 {
1367 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
1368 {
1369 if (cupsWriteRequestData(http, buffer, bytes) != HTTP_CONTINUE)
1370 break;
1371 else
1372 {
1373 /*
1374 * Check for side-channel requests...
1375 */
1376
1377 backendCheckSideChannel(snmp_fd, http->hostaddr);
1378 }
1379 }
1380
1381 close(fd);
1382 }
1383
1384 ippDelete(cupsGetResponse(http, resource));
1385 ippDelete(request);
1386
1387 fprintf(stderr, "DEBUG: Send-Document: %s (%s)\n",
1388 ippErrorString(cupsLastError()), cupsLastErrorString());
1389
1390 if (cupsLastError() > IPP_OK_CONFLICT)
1391 {
1392 ipp_status = cupsLastError();
1393
1394 _cupsLangPrintFilter(stderr, "ERROR",
1395 _("Unable to add document to print job."));
1396 break;
1397 }
1398 }
1399 }
1400
1401 if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
1402 {
1403 fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
1404 copies_remaining --;
1405 }
1406 else if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1407 ipp_status == IPP_PRINTER_BUSY)
1408 continue;
1409 else
1410 copies_remaining --;
1411
1412 /*
1413 * Wait for the job to complete...
1414 */
1415
1416 if (!job_id || !waitjob)
1417 continue;
1418
1419 _cupsLangPrintFilter(stderr, "INFO", _("Waiting for job to complete."));
1420
1421 for (delay = _cupsNextDelay(0, &prev_delay); !job_canceled;)
1422 {
1423 /*
1424 * Check for side-channel requests...
1425 */
1426
1427 backendCheckSideChannel(snmp_fd, http->hostaddr);
1428
1429 /*
1430 * Build an IPP_GET_JOB_ATTRIBUTES request...
1431 */
1432
1433 request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
1434 request->request.op.version[0] = version / 10;
1435 request->request.op.version[1] = version % 10;
1436
1437 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1438 NULL, uri);
1439
1440 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1441 job_id);
1442
1443 if (argv[2][0])
1444 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1445 "requesting-user-name", NULL, argv[2]);
1446
1447 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1448 "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
1449 NULL, jattrs);
1450
1451 /*
1452 * Do the request...
1453 */
1454
1455 httpReconnect(http);
1456 response = cupsDoRequest(http, request, resource);
1457 ipp_status = cupsLastError();
1458
1459 if (ipp_status == IPP_NOT_FOUND)
1460 {
1461 /*
1462 * Job has gone away and/or the server has no job history...
1463 */
1464
1465 ippDelete(response);
1466
1467 ipp_status = IPP_OK;
1468 break;
1469 }
1470
1471 fprintf(stderr, "DEBUG: Get-Job-Attributes: %s (%s)\n",
1472 ippErrorString(ipp_status), cupsLastErrorString());
1473
1474 if (ipp_status > IPP_OK_CONFLICT)
1475 {
1476 if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
1477 ipp_status != IPP_PRINTER_BUSY)
1478 {
1479 ippDelete(response);
1480
1481 _cupsLangPrintFilter(stderr, "ERROR",
1482 _("Unable to get print job status."));
1483 break;
1484 }
1485 }
1486
1487 if (response)
1488 {
1489 if ((job_state = ippFindAttribute(response, "job-state",
1490 IPP_TAG_ENUM)) != NULL)
1491 {
1492 /*
1493 * Reflect the remote job state in the local queue...
1494 */
1495
1496 fputs("STATE: -cups-remote-pending,"
1497 "cups-remote-pending-held,"
1498 "cups-remote-processing,"
1499 "cups-remote-stopped,"
1500 "cups-remote-canceled,"
1501 "cups-remote-aborted,"
1502 "cups-remote-completed\n", stderr);
1503 if (job_state->values[0].integer >= IPP_JOB_PENDING &&
1504 job_state->values[0].integer <= IPP_JOB_COMPLETED)
1505 fprintf(stderr, "STATE: +%s\n",
1506 remote_job_states[job_state->values[0].integer -
1507 IPP_JOB_PENDING]);
1508
1509 /*
1510 * Stop polling if the job is finished or pending-held...
1511 */
1512
1513 if (job_state->values[0].integer > IPP_JOB_STOPPED)
1514 {
1515 if ((job_sheets = ippFindAttribute(response,
1516 "job-media-sheets-completed",
1517 IPP_TAG_INTEGER)) != NULL)
1518 fprintf(stderr, "PAGE: total %d\n",
1519 job_sheets->values[0].integer);
1520
1521 ippDelete(response);
1522 break;
1523 }
1524 }
1525 else
1526 {
1527 /*
1528 * If the printer does not return a job-state attribute, it does not
1529 * conform to the IPP specification - break out immediately and fail
1530 * the job...
1531 */
1532
1533 fputs("DEBUG: No job-state available from printer - stopping queue.\n",
1534 stderr);
1535 ipp_status = IPP_INTERNAL_ERROR;
1536 break;
1537 }
1538 }
1539
1540 ippDelete(response);
1541
1542 /*
1543 * Wait before polling again...
1544 */
1545
1546 sleep(delay);
1547
1548 delay = _cupsNextDelay(delay, &prev_delay);
1549 }
1550 }
1551
1552 /*
1553 * Cancel the job as needed...
1554 */
1555
1556 if (job_canceled && job_id)
1557 cancel_job(http, uri, job_id, resource, argv[2], version);
1558
1559 /*
1560 * Check the printer state and report it if necessary...
1561 */
1562
1563 check_printer_state(http, uri, resource, argv[2], version, job_id);
1564
1565 /*
1566 * Collect the final page count as needed...
1567 */
1568
1569 if (have_supplies &&
1570 !backendSNMPSupplies(snmp_fd, http->hostaddr, &page_count, NULL) &&
1571 page_count > start_count)
1572 fprintf(stderr, "PAGE: total %d\n", page_count - start_count);
1573
1574 #ifdef HAVE_GSSAPI
1575 /*
1576 * See if we used Kerberos at all...
1577 */
1578
1579 if (http->gssctx)
1580 auth_info_required = "negotiate";
1581 #endif /* HAVE_GSSAPI */
1582
1583 /*
1584 * Free memory...
1585 */
1586
1587 cleanup:
1588
1589 cupsFreeOptions(num_options, options);
1590 _ppdCacheDestroy(pc);
1591
1592 httpClose(http);
1593
1594 ippDelete(supported);
1595
1596 /*
1597 * Remove the temporary file(s) if necessary...
1598 */
1599
1600 if (tmpfilename[0])
1601 unlink(tmpfilename);
1602
1603 #ifdef HAVE_LIBZ
1604 if (compression)
1605 {
1606 for (i = 0; i < num_files; i ++)
1607 unlink(files[i]);
1608 }
1609 #endif /* HAVE_LIBZ */
1610
1611 /*
1612 * Return the queue status...
1613 */
1614
1615 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
1616 ipp_status == IPP_AUTHENTICATION_CANCELED ||
1617 ipp_status <= IPP_OK_CONFLICT)
1618 fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
1619
1620 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
1621 ipp_status == IPP_AUTHENTICATION_CANCELED)
1622 return (CUPS_BACKEND_AUTH_REQUIRED);
1623 else if (ipp_status == IPP_INTERNAL_ERROR)
1624 return (CUPS_BACKEND_STOP);
1625 else if (ipp_status == IPP_DOCUMENT_FORMAT ||
1626 ipp_status == IPP_CONFLICT)
1627 return (CUPS_BACKEND_FAILED);
1628 else if (ipp_status > IPP_OK_CONFLICT)
1629 return (CUPS_BACKEND_RETRY_CURRENT);
1630 else
1631 {
1632 _cupsLangPrintFilter(stderr, "INFO", _("Ready to print."));
1633 return (CUPS_BACKEND_OK);
1634 }
1635 }
1636
1637
1638 /*
1639 * 'cancel_job()' - Cancel a print job.
1640 */
1641
1642 static void
1643 cancel_job(http_t *http, /* I - HTTP connection */
1644 const char *uri, /* I - printer-uri */
1645 int id, /* I - job-id */
1646 const char *resource, /* I - Resource path */
1647 const char *user, /* I - requesting-user-name */
1648 int version) /* I - IPP version */
1649 {
1650 ipp_t *request; /* Cancel-Job request */
1651
1652
1653 _cupsLangPrintFilter(stderr, "INFO", _("Canceling print job."));
1654
1655 request = ippNewRequest(IPP_CANCEL_JOB);
1656 request->request.op.version[0] = version / 10;
1657 request->request.op.version[1] = version % 10;
1658
1659 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1660 NULL, uri);
1661 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
1662
1663 if (user && user[0])
1664 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1665 "requesting-user-name", NULL, user);
1666
1667 /*
1668 * Do the request...
1669 */
1670
1671 ippDelete(cupsDoRequest(http, request, resource));
1672
1673 if (cupsLastError() > IPP_OK_CONFLICT)
1674 _cupsLangPrintFilter(stderr, "ERROR", _("Unable to cancel print job."));
1675 }
1676
1677
1678 /*
1679 * 'check_printer_state()' - Check the printer state.
1680 */
1681
1682 static ipp_pstate_t /* O - Current printer-state */
1683 check_printer_state(
1684 http_t *http, /* I - HTTP connection */
1685 const char *uri, /* I - Printer URI */
1686 const char *resource, /* I - Resource path */
1687 const char *user, /* I - Username, if any */
1688 int version, /* I - IPP version */
1689 int job_id)
1690 {
1691 ipp_t *request, /* IPP request */
1692 *response; /* IPP response */
1693 ipp_attribute_t *attr; /* Attribute in response */
1694 ipp_pstate_t printer_state = IPP_PRINTER_STOPPED;
1695 /* Current printer-state */
1696
1697
1698 /*
1699 * Send a Get-Printer-Attributes request and log the results...
1700 */
1701
1702 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
1703 request->request.op.version[0] = version / 10;
1704 request->request.op.version[1] = version % 10;
1705
1706 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1707 NULL, uri);
1708
1709 if (user && user[0])
1710 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1711 "requesting-user-name", NULL, user);
1712
1713 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1714 "requested-attributes",
1715 (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
1716
1717 if ((response = cupsDoRequest(http, request, resource)) != NULL)
1718 {
1719 report_printer_state(response, job_id);
1720
1721 if ((attr = ippFindAttribute(response, "printer-state",
1722 IPP_TAG_ENUM)) != NULL)
1723 printer_state = (ipp_pstate_t)attr->values[0].integer;
1724
1725 ippDelete(response);
1726 }
1727
1728 fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
1729 ippErrorString(cupsLastError()), cupsLastErrorString());
1730
1731 /*
1732 * Return the printer-state value...
1733 */
1734
1735 return (printer_state);
1736 }
1737
1738
1739 #ifdef HAVE_LIBZ
1740 /*
1741 * 'compress_files()' - Compress print files...
1742 */
1743
1744 static void
1745 compress_files(int num_files, /* I - Number of files */
1746 char **files) /* I - Files */
1747 {
1748 int i, /* Looping var */
1749 fd; /* Temporary file descriptor */
1750 ssize_t bytes; /* Bytes read/written */
1751 size_t total; /* Total bytes read */
1752 cups_file_t *in, /* Input file */
1753 *out; /* Output file */
1754 struct stat outinfo; /* Output file information */
1755 char filename[1024], /* Temporary filename */
1756 buffer[32768]; /* Copy buffer */
1757
1758
1759 fprintf(stderr, "DEBUG: Compressing %d job files...\n", num_files);
1760 for (i = 0; i < num_files; i ++)
1761 {
1762 if ((fd = cupsTempFd(filename, sizeof(filename))) < 0)
1763 {
1764 _cupsLangPrintError("ERROR", _("Unable to create compressed print file"));
1765 exit(CUPS_BACKEND_FAILED);
1766 }
1767
1768 if ((out = cupsFileOpenFd(fd, "w9")) == NULL)
1769 {
1770 _cupsLangPrintError("ERROR", _("Unable to open compressed print file"));
1771 exit(CUPS_BACKEND_FAILED);
1772 }
1773
1774 if ((in = cupsFileOpen(files[i], "r")) == NULL)
1775 {
1776 _cupsLangPrintError("ERROR", _("Unable to open print file"));
1777 cupsFileClose(out);
1778 exit(CUPS_BACKEND_FAILED);
1779 }
1780
1781 total = 0;
1782 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
1783 if (cupsFileWrite(out, buffer, bytes) < bytes)
1784 {
1785 _cupsLangPrintError("ERROR",
1786 _("Unable to generate compressed print file"));
1787 cupsFileClose(in);
1788 cupsFileClose(out);
1789 exit(CUPS_BACKEND_FAILED);
1790 }
1791 else
1792 total += bytes;
1793
1794 cupsFileClose(out);
1795 cupsFileClose(in);
1796
1797 files[i] = strdup(filename);
1798
1799 if (!stat(filename, &outinfo))
1800 fprintf(stderr,
1801 "DEBUG: File %d compressed to %.1f%% of original size, "
1802 CUPS_LLFMT " bytes...\n",
1803 i + 1, 100.0 * outinfo.st_size / total,
1804 CUPS_LLCAST outinfo.st_size);
1805 }
1806 }
1807 #endif /* HAVE_LIBZ */
1808
1809
1810 /*
1811 * 'monitor_printer()' - Monitor the printer state...
1812 */
1813
1814 static void * /* O - Thread exit code */
1815 monitor_printer(
1816 _cups_monitor_t *monitor) /* I - Monitoring data */
1817 {
1818 http_t *http; /* Connection to printer */
1819 ipp_t *request, /* IPP request */
1820 *response; /* IPP response */
1821 ipp_attribute_t *attr; /* Attribute in response */
1822 int delay, /* Current delay */
1823 prev_delay; /* Previous delay */
1824
1825
1826 /*
1827 * Make a copy of the printer connection...
1828 */
1829
1830 http = _httpCreate(monitor->hostname, monitor->port, NULL, monitor->encryption,
1831 AF_UNSPEC);
1832 cupsSetPasswordCB(password_cb);
1833
1834 /*
1835 * Loop until the job is canceled, aborted, or completed.
1836 */
1837
1838 delay = _cupsNextDelay(0, &prev_delay);
1839
1840 while (monitor->job_state < IPP_JOB_CANCELED && !job_canceled)
1841 {
1842 /*
1843 * Reconnect to the printer...
1844 */
1845
1846 if (!httpReconnect(http))
1847 {
1848 /*
1849 * Connected, so check on the printer state...
1850 */
1851
1852 monitor->printer_state = check_printer_state(http, monitor->uri,
1853 monitor->resource,
1854 monitor->user,
1855 monitor->version,
1856 monitor->job_id);
1857
1858 if (monitor->job_id > 0)
1859 {
1860 /*
1861 * Check the status of the job itself...
1862 */
1863
1864 request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
1865 request->request.op.version[0] = monitor->version / 10;
1866 request->request.op.version[1] = monitor->version % 10;
1867
1868 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1869 NULL, monitor->uri);
1870 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1871 monitor->job_id);
1872
1873 if (monitor->user && monitor->user[0])
1874 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1875 "requesting-user-name", NULL, monitor->user);
1876
1877 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1878 "requested-attributes",
1879 (int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs);
1880
1881 /*
1882 * Do the request...
1883 */
1884
1885 response = cupsDoRequest(http, request, monitor->resource);
1886
1887 fprintf(stderr, "DEBUG: Get-Job-Attributes: %s (%s)\n",
1888 ippErrorString(cupsLastError()), cupsLastErrorString());
1889
1890 if ((attr = ippFindAttribute(response, "job-state",
1891 IPP_TAG_ENUM)) != NULL)
1892 monitor->job_state = (ipp_jstate_t)attr->values[0].integer;
1893 else
1894 monitor->job_state = IPP_JOB_COMPLETED;
1895
1896 ippDelete(response);
1897 }
1898
1899 /*
1900 * Disconnect from the printer - we'll reconnect on the next poll...
1901 */
1902
1903 _httpDisconnect(http);
1904 }
1905
1906 /*
1907 * Sleep for N seconds...
1908 */
1909
1910 sleep(delay);
1911
1912 delay = _cupsNextDelay(delay, &prev_delay);
1913 }
1914
1915 /*
1916 * Cleanup and return...
1917 */
1918
1919 httpClose(http);
1920
1921 return (NULL);
1922 }
1923
1924
1925 /*
1926 * 'new_request()' - Create a new print creation or validation request.
1927 */
1928
1929 static ipp_t * /* O - Request data */
1930 new_request(
1931 ipp_op_t op, /* I - IPP operation code */
1932 int version, /* I - IPP version number */
1933 const char *uri, /* I - printer-uri value */
1934 const char *user, /* I - requesting-user-name value */
1935 const char *title, /* I - job-name value */
1936 int num_options, /* I - Number of options to send */
1937 cups_option_t *options, /* I - Options to send */
1938 const char *compression, /* I - compression value or NULL */
1939 int copies, /* I - copies value or 0 */
1940 const char *format, /* I - documet-format value or NULL */
1941 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
1942 ipp_attribute_t *media_col_sup, /* I - media-col-supported values */
1943 ipp_attribute_t *doc_handling_sup) /* I - multiple-document-handling-supported values */
1944 {
1945 int i; /* Looping var */
1946 ipp_t *request; /* Request data */
1947 const char *keyword; /* PWG keyword */
1948 _pwg_size_t *size; /* PWG media size */
1949 ipp_t *media_col, /* media-col value */
1950 *media_size; /* media-size value */
1951 const char *media_source, /* media-source value */
1952 *media_type, /* media-type value */
1953 *collate_str; /* multiple-document-handling value */
1954
1955
1956 /*
1957 * Create the IPP request...
1958 */
1959
1960 request = ippNewRequest(op);
1961 request->request.op.version[0] = version / 10;
1962 request->request.op.version[1] = version % 10;
1963
1964 fprintf(stderr, "DEBUG: %s IPP/%d.%d\n",
1965 ippOpString(request->request.op.operation_id),
1966 request->request.op.version[0],
1967 request->request.op.version[1]);
1968
1969 /*
1970 * Add standard attributes...
1971 */
1972
1973 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1974 NULL, uri);
1975 fprintf(stderr, "DEBUG: printer-uri=\"%s\"\n", uri);
1976
1977 if (user && *user)
1978 {
1979 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1980 "requesting-user-name", NULL, user);
1981 fprintf(stderr, "DEBUG: requesting-user-name=\"%s\"\n", user);
1982 }
1983
1984 if (title && *title)
1985 {
1986 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
1987 title);
1988 fprintf(stderr, "DEBUG: job-name=\"%s\"\n", title);
1989 }
1990
1991 if (format)
1992 {
1993 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
1994 "document-format", NULL, format);
1995 fprintf(stderr, "DEBUG: document-format=\"%s\"\n", format);
1996 }
1997
1998 #ifdef HAVE_LIBZ
1999 if (compression)
2000 {
2001 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2002 "compression", NULL, compression);
2003 fprintf(stderr, "DEBUG: compression=\"%s\"\n", compression);
2004 }
2005 #endif /* HAVE_LIBZ */
2006
2007 /*
2008 * Handle options on the command-line...
2009 */
2010
2011 if (num_options > 0)
2012 {
2013 if (pc)
2014 {
2015 /*
2016 * Send standard IPP attributes...
2017 */
2018
2019 if ((keyword = cupsGetOption("PageSize", num_options, options)) == NULL)
2020 keyword = cupsGetOption("media", num_options, options);
2021
2022 if ((size = _ppdCacheGetSize(pc, keyword)) != NULL)
2023 {
2024 /*
2025 * Add a media-col value...
2026 */
2027
2028 media_size = ippNew();
2029 ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2030 "x-dimension", size->width);
2031 ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2032 "y-dimension", size->length);
2033
2034 media_col = ippNew();
2035 ippAddCollection(media_col, IPP_TAG_ZERO, "media-size", media_size);
2036
2037 media_source = _ppdCacheGetSource(pc, cupsGetOption("InputSlot",
2038 num_options,
2039 options));
2040 media_type = _ppdCacheGetType(pc, cupsGetOption("MediaType",
2041 num_options,
2042 options));
2043
2044 for (i = 0; i < media_col_sup->num_values; i ++)
2045 {
2046 if (!strcmp(media_col_sup->values[i].string.text,
2047 "media-left-margin"))
2048 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2049 "media-left-margin", size->left);
2050 else if (!strcmp(media_col_sup->values[i].string.text,
2051 "media-bottom-margin"))
2052 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2053 "media-bottom-margin", size->left);
2054 else if (!strcmp(media_col_sup->values[i].string.text,
2055 "media-right-margin"))
2056 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2057 "media-right-margin", size->left);
2058 else if (!strcmp(media_col_sup->values[i].string.text,
2059 "media-top-margin"))
2060 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2061 "media-top-margin", size->left);
2062 else if (!strcmp(media_col_sup->values[i].string.text,
2063 "media-source") && media_source)
2064 ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD,
2065 "media-source", NULL, media_source);
2066 else if (!strcmp(media_col_sup->values[i].string.text,
2067 "media-type") && media_type)
2068 ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD,
2069 "media-type", NULL, media_type);
2070 }
2071
2072 ippAddCollection(request, IPP_TAG_JOB, "media-col", media_col);
2073 }
2074
2075 if ((keyword = cupsGetOption("output-bin", num_options,
2076 options)) == NULL)
2077 keyword = _ppdCacheGetBin(pc, cupsGetOption("OutputBin", num_options,
2078 options));
2079
2080 if (keyword)
2081 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-bin",
2082 NULL, keyword);
2083
2084 if ((keyword = cupsGetOption("output-mode", num_options,
2085 options)) != NULL)
2086 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2087 NULL, keyword);
2088 else if ((keyword = cupsGetOption("ColorModel", num_options,
2089 options)) != NULL)
2090 {
2091 if (!strcasecmp(keyword, "Gray"))
2092 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2093 NULL, "monochrome");
2094 else
2095 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2096 NULL, "color");
2097 }
2098
2099 if ((keyword = cupsGetOption("print-quality", num_options,
2100 options)) != NULL)
2101 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2102 atoi(keyword));
2103 else if ((keyword = cupsGetOption("cupsPrintQuality", num_options,
2104 options)) != NULL)
2105 {
2106 if (!strcasecmp(keyword, "draft"))
2107 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2108 IPP_QUALITY_DRAFT);
2109 else if (!strcasecmp(keyword, "normal"))
2110 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2111 IPP_QUALITY_NORMAL);
2112 else if (!strcasecmp(keyword, "high"))
2113 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2114 IPP_QUALITY_HIGH);
2115 }
2116
2117 if ((keyword = cupsGetOption("sides", num_options, options)) != NULL)
2118 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2119 NULL, keyword);
2120 else if (pc->sides_option &&
2121 (keyword = cupsGetOption(pc->sides_option, num_options,
2122 options)) != NULL)
2123 {
2124 if (!strcasecmp(keyword, pc->sides_1sided))
2125 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2126 NULL, "one-sided");
2127 else if (!strcasecmp(keyword, pc->sides_2sided_long))
2128 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2129 NULL, "two-sided-long-edge");
2130 if (!strcasecmp(keyword, pc->sides_2sided_short))
2131 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2132 NULL, "two-sided-short-edge");
2133 }
2134
2135 if (doc_handling_sup &&
2136 (keyword = cupsGetOption("collate", num_options, options)) != NULL)
2137 {
2138 if (!strcasecmp(keyword, "true"))
2139 collate_str = "separate-documents-collated-copies";
2140 else
2141 collate_str = "separate-documents-uncollated-copies";
2142
2143 for (i = 0; i < doc_handling_sup->num_values; i ++)
2144 if (!strcmp(doc_handling_sup->values[i].string.text, collate_str))
2145 {
2146 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2147 "multiple-document-handling", NULL, collate_str);
2148 break;
2149 }
2150 }
2151 }
2152 else
2153 {
2154 /*
2155 * When talking to another CUPS server, send all options...
2156 */
2157
2158 cupsEncodeOptions(request, num_options, options);
2159 }
2160
2161 if (copies > 1)
2162 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies", copies);
2163 }
2164
2165 return (request);
2166 }
2167
2168
2169 /*
2170 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
2171 */
2172
2173 static const char * /* O - Password */
2174 password_cb(const char *prompt) /* I - Prompt (not used) */
2175 {
2176 (void)prompt;
2177
2178 /*
2179 * Remember that we need to authenticate...
2180 */
2181
2182 auth_info_required = "username,password";
2183
2184 if (password && *password && password_tries < 3)
2185 {
2186 password_tries ++;
2187
2188 return (password);
2189 }
2190 else
2191 {
2192 /*
2193 * Give up after 3 tries or if we don't have a password to begin with...
2194 */
2195
2196 return (NULL);
2197 }
2198 }
2199
2200
2201 /*
2202 * 'report_attr()' - Report an IPP attribute value.
2203 */
2204
2205 static void
2206 report_attr(ipp_attribute_t *attr) /* I - Attribute */
2207 {
2208 int i; /* Looping var */
2209 char value[1024], /* Value string */
2210 *valptr, /* Pointer into value string */
2211 *attrptr; /* Pointer into attribute value */
2212
2213
2214 /*
2215 * Convert the attribute values into quoted strings...
2216 */
2217
2218 for (i = 0, valptr = value;
2219 i < attr->num_values && valptr < (value + sizeof(value) - 10);
2220 i ++)
2221 {
2222 if (i > 0)
2223 *valptr++ = ',';
2224
2225 switch (attr->value_tag)
2226 {
2227 case IPP_TAG_INTEGER :
2228 case IPP_TAG_ENUM :
2229 snprintf(valptr, sizeof(value) - (valptr - value), "%d",
2230 attr->values[i].integer);
2231 valptr += strlen(valptr);
2232 break;
2233
2234 case IPP_TAG_TEXT :
2235 case IPP_TAG_NAME :
2236 case IPP_TAG_KEYWORD :
2237 *valptr++ = '\"';
2238 for (attrptr = attr->values[i].string.text;
2239 *attrptr && valptr < (value + sizeof(value) - 10);
2240 attrptr ++)
2241 {
2242 if (*attrptr == '\\' || *attrptr == '\"')
2243 *valptr++ = '\\';
2244
2245 *valptr++ = *attrptr;
2246 }
2247 *valptr++ = '\"';
2248 break;
2249
2250 default :
2251 /*
2252 * Unsupported value type...
2253 */
2254
2255 return;
2256 }
2257 }
2258
2259 *valptr = '\0';
2260
2261 /*
2262 * Tell the scheduler about the new values...
2263 */
2264
2265 fprintf(stderr, "ATTR: %s=%s\n", attr->name, value);
2266 }
2267
2268
2269 /*
2270 * 'report_printer_state()' - Report the printer state.
2271 */
2272
2273 static int /* O - Number of reasons shown */
2274 report_printer_state(ipp_t *ipp, /* I - IPP response */
2275 int job_id) /* I - Current job ID */
2276 {
2277 int i; /* Looping var */
2278 int count; /* Count of reasons shown... */
2279 ipp_attribute_t *pa, /* printer-alert */
2280 *pam, /* printer-alert-message */
2281 *psm, /* printer-state-message */
2282 *reasons, /* printer-state-reasons */
2283 *marker; /* marker-* attributes */
2284 const char *reason; /* Current reason */
2285 const char *prefix; /* Prefix for STATE: line */
2286 char value[1024], /* State/message string */
2287 *valptr; /* Pointer into string */
2288 static int ipp_supplies = -1;
2289 /* Report supply levels? */
2290
2291
2292 /*
2293 * Report alerts and messages...
2294 */
2295
2296 if ((pa = ippFindAttribute(ipp, "printer-alert", IPP_TAG_TEXT)) != NULL)
2297 report_attr(pa);
2298
2299 if ((pam = ippFindAttribute(ipp, "printer-alert-message",
2300 IPP_TAG_TEXT)) != NULL)
2301 report_attr(pam);
2302
2303 if ((psm = ippFindAttribute(ipp, "printer-state-message",
2304 IPP_TAG_TEXT)) != NULL)
2305 {
2306 char *ptr; /* Pointer into message */
2307
2308
2309 strlcpy(value, "INFO: ", sizeof(value));
2310 for (ptr = psm->values[0].string.text, valptr = value + 6;
2311 *ptr && valptr < (value + sizeof(value) - 6);
2312 ptr ++)
2313 {
2314 if (*ptr < ' ' && *ptr > 0 && *ptr != '\t')
2315 {
2316 /*
2317 * Substitute "<XX>" for the control character; sprintf is safe because
2318 * we always leave 6 chars free at the end...
2319 */
2320
2321 sprintf(valptr, "<%02X>", *ptr);
2322 valptr += 4;
2323 }
2324 else
2325 *valptr++ = *ptr;
2326 }
2327
2328 *valptr++ = '\n';
2329 *valptr = '\0';
2330
2331 fputs(value, stderr);
2332 }
2333
2334 /*
2335 * Now report printer-state-reasons, filtering out some of the reasons we never
2336 * want to set...
2337 */
2338
2339 if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
2340 IPP_TAG_KEYWORD)) == NULL)
2341 return (0);
2342
2343 value[0] = '\0';
2344 prefix = "STATE: ";
2345
2346 for (i = 0, count = 0, valptr = value; i < reasons->num_values; i ++)
2347 {
2348 reason = reasons->values[i].string.text;
2349
2350 if (strcmp(reason, "paused") &&
2351 strcmp(reason, "com.apple.print.recoverable-warning"))
2352 {
2353 strlcpy(valptr, prefix, sizeof(value) - (valptr - value) - 1);
2354 valptr += strlen(valptr);
2355 strlcpy(valptr, reason, sizeof(value) - (valptr - value) - 1);
2356 valptr += strlen(valptr);
2357
2358 prefix = ",";
2359 }
2360 }
2361
2362 if (value[0])
2363 {
2364 *valptr++ = '\n';
2365 *valptr = '\0';
2366 fputs(value, stderr);
2367 }
2368
2369 /*
2370 * Relay the current marker-* attribute values...
2371 */
2372
2373 if (ipp_supplies < 0)
2374 {
2375 ppd_file_t *ppd; /* PPD file */
2376 ppd_attr_t *ppdattr; /* Attribute in PPD file */
2377
2378 if ((ppd = ppdOpenFile(getenv("PPD"))) != NULL &&
2379 (ppdattr = ppdFindAttr(ppd, "cupsIPPSupplies", NULL)) != NULL &&
2380 ppdattr->value && strcasecmp(ppdattr->value, "true"))
2381 ipp_supplies = 0;
2382 else
2383 ipp_supplies = 1;
2384
2385 ppdClose(ppd);
2386 }
2387
2388 if (ipp_supplies > 0)
2389 {
2390 if ((marker = ippFindAttribute(ipp, "marker-colors", IPP_TAG_NAME)) != NULL)
2391 report_attr(marker);
2392 if ((marker = ippFindAttribute(ipp, "marker-high-levels",
2393 IPP_TAG_INTEGER)) != NULL)
2394 report_attr(marker);
2395 if ((marker = ippFindAttribute(ipp, "marker-levels",
2396 IPP_TAG_INTEGER)) != NULL)
2397 report_attr(marker);
2398 if ((marker = ippFindAttribute(ipp, "marker-low-levels",
2399 IPP_TAG_INTEGER)) != NULL)
2400 report_attr(marker);
2401 if ((marker = ippFindAttribute(ipp, "marker-message",
2402 IPP_TAG_TEXT)) != NULL)
2403 report_attr(marker);
2404 if ((marker = ippFindAttribute(ipp, "marker-names", IPP_TAG_NAME)) != NULL)
2405 report_attr(marker);
2406 if ((marker = ippFindAttribute(ipp, "marker-types",
2407 IPP_TAG_KEYWORD)) != NULL)
2408 report_attr(marker);
2409 }
2410
2411 return (count);
2412 }
2413
2414
2415 /*
2416 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
2417 */
2418
2419 static void
2420 sigterm_handler(int sig) /* I - Signal */
2421 {
2422 (void)sig; /* remove compiler warnings... */
2423
2424 if (!job_canceled)
2425 {
2426 /*
2427 * Flag that the job should be cancelled...
2428 */
2429
2430 job_canceled = 1;
2431 return;
2432 }
2433
2434 /*
2435 * The scheduler already tried to cancel us once, now just terminate
2436 * after removing our temp files!
2437 */
2438
2439 if (tmpfilename[0])
2440 unlink(tmpfilename);
2441
2442 exit(1);
2443 }
2444
2445
2446 /*
2447 * End of "$Id: ipp.c 7948 2008-09-17 00:04:12Z mike $".
2448 */