]> git.ipfire.org Git - thirdparty/cups.git/blob - backend/ipp.c
Merge pull request #1306 from weblate/weblate-cups-cups
[thirdparty/cups.git] / backend / ipp.c
1 /*
2 * IPP backend for CUPS.
3 *
4 * Copyright © 2021-2025 by OpenPrinting
5 * Copyright © 2007-2021 by Apple Inc.
6 * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
7 *
8 * Licensed under Apache License v2.0. See the file "LICENSE" for more
9 * information.
10 */
11
12 #include "backend-private.h"
13 #include <cups/ppd-private.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <sys/wait.h>
17 #if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
18 # include <xpc/xpc.h>
19 # define kPMPrintUIToolAgent "com.apple.printuitool.agent"
20 # define kPMStartJob 100
21 # define kPMWaitForJob 101
22 extern void xpc_connection_set_target_uid(xpc_connection_t connection,
23 uid_t uid);
24 #endif /* HAVE_GSSAPI && HAVE_XPC */
25
26
27 /*
28 * Bits for job-state-reasons we care about...
29 */
30
31 #define _CUPS_JSR_ACCOUNT_AUTHORIZATION_FAILED 0x01
32 #define _CUPS_JSR_ACCOUNT_CLOSED 0x02
33 #define _CUPS_JSR_ACCOUNT_INFO_NEEDED 0x04
34 #define _CUPS_JSR_ACCOUNT_LIMIT_REACHED 0x08
35 #define _CUPS_JSR_JOB_PASSWORD_WAIT 0x10
36 #define _CUPS_JSR_JOB_RELEASE_WAIT 0x20
37 #define _CUPS_JSR_DOCUMENT_FORMAT_ERROR 0x40
38 #define _CUPS_JSR_DOCUMENT_UNPRINTABLE 0x80
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 job_reasons, /* Job state reasons bits */
55 create_job, /* Support Create-Job? */
56 get_job_attrs; /* Support Get-Job-Attributes? */
57 const char *job_name; /* Job name for submitted job */
58 http_encryption_t encryption; /* Use encryption? */
59 ipp_jstate_t job_state; /* Current job state */
60 ipp_pstate_t printer_state; /* Current printer state */
61 int retryable; /* Is this a job that should be retried? */
62 } _cups_monitor_t;
63
64
65 /*
66 * Globals...
67 */
68 #if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
69 static pid_t child_pid = 0; /* Child process ID */
70 #endif /* HAVE_GSSAPI && HAVE_XPC */
71 static const char * const jattrs[] = /* Job attributes we want */
72 {
73 "job-id",
74 "job-impressions-completed",
75 "job-media-sheets-completed",
76 "job-name",
77 "job-originating-user-name",
78 "job-state",
79 "job-state-reasons"
80 };
81 static int job_canceled = 0,
82 /* Job cancelled? */
83 uri_credentials = 0;
84 /* Credentials supplied in URI? */
85 static char device_username[256] = "",
86 /* Username for device URI */
87 *device_password = NULL;
88 /* Password for device URI */
89 static const char * const pattrs[] = /* Printer attributes we want */
90 {
91 "compression-supported",
92 "copies-supported",
93 "cups-version",
94 "document-format-supported",
95 "job-password-encryption-supported",
96 "marker-colors",
97 "marker-high-levels",
98 "marker-levels",
99 "marker-low-levels",
100 "marker-message",
101 "marker-names",
102 "marker-types",
103 "media-col-supported",
104 "multiple-document-handling-supported",
105 "operations-supported",
106 "print-color-mode-supported",
107 "print-scaling-supported",
108 "printer-alert",
109 "printer-alert-description",
110 "printer-is-accepting-jobs",
111 "printer-mandatory-job-attributes",
112 "printer-state",
113 "printer-state-message",
114 "printer-state-reasons"
115 };
116 static const char * const remote_job_states[] =
117 { /* Remote job state keywords */
118 "+cups-remote-pending",
119 "+cups-remote-pending-held",
120 "+cups-remote-processing",
121 "+cups-remote-stopped",
122 "+cups-remote-canceled",
123 "+cups-remote-aborted",
124 "+cups-remote-completed"
125 };
126 static cups_mutex_t report_mutex = CUPS_MUTEX_INITIALIZER;
127 /* Mutex to control access */
128 static int num_attr_cache = 0;
129 /* Number of cached attributes */
130 static cups_option_t *attr_cache = NULL;
131 /* Cached attributes */
132 static cups_array_t *state_reasons; /* Array of printe-state-reasons keywords */
133 static char tmpfilename[1024] = "";
134 /* Temporary spool file name */
135 static char mandatory_attrs[1024] = "";
136 /* cupsMandatory value */
137
138
139 /*
140 * Local functions...
141 */
142
143 static int adjust_options(int num_options, cups_option_t **options);
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 static void debug_attributes(ipp_t *ipp);
151 static void *monitor_printer(_cups_monitor_t *monitor);
152 static ipp_t *new_request(ipp_op_t op, int version, const char *uri,
153 const char *user, const char *title,
154 int num_options, cups_option_t *options,
155 const char *compression, int copies,
156 const char *format, _ppd_cache_t *pc,
157 ppd_file_t *ppd,
158 ipp_attribute_t *media_col_sup,
159 ipp_attribute_t *doc_handling_sup,
160 ipp_attribute_t *print_color_mode_sup,
161 ipp_attribute_t *print_scaling_sup);
162 static const char *password_cb(const char *prompt, http_t *http,
163 const char *method, const char *resource,
164 int *user_data);
165 static const char *quote_string(const char *s, char *q, size_t qsize);
166 static void report_attr(ipp_attribute_t *attr);
167 static void report_printer_state(ipp_t *ipp);
168 #if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
169 static int run_as_user(char *argv[], uid_t uid,
170 const char *device_uri, int fd);
171 #endif /* HAVE_GSSAPI && HAVE_XPC */
172 static void sigterm_handler(int sig);
173 static int timeout_cb(http_t *http, void *user_data);
174 static void update_reasons(ipp_attribute_t *attr, const char *s);
175
176
177 /*
178 * 'main()' - Send a file to the printer or server.
179 *
180 * Usage:
181 *
182 * printer-uri job-id user title copies options [file]
183 */
184
185 int /* O - Exit status */
186 main(int argc, /* I - Number of command-line args */
187 char *argv[]) /* I - Command-line arguments */
188 {
189 int i; /* Looping var */
190 int send_options; /* Send job options? */
191 int num_options; /* Number of printer options */
192 cups_option_t *options; /* Printer options */
193 const char *device_uri; /* Device URI */
194 char scheme[255], /* Scheme in URI */
195 hostname[1024], /* Hostname */
196 resource[1024], /* Resource info (printer name) */
197 addrname[256], /* Address name */
198 username[IPP_MAX_NAME], /* Requesting user name */
199 *optptr, /* Pointer to URI options */
200 *name, /* Name of option */
201 *value, /* Value of option */
202 sep; /* Separator character */
203 int password_tries = 0; /* Password tries */
204 http_addrlist_t *addrlist; /* Address of printer */
205 int snmp_enabled = 1; /* Is SNMP enabled? */
206 int snmp_fd, /* SNMP socket */
207 start_count, /* Page count via SNMP at start */
208 page_count, /* Page count via SNMP */
209 have_supplies; /* Printer supports supply levels? */
210 int num_files; /* Number of files to print */
211 char **files, /* Files to print */
212 *compatfile = NULL; /* Compatibility filename */
213 off_t compatsize = 0; /* Size of compatibility file */
214 int port; /* Port number (not used) */
215 char uri[HTTP_MAX_URI]; /* Updated URI without user/pass */
216 char print_job_name[256]; /* Update job-name for Print-Job */
217 http_status_t http_status; /* Status of HTTP request */
218 ipp_status_t ipp_status; /* Status of IPP request */
219 http_t *http; /* HTTP connection */
220 ipp_t *request, /* IPP request */
221 *response, /* IPP response */
222 *supported; /* get-printer-attributes response */
223 time_t start_time; /* Time of first connect */
224 int contimeout; /* Connection timeout */
225 int delay, /* Delay for retries */
226 prev_delay; /* Previous delay */
227 const char *compression; /* Compression mode */
228 int waitjob, /* Wait for job complete? */
229 waitjob_tries = 0, /* Number of times we've waited */
230 waitprinter; /* Wait for printer ready? */
231 _cups_monitor_t monitor; /* Monitoring data */
232 ipp_attribute_t *job_id_attr; /* job-id attribute */
233 int job_id; /* job-id value */
234 ipp_attribute_t *job_sheets; /* job-media-sheets-completed */
235 ipp_attribute_t *job_state; /* job-state */
236 ipp_attribute_t *compression_sup; /* compression-supported */
237 ipp_attribute_t *copies_sup; /* copies-supported */
238 ipp_attribute_t *cups_version; /* cups-version */
239 ipp_attribute_t *encryption_sup; /* job-password-encryption-supported */
240 ipp_attribute_t *format_sup; /* document-format-supported */
241 ipp_attribute_t *job_auth; /* job-authorization-uri */
242 ipp_attribute_t *media_col_sup; /* media-col-supported */
243 ipp_attribute_t *operations_sup; /* operations-supported */
244 ipp_attribute_t *doc_handling_sup; /* multiple-document-handling-supported */
245 ipp_attribute_t *printer_state; /* printer-state attribute */
246 ipp_attribute_t *printer_accepting; /* printer-is-accepting-jobs */
247 ipp_attribute_t *print_color_mode_sup;/* Does printer support print-color-mode? */
248 ipp_attribute_t *print_scaling_sup; /* print-scaling-supported */
249 int create_job = 0, /* Does printer support Create-Job? */
250 get_job_attrs = 0, /* Does printer support Get-Job-Attributes? */
251 send_document = 0, /* Does printer support Send-Document? */
252 validate_job = 0, /* Does printer support Validate-Job? */
253 validate_retried = 0, /* Was Validate-Job request retried? */
254 copies, /* Number of copies for job */
255 copies_remaining; /* Number of copies remaining */
256 const char *auth_info_required, /* New auth-info-required value */
257 *content_type, /* CONTENT_TYPE environment variable */
258 *final_content_type, /* FINAL_CONTENT_TYPE environment var */
259 *document_format; /* document-format value */
260 int fd; /* File descriptor */
261 off_t bytes = 0; /* Bytes copied */
262 char buffer[16384]; /* Copy buffer */
263 struct sigaction action; /* Actions for POSIX signals */
264 int version; /* IPP version */
265 ppd_file_t *ppd = NULL; /* PPD file */
266 _ppd_cache_t *pc = NULL; /* PPD cache and mapping data */
267 fd_set input; /* Input set for select() */
268
269
270 /*
271 * Make sure status messages are not buffered...
272 */
273
274 setbuf(stderr, NULL);
275
276 /*
277 * Ignore SIGPIPE and catch SIGTERM signals...
278 */
279
280 memset(&action, 0, sizeof(action));
281 action.sa_handler = SIG_IGN;
282 sigaction(SIGPIPE, &action, NULL);
283
284 sigemptyset(&action.sa_mask);
285 sigaddset(&action.sa_mask, SIGTERM);
286 action.sa_handler = sigterm_handler;
287 sigaction(SIGTERM, &action, NULL);
288
289 /*
290 * Check command-line...
291 */
292
293 if (argc == 1)
294 {
295 char *s;
296
297 if ((s = strrchr(argv[0], '/')) != NULL)
298 s ++;
299 else
300 s = argv[0];
301
302 printf("network %s \"Unknown\" \"%s (%s)\"\n",
303 s, _cupsLangString(cupsLangDefault(),
304 _("Internet Printing Protocol")), s);
305 return (CUPS_BACKEND_OK);
306 }
307 else if (argc < 6)
308 {
309 _cupsLangPrintf(stderr,
310 _("Usage: %s job-id user title copies options [file]"),
311 argv[0]);
312 return (CUPS_BACKEND_STOP);
313 }
314
315 cupsCopyString(username, argv[2], sizeof(username));
316
317 /*
318 * Get the device URI...
319 */
320
321 while ((device_uri = cupsBackendDeviceURI(argv)) == NULL)
322 {
323 _cupsLangPrintFilter(stderr, "INFO", _("Unable to locate printer."));
324 sleep(10);
325
326 if (getenv("CLASS") != NULL)
327 return (CUPS_BACKEND_FAILED);
328 }
329
330 if ((auth_info_required = getenv("AUTH_INFO_REQUIRED")) == NULL)
331 auth_info_required = "none";
332
333 state_reasons = cupsArrayNewStrings(getenv("PRINTER_STATE_REASONS"), ',');
334
335 #ifdef HAVE_GSSAPI
336 /*
337 * For Kerberos, become the printing user (if we can) to get the credentials
338 * that way.
339 */
340
341 if (!getuid() && (value = getenv("AUTH_UID")) != NULL)
342 {
343 uid_t uid = (uid_t)strtoul(value, NULL, 10);
344 /* User ID */
345
346 # ifdef HAVE_XPC
347 if (uid)
348 {
349 if (argc == 6)
350 return (run_as_user(argv, uid, device_uri, 0));
351 else
352 {
353 int status = 0; /* Exit status */
354
355 for (i = 6; i < argc && !status && !job_canceled; i ++)
356 {
357 if ((fd = open(argv[i], O_RDONLY)) >= 0)
358 {
359 status = run_as_user(argv, uid, device_uri, fd);
360 close(fd);
361 }
362 else
363 {
364 _cupsLangPrintError("ERROR", _("Unable to open print file"));
365 status = CUPS_BACKEND_FAILED;
366 }
367 }
368
369 return (status);
370 }
371 }
372
373 # else /* No XPC, just try to run as the user ID */
374 if (uid)
375 setuid(uid);
376 # endif /* HAVE_XPC */
377 }
378 #endif /* HAVE_GSSAPI */
379
380 /*
381 * Get the (final) content type...
382 */
383
384 if ((content_type = getenv("CONTENT_TYPE")) == NULL)
385 content_type = "application/octet-stream";
386
387 if ((final_content_type = getenv("FINAL_CONTENT_TYPE")) == NULL)
388 {
389 final_content_type = content_type;
390
391 if (!strncmp(final_content_type, "printer/", 8))
392 final_content_type = "application/vnd.cups-raw";
393 }
394
395 /*
396 * Extract the hostname and printer name from the URI...
397 */
398
399 httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme),
400 device_username, sizeof(device_username), hostname, sizeof(hostname), &port,
401 resource, sizeof(resource));
402
403 if (!port)
404 port = IPP_PORT; /* Default to port 631 */
405
406 if (!strcmp(scheme, "https") || !strcmp(scheme, "ipps"))
407 cupsSetEncryption(HTTP_ENCRYPTION_ALWAYS);
408 else
409 cupsSetEncryption(HTTP_ENCRYPTION_IF_REQUESTED);
410
411 if (!strcmp(auth_info_required, "negotiate") &&
412 (isdigit(hostname[0] & 255) || hostname[0] == '['))
413 {
414 /*
415 * IP addresses are not allowed with Kerberos...
416 */
417
418 _cupsLangPrintFilter(stderr, "ERROR",
419 _("IP address is not allowed as hostname when using Negotiate - use FQDN."));
420 update_reasons(NULL, "-connecting-to-device");
421 return (CUPS_BACKEND_FAILED);
422 }
423
424 /*
425 * See if there are any options...
426 */
427
428 compression = NULL;
429 version = 20;
430 waitjob = 1;
431 waitprinter = 1;
432 contimeout = 7 * 24 * 60 * 60;
433
434 if ((optptr = strchr(resource, '?')) != NULL)
435 {
436 /*
437 * Yup, terminate the device name string and move to the first
438 * character of the optptr...
439 */
440
441 *optptr++ = '\0';
442
443 /*
444 * Then parse the optptr...
445 */
446
447 while (*optptr)
448 {
449 /*
450 * Get the name...
451 */
452
453 name = optptr;
454
455 while (*optptr && *optptr != '=' && *optptr != '+' && *optptr != '&')
456 optptr ++;
457
458 if ((sep = *optptr) != '\0')
459 *optptr++ = '\0';
460
461 if (sep == '=')
462 {
463 /*
464 * Get the value...
465 */
466
467 value = optptr;
468
469 while (*optptr && *optptr != '+' && *optptr != '&')
470 optptr ++;
471
472 if (*optptr)
473 *optptr++ = '\0';
474 }
475 else
476 value = (char *)"";
477
478 /*
479 * Process the option...
480 */
481
482 if (!_cups_strcasecmp(name, "waitjob"))
483 {
484 /*
485 * Wait for job completion?
486 */
487
488 waitjob = !_cups_strcasecmp(value, "on") ||
489 !_cups_strcasecmp(value, "yes") ||
490 !_cups_strcasecmp(value, "true");
491 }
492 else if (!_cups_strcasecmp(name, "waitprinter"))
493 {
494 /*
495 * Wait for printer idle?
496 */
497
498 waitprinter = !_cups_strcasecmp(value, "on") ||
499 !_cups_strcasecmp(value, "yes") ||
500 !_cups_strcasecmp(value, "true");
501 }
502 else if (!_cups_strcasecmp(name, "encryption"))
503 {
504 /*
505 * Enable/disable encryption?
506 */
507
508 if (!_cups_strcasecmp(value, "always"))
509 cupsSetEncryption(HTTP_ENCRYPTION_ALWAYS);
510 else if (!_cups_strcasecmp(value, "required"))
511 cupsSetEncryption(HTTP_ENCRYPTION_REQUIRED);
512 else if (!_cups_strcasecmp(value, "never"))
513 cupsSetEncryption(HTTP_ENCRYPTION_NEVER);
514 else if (!_cups_strcasecmp(value, "ifrequested"))
515 cupsSetEncryption(HTTP_ENCRYPTION_IF_REQUESTED);
516 else
517 {
518 _cupsLangPrintFilter(stderr, "ERROR",
519 _("Unknown encryption option value: \"%s\"."),
520 value);
521 }
522 }
523 else if (!_cups_strcasecmp(name, "snmp"))
524 {
525 /*
526 * Enable/disable SNMP stuff...
527 */
528
529 snmp_enabled = !value[0] || !_cups_strcasecmp(value, "on") ||
530 !_cups_strcasecmp(value, "yes") ||
531 !_cups_strcasecmp(value, "true");
532 }
533 else if (!_cups_strcasecmp(name, "version"))
534 {
535 if (!strcmp(value, "1.0"))
536 version = 10;
537 else if (!strcmp(value, "1.1"))
538 version = 11;
539 else if (!strcmp(value, "2.0"))
540 version = 20;
541 else if (!strcmp(value, "2.1"))
542 version = 21;
543 else if (!strcmp(value, "2.2"))
544 version = 22;
545 else
546 {
547 _cupsLangPrintFilter(stderr, "ERROR",
548 _("Unknown version option value: \"%s\"."),
549 value);
550 }
551 }
552 else if (!_cups_strcasecmp(name, "compression"))
553 {
554 if (!_cups_strcasecmp(value, "true") || !_cups_strcasecmp(value, "yes") ||
555 !_cups_strcasecmp(value, "on") || !_cups_strcasecmp(value, "gzip"))
556 compression = "gzip";
557 else if (!_cups_strcasecmp(value, "deflate"))
558 compression = "deflate";
559 else if (!_cups_strcasecmp(value, "false") ||
560 !_cups_strcasecmp(value, "no") ||
561 !_cups_strcasecmp(value, "off") ||
562 !_cups_strcasecmp(value, "none"))
563 compression = "none";
564 }
565 else if (!_cups_strcasecmp(name, "contimeout"))
566 {
567 int value_int = atoi(value);
568 /*
569 * Set the connection timeout...
570 */
571
572 if (value_int > 0)
573 contimeout = value_int;
574 }
575 else
576 {
577 /*
578 * Unknown option...
579 */
580
581 _cupsLangPrintFilter(stderr, "ERROR",
582 _("Unknown option \"%s\" with value \"%s\"."),
583 name, value);
584 }
585 }
586 }
587
588 /*
589 * If we have 7 arguments, print the file named on the command-line.
590 * Otherwise, copy stdin to a temporary file and print the temporary
591 * file.
592 */
593
594 if (argc == 6)
595 {
596 num_files = 0;
597 files = NULL;
598 send_options = !_cups_strcasecmp(final_content_type, "application/pdf") ||
599 !_cups_strcasecmp(final_content_type, "application/vnd.cups-pdf") ||
600 !_cups_strncasecmp(final_content_type, "image/", 6);
601
602 fputs("DEBUG: Sending stdin for job...\n", stderr);
603 }
604 else
605 {
606 /*
607 * Point to the files on the command-line...
608 */
609
610 num_files = argc - 6;
611 files = argv + 6;
612 send_options = 1;
613
614 fprintf(stderr, "DEBUG: %d files to send in job...\n", num_files);
615 }
616
617 /*
618 * Set the authentication info, if any...
619 */
620
621 cupsSetPasswordCB2((cups_password_cb2_t)password_cb, &password_tries);
622
623 if (device_username[0])
624 {
625 /*
626 * Use authentication information in the device URI...
627 */
628
629 if ((device_password = strchr(device_username, ':')) != NULL)
630 *device_password++ = '\0';
631
632 cupsSetUser(device_username);
633 uri_credentials = 1;
634 }
635 else
636 {
637 /*
638 * Try loading authentication information from the environment.
639 */
640
641 const char *ptr = getenv("AUTH_USERNAME");
642
643 if (ptr)
644 {
645 cupsCopyString(device_username, ptr, sizeof(device_username));
646 cupsSetUser(ptr);
647 }
648
649 device_password = getenv("AUTH_PASSWORD");
650 }
651
652 /*
653 * Try finding the remote server...
654 */
655
656 start_time = time(NULL);
657
658 addrlist = backendLookup(hostname, port, &job_canceled);
659
660 http = httpConnect2(hostname, port, addrlist, AF_UNSPEC, cupsEncryption(), 1,
661 0, NULL);
662 httpSetTimeout(http, 30.0, timeout_cb, NULL);
663
664 /*
665 * See if the printer supports SNMP...
666 */
667
668 if (snmp_enabled)
669 snmp_fd = _cupsSNMPOpen(addrlist->addr.addr.sa_family);
670 else
671 snmp_fd = -1;
672
673 if (snmp_fd >= 0)
674 have_supplies = !backendSNMPSupplies(snmp_fd, &(addrlist->addr),
675 &start_count, NULL);
676 else
677 have_supplies = start_count = 0;
678
679 /*
680 * Wait for data from the filter...
681 */
682
683 if (num_files == 0)
684 {
685 if (!backendWaitLoop(snmp_fd, &(addrlist->addr), 0, backendNetworkSideCB))
686 return (CUPS_BACKEND_OK);
687 else if ((bytes = read(0, buffer, sizeof(buffer))) <= 0)
688 return (CUPS_BACKEND_OK);
689 }
690
691 /*
692 * Try connecting to the remote server...
693 */
694
695 delay = _cupsNextDelay(0, &prev_delay);
696
697 do
698 {
699 fprintf(stderr, "DEBUG: Connecting to %s:%d\n", hostname, port);
700 _cupsLangPrintFilter(stderr, "INFO", _("Connecting to printer."));
701
702 if (httpReconnect2(http, 30000, NULL))
703 {
704 int error = errno; /* Connection error */
705
706 if (http->status == HTTP_STATUS_CUPS_PKI_ERROR)
707 update_reasons(NULL, "+cups-certificate-error");
708
709 if (job_canceled)
710 break;
711
712 if (getenv("CLASS") != NULL)
713 {
714 /*
715 * If the CLASS environment variable is set, the job was submitted
716 * to a class and not to a specific queue. In this case, we want
717 * to abort immediately so that the job can be requeued on the next
718 * available printer in the class.
719 */
720
721 _cupsLangPrintFilter(stderr, "INFO",
722 _("Unable to contact printer, queuing on next "
723 "printer in class."));
724
725 /*
726 * Sleep 5 seconds to keep the job from requeuing too rapidly...
727 */
728
729 sleep(5);
730
731 update_reasons(NULL, "-connecting-to-device");
732
733 return (CUPS_BACKEND_FAILED);
734 }
735
736 fprintf(stderr, "DEBUG: Connection error: %s\n", strerror(errno));
737
738 if (errno == ECONNREFUSED || errno == EHOSTDOWN || errno == EHOSTUNREACH || errno == ETIMEDOUT || errno == ENOTCONN)
739 {
740 if (contimeout && (time(NULL) - start_time) > contimeout)
741 {
742 _cupsLangPrintFilter(stderr, "ERROR",
743 _("The printer is not responding."));
744 update_reasons(NULL, "-connecting-to-device");
745 return (CUPS_BACKEND_FAILED);
746 }
747
748 switch (error)
749 {
750 case EHOSTDOWN :
751 _cupsLangPrintFilter(stderr, "WARNING",
752 _("The printer may not exist or "
753 "is unavailable at this time."));
754 break;
755
756 case EHOSTUNREACH :
757 default :
758 _cupsLangPrintFilter(stderr, "WARNING",
759 _("The printer is unreachable at this "
760 "time."));
761 break;
762
763 case ECONNREFUSED :
764 _cupsLangPrintFilter(stderr, "WARNING",
765 _("The printer is in use."));
766 break;
767 }
768
769 sleep((unsigned)delay);
770
771 delay = _cupsNextDelay(delay, &prev_delay);
772 }
773 else
774 {
775 _cupsLangPrintFilter(stderr, "ERROR",
776 _("The printer is not responding."));
777 sleep(30);
778 }
779
780 if (job_canceled)
781 break;
782 }
783 else
784 update_reasons(NULL, "-cups-certificate-error");
785 }
786 while (http->fd < 0);
787
788 if (job_canceled)
789 return (CUPS_BACKEND_OK);
790
791 if (httpIsEncrypted(http))
792 {
793 /*
794 * Validate TLS credentials...
795 */
796
797 char *creds; /* TLS credentials */
798 char *lcreds = NULL; /* Loaded credentials */
799 http_trust_t trust; /* Trust level */
800 char credinfo[1024], /* Information on credentials */
801 lcredinfo[1024];/* Information on saved credentials */
802 static const char * const trusts[] = { NULL, "+cups-pki-invalid", "+cups-pki-changed", "+cups-pki-expired", NULL, "+cups-pki-unknown" };
803 /* Trust keywords */
804 static const char * const trust_msgs[] =
805 {
806 _("Credentials are OK/trusted."),
807 _("Credentials are invalid."),
808 _("Credentials have changed."),
809 _("Credentials are expired."),
810 _("Credentials have been renewed."),
811 _("Credentials are unknown/new.")
812 };
813
814 fputs("DEBUG: Connection is encrypted.\n", stderr);
815
816 if ((creds = httpCopyPeerCredentials(http)) != NULL)
817 {
818 trust = cupsGetCredentialsTrust(NULL, hostname, creds, /*require_ca*/false);
819 cupsGetCredentialsInfo(creds, credinfo, sizeof(credinfo));
820
821 fprintf(stderr, "DEBUG: %s (%s)\n", trust_msgs[trust], cupsGetErrorString());
822 fprintf(stderr, "DEBUG: Printer credentials: %s\n", credinfo);
823
824 if ((lcreds = cupsCopyCredentials(NULL, hostname)) != NULL)
825 {
826 cupsGetCredentialsInfo(lcreds, lcredinfo, sizeof(lcredinfo));
827 fprintf(stderr, "DEBUG: Stored credentials: %s\n", lcredinfo);
828 }
829 else
830 {
831 fputs("DEBUG: No stored credentials.\n", stderr);
832 }
833
834 update_reasons(NULL, "-cups-pki-invalid,cups-pki-changed,cups-pki-expired,cups-pki-unknown");
835 if (trusts[trust])
836 {
837 update_reasons(NULL, trusts[trust]);
838 _cupsLangPrintFilter(stderr, "ALERT", "%s", trust_msgs[trust]);
839 return (CUPS_BACKEND_STOP);
840 }
841
842 /*
843 * Save the credentials we have so we can detect changes...
844 */
845
846 cupsSaveCredentials(NULL, hostname, creds, /*key*/NULL);
847
848 free(lcreds);
849 free(creds);
850 }
851 else
852 {
853 fputs("DEBUG: No printer credentials.\n", stderr);
854
855 update_reasons(NULL, "cups-pki-unknown");
856 return (CUPS_BACKEND_STOP);
857 }
858 }
859
860 update_reasons(NULL, "-connecting-to-device");
861 _cupsLangPrintFilter(stderr, "INFO", _("Connected to printer."));
862
863 fprintf(stderr, "DEBUG: Connected to %s:%d...\n",
864 httpAddrGetString(http->hostaddr, addrname, sizeof(addrname)),
865 httpAddrGetPort(http->hostaddr));
866
867 /*
868 * Build a URI for the printer and fill the standard IPP attributes for
869 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
870 * might contain username:password information...
871 */
872
873 httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), scheme, NULL, hostname,
874 port, resource);
875
876 /*
877 * First validate the destination and see if the device supports multiple
878 * copies...
879 */
880
881 compression_sup = NULL;
882 copies_sup = NULL;
883 cups_version = NULL;
884 encryption_sup = NULL;
885 format_sup = NULL;
886 media_col_sup = NULL;
887 supported = NULL;
888 operations_sup = NULL;
889 doc_handling_sup = NULL;
890 print_color_mode_sup = NULL;
891 print_scaling_sup = NULL;
892
893 do
894 {
895 /*
896 * Check for side-channel requests...
897 */
898
899 backendCheckSideChannel(snmp_fd, http->hostaddr);
900
901 /*
902 * Build the IPP request...
903 */
904
905 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
906 ippSetVersion(request, version / 10, version % 10);
907 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
908 NULL, uri);
909
910 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
911 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
912 NULL, pattrs);
913
914 /*
915 * Do the request...
916 */
917
918 fputs("DEBUG: Getting supported attributes...\n", stderr);
919
920 if (http->version < HTTP_VERSION_1_1)
921 {
922 fprintf(stderr, "DEBUG: Printer responded with HTTP version %d.%d.\n",
923 http->version / 100, http->version % 100);
924 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
925 "cups-ipp-wrong-http-version");
926 }
927
928 supported = cupsDoRequest(http, request, resource);
929 ipp_status = cupsGetError();
930
931 fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
932 ippErrorString(ipp_status), cupsGetErrorString());
933
934 if (ipp_status <= IPP_STATUS_OK_CONFLICTING)
935 password_tries = 0;
936 else
937 {
938 fprintf(stderr, "DEBUG: Get-Printer-Attributes returned %s.\n",
939 ippErrorString(ipp_status));
940
941 if (ipp_status == IPP_STATUS_ERROR_BUSY ||
942 ipp_status == IPP_STATUS_ERROR_SERVICE_UNAVAILABLE)
943 {
944 if (contimeout && (time(NULL) - start_time) > contimeout)
945 {
946 _cupsLangPrintFilter(stderr, "ERROR",
947 _("The printer is not responding."));
948 return (CUPS_BACKEND_FAILED);
949 }
950
951 _cupsLangPrintFilter(stderr, "INFO", _("The printer is in use."));
952
953 report_printer_state(supported);
954
955 sleep((unsigned)delay);
956
957 delay = _cupsNextDelay(delay, &prev_delay);
958 }
959 else if ((ipp_status == IPP_STATUS_ERROR_BAD_REQUEST ||
960 ipp_status == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED) && version > 10)
961 {
962 /*
963 * Switch to IPP/1.1 or IPP/1.0...
964 */
965
966 if (version >= 20)
967 {
968 _cupsLangPrintFilter(stderr, "INFO", _("Preparing to print."));
969 fprintf(stderr,
970 "DEBUG: The printer does not support IPP/%d.%d, trying "
971 "IPP/1.1.\n", version / 10, version % 10);
972 version = 11;
973 }
974 else
975 {
976 _cupsLangPrintFilter(stderr, "INFO", _("Preparing to print."));
977 fprintf(stderr,
978 "DEBUG: The printer does not support IPP/%d.%d, trying "
979 "IPP/1.0.\n", version / 10, version % 10);
980 version = 10;
981 }
982
983 httpReconnect2(http, 30000, NULL);
984 }
985 else if (ipp_status == IPP_STATUS_ERROR_NOT_FOUND)
986 {
987 _cupsLangPrintFilter(stderr, "ERROR",
988 _("The printer configuration is incorrect or the "
989 "printer no longer exists."));
990
991 ippDelete(supported);
992
993 return (CUPS_BACKEND_STOP);
994 }
995 else if (ipp_status == IPP_STATUS_ERROR_FORBIDDEN ||
996 ipp_status == IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED)
997 {
998 const char *www_auth = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE);
999 /* WWW-Authenticate field value */
1000
1001 if (!strncmp(www_auth, "Negotiate", 9))
1002 auth_info_required = "negotiate";
1003 else if (www_auth[0])
1004 auth_info_required = "username,password";
1005
1006 fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
1007 return (CUPS_BACKEND_AUTH_REQUIRED);
1008 }
1009 else if (ipp_status != IPP_STATUS_ERROR_NOT_AUTHORIZED)
1010 {
1011 _cupsLangPrintFilter(stderr, "ERROR",
1012 _("Unable to get printer status."));
1013 sleep(10);
1014
1015 httpReconnect2(http, 30000, NULL);
1016 }
1017
1018 ippDelete(supported);
1019 supported = NULL;
1020 continue;
1021 }
1022
1023 if (!getenv("CLASS"))
1024 {
1025 /*
1026 * Check printer-is-accepting-jobs = false and printer-state-reasons for the
1027 * "spool-area-full" keyword...
1028 */
1029
1030 int busy = 0;
1031
1032 if ((printer_accepting = ippFindAttribute(supported,
1033 "printer-is-accepting-jobs",
1034 IPP_TAG_BOOLEAN)) != NULL &&
1035 !printer_accepting->values[0].boolean)
1036 busy = 1;
1037 else if (!printer_accepting)
1038 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1039 "cups-ipp-missing-printer-is-accepting-jobs");
1040
1041 if ((printer_state = ippFindAttribute(supported,
1042 "printer-state-reasons",
1043 IPP_TAG_KEYWORD)) == NULL)
1044 {
1045 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1046 "cups-ipp-missing-printer-state-reasons");
1047 }
1048 else if (!busy)
1049 {
1050 for (i = 0; i < printer_state->num_values; i ++)
1051 {
1052 if (!strcmp(printer_state->values[0].string.text,
1053 "spool-area-full") ||
1054 !strncmp(printer_state->values[0].string.text, "spool-area-full-",
1055 16))
1056 {
1057 busy = 1;
1058 break;
1059 }
1060 }
1061 }
1062
1063 if (busy)
1064 {
1065 _cupsLangPrintFilter(stderr, "INFO", _("The printer is in use."));
1066
1067 report_printer_state(supported);
1068
1069 sleep((unsigned)delay);
1070
1071 delay = _cupsNextDelay(delay, &prev_delay);
1072
1073 ippDelete(supported);
1074 supported = NULL;
1075 ipp_status = IPP_STATUS_ERROR_BUSY;
1076 continue;
1077 }
1078 }
1079
1080 /*
1081 * Check for supported attributes...
1082 */
1083
1084 if ((compression_sup = ippFindAttribute(supported, "compression-supported", IPP_TAG_KEYWORD)) != NULL)
1085 {
1086 /*
1087 * Check whether the requested compression is supported and/or default to
1088 * compression if supported...
1089 */
1090
1091 if (compression && !ippContainsString(compression_sup, compression))
1092 {
1093 fprintf(stderr, "DEBUG: Printer does not support the requested "
1094 "compression value \"%s\".\n", compression);
1095 compression = NULL;
1096 }
1097 else if (!compression && (!strcmp(final_content_type, "image/pwg-raster") || !strcmp(final_content_type, "image/urf")))
1098 {
1099 if (ippContainsString(compression_sup, "gzip"))
1100 compression = "gzip";
1101 else if (ippContainsString(compression_sup, "deflate"))
1102 compression = "deflate";
1103
1104 if (compression)
1105 fprintf(stderr, "DEBUG: Automatically using \"%s\" compression.\n",
1106 compression);
1107 }
1108 }
1109
1110 if ((copies_sup = ippFindAttribute(supported, "copies-supported",
1111 IPP_TAG_RANGE)) != NULL)
1112 {
1113 /*
1114 * Has the "copies-supported" attribute - does it have an upper
1115 * bound > 1?
1116 */
1117
1118 fprintf(stderr, "DEBUG: copies-supported=%d-%d\n",
1119 copies_sup->values[0].range.lower,
1120 copies_sup->values[0].range.upper);
1121
1122 if (copies_sup->values[0].range.upper <= 1)
1123 copies_sup = NULL; /* No */
1124 }
1125
1126 if ((cups_version = ippFindAttribute(supported, "cups-version", IPP_TAG_TEXT)) != NULL)
1127 {
1128 const char *val = ippGetString(cups_version, 0, NULL);
1129
1130 fprintf(stderr, "DEBUG: cups-version = \"%s\"\n", val);
1131 if (!strcmp(val, "cups-version"))
1132 cups_version = NULL; /* Bogus cups-version value returned by buggy printers! */
1133 }
1134
1135 encryption_sup = ippFindAttribute(supported, "job-password-encryption-supported", IPP_TAG_KEYWORD);
1136
1137 if ((format_sup = ippFindAttribute(supported, "document-format-supported",
1138 IPP_TAG_MIMETYPE)) != NULL)
1139 {
1140 fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
1141 format_sup->num_values);
1142 for (i = 0; i < format_sup->num_values; i ++)
1143 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
1144 format_sup->values[i].string.text);
1145 }
1146
1147 if ((media_col_sup = ippFindAttribute(supported, "media-col-supported",
1148 IPP_TAG_KEYWORD)) != NULL)
1149 {
1150 fprintf(stderr, "DEBUG: media-col-supported (%d values)\n",
1151 media_col_sup->num_values);
1152 for (i = 0; i < media_col_sup->num_values; i ++)
1153 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
1154 media_col_sup->values[i].string.text);
1155 }
1156
1157 print_color_mode_sup = ippFindAttribute(supported, "print-color-mode-supported", IPP_TAG_KEYWORD);
1158
1159 if ((print_scaling_sup = ippFindAttribute(supported, "print-scaling-supported", IPP_TAG_KEYWORD)) != NULL)
1160 {
1161 int count = ippGetCount(print_scaling_sup);
1162
1163 fprintf(stderr, "DEBUG: print-scaling-supported (%d values)\n", count);
1164 for (i = 0; i < count; i ++)
1165 fprintf(stderr, "DEBUG: [%d] = %s\n", i, ippGetString(print_scaling_sup, i, NULL));
1166 }
1167
1168 if ((operations_sup = ippFindAttribute(supported, "operations-supported",
1169 IPP_TAG_ENUM)) != NULL)
1170 {
1171 fprintf(stderr, "DEBUG: operations-supported (%d values)\n",
1172 operations_sup->num_values);
1173 for (i = 0; i < operations_sup->num_values; i ++)
1174 fprintf(stderr, "DEBUG: [%d] = %s\n", i,
1175 ippOpString(operations_sup->values[i].integer));
1176
1177 for (i = 0; i < operations_sup->num_values; i ++)
1178 if (operations_sup->values[i].integer == IPP_OP_PRINT_JOB)
1179 break;
1180
1181 if (i >= operations_sup->num_values)
1182 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1183 "cups-ipp-missing-print-job");
1184
1185 for (i = 0; i < operations_sup->num_values; i ++)
1186 if (operations_sup->values[i].integer == IPP_OP_CANCEL_JOB)
1187 break;
1188
1189 if (i >= operations_sup->num_values)
1190 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1191 "cups-ipp-missing-cancel-job");
1192
1193 for (i = 0; i < operations_sup->num_values; i ++)
1194 if (operations_sup->values[i].integer == IPP_OP_GET_JOB_ATTRIBUTES)
1195 break;
1196
1197 if (i >= operations_sup->num_values)
1198 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1199 "cups-ipp-missing-get-job-attributes");
1200
1201 for (i = 0; i < operations_sup->num_values; i ++)
1202 if (operations_sup->values[i].integer == IPP_OP_GET_PRINTER_ATTRIBUTES)
1203 break;
1204
1205 if (i >= operations_sup->num_values)
1206 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1207 "cups-ipp-missing-get-printer-attributes");
1208
1209 for (i = 0; i < operations_sup->num_values; i ++)
1210 {
1211 if (operations_sup->values[i].integer == IPP_OP_VALIDATE_JOB)
1212 validate_job = 1;
1213 else if (operations_sup->values[i].integer == IPP_OP_CREATE_JOB)
1214 create_job = 1;
1215 else if (operations_sup->values[i].integer == IPP_OP_SEND_DOCUMENT)
1216 send_document = 1;
1217 else if (operations_sup->values[i].integer == IPP_OP_GET_JOB_ATTRIBUTES)
1218 get_job_attrs = 1;
1219 }
1220
1221 if (create_job && !send_document)
1222 {
1223 fputs("DEBUG: Printer supports Create-Job but not Send-Document.\n",
1224 stderr);
1225 create_job = 0;
1226
1227 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1228 "cups-ipp-missing-send-document");
1229 }
1230
1231 if (!validate_job)
1232 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1233 "cups-ipp-missing-validate-job");
1234 }
1235 else
1236 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1237 "cups-ipp-missing-operations-supported");
1238
1239 doc_handling_sup = ippFindAttribute(supported,
1240 "multiple-document-handling-supported",
1241 IPP_TAG_KEYWORD);
1242
1243 report_printer_state(supported);
1244 }
1245 while (!job_canceled && ipp_status > IPP_STATUS_OK_CONFLICTING);
1246
1247 if (job_canceled)
1248 return (CUPS_BACKEND_OK);
1249
1250 /*
1251 * See if the printer is accepting jobs and is not stopped; if either
1252 * condition is true and we are printing to a class, requeue the job...
1253 */
1254
1255 if (getenv("CLASS") != NULL)
1256 {
1257 printer_state = ippFindAttribute(supported, "printer-state",
1258 IPP_TAG_ENUM);
1259 printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs",
1260 IPP_TAG_BOOLEAN);
1261
1262 if (printer_state == NULL ||
1263 (printer_state->values[0].integer > IPP_PSTATE_PROCESSING &&
1264 waitprinter) ||
1265 printer_accepting == NULL ||
1266 !printer_accepting->values[0].boolean)
1267 {
1268 /*
1269 * If the CLASS environment variable is set, the job was submitted
1270 * to a class and not to a specific queue. In this case, we want
1271 * to abort immediately so that the job can be requeued on the next
1272 * available printer in the class.
1273 */
1274
1275 _cupsLangPrintFilter(stderr, "INFO",
1276 _("Unable to contact printer, queuing on next "
1277 "printer in class."));
1278
1279 ippDelete(supported);
1280 httpClose(http);
1281
1282 /*
1283 * Sleep 5 seconds to keep the job from requeuing too rapidly...
1284 */
1285
1286 sleep(5);
1287
1288 return (CUPS_BACKEND_FAILED);
1289 }
1290 }
1291
1292 /*
1293 * See if the printer supports multiple copies...
1294 */
1295
1296 copies = atoi(argv[4]);
1297
1298 if (copies_sup || argc < 7)
1299 copies_remaining = 1;
1300 else
1301 copies_remaining = copies;
1302
1303 /*
1304 * Prepare remaining printing options...
1305 */
1306
1307 options = NULL;
1308
1309 if (send_options)
1310 {
1311 num_options = cupsParseOptions(argv[5], 0, &options);
1312
1313 if (!cups_version && media_col_sup)
1314 {
1315 /*
1316 * Load the PPD file and generate PWG attribute mapping information...
1317 */
1318
1319 ppd_attr_t *mandatory; /* cupsMandatory value */
1320
1321 ppd = ppdOpenFile(getenv("PPD"));
1322 pc = _ppdCacheCreateWithPPD(NULL, ppd);
1323
1324 ppdMarkDefaults(ppd);
1325 cupsMarkOptions(ppd, num_options, options);
1326
1327 if ((mandatory = ppdFindAttr(ppd, "cupsMandatory", NULL)) != NULL)
1328 cupsCopyString(mandatory_attrs, mandatory->value, sizeof(mandatory_attrs));
1329 }
1330
1331 /*
1332 * Validate job-password/-encryption...
1333 */
1334
1335 if (cupsGetOption("job-password", num_options, options))
1336 {
1337 const char *keyword; /* job-password-encryption value */
1338 static const char * const hashes[] =
1339 { /* List of supported hash algorithms, in order of preference */
1340 "sha-512",
1341 "sha-384",
1342 "sha-512_256",
1343 "sha-512-224",
1344 "sha-256",
1345 "sha-224",
1346 "sha",
1347 "none"
1348 };
1349
1350 if ((keyword = cupsGetOption("job-password-encryption", num_options, options)) == NULL || !ippContainsString(encryption_sup, keyword))
1351 {
1352 /*
1353 * Either no job-password-encryption or the value isn't supported by
1354 * the printer...
1355 */
1356
1357 size_t j;
1358 for (j = 0; j < (sizeof(hashes) / sizeof(hashes[0])); j ++)
1359 if (ippContainsString(encryption_sup, hashes[j]))
1360 {
1361 num_options = cupsAddOption("job-password-encryption", hashes[j], num_options, &options);
1362 break;
1363 }
1364 }
1365 }
1366 }
1367 else
1368 num_options = 0;
1369
1370 document_format = NULL;
1371
1372 if (format_sup != NULL)
1373 {
1374 if (ippContainsString(format_sup, final_content_type))
1375 document_format = final_content_type;
1376 else if (ippContainsString(format_sup, "application/octet-stream"))
1377 document_format = "application/octet-stream";
1378 }
1379
1380 fprintf(stderr, "DEBUG: final_content_type=\"%s\", document_format=\"%s\"\n",
1381 final_content_type, document_format ? document_format : "(null)");
1382
1383 /*
1384 * If the printer does not support HTTP/1.1 (which IPP requires), copy stdin
1385 * to a temporary file so that we can do a HTTP/1.0 submission...
1386 *
1387 * (I hate compatibility hacks!)
1388 */
1389
1390 if (http->version < HTTP_VERSION_1_1 && num_files == 0)
1391 {
1392 if ((fd = cupsTempFd(tmpfilename, sizeof(tmpfilename))) < 0)
1393 {
1394 perror("DEBUG: Unable to create temporary file");
1395 return (CUPS_BACKEND_FAILED);
1396 }
1397
1398 _cupsLangPrintFilter(stderr, "INFO", _("Copying print data."));
1399
1400 if ((compatsize = write(fd, buffer, (size_t)bytes)) < 0)
1401 {
1402 perror("DEBUG: Unable to write temporary file");
1403 return (CUPS_BACKEND_FAILED);
1404 }
1405
1406 if ((bytes = backendRunLoop(-1, fd, snmp_fd, &(addrlist->addr), 0, 0,
1407 backendNetworkSideCB)) < 0)
1408 return (CUPS_BACKEND_FAILED);
1409
1410 compatsize += bytes;
1411
1412 close(fd);
1413
1414 compatfile = tmpfilename;
1415 files = &compatfile;
1416 num_files = 1;
1417 }
1418 else if (http->version < HTTP_VERSION_1_1 && num_files == 1)
1419 {
1420 struct stat fileinfo; /* File information */
1421
1422 if (!stat(files[0], &fileinfo))
1423 compatsize = fileinfo.st_size;
1424 }
1425
1426 /*
1427 * If the printer only claims to support IPP/1.0, or if the user specifically
1428 * included version=1.0 in the URI, then do not try to use Create-Job or
1429 * Send-Document. This is another dreaded compatibility hack, but
1430 * unfortunately there are enough broken printers out there that we need
1431 * this for now...
1432 */
1433
1434 if (version == 10)
1435 create_job = 0;
1436
1437 /*
1438 * Start monitoring the printer in the background...
1439 */
1440
1441 monitor.uri = uri;
1442 monitor.hostname = hostname;
1443 monitor.user = username;
1444 monitor.resource = resource;
1445 monitor.port = port;
1446 monitor.version = version;
1447 monitor.job_id = 0;
1448 monitor.create_job = create_job;
1449 monitor.get_job_attrs = get_job_attrs;
1450 monitor.encryption = cupsEncryption();
1451 monitor.job_state = IPP_JSTATE_PENDING;
1452 monitor.printer_state = IPP_PSTATE_IDLE;
1453 monitor.retryable = argc == 6 && document_format && strcmp(document_format, "image/pwg-raster") && strcmp(document_format, "image/urf");
1454
1455 fprintf(stderr, "DEBUG: retryable=%d\n", monitor.retryable);
1456
1457 if (create_job)
1458 {
1459 monitor.job_name = argv[3];
1460 }
1461 else
1462 {
1463 /*
1464 * TODO: make this compatible with UTF-8 - possible UTF-8 truncation here..
1465 */
1466
1467 snprintf(print_job_name, sizeof(print_job_name), "%s - %s", argv[1],
1468 argv[3]);
1469 monitor.job_name = print_job_name;
1470 }
1471
1472 cupsThreadCreate((cups_thread_func_t)monitor_printer, &monitor);
1473
1474 /*
1475 * Validate access to the printer...
1476 */
1477
1478 while (!job_canceled && validate_job)
1479 {
1480 request = new_request(IPP_OP_VALIDATE_JOB, version, uri, username,
1481 monitor.job_name, num_options, options, compression,
1482 copies_sup ? copies : 1, document_format, pc, ppd,
1483 media_col_sup, doc_handling_sup, print_color_mode_sup, print_scaling_sup);
1484
1485 response = cupsDoRequest(http, request, resource);
1486
1487 ipp_status = cupsGetError();
1488
1489 fprintf(stderr, "DEBUG: Validate-Job: %s (%s)\n",
1490 ippErrorString(ipp_status), cupsGetErrorString());
1491 debug_attributes(response);
1492
1493 if ((job_auth = ippFindAttribute(response, "job-authorization-uri",
1494 IPP_TAG_URI)) != NULL)
1495 num_options = cupsAddOption("job-authorization-uri",
1496 ippGetString(job_auth, 0, NULL), num_options,
1497 &options);
1498
1499 if (ipp_status == IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED || ipp_status == IPP_STATUS_OK_CONFLICTING)
1500 {
1501 /*
1502 * One or more options are not supported...
1503 */
1504
1505 if (ippFindAttribute(response, "sides", IPP_TAG_ZERO))
1506 {
1507 /*
1508 * The sides value is not supported, revert to one-sided as needed...
1509 */
1510
1511 const char *sides = cupsGetOption("sides", num_options, options);
1512
1513 if (!sides || !strncmp(sides, "two-sided-", 10))
1514 {
1515 fputs("DEBUG: Unable to do two-sided printing, setting sides to 'one-sided'.\n", stderr);
1516 num_options = cupsAddOption("sides", "one-sided", num_options, &options);
1517 }
1518 }
1519 }
1520 else if ((ipp_status == IPP_STATUS_ERROR_BAD_REQUEST || ipp_status == IPP_STATUS_ERROR_INTERNAL) && !strcmp(username, argv[2]))
1521 {
1522 /*
1523 * Issue #1145: Some printers have trouble with valid character in the
1524 * requesting-user-name attribute. Sanitize the username and try again
1525 * if so...
1526 */
1527
1528 char *argptr = argv[2], /* Pointer into local username */
1529 *userptr = username; /* Pointer into requesting-user-name value */
1530
1531 fputs("DEBUG: Trying sanitized requesting-user-name value.\n", stderr);
1532
1533 while (*argptr && userptr < (username + sizeof(username) - 1))
1534 {
1535 if (isalnum(*argptr & 255))
1536 *userptr++ = *argptr;
1537 argptr ++;
1538 }
1539
1540 *userptr = '\0';
1541 }
1542
1543 ippDelete(response);
1544
1545 if (job_canceled)
1546 break;
1547
1548 if (ipp_status == IPP_STATUS_ERROR_SERVICE_UNAVAILABLE ||
1549 ipp_status == IPP_STATUS_ERROR_BUSY)
1550 {
1551 _cupsLangPrintFilter(stderr, "INFO", _("The printer is in use."));
1552 sleep(10);
1553 }
1554 else if (ipp_status == IPP_STATUS_ERROR_DOCUMENT_FORMAT_NOT_SUPPORTED ||
1555 ipp_status == IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES ||
1556 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED ||
1557 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED ||
1558 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED ||
1559 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED)
1560 goto cleanup;
1561 else if (ipp_status == IPP_STATUS_ERROR_FORBIDDEN ||
1562 ipp_status == IPP_STATUS_ERROR_NOT_AUTHORIZED ||
1563 ipp_status == IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED)
1564 {
1565 const char *www_auth = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE);
1566 /* WWW-Authenticate field value */
1567
1568 if (!strncmp(www_auth, "Negotiate", 9))
1569 auth_info_required = "negotiate";
1570 else if (www_auth[0])
1571 auth_info_required = "username,password";
1572
1573 goto cleanup;
1574 }
1575 else if (ipp_status == IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED)
1576 {
1577 /*
1578 * This is all too common...
1579 */
1580
1581 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1582 "cups-ipp-missing-validate-job");
1583 break;
1584 }
1585 else if (ipp_status < IPP_STATUS_REDIRECTION_OTHER_SITE ||
1586 ipp_status == IPP_STATUS_ERROR_BAD_REQUEST)
1587 break;
1588 else if (job_auth == NULL && ipp_status > IPP_STATUS_ERROR_BAD_REQUEST)
1589 {
1590 if (!validate_retried)
1591 {
1592 // Retry Validate-Job operation once, to work around known printer bug...
1593 validate_retried = 1;
1594 sleep(10);
1595 continue;
1596 }
1597
1598 goto cleanup;
1599 }
1600 }
1601
1602 /*
1603 * Then issue the print-job request...
1604 */
1605
1606 job_id = 0;
1607
1608 while (!job_canceled && copies_remaining > 0)
1609 {
1610 /*
1611 * Check for side-channel requests...
1612 */
1613
1614 backendCheckSideChannel(snmp_fd, http->hostaddr);
1615
1616 /*
1617 * Build the IPP job creation request...
1618 */
1619
1620 if (job_canceled)
1621 break;
1622
1623 request = new_request((num_files > 1 || create_job) ? IPP_OP_CREATE_JOB :
1624 IPP_OP_PRINT_JOB,
1625 version, uri, username, monitor.job_name, num_options,
1626 options, compression, copies_sup ? copies : 1,
1627 document_format, pc, ppd, media_col_sup,
1628 doc_handling_sup, print_color_mode_sup, print_scaling_sup);
1629
1630 /*
1631 * Do the request...
1632 */
1633
1634 if (num_files > 1 || create_job)
1635 response = cupsDoRequest(http, request, resource);
1636 else
1637 {
1638 size_t length = 0; /* Length of request */
1639
1640 if (compatsize > 0)
1641 {
1642 fputs("DEBUG: Sending file using HTTP/1.0 Content-Length...\n", stderr);
1643 length = ippLength(request) + (size_t)compatsize;
1644 }
1645 else
1646 fputs("DEBUG: Sending file using HTTP/1.1 chunking...\n", stderr);
1647
1648 http_status = cupsSendRequest(http, request, resource, length);
1649 if (http_status == HTTP_STATUS_CONTINUE && request->state == IPP_STATE_DATA)
1650 {
1651 if (compression && strcmp(compression, "none"))
1652 httpSetField(http, HTTP_FIELD_CONTENT_ENCODING, compression);
1653
1654 if (num_files == 1)
1655 {
1656 if ((fd = open(files[0], O_RDONLY)) < 0)
1657 {
1658 _cupsLangPrintError("ERROR", _("Unable to open print file"));
1659 return (CUPS_BACKEND_FAILED);
1660 }
1661 }
1662 else
1663 {
1664 fd = 0;
1665 http_status = cupsWriteRequestData(http, buffer, (size_t)bytes);
1666 }
1667
1668 while (http_status == HTTP_STATUS_CONTINUE &&
1669 (!job_canceled || compatsize > 0))
1670 {
1671 /*
1672 * Check for side-channel requests and more print data...
1673 */
1674
1675 FD_ZERO(&input);
1676 FD_SET(fd, &input);
1677 FD_SET(snmp_fd, &input);
1678 FD_SET(CUPS_SC_FD, &input);
1679
1680 while (select(fd > snmp_fd ? fd + 1 : snmp_fd + 1, &input, NULL, NULL,
1681 NULL) <= 0 && !job_canceled);
1682
1683 if (FD_ISSET(snmp_fd, &input))
1684 backendCheckSideChannel(snmp_fd, http->hostaddr);
1685
1686 if (FD_ISSET(fd, &input))
1687 {
1688 if ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
1689 {
1690 fprintf(stderr, "DEBUG: Read %d bytes...\n", (int)bytes);
1691
1692 if ((http_status = cupsWriteRequestData(http, buffer, (size_t)bytes))
1693 != HTTP_STATUS_CONTINUE)
1694 break;
1695 }
1696 else if (bytes == 0 || (errno != EINTR && errno != EAGAIN))
1697 break;
1698 }
1699 }
1700
1701 if (http_status == HTTP_STATUS_ERROR)
1702 fprintf(stderr, "DEBUG: Error writing document data for "
1703 "Print-Job: %s\n", strerror(httpError(http)));
1704
1705 if (num_files == 1)
1706 close(fd);
1707 }
1708
1709 response = cupsGetResponse(http, resource);
1710 ippDelete(request);
1711 }
1712
1713 ipp_status = cupsGetError();
1714
1715 fprintf(stderr, "DEBUG: %s: %s (%s)\n",
1716 (num_files > 1 || create_job) ? "Create-Job" : "Print-Job",
1717 ippErrorString(ipp_status), cupsGetErrorString());
1718 debug_attributes(response);
1719
1720 if (ipp_status > IPP_STATUS_OK_CONFLICTING)
1721 {
1722 job_id = 0;
1723
1724 if (job_canceled)
1725 break;
1726
1727 if (ipp_status == IPP_STATUS_ERROR_SERVICE_UNAVAILABLE ||
1728 ipp_status == IPP_STATUS_ERROR_NOT_POSSIBLE ||
1729 ipp_status == IPP_STATUS_ERROR_BUSY)
1730 {
1731 _cupsLangPrintFilter(stderr, "INFO", _("The printer is in use."));
1732 sleep(10);
1733
1734 if (num_files == 0)
1735 {
1736 /*
1737 * We can't re-submit when we have no files to print, so exit
1738 * immediately with the right status code...
1739 */
1740
1741 goto cleanup;
1742 }
1743 }
1744 else if (ipp_status == IPP_STATUS_ERROR_JOB_CANCELED ||
1745 ipp_status == IPP_STATUS_ERROR_NOT_AUTHORIZED ||
1746 ipp_status == IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES ||
1747 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED ||
1748 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED ||
1749 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED ||
1750 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED)
1751 goto cleanup;
1752 else
1753 {
1754 /*
1755 * Update auth-info-required as needed...
1756 */
1757
1758 _cupsLangPrintFilter(stderr, "ERROR",
1759 _("Print job was not accepted."));
1760
1761 if (ipp_status == IPP_STATUS_ERROR_FORBIDDEN ||
1762 ipp_status == IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED)
1763 {
1764 const char *www_auth = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE);
1765 /* WWW-Authenticate field value */
1766
1767 if (!strncmp(www_auth, "Negotiate", 9))
1768 auth_info_required = "negotiate";
1769 else if (www_auth[0])
1770 auth_info_required = "username,password";
1771 }
1772 else if (ipp_status == IPP_STATUS_ERROR_REQUEST_VALUE)
1773 {
1774 /*
1775 * Print file is too large, abort this job...
1776 */
1777
1778 goto cleanup;
1779 }
1780 else
1781 sleep(10);
1782
1783 if (num_files == 0)
1784 {
1785 /*
1786 * We can't re-submit when we have no files to print, so exit
1787 * immediately with the right status code...
1788 */
1789
1790 goto cleanup;
1791 }
1792 }
1793 }
1794 else if ((job_id_attr = ippFindAttribute(response, "job-id",
1795 IPP_TAG_INTEGER)) == NULL)
1796 {
1797 fputs("DEBUG: Print job accepted - job ID unknown.\n", stderr);
1798 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1799 "cups-ipp-missing-job-id");
1800 job_id = 0;
1801 }
1802 else
1803 {
1804 password_tries = 0;
1805 monitor.job_id = job_id = job_id_attr->values[0].integer;
1806 fprintf(stderr, "DEBUG: Print job accepted - job ID %d.\n", job_id);
1807 }
1808
1809 ippDelete(response);
1810
1811 if (job_canceled)
1812 break;
1813
1814 if (job_id && (num_files > 1 || create_job))
1815 {
1816 for (i = 0; num_files == 0 || i < num_files; i ++)
1817 {
1818 /*
1819 * Check for side-channel requests...
1820 */
1821
1822 backendCheckSideChannel(snmp_fd, http->hostaddr);
1823
1824 /*
1825 * Send the next file in the job...
1826 */
1827
1828 request = ippNewRequest(IPP_OP_SEND_DOCUMENT);
1829 ippSetVersion(request, version / 10, version % 10);
1830
1831 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1832 NULL, uri);
1833
1834 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1835 job_id);
1836
1837 if (username[0])
1838 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1839 "requesting-user-name", NULL, username);
1840
1841 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document",
1842 (i + 1) >= num_files);
1843
1844 if (document_format)
1845 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
1846 "document-format", NULL, document_format);
1847
1848 if (compression)
1849 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1850 "compression", NULL, compression);
1851
1852 fprintf(stderr, "DEBUG: Sending file %d using chunking...\n", i + 1);
1853 fprintf(stderr, "DEBUG: IPP/%d.%d %s #%d\n", version / 10, version % 10, ippOpString(ippGetOperation(request)), ippGetRequestId(request));
1854 debug_attributes(request);
1855
1856 http_status = cupsSendRequest(http, request, resource, 0);
1857 if (http_status == HTTP_STATUS_CONTINUE && request->state == IPP_STATE_DATA)
1858 {
1859 if (compression && strcmp(compression, "none"))
1860 httpSetField(http, HTTP_FIELD_CONTENT_ENCODING, compression);
1861
1862 if (num_files == 0)
1863 {
1864 fd = 0;
1865 http_status = cupsWriteRequestData(http, buffer, (size_t)bytes);
1866 }
1867 else
1868 {
1869 if ((fd = open(files[i], O_RDONLY)) < 0)
1870 {
1871 _cupsLangPrintError("ERROR", _("Unable to open print file"));
1872 return (CUPS_BACKEND_FAILED);
1873 }
1874 }
1875 }
1876 else
1877 fd = -1;
1878
1879 if (fd >= 0)
1880 {
1881 while (!job_canceled && http_status == HTTP_STATUS_CONTINUE &&
1882 (bytes = read(fd, buffer, sizeof(buffer))) > 0)
1883 {
1884 if ((http_status = cupsWriteRequestData(http, buffer, (size_t)bytes))
1885 != HTTP_STATUS_CONTINUE)
1886 break;
1887 else
1888 {
1889 /*
1890 * Check for side-channel requests...
1891 */
1892
1893 backendCheckSideChannel(snmp_fd, http->hostaddr);
1894 }
1895 }
1896
1897 if (fd > 0)
1898 close(fd);
1899 }
1900
1901 if (http_status == HTTP_STATUS_ERROR)
1902 fprintf(stderr, "DEBUG: Error writing document data for "
1903 "Send-Document: %s\n", strerror(httpError(http)));
1904
1905 response = cupsGetResponse(http, resource);
1906 ippDelete(request);
1907
1908 fprintf(stderr, "DEBUG: Send-Document: %s (%s)\n", ippErrorString(cupsGetError()), cupsGetErrorString());
1909 debug_attributes(response);
1910
1911 if (cupsGetError() > IPP_STATUS_OK_CONFLICTING && !job_canceled)
1912 {
1913 ipp_attribute_t *reasons = ippFindAttribute(response, "job-state-reasons", IPP_TAG_KEYWORD);
1914 /* job-state-reasons values */
1915
1916 ipp_status = cupsGetError();
1917
1918 if (ippContainsString(reasons, "document-format-error"))
1919 ipp_status = IPP_STATUS_ERROR_DOCUMENT_FORMAT_ERROR;
1920 else if (ippContainsString(reasons, "document-unprintable-error"))
1921 ipp_status = IPP_STATUS_ERROR_DOCUMENT_UNPRINTABLE;
1922
1923 ippDelete(response);
1924 _cupsLangPrintFilter(stderr, "ERROR", _("Unable to add document to print job."));
1925 break;
1926 }
1927 else
1928 {
1929 ippDelete(response);
1930
1931 password_tries = 0;
1932
1933 if (num_files == 0 || fd < 0)
1934 break;
1935 }
1936 }
1937 }
1938
1939 if (job_canceled)
1940 break;
1941
1942 if (ipp_status <= IPP_STATUS_OK_CONFLICTING && argc > 6)
1943 {
1944 fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
1945 copies_remaining --;
1946 }
1947 else if ((ipp_status == IPP_STATUS_ERROR_DOCUMENT_FORMAT_NOT_SUPPORTED || ipp_status == IPP_STATUS_ERROR_DOCUMENT_FORMAT_ERROR || ipp_status == IPP_STATUS_ERROR_DOCUMENT_UNPRINTABLE) &&
1948 argc == 6 &&
1949 document_format && strcmp(document_format, "image/pwg-raster") && strcmp(document_format, "image/urf"))
1950 {
1951 /*
1952 * Need to reprocess the job as raster...
1953 */
1954
1955 fputs("JOBSTATE: cups-retry-as-raster\n", stderr);
1956 if (job_id > 0)
1957 cancel_job(http, uri, job_id, resource, username, version);
1958
1959 goto cleanup;
1960 }
1961 else if (ipp_status == IPP_STATUS_ERROR_SERVICE_UNAVAILABLE ||
1962 ipp_status == IPP_STATUS_ERROR_NOT_POSSIBLE ||
1963 ipp_status == IPP_STATUS_ERROR_BUSY)
1964 {
1965 if (argc == 6)
1966 {
1967 /*
1968 * Need to reprocess the entire job; if we have a job ID, cancel the
1969 * job first...
1970 */
1971
1972 if (job_id > 0)
1973 cancel_job(http, uri, job_id, resource, username, version);
1974
1975 goto cleanup;
1976 }
1977 continue;
1978 }
1979 else if (ipp_status == IPP_STATUS_ERROR_REQUEST_VALUE ||
1980 ipp_status == IPP_STATUS_ERROR_JOB_CANCELED ||
1981 ipp_status == IPP_STATUS_ERROR_NOT_AUTHORIZED ||
1982 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED ||
1983 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED ||
1984 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED ||
1985 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED ||
1986 ipp_status == IPP_STATUS_ERROR_INTERNAL)
1987 {
1988 /*
1989 * Print file is too large, job was canceled, we need new
1990 * authentication data, or we had some sort of error...
1991 */
1992
1993 goto cleanup;
1994 }
1995 else if (ipp_status == IPP_STATUS_ERROR_CUPS_UPGRADE_REQUIRED)
1996 {
1997 /*
1998 * Server is configured incorrectly; the policy for Create-Job and
1999 * Send-Document has to be the same (auth or no auth, encryption or
2000 * no encryption). Force the queue to stop since printing will never
2001 * work.
2002 */
2003
2004 fputs("DEBUG: The server or printer is configured incorrectly.\n",
2005 stderr);
2006 fputs("DEBUG: The policy for Create-Job and Send-Document must have the "
2007 "same authentication and encryption requirements.\n", stderr);
2008
2009 ipp_status = IPP_STATUS_ERROR_INTERNAL;
2010
2011 if (job_id > 0)
2012 cancel_job(http, uri, job_id, resource, username, version);
2013
2014 goto cleanup;
2015 }
2016 else if (ipp_status == IPP_STATUS_ERROR_NOT_FOUND)
2017 {
2018 /*
2019 * Printer does not actually implement support for Create-Job/
2020 * Send-Document, so log the conformance issue and stop the printer.
2021 */
2022
2023 fputs("DEBUG: This printer claims to support Create-Job and "
2024 "Send-Document, but those operations failed.\n", stderr);
2025 fputs("DEBUG: Add '?version=1.0' to the device URI to use legacy "
2026 "compatibility mode.\n", stderr);
2027 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
2028 "cups-ipp-missing-send-document");
2029
2030 ipp_status = IPP_STATUS_ERROR_INTERNAL; /* Force queue to stop */
2031
2032 goto cleanup;
2033 }
2034 else
2035 copies_remaining --;
2036
2037 /*
2038 * Wait for the job to complete...
2039 */
2040
2041 if (!job_id || !waitjob || !get_job_attrs)
2042 continue;
2043
2044 fputs("STATE: +cups-waiting-for-job-completed\n", stderr);
2045
2046 _cupsLangPrintFilter(stderr, "INFO", _("Waiting for job to complete."));
2047
2048 for (delay = _cupsNextDelay(0, &prev_delay); !job_canceled;)
2049 {
2050 /*
2051 * Check for side-channel requests...
2052 */
2053
2054 backendCheckSideChannel(snmp_fd, http->hostaddr);
2055
2056 /*
2057 * Check printer state...
2058 */
2059
2060 check_printer_state(http, uri, resource, username, version);
2061
2062 if (cupsGetError() <= IPP_STATUS_OK_CONFLICTING)
2063 password_tries = 0;
2064
2065 /*
2066 * Build an IPP_OP_GET_JOB_ATTRIBUTES request...
2067 */
2068
2069 request = ippNewRequest(IPP_OP_GET_JOB_ATTRIBUTES);
2070 ippSetVersion(request, version / 10, version % 10);
2071
2072 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2073 NULL, uri);
2074
2075 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
2076 job_id);
2077
2078 if (username[0])
2079 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
2080 "requesting-user-name", NULL, username);
2081
2082 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2083 "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
2084 NULL, jattrs);
2085
2086 fprintf(stderr, "DEBUG: IPP/%d.%d %s #%d\n", version / 10, version % 10, ippOpString(ippGetOperation(request)), ippGetRequestId(request));
2087 debug_attributes(request);
2088
2089 /*
2090 * Do the request...
2091 */
2092
2093 httpReconnect2(http, 30000, NULL);
2094 response = cupsDoRequest(http, request, resource);
2095 ipp_status = cupsGetError();
2096
2097 if (ipp_status == IPP_STATUS_ERROR_NOT_FOUND || ipp_status == IPP_STATUS_ERROR_NOT_POSSIBLE)
2098 {
2099 /*
2100 * Job has gone away and/or the server has no job history...
2101 */
2102
2103 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
2104 "cups-ipp-missing-job-history");
2105 ippDelete(response);
2106
2107 ipp_status = IPP_STATUS_OK;
2108 break;
2109 }
2110
2111 fprintf(stderr, "DEBUG: Get-Job-Attributes: %s (%s)\n",
2112 ippErrorString(ipp_status), cupsGetErrorString());
2113 debug_attributes(response);
2114
2115 if (ipp_status <= IPP_STATUS_OK_CONFLICTING)
2116 password_tries = 0;
2117 else
2118 {
2119 if (ipp_status != IPP_STATUS_ERROR_SERVICE_UNAVAILABLE &&
2120 ipp_status != IPP_STATUS_ERROR_BUSY)
2121 {
2122 ippDelete(response);
2123 ipp_status = IPP_STATUS_OK;
2124 break;
2125 }
2126 else if (ipp_status == IPP_STATUS_ERROR_INTERNAL)
2127 {
2128 waitjob_tries ++;
2129
2130 if (waitjob_tries > 4)
2131 {
2132 ippDelete(response);
2133 ipp_status = IPP_STATUS_OK;
2134 break;
2135 }
2136 }
2137 }
2138
2139 if (response)
2140 {
2141 if ((job_state = ippFindAttribute(response, "job-state",
2142 IPP_TAG_ENUM)) != NULL)
2143 {
2144 /*
2145 * Reflect the remote job state in the local queue...
2146 */
2147
2148 if (cups_version &&
2149 job_state->values[0].integer >= IPP_JSTATE_PENDING &&
2150 job_state->values[0].integer <= IPP_JSTATE_COMPLETED)
2151 update_reasons(NULL,
2152 remote_job_states[job_state->values[0].integer -
2153 IPP_JSTATE_PENDING]);
2154
2155 if ((job_sheets = ippFindAttribute(response, "job-impressions-completed", IPP_TAG_INTEGER)) == NULL)
2156 job_sheets = ippFindAttribute(response, "job-media-sheets-completed", IPP_TAG_INTEGER);
2157
2158 if (job_sheets)
2159 fprintf(stderr, "PAGE: total %d\n",
2160 job_sheets->values[0].integer);
2161
2162 /*
2163 * Stop polling if the job is finished or pending-held...
2164 */
2165
2166 if (job_state->values[0].integer > IPP_JSTATE_STOPPED || job_state->values[0].integer == IPP_JSTATE_HELD)
2167 {
2168 ippDelete(response);
2169 break;
2170 }
2171 }
2172 else if (ipp_status != IPP_STATUS_ERROR_SERVICE_UNAVAILABLE &&
2173 ipp_status != IPP_STATUS_ERROR_NOT_POSSIBLE &&
2174 ipp_status != IPP_STATUS_ERROR_BUSY)
2175 {
2176 /*
2177 * If the printer does not return a job-state attribute, it does not
2178 * conform to the IPP specification - break out immediately and fail
2179 * the job...
2180 */
2181
2182 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
2183 "cups-ipp-missing-job-state");
2184 ipp_status = IPP_STATUS_ERROR_INTERNAL;
2185 break;
2186 }
2187 }
2188
2189 ippDelete(response);
2190
2191 /*
2192 * Wait before polling again...
2193 */
2194
2195 sleep((unsigned)delay);
2196
2197 delay = _cupsNextDelay(delay, &prev_delay);
2198 }
2199 }
2200
2201 /*
2202 * Cancel the job as needed...
2203 */
2204
2205 if (job_canceled > 0 && job_id > 0)
2206 {
2207 cancel_job(http, uri, job_id, resource, username, version);
2208
2209 if (cupsGetError() > IPP_STATUS_OK_CONFLICTING)
2210 _cupsLangPrintFilter(stderr, "ERROR", _("Unable to cancel print job."));
2211 }
2212
2213 /*
2214 * Check the printer state and report it if necessary...
2215 */
2216
2217 check_printer_state(http, uri, resource, username, version);
2218
2219 if (cupsGetError() <= IPP_STATUS_OK_CONFLICTING)
2220 password_tries = 0;
2221
2222 /*
2223 * Collect the final page count as needed...
2224 */
2225
2226 if (have_supplies &&
2227 !backendSNMPSupplies(snmp_fd, &(http->addrlist->addr), &page_count,
2228 NULL) &&
2229 page_count > start_count)
2230 fprintf(stderr, "PAGE: total %d\n", page_count - start_count);
2231
2232 #ifdef HAVE_GSSAPI
2233 /*
2234 * See if we used Kerberos at all...
2235 */
2236
2237 if (http->gssctx)
2238 auth_info_required = "negotiate";
2239 #endif /* HAVE_GSSAPI */
2240
2241 /*
2242 * Free memory...
2243 */
2244
2245 cleanup:
2246
2247 cupsFreeOptions(num_options, options);
2248 _ppdCacheDestroy(pc);
2249 ppdClose(ppd);
2250
2251 httpClose(http);
2252
2253 ippDelete(supported);
2254
2255 /*
2256 * Remove the temporary file(s) if necessary...
2257 */
2258
2259 if (tmpfilename[0])
2260 unlink(tmpfilename);
2261
2262 /*
2263 * Return the queue status...
2264 */
2265
2266 if (ipp_status == IPP_STATUS_ERROR_NOT_AUTHORIZED || ipp_status == IPP_STATUS_ERROR_FORBIDDEN ||
2267 ipp_status == IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED ||
2268 ipp_status <= IPP_STATUS_OK_CONFLICTING)
2269 fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
2270
2271 if (ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED)
2272 fputs("JOBSTATE: account-info-needed\n", stderr);
2273 else if (ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED)
2274 fputs("JOBSTATE: account-closed\n", stderr);
2275 else if (ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED)
2276 fputs("JOBSTATE: account-limit-reached\n", stderr);
2277 else if (ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED)
2278 fputs("JOBSTATE: account-authorization-failed\n", stderr);
2279
2280 // job_canceled can be -1 which should not be treated as CUPS_BACKEND_OK
2281 if (job_canceled > 0)
2282 return (CUPS_BACKEND_OK);
2283 else if (ipp_status == IPP_STATUS_ERROR_NOT_AUTHORIZED || ipp_status == IPP_STATUS_ERROR_FORBIDDEN || ipp_status == IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED)
2284 return (CUPS_BACKEND_AUTH_REQUIRED);
2285 else if (ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED ||
2286 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED ||
2287 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED ||
2288 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED)
2289 return (CUPS_BACKEND_HOLD);
2290 else if (ipp_status == IPP_STATUS_ERROR_INTERNAL)
2291 return (CUPS_BACKEND_STOP);
2292 else if (ipp_status == IPP_STATUS_ERROR_CONFLICTING || ipp_status == IPP_STATUS_ERROR_REQUEST_ENTITY || ipp_status == IPP_STATUS_ERROR_REQUEST_VALUE)
2293 return (CUPS_BACKEND_FAILED);
2294 else if (ipp_status == IPP_STATUS_ERROR_REQUEST_VALUE ||
2295 ipp_status == IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES ||
2296 ipp_status == IPP_STATUS_ERROR_DOCUMENT_FORMAT_NOT_SUPPORTED || job_canceled < 0)
2297 {
2298 if (ipp_status == IPP_STATUS_ERROR_REQUEST_VALUE)
2299 _cupsLangPrintFilter(stderr, "ERROR", _("Print job too large."));
2300 else if (ipp_status == IPP_STATUS_ERROR_DOCUMENT_FORMAT_NOT_SUPPORTED)
2301 _cupsLangPrintFilter(stderr, "ERROR",
2302 _("Printer cannot print supplied content."));
2303 else if (ipp_status == IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES)
2304 _cupsLangPrintFilter(stderr, "ERROR",
2305 _("Printer cannot print with supplied options."));
2306 else
2307 _cupsLangPrintFilter(stderr, "ERROR", _("Print job canceled at printer."));
2308
2309 return (CUPS_BACKEND_CANCEL);
2310 }
2311 else if (ipp_status > IPP_STATUS_OK_CONFLICTING && ipp_status != IPP_STATUS_ERROR_JOB_CANCELED)
2312 return (CUPS_BACKEND_RETRY_CURRENT);
2313 else
2314 return (CUPS_BACKEND_OK);
2315 }
2316
2317
2318 /*
2319 * 'adjust_options()' - Adjust options which have the same meaning.
2320 *
2321 * In case the backend gets PPD option and IPP attribute of the same meaning
2322 * among options array, adjust their values to reflect the same choices,
2323 * if the values differ (preferring PPD option values).
2324 *
2325 * Support for each PPD x IPP option pair is added adhoc, based on demand.
2326 */
2327
2328 static int /* O - New number of options */
2329 adjust_options(int num_options, /* I - Number of options */
2330 cups_option_t **options) /* I - Array of job options */
2331 {
2332 const char *ppd_option_value = NULL; /* PPD option value */
2333 const char *ipp_attr_value = NULL; /* IPP attribute value */
2334
2335
2336 fprintf(stderr, "DEBUG: adjust_options()\n");
2337
2338 if (options == NULL || num_options < 2)
2339 {
2340 fprintf(stderr, "DEBUG: adjust_options(): Invalid values.\n");
2341 return (num_options);
2342 }
2343
2344 /*
2345 * PPD option ColorModel and IPP attribute print-color-mode
2346 */
2347
2348 ppd_option_value = cupsGetOption("ColorModel", num_options, *options);
2349 ipp_attr_value = cupsGetOption("print-color-mode", num_options, *options);
2350
2351 if (!ppd_option_value || !ipp_attr_value)
2352 return (num_options);
2353
2354 if (strcmp(ipp_attr_value, "monochrome") && (!strcmp(ppd_option_value, "Gray")
2355 || !strcmp(ppd_option_value, "FastGray")
2356 || !strcmp(ppd_option_value, "DeviceGray")))
2357 {
2358 fprintf(stderr, "DEBUG: adjust_options(): Adjusting print-color-mode to monochrome.\n");
2359 num_options = cupsAddOption("print-color-mode", "monochrome", num_options, options);
2360 }
2361 else if (strcmp(ipp_attr_value, "color") && (!strcmp(ppd_option_value, "CMY")
2362 || !strcmp(ppd_option_value, "CMYK")
2363 || !strcmp(ppd_option_value, "RGB")))
2364 {
2365 fprintf(stderr, "DEBUG: adjust_options(): Adjusting print-color-mode to color.\n");
2366 num_options = cupsAddOption("print-color-mode", "color", num_options, options);
2367 }
2368
2369 return (num_options);
2370 }
2371
2372
2373 /*
2374 * 'cancel_job()' - Cancel a print job.
2375 */
2376
2377 static void
2378 cancel_job(http_t *http, /* I - HTTP connection */
2379 const char *uri, /* I - printer-uri */
2380 int id, /* I - job-id */
2381 const char *resource, /* I - Resource path */
2382 const char *user, /* I - requesting-user-name */
2383 int version) /* I - IPP version */
2384 {
2385 ipp_t *request; /* Cancel-Job request */
2386
2387
2388 _cupsLangPrintFilter(stderr, "INFO", _("Canceling print job."));
2389
2390 request = ippNewRequest(IPP_OP_CANCEL_JOB);
2391 ippSetVersion(request, version / 10, version % 10);
2392
2393 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2394 NULL, uri);
2395 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
2396
2397 if (user && user[0])
2398 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
2399 "requesting-user-name", NULL, user);
2400
2401 /*
2402 * Do the request...
2403 */
2404
2405 ippDelete(cupsDoRequest(http, request, resource));
2406 }
2407
2408
2409 /*
2410 * 'check_printer_state()' - Check the printer state.
2411 */
2412
2413 static ipp_pstate_t /* O - Current printer-state */
2414 check_printer_state(
2415 http_t *http, /* I - HTTP connection */
2416 const char *uri, /* I - Printer URI */
2417 const char *resource, /* I - Resource path */
2418 const char *user, /* I - Username, if any */
2419 int version) /* I - IPP version */
2420 {
2421 ipp_t *request, /* IPP request */
2422 *response; /* IPP response */
2423 ipp_attribute_t *attr; /* Attribute in response */
2424 ipp_pstate_t printer_state = IPP_PSTATE_STOPPED;
2425 /* Current printer-state */
2426
2427
2428 /*
2429 * Send a Get-Printer-Attributes request and log the results...
2430 */
2431
2432 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
2433 ippSetVersion(request, version / 10, version % 10);
2434
2435 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2436 NULL, uri);
2437
2438 if (user && user[0])
2439 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
2440 "requesting-user-name", NULL, user);
2441
2442 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2443 "requested-attributes",
2444 (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
2445
2446 fprintf(stderr, "DEBUG: IPP/%d.%d %s #%d\n", version / 10, version % 10, ippOpString(ippGetOperation(request)), ippGetRequestId(request));
2447 debug_attributes(request);
2448
2449 if ((response = cupsDoRequest(http, request, resource)) != NULL)
2450 {
2451 report_printer_state(response);
2452
2453 if ((attr = ippFindAttribute(response, "printer-state",
2454 IPP_TAG_ENUM)) != NULL)
2455 printer_state = (ipp_pstate_t)attr->values[0].integer;
2456 }
2457
2458 fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
2459 ippErrorString(cupsGetError()), cupsGetErrorString());
2460 debug_attributes(response);
2461 ippDelete(response);
2462
2463 /*
2464 * Return the printer-state value...
2465 */
2466
2467 return (printer_state);
2468 }
2469
2470
2471 /*
2472 * 'debug_attributes()' - Print out the request or response attributes as DEBUG
2473 * messages...
2474 */
2475
2476 static void
2477 debug_attributes(ipp_t *ipp) /* I - Request or response message */
2478 {
2479 ipp_tag_t group; /* Current group */
2480 ipp_attribute_t *attr; /* Current attribute */
2481 char buffer[1024]; /* Value buffer */
2482
2483
2484 for (group = IPP_TAG_ZERO, attr = ippFirstAttribute(ipp);
2485 attr;
2486 attr = ippNextAttribute(ipp))
2487 {
2488 const char *name = ippGetName(attr);
2489
2490 if (!name)
2491 {
2492 group = IPP_TAG_ZERO;
2493 continue;
2494 }
2495
2496 if (group != ippGetGroupTag(attr))
2497 {
2498 group = ippGetGroupTag(attr);
2499 fprintf(stderr, "DEBUG: ---- %s ----\n", ippTagString(group));
2500 }
2501
2502 if (!strcmp(name, "job-password"))
2503 cupsCopyString(buffer, "---", sizeof(buffer));
2504 else
2505 ippAttributeString(attr, buffer, sizeof(buffer));
2506
2507 fprintf(stderr, "DEBUG: %s %s%s %s\n", name,
2508 ippGetCount(attr) > 1 ? "1setOf " : "",
2509 ippTagString(ippGetValueTag(attr)), buffer);
2510 }
2511
2512 fprintf(stderr, "DEBUG: ---- %s ----\n", ippTagString(IPP_TAG_END));
2513 }
2514
2515
2516 /*
2517 * 'monitor_printer()' - Monitor the printer state.
2518 */
2519
2520 static void * /* O - Thread exit code */
2521 monitor_printer(
2522 _cups_monitor_t *monitor) /* I - Monitoring data */
2523 {
2524 http_t *http; /* Connection to printer */
2525 ipp_t *request, /* IPP request */
2526 *response; /* IPP response */
2527 ipp_attribute_t *attr; /* Attribute in response */
2528 int delay, /* Current delay */
2529 prev_delay; /* Previous delay */
2530 ipp_op_t job_op; /* Operation to use */
2531 int job_id; /* Job ID */
2532 const char *job_name; /* Job name */
2533 ipp_jstate_t job_state; /* Job state */
2534 const char *job_user; /* Job originating user name */
2535 int password_tries = 0; /* Password tries */
2536
2537
2538 /*
2539 * Make a copy of the printer connection...
2540 */
2541
2542 http = httpConnect2(monitor->hostname, monitor->port, NULL, AF_UNSPEC,
2543 monitor->encryption, 1, 0, NULL);
2544 httpSetTimeout(http, 30.0, timeout_cb, NULL);
2545 if (device_username[0])
2546 cupsSetUser(device_username);
2547
2548 cupsSetPasswordCB2((cups_password_cb2_t)password_cb, &password_tries);
2549
2550 /*
2551 * Loop until the job is canceled, aborted, or completed.
2552 */
2553
2554 delay = _cupsNextDelay(0, &prev_delay);
2555
2556 monitor->job_reasons = 0;
2557
2558 while (monitor->job_state < IPP_JSTATE_CANCELED && !job_canceled)
2559 {
2560 /*
2561 * Reconnect to the printer as needed...
2562 */
2563
2564 if (httpGetFd(http) < 0)
2565 httpReconnect2(http, 30000, NULL);
2566
2567 if (httpGetFd(http) >= 0)
2568 {
2569 /*
2570 * Connected, so check on the printer state...
2571 */
2572
2573 monitor->printer_state = check_printer_state(http, monitor->uri,
2574 monitor->resource,
2575 monitor->user,
2576 monitor->version);
2577 if (cupsGetError() <= IPP_STATUS_OK_CONFLICTING)
2578 password_tries = 0;
2579
2580 if (monitor->job_id == 0 && monitor->create_job)
2581 {
2582 /*
2583 * No job-id yet, so continue...
2584 */
2585
2586 goto monitor_sleep;
2587 }
2588
2589 /*
2590 * Check the status of the job itself...
2591 */
2592
2593 job_op = (monitor->job_id > 0 && monitor->get_job_attrs) ?
2594 IPP_OP_GET_JOB_ATTRIBUTES : IPP_OP_GET_JOBS;
2595 request = ippNewRequest(job_op);
2596 ippSetVersion(request, monitor->version / 10, monitor->version % 10);
2597
2598 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2599 NULL, monitor->uri);
2600 if (job_op == IPP_OP_GET_JOB_ATTRIBUTES)
2601 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
2602 monitor->job_id);
2603
2604 if (monitor->user && monitor->user[0])
2605 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
2606 "requesting-user-name", NULL, monitor->user);
2607
2608 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2609 "requested-attributes",
2610 (int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs);
2611
2612 /*
2613 * Do the request...
2614 */
2615
2616 response = cupsDoRequest(http, request, monitor->resource);
2617
2618 fprintf(stderr, "DEBUG: (monitor) %s: %s (%s)\n", ippOpString(job_op),
2619 ippErrorString(cupsGetError()), cupsGetErrorString());
2620
2621 if (cupsGetError() <= IPP_STATUS_OK_CONFLICTING)
2622 password_tries = 0;
2623
2624 if (job_op == IPP_OP_GET_JOB_ATTRIBUTES)
2625 {
2626 if ((attr = ippFindAttribute(response, "job-state",
2627 IPP_TAG_ENUM)) != NULL)
2628 monitor->job_state = (ipp_jstate_t)attr->values[0].integer;
2629 else
2630 monitor->job_state = IPP_JSTATE_COMPLETED;
2631 }
2632 else if (response)
2633 {
2634 for (attr = response->attrs; attr; attr = attr->next)
2635 {
2636 job_id = 0;
2637 job_name = NULL;
2638 job_state = IPP_JSTATE_PENDING;
2639 job_user = NULL;
2640
2641 while (attr && attr->group_tag != IPP_TAG_JOB)
2642 attr = attr->next;
2643
2644 if (!attr)
2645 break;
2646
2647 while (attr && attr->group_tag == IPP_TAG_JOB)
2648 {
2649 if (!strcmp(attr->name, "job-id") &&
2650 attr->value_tag == IPP_TAG_INTEGER)
2651 job_id = attr->values[0].integer;
2652 else if (!strcmp(attr->name, "job-name") &&
2653 (attr->value_tag == IPP_TAG_NAME ||
2654 attr->value_tag == IPP_TAG_NAMELANG))
2655 job_name = attr->values[0].string.text;
2656 else if (!strcmp(attr->name, "job-state") &&
2657 attr->value_tag == IPP_TAG_ENUM)
2658 job_state = (ipp_jstate_t)attr->values[0].integer;
2659 else if (!strcmp(attr->name, "job-originating-user-name") &&
2660 (attr->value_tag == IPP_TAG_NAME ||
2661 attr->value_tag == IPP_TAG_NAMELANG))
2662 job_user = attr->values[0].string.text;
2663
2664 attr = attr->next;
2665 }
2666
2667 if (job_id > 0 && job_name && !strcmp(job_name, monitor->job_name) &&
2668 job_user && monitor->user && !strcmp(job_user, monitor->user))
2669 {
2670 monitor->job_id = job_id;
2671 monitor->job_state = job_state;
2672 break;
2673 }
2674
2675 if (!attr)
2676 break;
2677 }
2678 }
2679
2680 fprintf(stderr, "DEBUG: (monitor) job-state = %s\n", ippEnumString("job-state", (int)monitor->job_state));
2681
2682 if (!job_canceled &&
2683 (monitor->job_state == IPP_JSTATE_CANCELED ||
2684 monitor->job_state == IPP_JSTATE_ABORTED))
2685 {
2686 job_canceled = -1;
2687 fprintf(stderr, "DEBUG: (monitor) job_canceled = -1\n");
2688 }
2689
2690 if ((attr = ippFindAttribute(response, "job-state-reasons",
2691 IPP_TAG_KEYWORD)) != NULL)
2692 {
2693 int i, new_reasons = 0; /* Looping var, new reasons */
2694
2695 for (i = 0; i < attr->num_values; i ++)
2696 {
2697 if (!strcmp(attr->values[i].string.text, "account-authorization-failed"))
2698 new_reasons |= _CUPS_JSR_ACCOUNT_AUTHORIZATION_FAILED;
2699 else if (!strcmp(attr->values[i].string.text, "account-closed"))
2700 new_reasons |= _CUPS_JSR_ACCOUNT_CLOSED;
2701 else if (!strcmp(attr->values[i].string.text, "account-info-needed"))
2702 new_reasons |= _CUPS_JSR_ACCOUNT_INFO_NEEDED;
2703 else if (!strcmp(attr->values[i].string.text, "account-limit-reached"))
2704 new_reasons |= _CUPS_JSR_ACCOUNT_LIMIT_REACHED;
2705 else if (!strcmp(attr->values[i].string.text, "job-password-wait"))
2706 new_reasons |= _CUPS_JSR_JOB_PASSWORD_WAIT;
2707 else if (!strcmp(attr->values[i].string.text, "job-release-wait"))
2708 new_reasons |= _CUPS_JSR_JOB_RELEASE_WAIT;
2709 else if (!strcmp(attr->values[i].string.text, "document-format-error"))
2710 new_reasons |= _CUPS_JSR_DOCUMENT_FORMAT_ERROR;
2711 else if (!strcmp(attr->values[i].string.text, "document-unprintable-error"))
2712 new_reasons |= _CUPS_JSR_DOCUMENT_UNPRINTABLE;
2713
2714 if (!job_canceled && (!strncmp(attr->values[i].string.text, "job-canceled-", 13) || !strcmp(attr->values[i].string.text, "aborted-by-system")))
2715 job_canceled = 1;
2716 }
2717
2718 if (new_reasons != monitor->job_reasons)
2719 {
2720 if (new_reasons & _CUPS_JSR_ACCOUNT_AUTHORIZATION_FAILED)
2721 fputs("JOBSTATE: account-authorization-failed\n", stderr);
2722 else if (new_reasons & _CUPS_JSR_ACCOUNT_CLOSED)
2723 fputs("JOBSTATE: account-closed\n", stderr);
2724 else if (new_reasons & _CUPS_JSR_ACCOUNT_INFO_NEEDED)
2725 fputs("JOBSTATE: account-info-needed\n", stderr);
2726 else if (new_reasons & _CUPS_JSR_ACCOUNT_LIMIT_REACHED)
2727 fputs("JOBSTATE: account-limit-reached\n", stderr);
2728 else if (new_reasons & _CUPS_JSR_JOB_PASSWORD_WAIT)
2729 fputs("JOBSTATE: job-password-wait\n", stderr);
2730 else if (new_reasons & _CUPS_JSR_JOB_RELEASE_WAIT)
2731 fputs("JOBSTATE: job-release-wait\n", stderr);
2732 else if (new_reasons & (_CUPS_JSR_DOCUMENT_FORMAT_ERROR | _CUPS_JSR_DOCUMENT_UNPRINTABLE))
2733 {
2734 if (monitor->retryable)
2735 {
2736 /*
2737 * Can't print this, so retry as raster...
2738 */
2739
2740 job_canceled = 1;
2741 fputs("JOBSTATE: cups-retry-as-raster\n", stderr);
2742 }
2743 else if (new_reasons & _CUPS_JSR_DOCUMENT_FORMAT_ERROR)
2744 {
2745 fputs("JOBSTATE: document-format-error\n", stderr);
2746 }
2747 else
2748 {
2749 fputs("JOBSTATE: document-unprintable\n", stderr);
2750 }
2751 }
2752 else
2753 fputs("JOBSTATE: job-printing\n", stderr);
2754
2755 monitor->job_reasons = new_reasons;
2756 }
2757 }
2758
2759 ippDelete(response);
2760
2761 fprintf(stderr, "DEBUG: (monitor) job-state = %s\n", ippEnumString("job-state", (int)monitor->job_state));
2762
2763 if (!job_canceled &&
2764 (monitor->job_state == IPP_JSTATE_CANCELED ||
2765 monitor->job_state == IPP_JSTATE_ABORTED))
2766 job_canceled = -1;
2767 }
2768
2769 /*
2770 * Sleep for N seconds...
2771 */
2772
2773 monitor_sleep:
2774
2775 sleep((unsigned)delay);
2776
2777 delay = _cupsNextDelay(delay, &prev_delay);
2778 }
2779
2780 /*
2781 * Cancel the job if necessary...
2782 */
2783
2784 if (job_canceled > 0 && monitor->job_id > 0)
2785 {
2786 if (httpGetFd(http) < 0)
2787 httpReconnect2(http, 30000, NULL);
2788
2789 if (httpGetFd(http) >= 0)
2790 {
2791 cancel_job(http, monitor->uri, monitor->job_id, monitor->resource,
2792 monitor->user, monitor->version);
2793
2794 if (cupsGetError() > IPP_STATUS_OK_CONFLICTING)
2795 {
2796 fprintf(stderr, "DEBUG: (monitor) cancel_job() = %s\n", cupsGetErrorString());
2797 _cupsLangPrintFilter(stderr, "ERROR", _("Unable to cancel print job."));
2798 }
2799 }
2800 }
2801
2802 /*
2803 * Cleanup and return...
2804 */
2805
2806 httpClose(http);
2807
2808 return (NULL);
2809 }
2810
2811
2812 /*
2813 * 'new_request()' - Create a new print creation or validation request.
2814 */
2815
2816 static ipp_t * /* O - Request data */
2817 new_request(
2818 ipp_op_t op, /* I - IPP operation code */
2819 int version, /* I - IPP version number */
2820 const char *uri, /* I - printer-uri value */
2821 const char *user, /* I - requesting-user-name value */
2822 const char *title, /* I - job-name value */
2823 int num_options, /* I - Number of options to send */
2824 cups_option_t *options, /* I - Options to send */
2825 const char *compression, /* I - compression value or NULL */
2826 int copies, /* I - copies value or 0 */
2827 const char *format, /* I - document-format value or NULL */
2828 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2829 ppd_file_t *ppd, /* I - PPD file data */
2830 ipp_attribute_t *media_col_sup, /* I - media-col-supported values */
2831 ipp_attribute_t *doc_handling_sup, /* I - multiple-document-handling-supported values */
2832 ipp_attribute_t *print_color_mode_sup,
2833 /* I - Printer supports print-color-mode? */
2834 ipp_attribute_t *print_scaling_sup) /* I - print-scaling-supported values */
2835 {
2836 ipp_t *request; /* Request data */
2837 const char *keyword; /* PWG keyword */
2838
2839
2840 /*
2841 * Create the IPP request...
2842 */
2843
2844 request = ippNewRequest(op);
2845 ippSetVersion(request, version / 10, version % 10);
2846
2847 fprintf(stderr, "DEBUG: %s IPP/%d.%d\n",
2848 ippOpString(request->request.op.operation_id),
2849 request->request.op.version[0],
2850 request->request.op.version[1]);
2851
2852 /*
2853 * Add standard attributes...
2854 */
2855
2856 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
2857 fprintf(stderr, "DEBUG: printer-uri=\"%s\"\n", uri);
2858
2859 if (user && *user)
2860 {
2861 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, user);
2862 fprintf(stderr, "DEBUG: requesting-user-name=\"%s\"\n", user);
2863 }
2864
2865 if (title && *title)
2866 {
2867 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, title);
2868 fprintf(stderr, "DEBUG: job-name=\"%s\"\n", title);
2869 }
2870
2871 if (format && op != IPP_OP_CREATE_JOB)
2872 {
2873 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, format);
2874 fprintf(stderr, "DEBUG: document-format=\"%s\"\n", format);
2875 }
2876
2877 if (compression && op != IPP_OP_CREATE_JOB && op != IPP_OP_VALIDATE_JOB)
2878 {
2879 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "compression", NULL, compression);
2880 fprintf(stderr, "DEBUG: compression=\"%s\"\n", compression);
2881 }
2882
2883 /*
2884 * Handle options on the command-line...
2885 */
2886
2887 if (num_options > 0)
2888 {
2889 if (pc)
2890 {
2891 /*
2892 * Send standard IPP attributes...
2893 */
2894
2895 fputs("DEBUG: Adding standard IPP operation/job attributes.\n", stderr);
2896
2897 copies = _cupsConvertOptions(request, ppd, pc, media_col_sup, doc_handling_sup, print_color_mode_sup, user, format, copies, num_options, options);
2898
2899 if ((keyword = cupsGetOption("print-scaling", num_options, options)) != NULL && ippContainsString(print_scaling_sup, keyword))
2900 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "print-scaling", NULL, keyword);
2901
2902 /*
2903 * Map FaxOut options...
2904 */
2905
2906 if ((keyword = cupsGetOption("phone", num_options, options)) != NULL)
2907 {
2908 ipp_t *destination; /* destination collection */
2909 char phone[1024], /* Phone number string */
2910 *ptr, /* Pointer into string */
2911 tel_uri[1024]; /* tel: URI */
2912 static const char * const allowed = "0123456789#*-+.()pw";
2913 /* Allowed characters */
2914
2915 /*
2916 * Unescape and filter out spaces and other characters that are not
2917 * allowed in a tel: URI.
2918 */
2919
2920 _httpDecodeURI(phone, keyword, sizeof(phone));
2921 ptr = phone;
2922
2923 /*
2924 * Weed out "Custom." in the beginning, this allows to put the
2925 * "phone" option as custom string option into the PPD so that
2926 * print dialogs not supporting fax display the option and
2927 * allow entering the phone number. Print dialogs also send "None"
2928 * if no phone number got entered, filter this, too.
2929 */
2930 if (!_cups_strcasecmp(phone, "None"))
2931 *ptr = '\0';
2932 if (!_cups_strncasecmp(phone, "Custom.", 7))
2933 _cups_strcpy(ptr, ptr + 7);
2934
2935 for (; *ptr;)
2936 {
2937 if (*ptr == ',')
2938 *ptr = 'p';
2939 else if (!strchr(allowed, *ptr))
2940 _cups_strcpy(ptr, ptr + 1);
2941 else
2942 ptr ++;
2943 }
2944
2945 if (strlen(phone) > 0)
2946 {
2947 destination = ippNew();
2948
2949 httpAssembleURI(HTTP_URI_CODING_ALL, tel_uri, sizeof(tel_uri), "tel", NULL, NULL, 0, phone);
2950 ippAddString(destination, IPP_TAG_JOB, IPP_TAG_URI, "destination-uri", NULL, tel_uri);
2951 fprintf(stderr, "DEBUG: Faxing to phone %s; destination-uri: %s\n", phone, tel_uri);
2952
2953 if ((keyword = cupsGetOption("faxPrefix", num_options, options)) != NULL && *keyword)
2954 {
2955 char predial[1024]; /* Pre-dial string */
2956
2957 _httpDecodeURI(predial, keyword, sizeof(predial));
2958 ptr = predial;
2959 if (!_cups_strcasecmp(ptr, "None"))
2960 *ptr = '\0';
2961 if (!_cups_strncasecmp(ptr, "Custom.", 7))
2962 ptr += 7;
2963 if (strlen(ptr) > 0)
2964 {
2965 ippAddString(destination, IPP_TAG_JOB, IPP_TAG_TEXT, "pre-dial-string", NULL, ptr);
2966 fprintf(stderr, "DEBUG: Pre-dialing %s; pre-dial-string: %s\n", ptr, ptr);
2967 }
2968 else
2969 fprintf(stderr, "WARNING: Pre-dial number for fax not valid! Sending fax without pre-dial number.\n");
2970 }
2971
2972 ippAddCollection(request, IPP_TAG_JOB, "destination-uris", destination);
2973 ippDelete(destination);
2974 }
2975 else
2976 fprintf(stderr, "ERROR: Phone number for fax not valid! Fax cannot be sent.\n");
2977 }
2978 }
2979 else
2980 {
2981 /*
2982 * When talking to another CUPS server, send all options...
2983 */
2984
2985 fputs("DEBUG: Adding all operation/job attributes.\n", stderr);
2986 num_options = adjust_options(num_options, &options);
2987
2988 if (format && (!strcmp(format, "image/pwg-raster") || !strcmp(format, "image/urf")))
2989 num_options = cupsRemoveOption("copies", num_options, &options);
2990
2991 cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
2992 cupsEncodeOptions2(request, num_options, options, IPP_TAG_JOB);
2993 }
2994
2995 if (copies > 1 && (!pc || copies <= pc->max_copies))
2996 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies", copies);
2997 }
2998
2999 fprintf(stderr, "DEBUG: IPP/%d.%d %s #%d\n", version / 10, version % 10, ippOpString(ippGetOperation(request)), ippGetRequestId(request));
3000 debug_attributes(request);
3001
3002 return (request);
3003 }
3004
3005
3006 /*
3007 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
3008 */
3009
3010 static const char * /* O - Password */
3011 password_cb(const char *prompt, /* I - Prompt (not used) */
3012 http_t *http, /* I - Connection */
3013 const char *method, /* I - Request method (not used) */
3014 const char *resource, /* I - Resource path (not used) */
3015 int *password_tries) /* I - Password tries */
3016 {
3017 char def_username[HTTP_MAX_VALUE]; /* Default username */
3018
3019
3020 fprintf(stderr, "DEBUG: password_cb(prompt=\"%s\", http=%p, method=\"%s\", "
3021 "resource=\"%s\", password_tries=%p(%d)), device_password=%p\n",
3022 prompt, (void *)http, method, resource, (void *)password_tries, *password_tries,
3023 (void *)device_password);
3024
3025 (void)prompt;
3026 (void)method;
3027 (void)resource;
3028
3029 if (!uri_credentials)
3030 {
3031 /*
3032 * Remember that we need to authenticate...
3033 */
3034
3035 if (httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "username",
3036 def_username))
3037 {
3038 char quoted[HTTP_MAX_VALUE * 2 + 4];
3039 /* Quoted string */
3040
3041 fprintf(stderr, "ATTR: auth-info-default=%s,\n",
3042 quote_string(def_username, quoted, sizeof(quoted)));
3043 }
3044 }
3045
3046 if (device_password && *device_password && *password_tries < 3)
3047 {
3048 (*password_tries) ++;
3049
3050 cupsSetUser(device_username);
3051
3052 return (device_password);
3053 }
3054 else
3055 {
3056 /*
3057 * Give up after 3 tries or if we don't have a password to begin with...
3058 */
3059
3060 return (NULL);
3061 }
3062 }
3063
3064
3065 /*
3066 * 'quote_string()' - Quote a string value.
3067 */
3068
3069 static const char * /* O - Quoted string */
3070 quote_string(const char *s, /* I - String */
3071 char *q, /* I - Quoted string buffer */
3072 size_t qsize) /* I - Size of quoted string buffer */
3073 {
3074 char *qptr, /* Pointer into string buffer */
3075 *qend; /* End of string buffer */
3076
3077
3078 qptr = q;
3079 qend = q + qsize - 5;
3080
3081 if (qend < q)
3082 {
3083 *q = '\0';
3084 return (q);
3085 }
3086
3087 *qptr++ = '\'';
3088 *qptr++ = '\"';
3089
3090 while (*s && qptr < qend)
3091 {
3092 if (*s == '\\' || *s == '\"' || *s == '\'')
3093 {
3094 if (qptr < (qend - 4))
3095 {
3096 *qptr++ = '\\';
3097 *qptr++ = '\\';
3098 *qptr++ = '\\';
3099 }
3100 else
3101 break;
3102 }
3103
3104 *qptr++ = *s++;
3105 }
3106
3107 *qptr++ = '\"';
3108 *qptr++ = '\'';
3109 *qptr = '\0';
3110
3111 return (q);
3112 }
3113
3114
3115 /*
3116 * 'report_attr()' - Report an IPP attribute value.
3117 */
3118
3119 static void
3120 report_attr(ipp_attribute_t *attr) /* I - Attribute */
3121 {
3122 int i; /* Looping var */
3123 char value[1024], /* Value string */
3124 *valptr; /* Pointer into value string */
3125 const char *cached; /* Cached attribute */
3126
3127
3128 /*
3129 * Convert the attribute values into quoted strings...
3130 */
3131
3132 for (i = 0, valptr = value;
3133 i < attr->num_values && valptr < (value + sizeof(value) - 10);
3134 i ++)
3135 {
3136 if (i > 0)
3137 *valptr++ = ',';
3138
3139 switch (attr->value_tag)
3140 {
3141 case IPP_TAG_INTEGER :
3142 case IPP_TAG_ENUM :
3143 snprintf(valptr, sizeof(value) - (size_t)(valptr - value), "%d", attr->values[i].integer);
3144 valptr += strlen(valptr);
3145 break;
3146
3147 case IPP_TAG_TEXT :
3148 case IPP_TAG_NAME :
3149 case IPP_TAG_KEYWORD :
3150 quote_string(attr->values[i].string.text, valptr, (size_t)(value + sizeof(value) - valptr));
3151 valptr += strlen(valptr);
3152 break;
3153
3154 default :
3155 /*
3156 * Unsupported value type...
3157 */
3158
3159 return;
3160 }
3161 }
3162
3163 *valptr = '\0';
3164
3165 cupsMutexLock(&report_mutex);
3166
3167 if ((cached = cupsGetOption(attr->name, num_attr_cache,
3168 attr_cache)) == NULL || strcmp(cached, value))
3169 {
3170 /*
3171 * Tell the scheduler about the new values...
3172 */
3173
3174 num_attr_cache = cupsAddOption(attr->name, value, num_attr_cache,
3175 &attr_cache);
3176 fprintf(stderr, "ATTR: %s=%s\n", attr->name, value);
3177 }
3178
3179 cupsMutexUnlock(&report_mutex);
3180 }
3181
3182
3183 /*
3184 * 'report_printer_state()' - Report the printer state.
3185 */
3186
3187 static void
3188 report_printer_state(ipp_t *ipp) /* I - IPP response */
3189 {
3190 ipp_attribute_t *pa, /* printer-alert */
3191 *pam, /* printer-alert-message */
3192 *pmja, /* printer-mandatory-job-attributes */
3193 *psm, /* printer-state-message */
3194 *reasons, /* printer-state-reasons */
3195 *marker; /* marker-* attributes */
3196 char value[1024], /* State/message string */
3197 *valptr; /* Pointer into string */
3198 static int ipp_supplies = -1;
3199 /* Report supply levels? */
3200
3201
3202 /*
3203 * Report alerts and messages...
3204 */
3205
3206 if ((pa = ippFindAttribute(ipp, "printer-alert", IPP_TAG_STRING)) != NULL)
3207 report_attr(pa);
3208
3209 if ((pam = ippFindAttribute(ipp, "printer-alert-message",
3210 IPP_TAG_TEXT)) != NULL)
3211 report_attr(pam);
3212
3213 if ((pmja = ippFindAttribute(ipp, "printer-mandatory-job-attributes", IPP_TAG_KEYWORD)) != NULL)
3214 {
3215 int i, /* Looping var */
3216 count = ippGetCount(pmja); /* Number of values */
3217
3218 for (i = 0, valptr = value; i < count; i ++, valptr += strlen(valptr))
3219 {
3220 if (i)
3221 snprintf(valptr, sizeof(value) - (size_t)(valptr - value), " %s", ippGetString(pmja, i, NULL));
3222 else
3223 cupsCopyString(value, ippGetString(pmja, i, NULL), sizeof(value));
3224 }
3225
3226 if (strcmp(value, mandatory_attrs))
3227 {
3228 cupsCopyString(mandatory_attrs, value, sizeof(mandatory_attrs));
3229 fprintf(stderr, "PPD: cupsMandatory=\"%s\"\n", value);
3230 }
3231 }
3232
3233 if ((psm = ippFindAttribute(ipp, "printer-state-message",
3234 IPP_TAG_TEXT)) != NULL)
3235 {
3236 char *ptr; /* Pointer into message */
3237
3238
3239 cupsCopyString(value, "INFO: ", sizeof(value));
3240 for (ptr = psm->values[0].string.text, valptr = value + 6;
3241 *ptr && valptr < (value + sizeof(value) - 6);
3242 ptr ++)
3243 {
3244 if (*ptr < ' ' && *ptr > 0 && *ptr != '\t')
3245 {
3246 /*
3247 * Substitute "<XX>" for the control character...
3248 */
3249
3250 snprintf(valptr, sizeof(value) - (size_t)(valptr - value), "<%02X>", *ptr);
3251 valptr += 4;
3252 }
3253 else
3254 *valptr++ = *ptr;
3255 }
3256
3257 *valptr++ = '\n';
3258 *valptr = '\0';
3259
3260 fputs(value, stderr);
3261 }
3262
3263 /*
3264 * Now report printer-state-reasons, filtering out some of the reasons we never
3265 * want to set...
3266 */
3267
3268 if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
3269 IPP_TAG_KEYWORD)) == NULL)
3270 return;
3271
3272 update_reasons(reasons, NULL);
3273
3274 /*
3275 * Relay the current marker-* attribute values...
3276 */
3277
3278 if (ipp_supplies < 0)
3279 {
3280 ppd_file_t *ppd; /* PPD file */
3281 ppd_attr_t *ppdattr; /* Attribute in PPD file */
3282
3283 if ((ppd = ppdOpenFile(getenv("PPD"))) != NULL &&
3284 (ppdattr = ppdFindAttr(ppd, "cupsIPPSupplies", NULL)) != NULL &&
3285 ppdattr->value && _cups_strcasecmp(ppdattr->value, "true"))
3286 ipp_supplies = 0;
3287 else
3288 ipp_supplies = 1;
3289
3290 ppdClose(ppd);
3291 }
3292
3293 if (ipp_supplies > 0)
3294 {
3295 if ((marker = ippFindAttribute(ipp, "marker-colors", IPP_TAG_NAME)) != NULL)
3296 report_attr(marker);
3297 if ((marker = ippFindAttribute(ipp, "marker-high-levels",
3298 IPP_TAG_INTEGER)) != NULL)
3299 report_attr(marker);
3300 if ((marker = ippFindAttribute(ipp, "marker-levels",
3301 IPP_TAG_INTEGER)) != NULL)
3302 report_attr(marker);
3303 if ((marker = ippFindAttribute(ipp, "marker-low-levels",
3304 IPP_TAG_INTEGER)) != NULL)
3305 report_attr(marker);
3306 if ((marker = ippFindAttribute(ipp, "marker-message",
3307 IPP_TAG_TEXT)) != NULL)
3308 report_attr(marker);
3309 if ((marker = ippFindAttribute(ipp, "marker-names", IPP_TAG_NAME)) != NULL)
3310 report_attr(marker);
3311 if ((marker = ippFindAttribute(ipp, "marker-types",
3312 IPP_TAG_KEYWORD)) != NULL)
3313 report_attr(marker);
3314 }
3315 }
3316
3317
3318 #if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
3319 /*
3320 * 'run_as_user()' - Run the IPP backend as the printing user.
3321 *
3322 * This function uses an XPC-based user agent to run the backend as the printing
3323 * user. We need to do this in order to have access to the user's Kerberos
3324 * credentials.
3325 */
3326
3327 static int /* O - Exit status */
3328 run_as_user(char *argv[], /* I - Command-line arguments */
3329 uid_t uid, /* I - User ID */
3330 const char *device_uri, /* I - Device URI */
3331 int fd) /* I - File to print */
3332 {
3333 const char *auth_negotiate,/* AUTH_NEGOTIATE env var */
3334 *content_type, /* [FINAL_]CONTENT_TYPE env vars */
3335 *auth_info_required; /* New auth-info-required value */
3336 xpc_connection_t conn; /* Connection to XPC service */
3337 xpc_object_t request; /* Request message dictionary */
3338 __block xpc_object_t response; /* Response message dictionary */
3339 int status = CUPS_BACKEND_FAILED;
3340 /* Status of request */
3341
3342
3343 fprintf(stderr, "DEBUG: Running IPP backend as UID %u.\n", (unsigned)uid);
3344
3345 /*
3346 * Connect to the user agent for the specified UID...
3347 */
3348
3349 conn = xpc_connection_create_mach_service(kPMPrintUIToolAgent,
3350 dispatch_get_global_queue(0, 0), 0);
3351 if (!conn)
3352 {
3353 _cupsLangPrintFilter(stderr, "ERROR",
3354 _("Unable to start backend process."));
3355 fputs("DEBUG: Unable to create connection to agent.\n", stderr);
3356 goto cleanup;
3357 }
3358
3359 xpc_connection_set_event_handler(conn,
3360 ^(xpc_object_t event)
3361 {
3362 xpc_type_t messageType = xpc_get_type(event);
3363
3364 if (messageType == XPC_TYPE_ERROR)
3365 {
3366 if (event == XPC_ERROR_CONNECTION_INTERRUPTED)
3367 fprintf(stderr, "DEBUG: Interrupted connection to service %s.\n",
3368 xpc_connection_get_name(conn));
3369 else if (event == XPC_ERROR_CONNECTION_INVALID)
3370 fprintf(stderr, "DEBUG: Connection invalid for service %s.\n",
3371 xpc_connection_get_name(conn));
3372 else
3373 fprintf(stderr, "DEBUG: Unexpected error for service %s: %s\n",
3374 xpc_connection_get_name(conn),
3375 xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
3376 }
3377 });
3378 xpc_connection_set_target_uid(conn, uid);
3379 xpc_connection_resume(conn);
3380
3381 /*
3382 * Try starting the backend...
3383 */
3384
3385 request = xpc_dictionary_create_empty();
3386 xpc_dictionary_set_int64(request, "command", kPMStartJob);
3387 xpc_dictionary_set_string(request, "device-uri", device_uri);
3388 xpc_dictionary_set_string(request, "job-id", argv[1]);
3389 xpc_dictionary_set_string(request, "user", argv[2]);
3390 xpc_dictionary_set_string(request, "title", argv[3]);
3391 xpc_dictionary_set_string(request, "copies", argv[4]);
3392 xpc_dictionary_set_string(request, "options", argv[5]);
3393 if ((auth_info_required = getenv("AUTH_INFO_REQUIRED")) != NULL)
3394 xpc_dictionary_set_string(request, "auth-info-required",
3395 auth_info_required);
3396 if ((auth_negotiate = getenv("AUTH_NEGOTIATE")) != NULL)
3397 xpc_dictionary_set_string(request, "auth-negotiate", auth_negotiate);
3398 if ((content_type = getenv("CONTENT_TYPE")) != NULL)
3399 xpc_dictionary_set_string(request, "content-type", content_type);
3400 if ((content_type = getenv("FINAL_CONTENT_TYPE")) != NULL)
3401 xpc_dictionary_set_string(request, "final-content-type", content_type);
3402 xpc_dictionary_set_fd(request, "stdin", fd);
3403 xpc_dictionary_set_fd(request, "stderr", 2);
3404 xpc_dictionary_set_fd(request, "side-channel", CUPS_SC_FD);
3405
3406 response = xpc_connection_send_message_with_reply_sync(conn, request);
3407
3408 xpc_release(request);
3409
3410 if (response)
3411 {
3412 child_pid = (pid_t)xpc_dictionary_get_int64(response, "child-pid");
3413
3414 xpc_release(response);
3415
3416 if (child_pid)
3417 fprintf(stderr, "DEBUG: Child PID=%d.\n", (int)child_pid);
3418 else
3419 {
3420 _cupsLangPrintFilter(stderr, "ERROR",
3421 _("Unable to start backend process."));
3422 fputs("DEBUG: No child PID.\n", stderr);
3423 goto cleanup;
3424 }
3425 }
3426 else
3427 {
3428 _cupsLangPrintFilter(stderr, "ERROR",
3429 _("Unable to start backend process."));
3430 fputs("DEBUG: No reply from agent.\n", stderr);
3431 goto cleanup;
3432 }
3433
3434 /*
3435 * Then wait for the backend to finish...
3436 */
3437
3438 request = xpc_dictionary_create_empty();
3439 xpc_dictionary_set_int64(request, "command", kPMWaitForJob);
3440 xpc_dictionary_set_fd(request, "stderr", 2);
3441
3442 response = xpc_connection_send_message_with_reply_sync(conn, request);
3443 xpc_release(request);
3444
3445 if (response)
3446 {
3447 status = (int)xpc_dictionary_get_int64(response, "status");
3448
3449 if (status == SIGTERM || status == SIGKILL || status == SIGPIPE)
3450 {
3451 fprintf(stderr, "DEBUG: Child terminated on signal %d.\n", status);
3452 status = CUPS_BACKEND_FAILED;
3453 }
3454 else if (WIFSIGNALED(status))
3455 {
3456 fprintf(stderr, "DEBUG: Child crashed on signal %d.\n", status);
3457 status = CUPS_BACKEND_STOP;
3458 }
3459 else if (WIFEXITED(status))
3460 {
3461 status = WEXITSTATUS(status);
3462 fprintf(stderr, "DEBUG: Child exited with status %d.\n", status);
3463 }
3464
3465 xpc_release(response);
3466 }
3467 else
3468 _cupsLangPrintFilter(stderr, "ERROR",
3469 _("Unable to get backend exit status."));
3470
3471 cleanup:
3472
3473 if (conn)
3474 {
3475 xpc_connection_cancel(conn);
3476 xpc_release(conn);
3477 }
3478
3479 return (status);
3480 }
3481 #endif /* HAVE_GSSAPI && HAVE_XPC */
3482
3483
3484 /*
3485 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
3486 */
3487
3488 static void
3489 sigterm_handler(int sig) /* I - Signal */
3490 {
3491 (void)sig; /* remove compiler warnings... */
3492
3493 backendMessage("DEBUG: Got SIGTERM.\n");
3494
3495 #if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
3496 if (child_pid)
3497 {
3498 kill(child_pid, sig);
3499 child_pid = 0;
3500 }
3501 #endif /* HAVE_GSSAPI && HAVE_XPC */
3502
3503 if (!job_canceled)
3504 {
3505 /*
3506 * Flag that the job should be canceled...
3507 */
3508
3509 backendMessage("DEBUG: sigterm_handler: job_canceled = 1.\n");
3510
3511 job_canceled = 1;
3512 return;
3513 }
3514
3515 /*
3516 * The scheduler already tried to cancel us once, now just terminate
3517 * after removing our temp file!
3518 */
3519
3520 if (tmpfilename[0])
3521 unlink(tmpfilename);
3522
3523 _exit(1);
3524 }
3525
3526
3527 /*
3528 * 'timeout_cb()' - Handle HTTP timeouts.
3529 */
3530
3531 static int /* O - 1 to continue, 0 to cancel */
3532 timeout_cb(http_t *http, /* I - Connection to server (unused) */
3533 void *user_data) /* I - User data (unused) */
3534 {
3535 (void)http;
3536 (void)user_data;
3537
3538 return (!job_canceled);
3539 }
3540
3541
3542 /*
3543 * 'update_reasons()' - Update the printer-state-reasons values.
3544 */
3545
3546 static void
3547 update_reasons(ipp_attribute_t *attr, /* I - printer-state-reasons or NULL */
3548 const char *s) /* I - STATE: string or NULL */
3549 {
3550 char op; /* Add (+), remove (-), replace (\0) */
3551 cups_array_t *new_reasons; /* New reasons array */
3552 char *reason, /* Current reason */
3553 add[2048], /* Reasons added string */
3554 *addptr, /* Pointer into add string */
3555 rem[2048], /* Reasons removed string */
3556 *remptr; /* Pointer into remove string */
3557 const char *addprefix, /* Current add string prefix */
3558 *remprefix; /* Current remove string prefix */
3559
3560
3561 fprintf(stderr, "DEBUG: update_reasons(attr=%d(%s%s), s=\"%s\")\n",
3562 attr ? attr->num_values : 0, attr ? attr->values[0].string.text : "",
3563 attr && attr->num_values > 1 ? ",..." : "", s ? s : "(null)");
3564
3565 /*
3566 * Create an array of new reason keyword strings...
3567 */
3568
3569 if (attr)
3570 {
3571 int i; /* Looping var */
3572
3573 new_reasons = cupsArrayNew((cups_array_func_t)_cupsArrayStrcmp, NULL);
3574 op = '\0';
3575
3576 for (i = 0; i < attr->num_values; i ++)
3577 {
3578 reason = attr->values[i].string.text;
3579
3580 if (strcmp(reason, "none") &&
3581 strcmp(reason, "none-report") &&
3582 strcmp(reason, "paused") &&
3583 strncmp(reason, "spool-area-full", 15) &&
3584 strcmp(reason, "com.apple.print.recoverable-warning") &&
3585 strncmp(reason, "cups-", 5))
3586 cupsArrayAdd(new_reasons, reason);
3587 }
3588 }
3589 else if (s)
3590 {
3591 if (*s == '+' || *s == '-')
3592 op = *s++;
3593 else
3594 op = '\0';
3595
3596 new_reasons = cupsArrayNewStrings(s, ',');
3597 }
3598 else
3599 return;
3600
3601 /*
3602 * Compute the changes...
3603 */
3604
3605 add[0] = '\0';
3606 addprefix = "STATE: +";
3607 addptr = add;
3608 rem[0] = '\0';
3609 remprefix = "STATE: -";
3610 remptr = rem;
3611
3612 cupsMutexLock(&report_mutex);
3613
3614 fprintf(stderr, "DEBUG2: op='%c', new_reasons=%d, state_reasons=%d\n",
3615 op ? op : ' ', cupsArrayCount(new_reasons),
3616 cupsArrayCount(state_reasons));
3617
3618 if (op == '+')
3619 {
3620 /*
3621 * Add reasons...
3622 */
3623
3624 for (reason = (char *)cupsArrayFirst(new_reasons);
3625 reason;
3626 reason = (char *)cupsArrayNext(new_reasons))
3627 {
3628 if (!cupsArrayFind(state_reasons, reason))
3629 {
3630 if (!strncmp(reason, "cups-remote-", 12))
3631 {
3632 /*
3633 * If we are setting cups-remote-xxx, remove all other cups-remote-xxx
3634 * keywords...
3635 */
3636
3637 char *temp; /* Current reason in state_reasons */
3638
3639 cupsArraySave(state_reasons);
3640
3641 for (temp = (char *)cupsArrayFirst(state_reasons);
3642 temp;
3643 temp = (char *)cupsArrayNext(state_reasons))
3644 if (!strncmp(temp, "cups-remote-", 12))
3645 {
3646 snprintf(remptr, sizeof(rem) - (size_t)(remptr - rem), "%s%s", remprefix, temp);
3647 remptr += strlen(remptr);
3648 remprefix = ",";
3649
3650 cupsArrayRemove(state_reasons, temp);
3651 break;
3652 }
3653
3654 cupsArrayRestore(state_reasons);
3655 }
3656
3657 cupsArrayAdd(state_reasons, reason);
3658
3659 snprintf(addptr, sizeof(add) - (size_t)(addptr - add), "%s%s", addprefix, reason);
3660 addptr += strlen(addptr);
3661 addprefix = ",";
3662 }
3663 }
3664 }
3665 else if (op == '-')
3666 {
3667 /*
3668 * Remove reasons...
3669 */
3670
3671 for (reason = (char *)cupsArrayFirst(new_reasons);
3672 reason;
3673 reason = (char *)cupsArrayNext(new_reasons))
3674 {
3675 if (cupsArrayFind(state_reasons, reason))
3676 {
3677 snprintf(remptr, sizeof(rem) - (size_t)(remptr - rem), "%s%s", remprefix, reason);
3678 remptr += strlen(remptr);
3679 remprefix = ",";
3680
3681 cupsArrayRemove(state_reasons, reason);
3682 }
3683 }
3684 }
3685 else
3686 {
3687 /*
3688 * Replace reasons...
3689 */
3690
3691 for (reason = (char *)cupsArrayFirst(state_reasons);
3692 reason;
3693 reason = (char *)cupsArrayNext(state_reasons))
3694 {
3695 if (strncmp(reason, "cups-", 5) && !cupsArrayFind(new_reasons, reason))
3696 {
3697 snprintf(remptr, sizeof(rem) - (size_t)(remptr - rem), "%s%s", remprefix, reason);
3698 remptr += strlen(remptr);
3699 remprefix = ",";
3700
3701 cupsArrayRemove(state_reasons, reason);
3702 }
3703 }
3704
3705 for (reason = (char *)cupsArrayFirst(new_reasons);
3706 reason;
3707 reason = (char *)cupsArrayNext(new_reasons))
3708 {
3709 if (!cupsArrayFind(state_reasons, reason))
3710 {
3711 cupsArrayAdd(state_reasons, reason);
3712
3713 snprintf(addptr, sizeof(add) - (size_t)(addptr - add), "%s%s", addprefix, reason);
3714 addptr += strlen(addptr);
3715 addprefix = ",";
3716 }
3717 }
3718 }
3719
3720 cupsArrayDelete(new_reasons);
3721
3722 cupsMutexUnlock(&report_mutex);
3723
3724 /*
3725 * Report changes and return...
3726 */
3727
3728 if (add[0] && rem[0])
3729 fprintf(stderr, "%s\n%s\n", add, rem);
3730 else if (add[0])
3731 fprintf(stderr, "%s\n", add);
3732 else if (rem[0])
3733 fprintf(stderr, "%s\n", rem);
3734 }