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