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