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