]> git.ipfire.org Git - thirdparty/cups.git/blob - backend/ipp.c
Check for NULL response.
[thirdparty/cups.git] / backend / ipp.c
1 /*
2 * "$Id$"
3 *
4 * IPP backend for CUPS.
5 *
6 * Copyright 2007-2012 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 * run_as_user() - Run the IPP backend as the printing user.
30 * timeout_cb() - Handle HTTP timeouts.
31 * sigterm_handler() - Handle 'terminate' signals that stop the backend.
32 */
33
34 /*
35 * Include necessary headers.
36 */
37
38 #include "backend-private.h"
39 #include <cups/array-private.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <sys/wait.h>
43 #if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
44 # include <xpc/xpc.h>
45 # define kPMPrintUIToolAgent "com.apple.printuitool.agent"
46 # define kPMStartJob 100
47 # define kPMWaitForJob 101
48 extern void xpc_connection_set_target_uid(xpc_connection_t connection,
49 uid_t uid);
50 #endif /* HAVE_GSSAPI && HAVE_XPC */
51
52
53 /*
54 * Types...
55 */
56
57 typedef struct _cups_monitor_s /**** Monitoring data ****/
58 {
59 const char *uri, /* Printer URI */
60 *hostname, /* Hostname */
61 *user, /* Username */
62 *resource; /* Resource path */
63 int port, /* Port number */
64 version, /* IPP version */
65 job_id; /* Job ID for submitted job */
66 const char *job_name; /* Job name for submitted job */
67 http_encryption_t encryption; /* Use encryption? */
68 ipp_jstate_t job_state; /* Current job state */
69 ipp_pstate_t printer_state; /* Current printer state */
70 } _cups_monitor_t;
71
72
73 /*
74 * Globals...
75 */
76
77 static const char *auth_info_required;
78 /* New auth-info-required value */
79 #if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
80 static int child_pid = 0; /* Child process ID */
81 #endif /* HAVE_GSSAPI && HAVE_XPC */
82 static const char * const jattrs[] = /* Job attributes we want */
83 {
84 "job-impressions-completed",
85 "job-media-sheets-completed",
86 "job-name",
87 "job-originating-user-name",
88 "job-state",
89 "job-state-reasons"
90 };
91 static int job_canceled = 0;
92 /* Job cancelled? */
93 static char *password = NULL;
94 /* Password for device URI */
95 static int password_tries = 0;
96 /* Password tries */
97 static const char * const pattrs[] = /* Printer attributes we want */
98 {
99 "copies-supported",
100 "cups-version",
101 "document-format-supported",
102 "marker-colors",
103 "marker-high-levels",
104 "marker-levels",
105 "marker-low-levels",
106 "marker-message",
107 "marker-names",
108 "marker-types",
109 "media-col-supported",
110 "multiple-document-handling-supported",
111 "operations-supported",
112 "printer-alert",
113 "printer-alert-description",
114 "printer-is-accepting-jobs",
115 "printer-state",
116 "printer-state-message",
117 "printer-state-reasons"
118 };
119 static const char * const remote_job_states[] =
120 { /* Remote job state keywords */
121 "+cups-remote-pending",
122 "+cups-remote-pending-held",
123 "+cups-remote-processing",
124 "+cups-remote-stopped",
125 "+cups-remote-canceled",
126 "+cups-remote-aborted",
127 "+cups-remote-completed"
128 };
129 static _cups_mutex_t report_mutex = _CUPS_MUTEX_INITIALIZER;
130 /* Mutex to control access */
131 static int num_attr_cache = 0;
132 /* Number of cached attributes */
133 static cups_option_t *attr_cache = NULL;
134 /* Cached attributes */
135 static cups_array_t *state_reasons; /* Array of printe-state-reasons keywords */
136 static char tmpfilename[1024] = "";
137 /* Temporary spool file name */
138
139
140 /*
141 * Local functions...
142 */
143
144 static void cancel_job(http_t *http, const char *uri, int id,
145 const char *resource, const char *user,
146 int version);
147 static ipp_pstate_t check_printer_state(http_t *http, const char *uri,
148 const char *resource,
149 const char *user, int version);
150 #ifdef HAVE_LIBZ
151 static void compress_files(int num_files, char **files);
152 #endif /* HAVE_LIBZ */
153 static void *monitor_printer(_cups_monitor_t *monitor);
154 static ipp_t *new_request(ipp_op_t op, int version, const char *uri,
155 const char *user, const char *title,
156 int num_options, cups_option_t *options,
157 const char *compression, int copies,
158 const char *format, _ppd_cache_t *pc,
159 ipp_attribute_t *media_col_sup,
160 ipp_attribute_t *doc_handling_sup);
161 static const char *password_cb(const char *);
162 static void report_attr(ipp_attribute_t *attr);
163 static void report_printer_state(ipp_t *ipp);
164 #if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
165 static int run_as_user(int argc, char *argv[], uid_t uid,
166 const char *device_uri, int fd);
167 #endif /* HAVE_GSSAPI && HAVE_XPC */
168 static void sigterm_handler(int sig);
169 static int timeout_cb(http_t *http, void *user_data);
170 static void update_reasons(ipp_attribute_t *attr, const char *s);
171
172
173 /*
174 * 'main()' - Send a file to the printer or server.
175 *
176 * Usage:
177 *
178 * printer-uri job-id user title copies options [file]
179 */
180
181 int /* O - Exit status */
182 main(int argc, /* I - Number of command-line args */
183 char *argv[]) /* I - Command-line arguments */
184 {
185 int i; /* Looping var */
186 int send_options; /* Send job options? */
187 int num_options; /* Number of printer options */
188 cups_option_t *options; /* Printer options */
189 const char *device_uri; /* Device URI */
190 char scheme[255], /* Scheme in URI */
191 hostname[1024], /* Hostname */
192 username[255], /* Username info */
193 resource[1024], /* Resource info (printer name) */
194 addrname[256], /* Address name */
195 *optptr, /* Pointer to URI options */
196 *name, /* Name of option */
197 *value, /* Value of option */
198 sep; /* Separator character */
199 http_addrlist_t *addrlist; /* Address of printer */
200 int snmp_fd, /* SNMP socket */
201 start_count, /* Page count via SNMP at start */
202 page_count, /* Page count via SNMP */
203 have_supplies; /* Printer supports supply levels? */
204 int num_files; /* Number of files to print */
205 char **files, /* Files to print */
206 *compatfile = NULL; /* Compatibility filename */
207 off_t compatsize = 0; /* Size of compatibility file */
208 int port; /* Port number (not used) */
209 char portname[255]; /* Port name */
210 char uri[HTTP_MAX_URI]; /* Updated URI without user/pass */
211 char print_job_name[1024]; /* Update job-name for Print-Job */
212 http_status_t http_status; /* Status of HTTP request */
213 ipp_status_t ipp_status; /* Status of IPP request */
214 http_t *http; /* HTTP connection */
215 ipp_t *request, /* IPP request */
216 *response, /* IPP response */
217 *supported; /* get-printer-attributes response */
218 time_t start_time; /* Time of first connect */
219 int contimeout; /* Connection timeout */
220 int delay, /* Delay for retries */
221 prev_delay; /* Previous delay */
222 const char *compression; /* Compression mode */
223 int waitjob, /* Wait for job complete? */
224 waitprinter; /* Wait for printer ready? */
225 _cups_monitor_t monitor; /* Monitoring data */
226 ipp_attribute_t *job_id_attr; /* job-id attribute */
227 int job_id; /* job-id value */
228 ipp_attribute_t *job_sheets; /* job-media-sheets-completed */
229 ipp_attribute_t *job_state; /* job-state */
230 ipp_attribute_t *copies_sup; /* copies-supported */
231 ipp_attribute_t *cups_version; /* cups-version */
232 ipp_attribute_t *format_sup; /* document-format-supported */
233 ipp_attribute_t *media_col_sup; /* media-col-supported */
234 ipp_attribute_t *operations_sup; /* operations-supported */
235 ipp_attribute_t *doc_handling_sup; /* multiple-document-handling-supported */
236 ipp_attribute_t *printer_state; /* printer-state attribute */
237 ipp_attribute_t *printer_accepting; /* printer-is-accepting-jobs */
238 int create_job = 0, /* Does printer support Create-Job? */
239 send_document = 0, /* Does printer support Send-Document? */
240 validate_job = 0; /* Does printer support Validate-Job? */
241 int copies, /* Number of copies for job */
242 copies_remaining; /* Number of copies remaining */
243 const char *content_type, /* CONTENT_TYPE environment variable */
244 *final_content_type, /* FINAL_CONTENT_TYPE environment var */
245 *document_format; /* document-format value */
246 int fd; /* File descriptor */
247 off_t bytes = 0; /* Bytes copied */
248 char buffer[16384]; /* Copy buffer */
249 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
250 struct sigaction action; /* Actions for POSIX signals */
251 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
252 int version; /* IPP version */
253 ppd_file_t *ppd; /* PPD file */
254 _ppd_cache_t *pc; /* PPD cache and mapping data */
255 fd_set input; /* Input set for select() */
256
257
258 /*
259 * Make sure status messages are not buffered...
260 */
261
262 setbuf(stderr, NULL);
263
264 /*
265 * Ignore SIGPIPE and catch SIGTERM signals...
266 */
267
268 #ifdef HAVE_SIGSET
269 sigset(SIGPIPE, SIG_IGN);
270 sigset(SIGTERM, sigterm_handler);
271 #elif defined(HAVE_SIGACTION)
272 memset(&action, 0, sizeof(action));
273 action.sa_handler = SIG_IGN;
274 sigaction(SIGPIPE, &action, NULL);
275
276 sigemptyset(&action.sa_mask);
277 sigaddset(&action.sa_mask, SIGTERM);
278 action.sa_handler = sigterm_handler;
279 sigaction(SIGTERM, &action, NULL);
280 #else
281 signal(SIGPIPE, SIG_IGN);
282 signal(SIGTERM, sigterm_handler);
283 #endif /* HAVE_SIGSET */
284
285 /*
286 * Check command-line...
287 */
288
289 if (argc == 1)
290 {
291 char *s;
292
293 if ((s = strrchr(argv[0], '/')) != NULL)
294 s ++;
295 else
296 s = argv[0];
297
298 printf("network %s \"Unknown\" \"%s (%s)\"\n",
299 s, _cupsLangString(cupsLangDefault(),
300 _("Internet Printing Protocol")), s);
301 return (CUPS_BACKEND_OK);
302 }
303 else if (argc < 6)
304 {
305 _cupsLangPrintf(stderr,
306 _("Usage: %s job-id user title copies options [file]"),
307 argv[0]);
308 return (CUPS_BACKEND_STOP);
309 }
310
311 /*
312 * Get the device URI...
313 */
314
315 while ((device_uri = cupsBackendDeviceURI(argv)) == NULL)
316 {
317 _cupsLangPrintFilter(stderr, "INFO", _("Unable to locate printer."));
318 sleep(10);
319
320 if (getenv("CLASS") != NULL)
321 return (CUPS_BACKEND_FAILED);
322 }
323
324 if ((auth_info_required = getenv("AUTH_INFO_REQUIRED")) == NULL)
325 auth_info_required = "none";
326
327 state_reasons = _cupsArrayNewStrings(getenv("PRINTER_STATE_REASONS"));
328
329 #ifdef HAVE_GSSAPI
330 /*
331 * For Kerberos, become the printing user (if we can) to get the credentials
332 * that way.
333 */
334
335 if (!getuid() && (value = getenv("AUTH_UID")) != NULL)
336 {
337 uid_t uid = (uid_t)atoi(value);
338 /* User ID */
339
340 # ifdef HAVE_XPC
341 if (uid > 0)
342 {
343 if (argc == 6)
344 return (run_as_user(argc, argv, uid, device_uri, 0));
345 else
346 {
347 int status = 0; /* Exit status */
348
349 for (i = 6; i < argc && !status && !job_canceled; i ++)
350 {
351 if ((fd = open(argv[i], O_RDONLY)) >= 0)
352 {
353 status = run_as_user(argc, argv, uid, device_uri, fd);
354 close(fd);
355 }
356 else
357 {
358 _cupsLangPrintError("ERROR", _("Unable to open print file"));
359 status = CUPS_BACKEND_FAILED;
360 }
361 }
362
363 return (status);
364 }
365 }
366
367 # else /* No XPC, just try to run as the user ID */
368 if (uid > 0)
369 seteuid(uid);
370 # endif /* HAVE_XPC */
371 }
372 #endif /* HAVE_GSSAPI */
373
374 /*
375 * Get the (final) content type...
376 */
377
378 if ((content_type = getenv("CONTENT_TYPE")) == NULL)
379 content_type = "application/octet-stream";
380
381 if ((final_content_type = getenv("FINAL_CONTENT_TYPE")) == NULL)
382 {
383 final_content_type = content_type;
384
385 if (!strncmp(final_content_type, "printer/", 8))
386 final_content_type = "application/vnd.cups-raw";
387 }
388
389 /*
390 * Extract the hostname and printer name from the URI...
391 */
392
393 httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme),
394 username, sizeof(username), hostname, sizeof(hostname), &port,
395 resource, sizeof(resource));
396
397 if (!port)
398 port = IPP_PORT; /* Default to port 631 */
399
400 if (!strcmp(scheme, "https"))
401 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
402 else
403 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
404
405 /*
406 * See if there are any options...
407 */
408
409 compression = NULL;
410 version = 20;
411 waitjob = 1;
412 waitprinter = 1;
413 contimeout = 7 * 24 * 60 * 60;
414
415 if ((optptr = strchr(resource, '?')) != NULL)
416 {
417 /*
418 * Yup, terminate the device name string and move to the first
419 * character of the optptr...
420 */
421
422 *optptr++ = '\0';
423
424 /*
425 * Then parse the optptr...
426 */
427
428 while (*optptr)
429 {
430 /*
431 * Get the name...
432 */
433
434 name = optptr;
435
436 while (*optptr && *optptr != '=' && *optptr != '+' && *optptr != '&')
437 optptr ++;
438
439 if ((sep = *optptr) != '\0')
440 *optptr++ = '\0';
441
442 if (sep == '=')
443 {
444 /*
445 * Get the value...
446 */
447
448 value = optptr;
449
450 while (*optptr && *optptr != '+' && *optptr != '&')
451 optptr ++;
452
453 if (*optptr)
454 *optptr++ = '\0';
455 }
456 else
457 value = (char *)"";
458
459 /*
460 * Process the option...
461 */
462
463 if (!_cups_strcasecmp(name, "waitjob"))
464 {
465 /*
466 * Wait for job completion?
467 */
468
469 waitjob = !_cups_strcasecmp(value, "on") ||
470 !_cups_strcasecmp(value, "yes") ||
471 !_cups_strcasecmp(value, "true");
472 }
473 else if (!_cups_strcasecmp(name, "waitprinter"))
474 {
475 /*
476 * Wait for printer idle?
477 */
478
479 waitprinter = !_cups_strcasecmp(value, "on") ||
480 !_cups_strcasecmp(value, "yes") ||
481 !_cups_strcasecmp(value, "true");
482 }
483 else if (!_cups_strcasecmp(name, "encryption"))
484 {
485 /*
486 * Enable/disable encryption?
487 */
488
489 if (!_cups_strcasecmp(value, "always"))
490 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
491 else if (!_cups_strcasecmp(value, "required"))
492 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
493 else if (!_cups_strcasecmp(value, "never"))
494 cupsSetEncryption(HTTP_ENCRYPT_NEVER);
495 else if (!_cups_strcasecmp(value, "ifrequested"))
496 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
497 else
498 {
499 _cupsLangPrintFilter(stderr, "ERROR",
500 _("Unknown encryption option value: \"%s\"."),
501 value);
502 }
503 }
504 else if (!_cups_strcasecmp(name, "version"))
505 {
506 if (!strcmp(value, "1.0"))
507 version = 10;
508 else if (!strcmp(value, "1.1"))
509 version = 11;
510 else if (!strcmp(value, "2.0"))
511 version = 20;
512 else if (!strcmp(value, "2.1"))
513 version = 21;
514 else if (!strcmp(value, "2.2"))
515 version = 22;
516 else
517 {
518 _cupsLangPrintFilter(stderr, "ERROR",
519 _("Unknown version option value: \"%s\"."),
520 value);
521 }
522 }
523 #ifdef HAVE_LIBZ
524 else if (!_cups_strcasecmp(name, "compression"))
525 {
526 if (!_cups_strcasecmp(value, "true") || !_cups_strcasecmp(value, "yes") ||
527 !_cups_strcasecmp(value, "on") || !_cups_strcasecmp(value, "gzip"))
528 compression = "gzip";
529 }
530 #endif /* HAVE_LIBZ */
531 else if (!_cups_strcasecmp(name, "contimeout"))
532 {
533 /*
534 * Set the connection timeout...
535 */
536
537 if (atoi(value) > 0)
538 contimeout = atoi(value);
539 }
540 else
541 {
542 /*
543 * Unknown option...
544 */
545
546 _cupsLangPrintFilter(stderr, "ERROR",
547 _("Unknown option \"%s\" with value \"%s\"."),
548 name, value);
549 }
550 }
551 }
552
553 /*
554 * If we have 7 arguments, print the file named on the command-line.
555 * Otherwise, copy stdin to a temporary file and print the temporary
556 * file.
557 */
558
559 if (argc == 6)
560 {
561 num_files = 0;
562 files = NULL;
563 send_options = !_cups_strcasecmp(final_content_type, "application/pdf") ||
564 !_cups_strcasecmp(final_content_type, "application/vnd.cups-pdf") ||
565 !_cups_strncasecmp(final_content_type, "image/", 6);
566
567 fputs("DEBUG: Sending stdin for job...\n", stderr);
568 }
569 else
570 {
571 /*
572 * Point to the files on the command-line...
573 */
574
575 num_files = argc - 6;
576 files = argv + 6;
577 send_options = 1;
578
579 #ifdef HAVE_LIBZ
580 if (compression)
581 compress_files(num_files, files);
582 #endif /* HAVE_LIBZ */
583
584 fprintf(stderr, "DEBUG: %d files to send in job...\n", num_files);
585 }
586
587 /*
588 * Set the authentication info, if any...
589 */
590
591 cupsSetPasswordCB(password_cb);
592
593 if (username[0])
594 {
595 /*
596 * Use authenticaion information in the device URI...
597 */
598
599 if ((password = strchr(username, ':')) != NULL)
600 *password++ = '\0';
601
602 cupsSetUser(username);
603 }
604 else
605 {
606 /*
607 * Try loading authentication information from the environment.
608 */
609
610 const char *ptr = getenv("AUTH_USERNAME");
611
612 if (ptr)
613 cupsSetUser(ptr);
614
615 password = getenv("AUTH_PASSWORD");
616 }
617
618 /*
619 * Try finding the remote server...
620 */
621
622 start_time = time(NULL);
623
624 sprintf(portname, "%d", port);
625
626 update_reasons(NULL, "+connecting-to-device");
627 fprintf(stderr, "DEBUG: Looking up \"%s\"...\n", hostname);
628
629 while ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portname)) == NULL)
630 {
631 _cupsLangPrintFilter(stderr, "INFO",
632 _("Unable to locate printer \"%s\"."), hostname);
633 sleep(10);
634
635 if (getenv("CLASS") != NULL)
636 {
637 update_reasons(NULL, "-connecting-to-device");
638 return (CUPS_BACKEND_STOP);
639 }
640 }
641
642 http = _httpCreate(hostname, port, addrlist, cupsEncryption(), AF_UNSPEC);
643 httpSetTimeout(http, 30.0, timeout_cb, NULL);
644
645 /*
646 * See if the printer supports SNMP...
647 */
648
649 if ((snmp_fd = _cupsSNMPOpen(addrlist->addr.addr.sa_family)) >= 0)
650 {
651 have_supplies = !backendSNMPSupplies(snmp_fd, &(addrlist->addr),
652 &start_count, NULL);
653 }
654 else
655 have_supplies = start_count = 0;
656
657 /*
658 * Wait for data from the filter...
659 */
660
661 if (num_files == 0)
662 {
663 if (!backendWaitLoop(snmp_fd, &(addrlist->addr), 0, backendNetworkSideCB))
664 return (CUPS_BACKEND_OK);
665 else if ((bytes = read(0, buffer, sizeof(buffer))) <= 0)
666 return (CUPS_BACKEND_OK);
667 }
668
669 /*
670 * Try connecting to the remote server...
671 */
672
673 delay = _cupsNextDelay(0, &prev_delay);
674
675 do
676 {
677 fprintf(stderr, "DEBUG: Connecting to %s:%d\n", hostname, port);
678 _cupsLangPrintFilter(stderr, "INFO", _("Connecting to printer."));
679
680 if (httpReconnect(http))
681 {
682 int error = errno; /* Connection error */
683
684 if (http->status == HTTP_PKI_ERROR)
685 update_reasons(NULL, "+cups-certificate-error");
686
687 if (job_canceled)
688 break;
689
690 if (getenv("CLASS") != NULL)
691 {
692 /*
693 * If the CLASS environment variable is set, the job was submitted
694 * to a class and not to a specific queue. In this case, we want
695 * to abort immediately so that the job can be requeued on the next
696 * available printer in the class.
697 */
698
699 _cupsLangPrintFilter(stderr, "INFO",
700 _("Unable to contact printer, queuing on next "
701 "printer in class."));
702
703 /*
704 * Sleep 5 seconds to keep the job from requeuing too rapidly...
705 */
706
707 sleep(5);
708
709 update_reasons(NULL, "-connecting-to-device");
710
711 return (CUPS_BACKEND_FAILED);
712 }
713
714 fprintf(stderr, "DEBUG: Connection error: %s\n", strerror(errno));
715
716 if (errno == ECONNREFUSED || errno == EHOSTDOWN ||
717 errno == EHOSTUNREACH)
718 {
719 if (contimeout && (time(NULL) - start_time) > contimeout)
720 {
721 _cupsLangPrintFilter(stderr, "ERROR",
722 _("The printer is not responding."));
723 update_reasons(NULL, "-connecting-to-device");
724 return (CUPS_BACKEND_FAILED);
725 }
726
727 switch (error)
728 {
729 case EHOSTDOWN :
730 _cupsLangPrintFilter(stderr, "WARNING",
731 _("The printer may not exist or "
732 "is unavailable at this time."));
733 break;
734
735 case EHOSTUNREACH :
736 _cupsLangPrintFilter(stderr, "WARNING",
737 _("The printer is unreachable at this "
738 "time."));
739 break;
740
741 case ECONNREFUSED :
742 default :
743 _cupsLangPrintFilter(stderr, "WARNING",
744 _("The printer is busy."));
745 break;
746 }
747
748 sleep(delay);
749
750 delay = _cupsNextDelay(delay, &prev_delay);
751 }
752 else
753 {
754 _cupsLangPrintFilter(stderr, "ERROR",
755 _("The printer is not responding."));
756 sleep(30);
757 }
758
759 if (job_canceled)
760 break;
761 }
762 else
763 update_reasons(NULL, "-cups-certificate-error");
764 }
765 while (http->fd < 0);
766
767 if (job_canceled || !http)
768 return (CUPS_BACKEND_FAILED);
769
770 update_reasons(NULL, "-connecting-to-device");
771 _cupsLangPrintFilter(stderr, "INFO", _("Connected to printer."));
772
773 fprintf(stderr, "DEBUG: Connected to %s:%d...\n",
774 httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
775 _httpAddrPort(http->hostaddr));
776
777 /*
778 * Build a URI for the printer and fill the standard IPP attributes for
779 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
780 * might contain username:password information...
781 */
782
783 httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), scheme, NULL, hostname,
784 port, resource);
785
786 /*
787 * First validate the destination and see if the device supports multiple
788 * copies...
789 */
790
791 copies_sup = NULL;
792 cups_version = NULL;
793 format_sup = NULL;
794 media_col_sup = NULL;
795 supported = NULL;
796 operations_sup = NULL;
797 doc_handling_sup = NULL;
798
799 do
800 {
801 /*
802 * Check for side-channel requests...
803 */
804
805 backendCheckSideChannel(snmp_fd, http->hostaddr);
806
807 /*
808 * Build the IPP request...
809 */
810
811 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
812 request->request.op.version[0] = version / 10;
813 request->request.op.version[1] = version % 10;
814
815 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
816 NULL, uri);
817
818 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
819 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
820 NULL, pattrs);
821
822 /*
823 * Do the request...
824 */
825
826 fputs("DEBUG: Getting supported attributes...\n", stderr);
827
828 if (http->version < HTTP_1_1)
829 {
830 fprintf(stderr, "DEBUG: Printer responded with HTTP version %d.%d.\n",
831 http->version / 100, http->version % 100);
832 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
833 "cups-ipp-wrong-http-version");
834 }
835
836 supported = cupsDoRequest(http, request, resource);
837 ipp_status = cupsLastError();
838
839 fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
840 ippErrorString(ipp_status), cupsLastErrorString());
841
842 if (ipp_status > IPP_OK_CONFLICT)
843 {
844 fprintf(stderr, "DEBUG: Get-Printer-Attributes returned %s.\n",
845 ippErrorString(ipp_status));
846
847 if (ipp_status == IPP_PRINTER_BUSY ||
848 ipp_status == IPP_SERVICE_UNAVAILABLE)
849 {
850 if (contimeout && (time(NULL) - start_time) > contimeout)
851 {
852 _cupsLangPrintFilter(stderr, "ERROR",
853 _("The printer is not responding."));
854 return (CUPS_BACKEND_FAILED);
855 }
856
857 _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
858
859 report_printer_state(supported);
860
861 sleep(delay);
862
863 delay = _cupsNextDelay(delay, &prev_delay);
864 }
865 else if ((ipp_status == IPP_BAD_REQUEST ||
866 ipp_status == IPP_VERSION_NOT_SUPPORTED) && version > 10)
867 {
868 /*
869 * Switch to IPP/1.1 or IPP/1.0...
870 */
871
872 if (version >= 20)
873 {
874 _cupsLangPrintFilter(stderr, "INFO",
875 _("Printer does not support IPP/%d.%d, trying "
876 "IPP/%s."), version / 10, version % 10, "1.1");
877 version = 11;
878 }
879 else
880 {
881 _cupsLangPrintFilter(stderr, "INFO",
882 _("Printer does not support IPP/%d.%d, trying "
883 "IPP/%s."), version / 10, version % 10, "1.0");
884 version = 10;
885 }
886
887 httpReconnect(http);
888 }
889 else if (ipp_status == IPP_NOT_FOUND)
890 {
891 _cupsLangPrintFilter(stderr, "ERROR",
892 _("The printer URI is incorrect or no longer "
893 "exists."));
894
895 ippDelete(supported);
896
897 return (CUPS_BACKEND_STOP);
898 }
899 else if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
900 {
901 if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
902 "Negotiate", 9))
903 auth_info_required = "negotiate";
904
905 fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
906 return (CUPS_BACKEND_AUTH_REQUIRED);
907 }
908 else
909 {
910 _cupsLangPrintFilter(stderr, "ERROR",
911 _("Unable to get printer status."));
912 sleep(10);
913 }
914
915 ippDelete(supported);
916 supported = NULL;
917 continue;
918 }
919
920 if (!getenv("CLASS"))
921 {
922 /*
923 * Check printer-is-accepting-jobs = false and printer-state-reasons for the
924 * "spool-area-full" keyword...
925 */
926
927 int busy = 0;
928
929 if ((printer_accepting = ippFindAttribute(supported,
930 "printer-is-accepting-jobs",
931 IPP_TAG_BOOLEAN)) != NULL &&
932 !printer_accepting->values[0].boolean)
933 busy = 1;
934 else if (!printer_accepting)
935 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
936 "cups-ipp-missing-printer-is-accepting-jobs");
937
938 if ((printer_state = ippFindAttribute(supported,
939 "printer-state-reasons",
940 IPP_TAG_KEYWORD)) != NULL && !busy)
941 {
942 for (i = 0; i < printer_state->num_values; i ++)
943 if (!strcmp(printer_state->values[0].string.text,
944 "spool-area-full") ||
945 !strncmp(printer_state->values[0].string.text, "spool-area-full-",
946 16))
947 {
948 busy = 1;
949 break;
950 }
951 }
952 else
953 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
954 "cups-ipp-missing-printer-state-reasons");
955
956 if (busy)
957 {
958 _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
959
960 report_printer_state(supported);
961
962 sleep(delay);
963
964 delay = _cupsNextDelay(delay, &prev_delay);
965
966 ippDelete(supported);
967 supported = NULL;
968 continue;
969 }
970 }
971
972 /*
973 * Check for supported attributes...
974 */
975
976 if ((copies_sup = ippFindAttribute(supported, "copies-supported",
977 IPP_TAG_RANGE)) != NULL)
978 {
979 /*
980 * Has the "copies-supported" attribute - does it have an upper
981 * bound > 1?
982 */
983
984 fprintf(stderr, "DEBUG: copies-supported=%d-%d\n",
985 copies_sup->values[0].range.lower,
986 copies_sup->values[0].range.upper);
987
988 if (copies_sup->values[0].range.upper <= 1)
989 copies_sup = NULL; /* No */
990 }
991
992 cups_version = ippFindAttribute(supported, "cups-version", IPP_TAG_TEXT);
993
994 if ((format_sup = ippFindAttribute(supported, "document-format-supported",
995 IPP_TAG_MIMETYPE)) != NULL)
996 {
997 fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
998 format_sup->num_values);
999 for (i = 0; i < format_sup->num_values; i ++)
1000 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
1001 format_sup->values[i].string.text);
1002 }
1003
1004 if ((media_col_sup = ippFindAttribute(supported, "media-col-supported",
1005 IPP_TAG_KEYWORD)) != NULL)
1006 {
1007 fprintf(stderr, "DEBUG: media-col-supported (%d values)\n",
1008 media_col_sup->num_values);
1009 for (i = 0; i < media_col_sup->num_values; i ++)
1010 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
1011 media_col_sup->values[i].string.text);
1012 }
1013
1014 if ((operations_sup = ippFindAttribute(supported, "operations-supported",
1015 IPP_TAG_ENUM)) != NULL)
1016 {
1017 for (i = 0; i < operations_sup->num_values; i ++)
1018 if (operations_sup->values[i].integer == IPP_PRINT_JOB)
1019 break;
1020
1021 if (i >= operations_sup->num_values)
1022 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1023 "cups-ipp-missing-print-job");
1024
1025 for (i = 0; i < operations_sup->num_values; i ++)
1026 if (operations_sup->values[i].integer == IPP_CANCEL_JOB)
1027 break;
1028
1029 if (i >= operations_sup->num_values)
1030 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1031 "cups-ipp-missing-cancel-job");
1032
1033 for (i = 0; i < operations_sup->num_values; i ++)
1034 if (operations_sup->values[i].integer == IPP_GET_JOB_ATTRIBUTES)
1035 break;
1036
1037 if (i >= operations_sup->num_values)
1038 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1039 "cups-ipp-missing-get-job-attributes");
1040
1041 for (i = 0; i < operations_sup->num_values; i ++)
1042 if (operations_sup->values[i].integer == IPP_GET_PRINTER_ATTRIBUTES)
1043 break;
1044
1045 if (i >= operations_sup->num_values)
1046 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1047 "cups-ipp-missing-get-printer-attributes");
1048
1049 for (i = 0; i < operations_sup->num_values; i ++)
1050 {
1051 if (operations_sup->values[i].integer == IPP_VALIDATE_JOB)
1052 validate_job = 1;
1053 else if (operations_sup->values[i].integer == IPP_CREATE_JOB)
1054 create_job = 1;
1055 else if (operations_sup->values[i].integer == IPP_SEND_DOCUMENT)
1056 send_document = 1;
1057 }
1058
1059 if (!send_document)
1060 {
1061 fputs("DEBUG: Printer supports Create-Job but not Send-Document.\n",
1062 stderr);
1063 create_job = 0;
1064 }
1065
1066 if (!validate_job)
1067 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1068 "cups-ipp-missing-validate-job");
1069 }
1070 else
1071 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1072 "cups-ipp-missing-operations-supported");
1073
1074 doc_handling_sup = ippFindAttribute(supported,
1075 "multiple-document-handling-supported",
1076 IPP_TAG_KEYWORD);
1077
1078 report_printer_state(supported);
1079 }
1080 while (ipp_status > IPP_OK_CONFLICT);
1081
1082 /*
1083 * See if the printer is accepting jobs and is not stopped; if either
1084 * condition is true and we are printing to a class, requeue the job...
1085 */
1086
1087 if (getenv("CLASS") != NULL)
1088 {
1089 printer_state = ippFindAttribute(supported, "printer-state",
1090 IPP_TAG_ENUM);
1091 printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs",
1092 IPP_TAG_BOOLEAN);
1093
1094 if (printer_state == NULL ||
1095 (printer_state->values[0].integer > IPP_PRINTER_PROCESSING &&
1096 waitprinter) ||
1097 printer_accepting == NULL ||
1098 !printer_accepting->values[0].boolean)
1099 {
1100 /*
1101 * If the CLASS environment variable is set, the job was submitted
1102 * to a class and not to a specific queue. In this case, we want
1103 * to abort immediately so that the job can be requeued on the next
1104 * available printer in the class.
1105 */
1106
1107 _cupsLangPrintFilter(stderr, "INFO",
1108 _("Unable to contact printer, queuing on next "
1109 "printer in class."));
1110
1111 ippDelete(supported);
1112 httpClose(http);
1113
1114 /*
1115 * Sleep 5 seconds to keep the job from requeuing too rapidly...
1116 */
1117
1118 sleep(5);
1119
1120 return (CUPS_BACKEND_FAILED);
1121 }
1122 }
1123
1124 /*
1125 * See if the printer supports multiple copies...
1126 */
1127
1128 copies = atoi(argv[4]);
1129
1130 if (copies_sup || argc < 7)
1131 {
1132 copies_remaining = 1;
1133
1134 if (argc < 7 && !_cups_strncasecmp(final_content_type, "image/", 6))
1135 copies = 1;
1136 }
1137 else
1138 copies_remaining = copies;
1139
1140 /*
1141 * Prepare remaining printing options...
1142 */
1143
1144 options = NULL;
1145 pc = NULL;
1146
1147 if (send_options)
1148 {
1149 num_options = cupsParseOptions(argv[5], 0, &options);
1150
1151 if (!cups_version && media_col_sup)
1152 {
1153 /*
1154 * Load the PPD file and generate PWG attribute mapping information...
1155 */
1156
1157 ppd = ppdOpenFile(getenv("PPD"));
1158 pc = _ppdCacheCreateWithPPD(ppd);
1159
1160 ppdClose(ppd);
1161 }
1162 }
1163 else
1164 num_options = 0;
1165
1166 document_format = NULL;
1167
1168 if (format_sup != NULL)
1169 {
1170 for (i = 0; i < format_sup->num_values; i ++)
1171 if (!_cups_strcasecmp(final_content_type, format_sup->values[i].string.text))
1172 {
1173 document_format = final_content_type;
1174 break;
1175 }
1176
1177 if (!document_format)
1178 {
1179 for (i = 0; i < format_sup->num_values; i ++)
1180 if (!_cups_strcasecmp("application/octet-stream",
1181 format_sup->values[i].string.text))
1182 {
1183 document_format = "application/octet-stream";
1184 break;
1185 }
1186 }
1187 }
1188
1189 /*
1190 * If the printer does not support HTTP/1.1 (which IPP requires), copy stdin
1191 * to a temporary file so that we can do a HTTP/1.0 submission...
1192 *
1193 * (I hate compatibility hacks!)
1194 */
1195
1196 if (http->version < HTTP_1_1 && num_files == 0)
1197 {
1198 if ((fd = cupsTempFd(tmpfilename, sizeof(tmpfilename))) < 0)
1199 {
1200 perror("DEBUG: Unable to create temporary file");
1201 return (CUPS_BACKEND_FAILED);
1202 }
1203
1204 _cupsLangPrintFilter(stderr, "INFO", _("Copying print data."));
1205
1206 compatsize = backendRunLoop(-1, fd, snmp_fd, &(addrlist->addr), 0, 0,
1207 backendNetworkSideCB);
1208
1209 close(fd);
1210
1211 compatfile = tmpfilename;
1212 files = &compatfile;
1213 num_files = 1;
1214 }
1215 else if (http->version < HTTP_1_1 && num_files == 1)
1216 {
1217 struct stat fileinfo; /* File information */
1218
1219 if (!stat(files[0], &fileinfo))
1220 compatsize = fileinfo.st_size;
1221 }
1222
1223 /*
1224 * Start monitoring the printer in the background...
1225 */
1226
1227 monitor.uri = uri;
1228 monitor.hostname = hostname;
1229 monitor.user = argv[2];
1230 monitor.resource = resource;
1231 monitor.port = port;
1232 monitor.version = version;
1233 monitor.job_id = 0;
1234 monitor.encryption = cupsEncryption();
1235 monitor.job_state = IPP_JOB_PENDING;
1236 monitor.printer_state = IPP_PRINTER_IDLE;
1237
1238 if (create_job)
1239 {
1240 monitor.job_name = argv[3];
1241 }
1242 else
1243 {
1244 snprintf(print_job_name, sizeof(print_job_name), "%s - %s", argv[1],
1245 argv[3]);
1246 monitor.job_name = print_job_name;
1247 }
1248
1249 _cupsThreadCreate((_cups_thread_func_t)monitor_printer, &monitor);
1250
1251 /*
1252 * Validate access to the printer...
1253 */
1254
1255 while (!job_canceled && validate_job)
1256 {
1257 request = new_request(IPP_VALIDATE_JOB, version, uri, argv[2],
1258 monitor.job_name, num_options, options, compression,
1259 copies_sup ? copies : 1, document_format, pc,
1260 media_col_sup, doc_handling_sup);
1261
1262 ippDelete(cupsDoRequest(http, request, resource));
1263
1264 ipp_status = cupsLastError();
1265
1266 fprintf(stderr, "DEBUG: Validate-Job: %s (%s)\n",
1267 ippErrorString(ipp_status), cupsLastErrorString());
1268
1269 if (job_canceled)
1270 break;
1271
1272 if (ipp_status == IPP_SERVICE_UNAVAILABLE || ipp_status == IPP_PRINTER_BUSY)
1273 {
1274 _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
1275 sleep(10);
1276 }
1277 else if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
1278 ipp_status == IPP_AUTHENTICATION_CANCELED)
1279 {
1280 /*
1281 * Update auth-info-required as needed...
1282 */
1283
1284 fprintf(stderr, "DEBUG: WWW-Authenticate=\"%s\"\n",
1285 httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE));
1286
1287 /*
1288 * Normal authentication goes through the password callback, which sets
1289 * auth_info_required to "username,password". Kerberos goes directly
1290 * through GSSAPI, so look for Negotiate in the WWW-Authenticate header
1291 * here and set auth_info_required as needed...
1292 */
1293
1294 if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
1295 "Negotiate", 9))
1296 auth_info_required = "negotiate";
1297
1298 goto cleanup;
1299 }
1300 else if (ipp_status == IPP_OPERATION_NOT_SUPPORTED)
1301 {
1302 /*
1303 * This is all too common...
1304 */
1305
1306 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1307 "cups-ipp-missing-validate-job");
1308 break;
1309 }
1310 else if (ipp_status < IPP_REDIRECTION_OTHER_SITE)
1311 break;
1312 }
1313
1314 /*
1315 * Then issue the print-job request...
1316 */
1317
1318 job_id = 0;
1319
1320 while (!job_canceled && copies_remaining > 0)
1321 {
1322 /*
1323 * Check for side-channel requests...
1324 */
1325
1326 backendCheckSideChannel(snmp_fd, http->hostaddr);
1327
1328 /*
1329 * Build the IPP job creation request...
1330 */
1331
1332 if (job_canceled)
1333 break;
1334
1335 request = new_request((num_files > 1 || create_job) ? IPP_CREATE_JOB :
1336 IPP_PRINT_JOB,
1337 version, uri, argv[2], monitor.job_name, num_options,
1338 options, compression, copies_sup ? copies : 1,
1339 document_format, pc, media_col_sup, doc_handling_sup);
1340
1341 /*
1342 * Do the request...
1343 */
1344
1345 if (num_files > 1 || create_job)
1346 response = cupsDoRequest(http, request, resource);
1347 else
1348 {
1349 size_t length = 0; /* Length of request */
1350
1351 if (compatsize > 0)
1352 {
1353 fputs("DEBUG: Sending file using HTTP/1.0 Content-Length...\n", stderr);
1354 length = ippLength(request) + (size_t)compatsize;
1355 }
1356 else
1357 fputs("DEBUG: Sending file using HTTP/1.1 chunking...\n", stderr);
1358
1359 http_status = cupsSendRequest(http, request, resource, length);
1360 if (http_status == HTTP_CONTINUE && request->state == IPP_DATA)
1361 {
1362 if (num_files == 1)
1363 {
1364 if ((fd = open(files[0], O_RDONLY)) < 0)
1365 {
1366 _cupsLangPrintError("ERROR", _("Unable to open print file"));
1367 return (CUPS_BACKEND_FAILED);
1368 }
1369 }
1370 else
1371 {
1372 fd = 0;
1373 http_status = cupsWriteRequestData(http, buffer, bytes);
1374 }
1375
1376 while (http_status == HTTP_CONTINUE &&
1377 (!job_canceled || compatsize > 0))
1378 {
1379 /*
1380 * Check for side-channel requests and more print data...
1381 */
1382
1383 FD_ZERO(&input);
1384 FD_SET(fd, &input);
1385 FD_SET(snmp_fd, &input);
1386
1387 while (select(fd > snmp_fd ? fd + 1 : snmp_fd + 1, &input, NULL, NULL,
1388 NULL) <= 0 && !job_canceled);
1389
1390 if (FD_ISSET(snmp_fd, &input))
1391 backendCheckSideChannel(snmp_fd, http->hostaddr);
1392
1393 if (FD_ISSET(fd, &input))
1394 {
1395 if ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
1396 {
1397 fprintf(stderr, "DEBUG: Read %d bytes...\n", (int)bytes);
1398
1399 if (cupsWriteRequestData(http, buffer, bytes) != HTTP_CONTINUE)
1400 break;
1401 }
1402 else if (bytes == 0 || (errno != EINTR && errno != EAGAIN))
1403 break;
1404 }
1405 }
1406
1407 if (num_files == 1)
1408 close(fd);
1409 }
1410
1411 response = cupsGetResponse(http, resource);
1412 ippDelete(request);
1413 }
1414
1415 ipp_status = cupsLastError();
1416
1417 fprintf(stderr, "DEBUG: %s: %s (%s)\n",
1418 (num_files > 1 || create_job) ? "Create-Job" : "Print-Job",
1419 ippErrorString(ipp_status), cupsLastErrorString());
1420
1421 if (ipp_status > IPP_OK_CONFLICT)
1422 {
1423 job_id = 0;
1424
1425 if (job_canceled)
1426 break;
1427
1428 if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1429 ipp_status == IPP_NOT_POSSIBLE ||
1430 ipp_status == IPP_PRINTER_BUSY)
1431 {
1432 _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
1433 sleep(10);
1434
1435 if (num_files == 0)
1436 {
1437 /*
1438 * We can't re-submit when we have no files to print, so exit
1439 * immediately with the right status code...
1440 */
1441
1442 goto cleanup;
1443 }
1444 }
1445 else if (ipp_status == IPP_ERROR_JOB_CANCELED)
1446 goto cleanup;
1447 else
1448 {
1449 /*
1450 * Update auth-info-required as needed...
1451 */
1452
1453 _cupsLangPrintFilter(stderr, "ERROR",
1454 _("Print file was not accepted."));
1455
1456 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
1457 {
1458 fprintf(stderr, "DEBUG: WWW-Authenticate=\"%s\"\n",
1459 httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE));
1460
1461 /*
1462 * Normal authentication goes through the password callback, which sets
1463 * auth_info_required to "username,password". Kerberos goes directly
1464 * through GSSAPI, so look for Negotiate in the WWW-Authenticate header
1465 * here and set auth_info_required as needed...
1466 */
1467
1468 if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
1469 "Negotiate", 9))
1470 auth_info_required = "negotiate";
1471 }
1472 else
1473 sleep(10);
1474
1475 if (num_files == 0)
1476 {
1477 /*
1478 * We can't re-submit when we have no files to print, so exit
1479 * immediately with the right status code...
1480 */
1481
1482 goto cleanup;
1483 }
1484 }
1485 }
1486 else if ((job_id_attr = ippFindAttribute(response, "job-id",
1487 IPP_TAG_INTEGER)) == NULL)
1488 {
1489 _cupsLangPrintFilter(stderr, "INFO",
1490 _("Print file accepted - job ID unknown."));
1491 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1492 "cups-ipp-missing-job-id");
1493 job_id = 0;
1494 }
1495 else
1496 {
1497 monitor.job_id = job_id = job_id_attr->values[0].integer;
1498 _cupsLangPrintFilter(stderr, "INFO",
1499 _("Print file accepted - job ID %d."), job_id);
1500 }
1501
1502 fprintf(stderr, "DEBUG: job-id=%d\n", job_id);
1503 ippDelete(response);
1504
1505 if (job_canceled)
1506 break;
1507
1508 if (job_id && (num_files > 1 || create_job))
1509 {
1510 for (i = 0; num_files == 0 || i < num_files; i ++)
1511 {
1512 /*
1513 * Check for side-channel requests...
1514 */
1515
1516 backendCheckSideChannel(snmp_fd, http->hostaddr);
1517
1518 /*
1519 * Send the next file in the job...
1520 */
1521
1522 request = ippNewRequest(IPP_SEND_DOCUMENT);
1523 request->request.op.version[0] = version / 10;
1524 request->request.op.version[1] = version % 10;
1525
1526 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1527 NULL, uri);
1528
1529 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1530 job_id);
1531
1532 if (argv[2][0])
1533 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1534 "requesting-user-name", NULL, argv[2]);
1535
1536 if ((i + 1) >= num_files)
1537 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
1538
1539 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
1540 "document-format", NULL, document_format);
1541
1542 fprintf(stderr, "DEBUG: Sending file %d using chunking...\n", i + 1);
1543 http_status = cupsSendRequest(http, request, resource, 0);
1544 if (http_status == HTTP_CONTINUE && request->state == IPP_DATA)
1545 {
1546 if (num_files == 0)
1547 {
1548 fd = 0;
1549 http_status = cupsWriteRequestData(http, buffer, bytes);
1550 }
1551 else
1552 {
1553 if ((fd = open(files[i], O_RDONLY)) < 0)
1554 {
1555 _cupsLangPrintError("ERROR", _("Unable to open print file"));
1556 return (CUPS_BACKEND_FAILED);
1557 }
1558 }
1559 }
1560 else
1561 fd = -1;
1562
1563 if (fd >= 0)
1564 {
1565 while (!job_canceled &&
1566 (bytes = read(fd, buffer, sizeof(buffer))) > 0)
1567 {
1568 if (cupsWriteRequestData(http, buffer, bytes) != HTTP_CONTINUE)
1569 break;
1570 else
1571 {
1572 /*
1573 * Check for side-channel requests...
1574 */
1575
1576 backendCheckSideChannel(snmp_fd, http->hostaddr);
1577 }
1578 }
1579
1580 if (fd > 0)
1581 close(fd);
1582 }
1583
1584 ippDelete(cupsGetResponse(http, resource));
1585 ippDelete(request);
1586
1587 fprintf(stderr, "DEBUG: Send-Document: %s (%s)\n",
1588 ippErrorString(cupsLastError()), cupsLastErrorString());
1589
1590 if (cupsLastError() > IPP_OK_CONFLICT)
1591 {
1592 ipp_status = cupsLastError();
1593
1594 _cupsLangPrintFilter(stderr, "ERROR",
1595 _("Unable to add document to print job."));
1596 break;
1597 }
1598 else if (num_files == 0 || fd < 0)
1599 break;
1600 }
1601 }
1602
1603 if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
1604 {
1605 fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
1606 copies_remaining --;
1607 }
1608 else if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1609 ipp_status == IPP_NOT_POSSIBLE ||
1610 ipp_status == IPP_PRINTER_BUSY)
1611 continue;
1612 else
1613 copies_remaining --;
1614
1615 /*
1616 * Wait for the job to complete...
1617 */
1618
1619 if (!job_id || !waitjob)
1620 continue;
1621
1622 _cupsLangPrintFilter(stderr, "INFO", _("Waiting for job to complete."));
1623
1624 for (delay = _cupsNextDelay(0, &prev_delay); !job_canceled;)
1625 {
1626 /*
1627 * Check for side-channel requests...
1628 */
1629
1630 backendCheckSideChannel(snmp_fd, http->hostaddr);
1631
1632 /*
1633 * Build an IPP_GET_JOB_ATTRIBUTES request...
1634 */
1635
1636 request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
1637 request->request.op.version[0] = version / 10;
1638 request->request.op.version[1] = version % 10;
1639
1640 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1641 NULL, uri);
1642
1643 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1644 job_id);
1645
1646 if (argv[2][0])
1647 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1648 "requesting-user-name", NULL, argv[2]);
1649
1650 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1651 "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
1652 NULL, jattrs);
1653
1654 /*
1655 * Do the request...
1656 */
1657
1658 httpReconnect(http);
1659 response = cupsDoRequest(http, request, resource);
1660 ipp_status = cupsLastError();
1661
1662 if (ipp_status == IPP_NOT_FOUND)
1663 {
1664 /*
1665 * Job has gone away and/or the server has no job history...
1666 */
1667
1668 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1669 "cups-ipp-missing-job-history");
1670 ippDelete(response);
1671
1672 ipp_status = IPP_OK;
1673 break;
1674 }
1675
1676 fprintf(stderr, "DEBUG: Get-Job-Attributes: %s (%s)\n",
1677 ippErrorString(ipp_status), cupsLastErrorString());
1678
1679 if (ipp_status > IPP_OK_CONFLICT)
1680 {
1681 if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
1682 ipp_status != IPP_NOT_POSSIBLE &&
1683 ipp_status != IPP_PRINTER_BUSY)
1684 {
1685 ippDelete(response);
1686
1687 _cupsLangPrintFilter(stderr, "ERROR",
1688 _("Unable to get print job status."));
1689 break;
1690 }
1691 }
1692
1693 if (response)
1694 {
1695 if ((job_state = ippFindAttribute(response, "job-state",
1696 IPP_TAG_ENUM)) != NULL)
1697 {
1698 /*
1699 * Reflect the remote job state in the local queue...
1700 */
1701
1702 if (cups_version &&
1703 job_state->values[0].integer >= IPP_JOB_PENDING &&
1704 job_state->values[0].integer <= IPP_JOB_COMPLETED)
1705 update_reasons(NULL,
1706 remote_job_states[job_state->values[0].integer -
1707 IPP_JOB_PENDING]);
1708
1709 if ((job_sheets = ippFindAttribute(response,
1710 "job-media-sheets-completed",
1711 IPP_TAG_INTEGER)) == NULL)
1712 job_sheets = ippFindAttribute(response,
1713 "job-impressions-completed",
1714 IPP_TAG_INTEGER);
1715
1716 if (job_sheets)
1717 fprintf(stderr, "PAGE: total %d\n",
1718 job_sheets->values[0].integer);
1719
1720 /*
1721 * Stop polling if the job is finished or pending-held...
1722 */
1723
1724 if (job_state->values[0].integer > IPP_JOB_STOPPED)
1725 {
1726 ippDelete(response);
1727 break;
1728 }
1729 }
1730 else if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
1731 ipp_status != IPP_NOT_POSSIBLE &&
1732 ipp_status != IPP_PRINTER_BUSY)
1733 {
1734 /*
1735 * If the printer does not return a job-state attribute, it does not
1736 * conform to the IPP specification - break out immediately and fail
1737 * the job...
1738 */
1739
1740 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1741 "cups-ipp-missing-job-state");
1742 ipp_status = IPP_INTERNAL_ERROR;
1743 break;
1744 }
1745 }
1746
1747 ippDelete(response);
1748
1749 /*
1750 * Wait before polling again...
1751 */
1752
1753 sleep(delay);
1754
1755 delay = _cupsNextDelay(delay, &prev_delay);
1756 }
1757 }
1758
1759 /*
1760 * Cancel the job as needed...
1761 */
1762
1763 if (job_canceled && job_id)
1764 cancel_job(http, uri, job_id, resource, argv[2], version);
1765
1766 /*
1767 * Check the printer state and report it if necessary...
1768 */
1769
1770 check_printer_state(http, uri, resource, argv[2], version);
1771
1772 /*
1773 * Collect the final page count as needed...
1774 */
1775
1776 if (have_supplies &&
1777 !backendSNMPSupplies(snmp_fd, http->hostaddr, &page_count, NULL) &&
1778 page_count > start_count)
1779 fprintf(stderr, "PAGE: total %d\n", page_count - start_count);
1780
1781 #ifdef HAVE_GSSAPI
1782 /*
1783 * See if we used Kerberos at all...
1784 */
1785
1786 if (http->gssctx)
1787 auth_info_required = "negotiate";
1788 #endif /* HAVE_GSSAPI */
1789
1790 /*
1791 * Free memory...
1792 */
1793
1794 cleanup:
1795
1796 cupsFreeOptions(num_options, options);
1797 _ppdCacheDestroy(pc);
1798
1799 httpClose(http);
1800
1801 ippDelete(supported);
1802
1803 /*
1804 * Remove the temporary file(s) if necessary...
1805 */
1806
1807 if (tmpfilename[0])
1808 unlink(tmpfilename);
1809
1810 #ifdef HAVE_LIBZ
1811 if (compression)
1812 {
1813 for (i = 0; i < num_files; i ++)
1814 unlink(files[i]);
1815 }
1816 #endif /* HAVE_LIBZ */
1817
1818 /*
1819 * Return the queue status...
1820 */
1821
1822 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
1823 ipp_status == IPP_AUTHENTICATION_CANCELED ||
1824 ipp_status <= IPP_OK_CONFLICT)
1825 fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
1826
1827 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
1828 ipp_status == IPP_AUTHENTICATION_CANCELED)
1829 return (CUPS_BACKEND_AUTH_REQUIRED);
1830 else if (ipp_status == IPP_INTERNAL_ERROR)
1831 return (CUPS_BACKEND_STOP);
1832 else if (ipp_status == IPP_DOCUMENT_FORMAT ||
1833 ipp_status == IPP_CONFLICT)
1834 return (CUPS_BACKEND_FAILED);
1835 else if (ipp_status > IPP_OK_CONFLICT && ipp_status != IPP_ERROR_JOB_CANCELED)
1836 return (CUPS_BACKEND_RETRY_CURRENT);
1837 else
1838 {
1839 _cupsLangPrintFilter(stderr, "INFO", _("Ready to print."));
1840 return (CUPS_BACKEND_OK);
1841 }
1842 }
1843
1844
1845 /*
1846 * 'cancel_job()' - Cancel a print job.
1847 */
1848
1849 static void
1850 cancel_job(http_t *http, /* I - HTTP connection */
1851 const char *uri, /* I - printer-uri */
1852 int id, /* I - job-id */
1853 const char *resource, /* I - Resource path */
1854 const char *user, /* I - requesting-user-name */
1855 int version) /* I - IPP version */
1856 {
1857 ipp_t *request; /* Cancel-Job request */
1858
1859
1860 _cupsLangPrintFilter(stderr, "INFO", _("Canceling print job."));
1861
1862 request = ippNewRequest(IPP_CANCEL_JOB);
1863 request->request.op.version[0] = version / 10;
1864 request->request.op.version[1] = version % 10;
1865
1866 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1867 NULL, uri);
1868 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
1869
1870 if (user && user[0])
1871 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1872 "requesting-user-name", NULL, user);
1873
1874 /*
1875 * Do the request...
1876 */
1877
1878 ippDelete(cupsDoRequest(http, request, resource));
1879
1880 if (cupsLastError() > IPP_OK_CONFLICT)
1881 _cupsLangPrintFilter(stderr, "ERROR", _("Unable to cancel print job."));
1882 }
1883
1884
1885 /*
1886 * 'check_printer_state()' - Check the printer state.
1887 */
1888
1889 static ipp_pstate_t /* O - Current printer-state */
1890 check_printer_state(
1891 http_t *http, /* I - HTTP connection */
1892 const char *uri, /* I - Printer URI */
1893 const char *resource, /* I - Resource path */
1894 const char *user, /* I - Username, if any */
1895 int version) /* I - IPP version */
1896 {
1897 ipp_t *request, /* IPP request */
1898 *response; /* IPP response */
1899 ipp_attribute_t *attr; /* Attribute in response */
1900 ipp_pstate_t printer_state = IPP_PRINTER_STOPPED;
1901 /* Current printer-state */
1902
1903
1904 /*
1905 * Send a Get-Printer-Attributes request and log the results...
1906 */
1907
1908 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
1909 request->request.op.version[0] = version / 10;
1910 request->request.op.version[1] = version % 10;
1911
1912 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1913 NULL, uri);
1914
1915 if (user && user[0])
1916 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1917 "requesting-user-name", NULL, user);
1918
1919 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1920 "requested-attributes",
1921 (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
1922
1923 if ((response = cupsDoRequest(http, request, resource)) != NULL)
1924 {
1925 report_printer_state(response);
1926
1927 if ((attr = ippFindAttribute(response, "printer-state",
1928 IPP_TAG_ENUM)) != NULL)
1929 printer_state = (ipp_pstate_t)attr->values[0].integer;
1930
1931 ippDelete(response);
1932 }
1933
1934 fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
1935 ippErrorString(cupsLastError()), cupsLastErrorString());
1936
1937 /*
1938 * Return the printer-state value...
1939 */
1940
1941 return (printer_state);
1942 }
1943
1944
1945 #ifdef HAVE_LIBZ
1946 /*
1947 * 'compress_files()' - Compress print files.
1948 */
1949
1950 static void
1951 compress_files(int num_files, /* I - Number of files */
1952 char **files) /* I - Files */
1953 {
1954 int i, /* Looping var */
1955 fd; /* Temporary file descriptor */
1956 ssize_t bytes; /* Bytes read/written */
1957 size_t total; /* Total bytes read */
1958 cups_file_t *in, /* Input file */
1959 *out; /* Output file */
1960 struct stat outinfo; /* Output file information */
1961 char filename[1024], /* Temporary filename */
1962 buffer[32768]; /* Copy buffer */
1963
1964
1965 fprintf(stderr, "DEBUG: Compressing %d job files...\n", num_files);
1966 for (i = 0; i < num_files; i ++)
1967 {
1968 if ((fd = cupsTempFd(filename, sizeof(filename))) < 0)
1969 {
1970 _cupsLangPrintError("ERROR", _("Unable to create compressed print file"));
1971 exit(CUPS_BACKEND_FAILED);
1972 }
1973
1974 if ((out = cupsFileOpenFd(fd, "w9")) == NULL)
1975 {
1976 _cupsLangPrintError("ERROR", _("Unable to open compressed print file"));
1977 exit(CUPS_BACKEND_FAILED);
1978 }
1979
1980 if ((in = cupsFileOpen(files[i], "r")) == NULL)
1981 {
1982 _cupsLangPrintError("ERROR", _("Unable to open print file"));
1983 cupsFileClose(out);
1984 exit(CUPS_BACKEND_FAILED);
1985 }
1986
1987 total = 0;
1988 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
1989 if (cupsFileWrite(out, buffer, bytes) < bytes)
1990 {
1991 _cupsLangPrintError("ERROR",
1992 _("Unable to generate compressed print file"));
1993 cupsFileClose(in);
1994 cupsFileClose(out);
1995 exit(CUPS_BACKEND_FAILED);
1996 }
1997 else
1998 total += bytes;
1999
2000 cupsFileClose(out);
2001 cupsFileClose(in);
2002
2003 files[i] = strdup(filename);
2004
2005 if (!stat(filename, &outinfo))
2006 fprintf(stderr,
2007 "DEBUG: File %d compressed to %.1f%% of original size, "
2008 CUPS_LLFMT " bytes...\n",
2009 i + 1, 100.0 * outinfo.st_size / total,
2010 CUPS_LLCAST outinfo.st_size);
2011 }
2012 }
2013 #endif /* HAVE_LIBZ */
2014
2015
2016 /*
2017 * 'monitor_printer()' - Monitor the printer state.
2018 */
2019
2020 static void * /* O - Thread exit code */
2021 monitor_printer(
2022 _cups_monitor_t *monitor) /* I - Monitoring data */
2023 {
2024 http_t *http; /* Connection to printer */
2025 ipp_t *request, /* IPP request */
2026 *response; /* IPP response */
2027 ipp_attribute_t *attr; /* Attribute in response */
2028 int delay, /* Current delay */
2029 prev_delay; /* Previous delay */
2030 ipp_op_t job_op; /* Operation to use */
2031 int job_id; /* Job ID */
2032 const char *job_name; /* Job name */
2033 ipp_jstate_t job_state; /* Job state */
2034 const char *job_user; /* Job originating user name */
2035
2036
2037 /*
2038 * Make a copy of the printer connection...
2039 */
2040
2041 http = _httpCreate(monitor->hostname, monitor->port, NULL, monitor->encryption,
2042 AF_UNSPEC);
2043 httpSetTimeout(http, 30.0, timeout_cb, NULL);
2044 cupsSetPasswordCB(password_cb);
2045
2046 /*
2047 * Loop until the job is canceled, aborted, or completed.
2048 */
2049
2050 delay = _cupsNextDelay(0, &prev_delay);
2051
2052 while (monitor->job_state < IPP_JOB_CANCELED && !job_canceled)
2053 {
2054 /*
2055 * Reconnect to the printer...
2056 */
2057
2058 if (!httpReconnect(http))
2059 {
2060 /*
2061 * Connected, so check on the printer state...
2062 */
2063
2064 monitor->printer_state = check_printer_state(http, monitor->uri,
2065 monitor->resource,
2066 monitor->user,
2067 monitor->version);
2068
2069 /*
2070 * Check the status of the job itself...
2071 */
2072
2073 job_op = monitor->job_id > 0 ? IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS;
2074 request = ippNewRequest(job_op);
2075 request->request.op.version[0] = monitor->version / 10;
2076 request->request.op.version[1] = monitor->version % 10;
2077
2078 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2079 NULL, monitor->uri);
2080 if (job_op == IPP_GET_JOB_ATTRIBUTES)
2081 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
2082 monitor->job_id);
2083
2084 if (monitor->user && monitor->user[0])
2085 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
2086 "requesting-user-name", NULL, monitor->user);
2087
2088 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2089 "requested-attributes",
2090 (int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs);
2091
2092 /*
2093 * Do the request...
2094 */
2095
2096 response = cupsDoRequest(http, request, monitor->resource);
2097
2098 fprintf(stderr, "DEBUG: %s: %s (%s)\n", ippOpString(job_op),
2099 ippErrorString(cupsLastError()), cupsLastErrorString());
2100
2101 if (job_op == IPP_GET_JOB_ATTRIBUTES)
2102 {
2103 if ((attr = ippFindAttribute(response, "job-state",
2104 IPP_TAG_ENUM)) != NULL)
2105 monitor->job_state = (ipp_jstate_t)attr->values[0].integer;
2106 else
2107 monitor->job_state = IPP_JOB_COMPLETED;
2108 }
2109 else if (response)
2110 {
2111 for (attr = response->attrs; attr; attr = attr->next)
2112 {
2113 job_id = 0;
2114 job_name = NULL;
2115 job_state = IPP_JOB_PENDING;
2116 job_user = NULL;
2117
2118 while (attr && attr->group_tag != IPP_TAG_JOB)
2119 attr = attr->next;
2120
2121 if (!attr)
2122 break;
2123
2124 while (attr && attr->group_tag == IPP_TAG_JOB)
2125 {
2126 if (!strcmp(attr->name, "job-id") &&
2127 attr->value_tag == IPP_TAG_INTEGER)
2128 job_id = attr->values[0].integer;
2129 else if (!strcmp(attr->name, "job-name") &&
2130 (attr->value_tag == IPP_TAG_NAME ||
2131 attr->value_tag == IPP_TAG_NAMELANG))
2132 job_name = attr->values[0].string.text;
2133 else if (!strcmp(attr->name, "job-state") &&
2134 attr->value_tag == IPP_TAG_ENUM)
2135 job_state = attr->values[0].integer;
2136 else if (!strcmp(attr->name, "job-originating-user-name") &&
2137 (attr->value_tag == IPP_TAG_NAME ||
2138 attr->value_tag == IPP_TAG_NAMELANG))
2139 job_user = attr->values[0].string.text;
2140
2141 attr = attr->next;
2142 }
2143
2144 if (job_id > 0 && job_name && !strcmp(job_name, monitor->job_name) &&
2145 job_user && monitor->user && !strcmp(job_user, monitor->user))
2146 {
2147 monitor->job_id = job_id;
2148 monitor->job_state = job_state;
2149 break;
2150 }
2151
2152 if (!attr)
2153 break;
2154 }
2155 }
2156
2157 ippDelete(response);
2158
2159 /*
2160 * Disconnect from the printer - we'll reconnect on the next poll...
2161 */
2162
2163 _httpDisconnect(http);
2164 }
2165
2166 /*
2167 * Sleep for N seconds...
2168 */
2169
2170 sleep(delay);
2171
2172 delay = _cupsNextDelay(delay, &prev_delay);
2173 }
2174
2175 /*
2176 * Cancel the job if necessary...
2177 */
2178
2179 if (job_canceled && monitor->job_id > 0)
2180 if (!httpReconnect(http))
2181 cancel_job(http, monitor->uri, monitor->job_id, monitor->resource,
2182 monitor->user, monitor->version);
2183
2184 /*
2185 * Cleanup and return...
2186 */
2187
2188 httpClose(http);
2189
2190 return (NULL);
2191 }
2192
2193
2194 /*
2195 * 'new_request()' - Create a new print creation or validation request.
2196 */
2197
2198 static ipp_t * /* O - Request data */
2199 new_request(
2200 ipp_op_t op, /* I - IPP operation code */
2201 int version, /* I - IPP version number */
2202 const char *uri, /* I - printer-uri value */
2203 const char *user, /* I - requesting-user-name value */
2204 const char *title, /* I - job-name value */
2205 int num_options, /* I - Number of options to send */
2206 cups_option_t *options, /* I - Options to send */
2207 const char *compression, /* I - compression value or NULL */
2208 int copies, /* I - copies value or 0 */
2209 const char *format, /* I - document-format value or NULL */
2210 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2211 ipp_attribute_t *media_col_sup, /* I - media-col-supported values */
2212 ipp_attribute_t *doc_handling_sup) /* I - multiple-document-handling-supported values */
2213 {
2214 int i; /* Looping var */
2215 ipp_t *request; /* Request data */
2216 const char *keyword; /* PWG keyword */
2217 _pwg_size_t *size; /* PWG media size */
2218 ipp_t *media_col, /* media-col value */
2219 *media_size; /* media-size value */
2220 const char *media_source, /* media-source value */
2221 *media_type, /* media-type value */
2222 *collate_str; /* multiple-document-handling value */
2223
2224
2225 /*
2226 * Create the IPP request...
2227 */
2228
2229 request = ippNewRequest(op);
2230 request->request.op.version[0] = version / 10;
2231 request->request.op.version[1] = version % 10;
2232
2233 fprintf(stderr, "DEBUG: %s IPP/%d.%d\n",
2234 ippOpString(request->request.op.operation_id),
2235 request->request.op.version[0],
2236 request->request.op.version[1]);
2237
2238 /*
2239 * Add standard attributes...
2240 */
2241
2242 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2243 NULL, uri);
2244 fprintf(stderr, "DEBUG: printer-uri=\"%s\"\n", uri);
2245
2246 if (user && *user)
2247 {
2248 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
2249 "requesting-user-name", NULL, user);
2250 fprintf(stderr, "DEBUG: requesting-user-name=\"%s\"\n", user);
2251 }
2252
2253 if (title && *title)
2254 {
2255 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
2256 title);
2257 fprintf(stderr, "DEBUG: job-name=\"%s\"\n", title);
2258 }
2259
2260 if (format)
2261 {
2262 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
2263 "document-format", NULL, format);
2264 fprintf(stderr, "DEBUG: document-format=\"%s\"\n", format);
2265 }
2266
2267 #ifdef HAVE_LIBZ
2268 if (compression)
2269 {
2270 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2271 "compression", NULL, compression);
2272 fprintf(stderr, "DEBUG: compression=\"%s\"\n", compression);
2273 }
2274 #endif /* HAVE_LIBZ */
2275
2276 /*
2277 * Handle options on the command-line...
2278 */
2279
2280 if (num_options > 0)
2281 {
2282 if (pc)
2283 {
2284 int num_finishings = 0, /* Number of finishing values */
2285 finishings[10]; /* Finishing enum values */
2286
2287 /*
2288 * Send standard IPP attributes...
2289 */
2290
2291 if ((keyword = cupsGetOption("PageSize", num_options, options)) == NULL)
2292 keyword = cupsGetOption("media", num_options, options);
2293
2294 if ((size = _ppdCacheGetSize(pc, keyword)) != NULL)
2295 {
2296 /*
2297 * Add a media-col value...
2298 */
2299
2300 media_size = ippNew();
2301 ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2302 "x-dimension", size->width);
2303 ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2304 "y-dimension", size->length);
2305
2306 media_col = ippNew();
2307 ippAddCollection(media_col, IPP_TAG_ZERO, "media-size", media_size);
2308
2309 media_source = _ppdCacheGetSource(pc, cupsGetOption("InputSlot",
2310 num_options,
2311 options));
2312 media_type = _ppdCacheGetType(pc, cupsGetOption("MediaType",
2313 num_options,
2314 options));
2315
2316 for (i = 0; i < media_col_sup->num_values; i ++)
2317 {
2318 if (!strcmp(media_col_sup->values[i].string.text,
2319 "media-left-margin"))
2320 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2321 "media-left-margin", size->left);
2322 else if (!strcmp(media_col_sup->values[i].string.text,
2323 "media-bottom-margin"))
2324 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2325 "media-bottom-margin", size->bottom);
2326 else if (!strcmp(media_col_sup->values[i].string.text,
2327 "media-right-margin"))
2328 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2329 "media-right-margin", size->right);
2330 else if (!strcmp(media_col_sup->values[i].string.text,
2331 "media-top-margin"))
2332 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2333 "media-top-margin", size->top);
2334 else if (!strcmp(media_col_sup->values[i].string.text,
2335 "media-source") && media_source)
2336 ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD,
2337 "media-source", NULL, media_source);
2338 else if (!strcmp(media_col_sup->values[i].string.text,
2339 "media-type") && media_type)
2340 ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD,
2341 "media-type", NULL, media_type);
2342 }
2343
2344 ippAddCollection(request, IPP_TAG_JOB, "media-col", media_col);
2345 }
2346
2347 if ((keyword = cupsGetOption("output-bin", num_options,
2348 options)) == NULL)
2349 keyword = _ppdCacheGetBin(pc, cupsGetOption("OutputBin", num_options,
2350 options));
2351
2352 if (keyword)
2353 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-bin",
2354 NULL, keyword);
2355
2356 if ((keyword = cupsGetOption("output-mode", num_options,
2357 options)) != NULL)
2358 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2359 NULL, keyword);
2360 else if ((keyword = cupsGetOption("ColorModel", num_options,
2361 options)) != NULL)
2362 {
2363 if (!_cups_strcasecmp(keyword, "Gray"))
2364 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2365 NULL, "monochrome");
2366 else
2367 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2368 NULL, "color");
2369 }
2370
2371 if ((keyword = cupsGetOption("print-quality", num_options,
2372 options)) != NULL)
2373 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2374 atoi(keyword));
2375 else if ((keyword = cupsGetOption("cupsPrintQuality", num_options,
2376 options)) != NULL)
2377 {
2378 if (!_cups_strcasecmp(keyword, "draft"))
2379 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2380 IPP_QUALITY_DRAFT);
2381 else if (!_cups_strcasecmp(keyword, "normal"))
2382 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2383 IPP_QUALITY_NORMAL);
2384 else if (!_cups_strcasecmp(keyword, "high"))
2385 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2386 IPP_QUALITY_HIGH);
2387 }
2388
2389 if ((keyword = cupsGetOption("sides", num_options, options)) != NULL)
2390 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2391 NULL, keyword);
2392 else if (pc->sides_option &&
2393 (keyword = cupsGetOption(pc->sides_option, num_options,
2394 options)) != NULL)
2395 {
2396 if (!_cups_strcasecmp(keyword, pc->sides_1sided))
2397 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2398 NULL, "one-sided");
2399 else if (!_cups_strcasecmp(keyword, pc->sides_2sided_long))
2400 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2401 NULL, "two-sided-long-edge");
2402 if (!_cups_strcasecmp(keyword, pc->sides_2sided_short))
2403 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2404 NULL, "two-sided-short-edge");
2405 }
2406
2407 if (doc_handling_sup &&
2408 (!format || _cups_strncasecmp(format, "image/", 6)) &&
2409 (keyword = cupsGetOption("collate", num_options, options)) != NULL)
2410 {
2411 if (!_cups_strcasecmp(keyword, "true"))
2412 collate_str = "separate-documents-collated-copies";
2413 else
2414 collate_str = "separate-documents-uncollated-copies";
2415
2416 for (i = 0; i < doc_handling_sup->num_values; i ++)
2417 if (!strcmp(doc_handling_sup->values[i].string.text, collate_str))
2418 {
2419 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2420 "multiple-document-handling", NULL, collate_str);
2421 break;
2422 }
2423 }
2424
2425 /*
2426 * Map finishing options...
2427 */
2428
2429 num_finishings = _ppdCacheGetFinishingValues(pc, num_options, options,
2430 (int)(sizeof(finishings) /
2431 sizeof(finishings[0])),
2432 finishings);
2433 if (num_finishings > 0)
2434 ippAddIntegers(request, IPP_TAG_JOB, IPP_TAG_ENUM, "finishings",
2435 num_finishings, finishings);
2436
2437 /*
2438 * Map FaxOut options...
2439 */
2440
2441 if ((keyword = cupsGetOption("phone", num_options, options)) != NULL)
2442 {
2443 ipp_t *destination; /* destination collection */
2444 char tel_uri[1024]; /* tel: URI */
2445
2446 destination = ippNew();
2447
2448 httpAssembleURI(HTTP_URI_CODING_ALL, tel_uri, sizeof(tel_uri), "tel",
2449 NULL, NULL, 0, keyword);
2450 ippAddString(destination, IPP_TAG_JOB, IPP_TAG_URI, "destination-uri",
2451 NULL, tel_uri);
2452
2453 if ((keyword = cupsGetOption("faxPrefix", num_options,
2454 options)) != NULL && *keyword)
2455 ippAddString(destination, IPP_TAG_JOB, IPP_TAG_TEXT,
2456 "pre-dial-string", NULL, keyword);
2457
2458 ippAddCollection(request, IPP_TAG_JOB, "destination-uris", destination);
2459 ippDelete(destination);
2460 }
2461 }
2462 else
2463 {
2464 /*
2465 * When talking to another CUPS server, send all options...
2466 */
2467
2468 cupsEncodeOptions(request, num_options, options);
2469 }
2470
2471 if (copies > 1)
2472 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies", copies);
2473 }
2474
2475 return (request);
2476 }
2477
2478
2479 /*
2480 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
2481 */
2482
2483 static const char * /* O - Password */
2484 password_cb(const char *prompt) /* I - Prompt (not used) */
2485 {
2486 (void)prompt;
2487
2488 /*
2489 * Remember that we need to authenticate...
2490 */
2491
2492 auth_info_required = "username,password";
2493
2494 if (password && *password && password_tries < 3)
2495 {
2496 password_tries ++;
2497
2498 return (password);
2499 }
2500 else
2501 {
2502 /*
2503 * Give up after 3 tries or if we don't have a password to begin with...
2504 */
2505
2506 return (NULL);
2507 }
2508 }
2509
2510
2511 /*
2512 * 'report_attr()' - Report an IPP attribute value.
2513 */
2514
2515 static void
2516 report_attr(ipp_attribute_t *attr) /* I - Attribute */
2517 {
2518 int i; /* Looping var */
2519 char value[1024], /* Value string */
2520 *valptr, /* Pointer into value string */
2521 *attrptr; /* Pointer into attribute value */
2522 const char *cached; /* Cached attribute */
2523
2524
2525 /*
2526 * Convert the attribute values into quoted strings...
2527 */
2528
2529 for (i = 0, valptr = value;
2530 i < attr->num_values && valptr < (value + sizeof(value) - 10);
2531 i ++)
2532 {
2533 if (i > 0)
2534 *valptr++ = ',';
2535
2536 switch (attr->value_tag)
2537 {
2538 case IPP_TAG_INTEGER :
2539 case IPP_TAG_ENUM :
2540 snprintf(valptr, sizeof(value) - (valptr - value), "%d",
2541 attr->values[i].integer);
2542 valptr += strlen(valptr);
2543 break;
2544
2545 case IPP_TAG_TEXT :
2546 case IPP_TAG_NAME :
2547 case IPP_TAG_KEYWORD :
2548 *valptr++ = '\"';
2549 for (attrptr = attr->values[i].string.text;
2550 *attrptr && valptr < (value + sizeof(value) - 10);
2551 attrptr ++)
2552 {
2553 if (*attrptr == '\\' || *attrptr == '\"')
2554 *valptr++ = '\\';
2555
2556 *valptr++ = *attrptr;
2557 }
2558 *valptr++ = '\"';
2559 break;
2560
2561 default :
2562 /*
2563 * Unsupported value type...
2564 */
2565
2566 return;
2567 }
2568 }
2569
2570 *valptr = '\0';
2571
2572 _cupsMutexLock(&report_mutex);
2573
2574 if ((cached = cupsGetOption(attr->name, num_attr_cache,
2575 attr_cache)) == NULL || strcmp(cached, value))
2576 {
2577 /*
2578 * Tell the scheduler about the new values...
2579 */
2580
2581 num_attr_cache = cupsAddOption(attr->name, value, num_attr_cache,
2582 &attr_cache);
2583 fprintf(stderr, "ATTR: %s=%s\n", attr->name, value);
2584 }
2585
2586 _cupsMutexUnlock(&report_mutex);
2587 }
2588
2589
2590 /*
2591 * 'report_printer_state()' - Report the printer state.
2592 */
2593
2594 static void
2595 report_printer_state(ipp_t *ipp) /* I - IPP response */
2596 {
2597 ipp_attribute_t *pa, /* printer-alert */
2598 *pam, /* printer-alert-message */
2599 *psm, /* printer-state-message */
2600 *reasons, /* printer-state-reasons */
2601 *marker; /* marker-* attributes */
2602 char value[1024], /* State/message string */
2603 *valptr; /* Pointer into string */
2604 static int ipp_supplies = -1;
2605 /* Report supply levels? */
2606
2607
2608 /*
2609 * Report alerts and messages...
2610 */
2611
2612 if ((pa = ippFindAttribute(ipp, "printer-alert", IPP_TAG_TEXT)) != NULL)
2613 report_attr(pa);
2614
2615 if ((pam = ippFindAttribute(ipp, "printer-alert-message",
2616 IPP_TAG_TEXT)) != NULL)
2617 report_attr(pam);
2618
2619 if ((psm = ippFindAttribute(ipp, "printer-state-message",
2620 IPP_TAG_TEXT)) != NULL)
2621 {
2622 char *ptr; /* Pointer into message */
2623
2624
2625 strlcpy(value, "INFO: ", sizeof(value));
2626 for (ptr = psm->values[0].string.text, valptr = value + 6;
2627 *ptr && valptr < (value + sizeof(value) - 6);
2628 ptr ++)
2629 {
2630 if (*ptr < ' ' && *ptr > 0 && *ptr != '\t')
2631 {
2632 /*
2633 * Substitute "<XX>" for the control character; sprintf is safe because
2634 * we always leave 6 chars free at the end...
2635 */
2636
2637 sprintf(valptr, "<%02X>", *ptr);
2638 valptr += 4;
2639 }
2640 else
2641 *valptr++ = *ptr;
2642 }
2643
2644 *valptr++ = '\n';
2645 *valptr = '\0';
2646
2647 fputs(value, stderr);
2648 }
2649
2650 /*
2651 * Now report printer-state-reasons, filtering out some of the reasons we never
2652 * want to set...
2653 */
2654
2655 if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
2656 IPP_TAG_KEYWORD)) == NULL)
2657 return;
2658
2659 update_reasons(reasons, NULL);
2660
2661 /*
2662 * Relay the current marker-* attribute values...
2663 */
2664
2665 if (ipp_supplies < 0)
2666 {
2667 ppd_file_t *ppd; /* PPD file */
2668 ppd_attr_t *ppdattr; /* Attribute in PPD file */
2669
2670 if ((ppd = ppdOpenFile(getenv("PPD"))) != NULL &&
2671 (ppdattr = ppdFindAttr(ppd, "cupsIPPSupplies", NULL)) != NULL &&
2672 ppdattr->value && _cups_strcasecmp(ppdattr->value, "true"))
2673 ipp_supplies = 0;
2674 else
2675 ipp_supplies = 1;
2676
2677 ppdClose(ppd);
2678 }
2679
2680 if (ipp_supplies > 0)
2681 {
2682 if ((marker = ippFindAttribute(ipp, "marker-colors", IPP_TAG_NAME)) != NULL)
2683 report_attr(marker);
2684 if ((marker = ippFindAttribute(ipp, "marker-high-levels",
2685 IPP_TAG_INTEGER)) != NULL)
2686 report_attr(marker);
2687 if ((marker = ippFindAttribute(ipp, "marker-levels",
2688 IPP_TAG_INTEGER)) != NULL)
2689 report_attr(marker);
2690 if ((marker = ippFindAttribute(ipp, "marker-low-levels",
2691 IPP_TAG_INTEGER)) != NULL)
2692 report_attr(marker);
2693 if ((marker = ippFindAttribute(ipp, "marker-message",
2694 IPP_TAG_TEXT)) != NULL)
2695 report_attr(marker);
2696 if ((marker = ippFindAttribute(ipp, "marker-names", IPP_TAG_NAME)) != NULL)
2697 report_attr(marker);
2698 if ((marker = ippFindAttribute(ipp, "marker-types",
2699 IPP_TAG_KEYWORD)) != NULL)
2700 report_attr(marker);
2701 }
2702 }
2703
2704
2705 #if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
2706 /*
2707 * 'run_as_user()' - Run the IPP backend as the printing user.
2708 *
2709 * This function uses an XPC-based user agent to run the backend as the printing
2710 * user. We need to do this in order to have access to the user's Kerberos
2711 * credentials.
2712 */
2713
2714 static int /* O - Exit status */
2715 run_as_user(int argc, /* I - Number of command-line args */
2716 char *argv[], /* I - Command-line arguments */
2717 uid_t uid, /* I - User ID */
2718 const char *device_uri, /* I - Device URI */
2719 int fd) /* I - File to print */
2720 {
2721 const char *auth_negotiate;/* AUTH_NEGOTIATE env var */
2722 xpc_connection_t conn; /* Connection to XPC service */
2723 xpc_object_t request; /* Request message dictionary */
2724 __block xpc_object_t response; /* Response message dictionary */
2725 dispatch_semaphore_t sem; /* Semaphore for waiting for response */
2726 int status = CUPS_BACKEND_FAILED;
2727 /* Status of request */
2728
2729
2730 fprintf(stderr, "DEBUG: Running IPP backend as UID %d.\n", (int)uid);
2731
2732 /*
2733 * Connect to the user agent for the specified UID...
2734 */
2735
2736 conn = xpc_connection_create_mach_service(kPMPrintUIToolAgent,
2737 dispatch_get_global_queue(0, 0), 0);
2738 if (!conn)
2739 {
2740 _cupsLangPrintFilter(stderr, "ERROR",
2741 _("Unable to start backend process."));
2742 fputs("DEBUG: Unable to create connection to agent.\n", stderr);
2743 goto cleanup;
2744 }
2745
2746 xpc_connection_set_event_handler(conn,
2747 ^(xpc_object_t event)
2748 {
2749 xpc_type_t messageType = xpc_get_type(event);
2750
2751 if (messageType == XPC_TYPE_ERROR)
2752 {
2753 if (event == XPC_ERROR_CONNECTION_INTERRUPTED)
2754 fprintf(stderr, "DEBUG: Interrupted connection to service %s.\n",
2755 xpc_connection_get_name(conn));
2756 else if (event == XPC_ERROR_CONNECTION_INVALID)
2757 fprintf(stderr, "DEBUG: Connection invalid for service %s.\n",
2758 xpc_connection_get_name(conn));
2759 else
2760 fprintf(stderr, "DEBUG: Unxpected error for service %s: %s\n",
2761 xpc_connection_get_name(conn),
2762 xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
2763 }
2764 });
2765 xpc_connection_set_target_uid(conn, uid);
2766 xpc_connection_resume(conn);
2767
2768 /*
2769 * Try starting the backend...
2770 */
2771
2772 request = xpc_dictionary_create(NULL, NULL, 0);
2773 xpc_dictionary_set_int64(request, "command", kPMStartJob);
2774 xpc_dictionary_set_string(request, "device-uri", device_uri);
2775 xpc_dictionary_set_string(request, "job-id", argv[1]);
2776 xpc_dictionary_set_string(request, "user", argv[2]);
2777 xpc_dictionary_set_string(request, "title", argv[3]);
2778 xpc_dictionary_set_string(request, "copies", argv[4]);
2779 xpc_dictionary_set_string(request, "options", argv[5]);
2780 xpc_dictionary_set_string(request, "auth-info-required",
2781 getenv("AUTH_INFO_REQUIRED"));
2782 if ((auth_negotiate = getenv("AUTH_NEGOTIATE")) != NULL)
2783 xpc_dictionary_set_string(request, "auth-negotiate", auth_negotiate);
2784 xpc_dictionary_set_fd(request, "stdin", fd);
2785 xpc_dictionary_set_fd(request, "stderr", 2);
2786 xpc_dictionary_set_fd(request, "side-channel", CUPS_SC_FD);
2787
2788 sem = dispatch_semaphore_create(0);
2789 response = NULL;
2790
2791 xpc_connection_send_message_with_reply(conn, request,
2792 dispatch_get_global_queue(0,0),
2793 ^(xpc_object_t reply)
2794 {
2795 /* Save the response and wake up */
2796 if (xpc_get_type(reply)
2797 == XPC_TYPE_DICTIONARY)
2798 response = xpc_retain(reply);
2799
2800 dispatch_semaphore_signal(sem);
2801 });
2802
2803 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
2804 xpc_release(request);
2805 dispatch_release(sem);
2806
2807 if (response)
2808 {
2809 child_pid = xpc_dictionary_get_int64(response, "child-pid");
2810
2811 xpc_release(response);
2812
2813 if (child_pid)
2814 fprintf(stderr, "DEBUG: Child PID=%d.\n", child_pid);
2815 else
2816 {
2817 _cupsLangPrintFilter(stderr, "ERROR",
2818 _("Unable to start backend process."));
2819 fputs("DEBUG: No child PID.\n", stderr);
2820 goto cleanup;
2821 }
2822 }
2823 else
2824 {
2825 _cupsLangPrintFilter(stderr, "ERROR",
2826 _("Unable to start backend process."));
2827 fputs("DEBUG: No reply from agent.\n", stderr);
2828 goto cleanup;
2829 }
2830
2831 /*
2832 * Then wait for the backend to finish...
2833 */
2834
2835 request = xpc_dictionary_create(NULL, NULL, 0);
2836 xpc_dictionary_set_int64(request, "command", kPMWaitForJob);
2837 xpc_dictionary_set_fd(request, "stderr", 2);
2838
2839 sem = dispatch_semaphore_create(0);
2840 response = NULL;
2841
2842 xpc_connection_send_message_with_reply(conn, request,
2843 dispatch_get_global_queue(0,0),
2844 ^(xpc_object_t reply)
2845 {
2846 /* Save the response and wake up */
2847 if (xpc_get_type(reply)
2848 == XPC_TYPE_DICTIONARY)
2849 response = xpc_retain(reply);
2850
2851 dispatch_semaphore_signal(sem);
2852 });
2853
2854 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
2855 xpc_release(request);
2856 dispatch_release(sem);
2857
2858 if (response)
2859 {
2860 status = xpc_dictionary_get_int64(response, "status");
2861
2862 if (status == SIGTERM || status == SIGKILL || status == SIGPIPE)
2863 {
2864 fprintf(stderr, "DEBUG: Child terminated on signal %d.\n", status);
2865 status = CUPS_BACKEND_FAILED;
2866 }
2867 else if (WIFSIGNALED(status))
2868 {
2869 fprintf(stderr, "DEBUG: Child crashed on signal %d.\n", status);
2870 status = CUPS_BACKEND_STOP;
2871 }
2872 else if (WIFEXITED(status))
2873 {
2874 status = WEXITSTATUS(status);
2875 fprintf(stderr, "DEBUG: Child exited with status %d.\n", status);
2876 }
2877
2878 xpc_release(response);
2879 }
2880 else
2881 _cupsLangPrintFilter(stderr, "ERROR",
2882 _("Unable to get backend exit status."));
2883
2884 cleanup:
2885
2886 if (conn)
2887 {
2888 xpc_connection_suspend(conn);
2889 xpc_connection_cancel(conn);
2890 xpc_release(conn);
2891 }
2892
2893 return (status);
2894 }
2895 #endif /* HAVE_GSSAPI && HAVE_XPC */
2896
2897
2898 /*
2899 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
2900 */
2901
2902 static void
2903 sigterm_handler(int sig) /* I - Signal */
2904 {
2905 (void)sig; /* remove compiler warnings... */
2906
2907 #if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
2908 if (child_pid)
2909 {
2910 kill(child_pid, sig);
2911 child_pid = 0;
2912 }
2913 #endif /* HAVE_GSSAPI && HAVE_XPC */
2914
2915 if (!job_canceled)
2916 {
2917 /*
2918 * Flag that the job should be canceled...
2919 */
2920
2921 job_canceled = 1;
2922 return;
2923 }
2924
2925 /*
2926 * The scheduler already tried to cancel us once, now just terminate
2927 * after removing our temp file!
2928 */
2929
2930 if (tmpfilename[0])
2931 unlink(tmpfilename);
2932
2933 exit(1);
2934 }
2935
2936
2937 /*
2938 * 'timeout_cb()' - Handle HTTP timeouts.
2939 */
2940
2941 static int /* O - 1 to continue, 0 to cancel */
2942 timeout_cb(http_t *http, /* I - Connection to server (unused) */
2943 void *user_data) /* I - User data (unused) */
2944 {
2945 (void)http;
2946 (void)user_data;
2947
2948 return (!job_canceled);
2949 }
2950
2951
2952 /*
2953 * 'update_reasons()' - Update the printer-state-reasons values.
2954 */
2955
2956 static void
2957 update_reasons(ipp_attribute_t *attr, /* I - printer-state-reasons or NULL */
2958 const char *s) /* I - STATE: string or NULL */
2959 {
2960 char op; /* Add (+), remove (-), replace (\0) */
2961 cups_array_t *new_reasons; /* New reasons array */
2962 char *reason, /* Current reason */
2963 add[2048], /* Reasons added string */
2964 *addptr, /* Pointer into add string */
2965 rem[2048], /* Reasons removed string */
2966 *remptr; /* Pointer into remove string */
2967 const char *addprefix, /* Current add string prefix */
2968 *remprefix; /* Current remove string prefix */
2969
2970
2971 fprintf(stderr, "DEBUG: update_reasons(attr=%d(%s%s), s=\"%s\")\n",
2972 attr ? attr->num_values : 0, attr ? attr->values[0].string.text : "",
2973 attr && attr->num_values > 1 ? ",..." : "", s ? s : "(null)");
2974
2975 /*
2976 * Create an array of new reason keyword strings...
2977 */
2978
2979 if (attr)
2980 {
2981 int i; /* Looping var */
2982
2983 new_reasons = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2984 op = '\0';
2985
2986 for (i = 0; i < attr->num_values; i ++)
2987 {
2988 reason = attr->values[i].string.text;
2989
2990 if (strcmp(reason, "none") &&
2991 strcmp(reason, "none-report") &&
2992 strcmp(reason, "paused") &&
2993 strncmp(reason, "spool-area-full", 15) &&
2994 strcmp(reason, "com.apple.print.recoverable-warning") &&
2995 strncmp(reason, "cups-", 5))
2996 cupsArrayAdd(new_reasons, reason);
2997 }
2998 }
2999 else if (s)
3000 {
3001 if (*s == '+' || *s == '-')
3002 op = *s++;
3003 else
3004 op = '\0';
3005
3006 new_reasons = _cupsArrayNewStrings(s);
3007 }
3008 else
3009 return;
3010
3011 /*
3012 * Compute the changes...
3013 */
3014
3015 add[0] = '\0';
3016 addprefix = "STATE: +";
3017 addptr = add;
3018 rem[0] = '\0';
3019 remprefix = "STATE: -";
3020 remptr = rem;
3021
3022 fprintf(stderr, "DEBUG2: op='%c', new_reasons=%d, state_reasons=%d\n",
3023 op ? op : ' ', cupsArrayCount(new_reasons),
3024 cupsArrayCount(state_reasons));
3025
3026 _cupsMutexLock(&report_mutex);
3027
3028 if (op == '+')
3029 {
3030 /*
3031 * Add reasons...
3032 */
3033
3034 for (reason = (char *)cupsArrayFirst(new_reasons);
3035 reason;
3036 reason = (char *)cupsArrayNext(new_reasons))
3037 {
3038 if (!cupsArrayFind(state_reasons, reason))
3039 {
3040 if (!strncmp(reason, "cups-remote-", 12))
3041 {
3042 /*
3043 * If we are setting cups-remote-xxx, remove all other cups-remote-xxx
3044 * keywords...
3045 */
3046
3047 char *temp; /* Current reason in state_reasons */
3048
3049 cupsArraySave(state_reasons);
3050
3051 for (temp = (char *)cupsArrayFirst(state_reasons);
3052 temp;
3053 temp = (char *)cupsArrayNext(state_reasons))
3054 if (!strncmp(temp, "cups-remote-", 12))
3055 {
3056 snprintf(remptr, sizeof(rem) - (remptr - rem), "%s%s", remprefix,
3057 temp);
3058 remptr += strlen(remptr);
3059 remprefix = ",";
3060
3061 cupsArrayRemove(state_reasons, temp);
3062 break;
3063 }
3064
3065 cupsArrayRestore(state_reasons);
3066 }
3067
3068 cupsArrayAdd(state_reasons, reason);
3069
3070 snprintf(addptr, sizeof(add) - (addptr - add), "%s%s", addprefix,
3071 reason);
3072 addptr += strlen(addptr);
3073 addprefix = ",";
3074 }
3075 }
3076 }
3077 else if (op == '-')
3078 {
3079 /*
3080 * Remove reasons...
3081 */
3082
3083 for (reason = (char *)cupsArrayFirst(new_reasons);
3084 reason;
3085 reason = (char *)cupsArrayNext(new_reasons))
3086 {
3087 if (cupsArrayFind(state_reasons, reason))
3088 {
3089 snprintf(remptr, sizeof(rem) - (remptr - rem), "%s%s", remprefix,
3090 reason);
3091 remptr += strlen(remptr);
3092 remprefix = ",";
3093
3094 cupsArrayRemove(state_reasons, reason);
3095 }
3096 }
3097 }
3098 else
3099 {
3100 /*
3101 * Replace reasons...
3102 */
3103
3104 for (reason = (char *)cupsArrayFirst(state_reasons);
3105 reason;
3106 reason = (char *)cupsArrayNext(state_reasons))
3107 {
3108 if (strncmp(reason, "cups-", 5) && !cupsArrayFind(new_reasons, reason))
3109 {
3110 snprintf(remptr, sizeof(rem) - (remptr - rem), "%s%s", remprefix,
3111 reason);
3112 remptr += strlen(remptr);
3113 remprefix = ",";
3114
3115 cupsArrayRemove(state_reasons, reason);
3116 }
3117 }
3118
3119 for (reason = (char *)cupsArrayFirst(new_reasons);
3120 reason;
3121 reason = (char *)cupsArrayNext(new_reasons))
3122 {
3123 if (!cupsArrayFind(state_reasons, reason))
3124 {
3125 cupsArrayAdd(state_reasons, reason);
3126
3127 snprintf(addptr, sizeof(add) - (addptr - add), "%s%s", addprefix,
3128 reason);
3129 addptr += strlen(addptr);
3130 addprefix = ",";
3131 }
3132 }
3133 }
3134
3135 _cupsMutexUnlock(&report_mutex);
3136
3137 /*
3138 * Report changes and return...
3139 */
3140
3141 if (add[0] && rem[0])
3142 fprintf(stderr, "%s\n%s\n", add, rem);
3143 else if (add[0])
3144 fprintf(stderr, "%s\n", add);
3145 else if (rem[0])
3146 fprintf(stderr, "%s\n", rem);
3147 }
3148
3149 /*
3150 * End of "$Id$".
3151 */