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