]> git.ipfire.org Git - thirdparty/cups.git/blob - backend/ipp.c
The IPP backend could get into an infinite loop for certain errors, causing a
[thirdparty/cups.git] / backend / ipp.c
1 /*
2 * IPP backend for CUPS.
3 *
4 * Copyright 2007-2017 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_OP_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 else if (job_auth == NULL && ipp_status > IPP_BAD_REQUEST)
1537 goto cleanup;
1538 }
1539
1540 /*
1541 * Then issue the print-job request...
1542 */
1543
1544 job_id = 0;
1545
1546 while (!job_canceled && copies_remaining > 0)
1547 {
1548 /*
1549 * Check for side-channel requests...
1550 */
1551
1552 backendCheckSideChannel(snmp_fd, http->hostaddr);
1553
1554 /*
1555 * Build the IPP job creation request...
1556 */
1557
1558 if (job_canceled)
1559 break;
1560
1561 request = new_request((num_files > 1 || create_job) ? IPP_CREATE_JOB :
1562 IPP_PRINT_JOB,
1563 version, uri, argv[2], monitor.job_name, num_options,
1564 options, compression, copies_sup ? copies : 1,
1565 document_format, pc, ppd, media_col_sup,
1566 doc_handling_sup, print_color_mode_sup);
1567
1568 /*
1569 * Do the request...
1570 */
1571
1572 if (num_files > 1 || create_job)
1573 response = cupsDoRequest(http, request, resource);
1574 else
1575 {
1576 size_t length = 0; /* Length of request */
1577
1578 if (compatsize > 0)
1579 {
1580 fputs("DEBUG: Sending file using HTTP/1.0 Content-Length...\n", stderr);
1581 length = ippLength(request) + (size_t)compatsize;
1582 }
1583 else
1584 fputs("DEBUG: Sending file using HTTP/1.1 chunking...\n", stderr);
1585
1586 http_status = cupsSendRequest(http, request, resource, length);
1587 if (http_status == HTTP_CONTINUE && request->state == IPP_DATA)
1588 {
1589 if (compression && strcmp(compression, "none"))
1590 httpSetField(http, HTTP_FIELD_CONTENT_ENCODING, compression);
1591
1592 if (num_files == 1)
1593 {
1594 if ((fd = open(files[0], O_RDONLY)) < 0)
1595 {
1596 _cupsLangPrintError("ERROR", _("Unable to open print file"));
1597 return (CUPS_BACKEND_FAILED);
1598 }
1599 }
1600 else
1601 {
1602 fd = 0;
1603 http_status = cupsWriteRequestData(http, buffer, (size_t)bytes);
1604 }
1605
1606 while (http_status == HTTP_CONTINUE &&
1607 (!job_canceled || compatsize > 0))
1608 {
1609 /*
1610 * Check for side-channel requests and more print data...
1611 */
1612
1613 FD_ZERO(&input);
1614 FD_SET(fd, &input);
1615 FD_SET(snmp_fd, &input);
1616 FD_SET(CUPS_SC_FD, &input);
1617
1618 while (select(fd > snmp_fd ? fd + 1 : snmp_fd + 1, &input, NULL, NULL,
1619 NULL) <= 0 && !job_canceled);
1620
1621 if (FD_ISSET(snmp_fd, &input))
1622 backendCheckSideChannel(snmp_fd, http->hostaddr);
1623
1624 if (FD_ISSET(fd, &input))
1625 {
1626 if ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
1627 {
1628 fprintf(stderr, "DEBUG: Read %d bytes...\n", (int)bytes);
1629
1630 if ((http_status = cupsWriteRequestData(http, buffer, (size_t)bytes))
1631 != HTTP_CONTINUE)
1632 break;
1633 }
1634 else if (bytes == 0 || (errno != EINTR && errno != EAGAIN))
1635 break;
1636 }
1637 }
1638
1639 if (http_status == HTTP_ERROR)
1640 fprintf(stderr, "DEBUG: Error writing document data for "
1641 "Print-Job: %s\n", strerror(httpError(http)));
1642
1643 if (num_files == 1)
1644 close(fd);
1645 }
1646
1647 response = cupsGetResponse(http, resource);
1648 ippDelete(request);
1649 }
1650
1651 ipp_status = cupsLastError();
1652
1653 fprintf(stderr, "DEBUG: %s: %s (%s)\n",
1654 (num_files > 1 || create_job) ? "Create-Job" : "Print-Job",
1655 ippErrorString(ipp_status), cupsLastErrorString());
1656 debug_attributes(response);
1657
1658 if (ipp_status > IPP_OK_CONFLICT)
1659 {
1660 job_id = 0;
1661
1662 if (job_canceled)
1663 break;
1664
1665 if (ipp_status == IPP_STATUS_ERROR_SERVICE_UNAVAILABLE ||
1666 ipp_status == IPP_STATUS_ERROR_NOT_POSSIBLE ||
1667 ipp_status == IPP_STATUS_ERROR_BUSY)
1668 {
1669 _cupsLangPrintFilter(stderr, "INFO", _("The printer is in use."));
1670 sleep(10);
1671
1672 if (num_files == 0)
1673 {
1674 /*
1675 * We can't re-submit when we have no files to print, so exit
1676 * immediately with the right status code...
1677 */
1678
1679 goto cleanup;
1680 }
1681 }
1682 else if (ipp_status == IPP_STATUS_ERROR_JOB_CANCELED ||
1683 ipp_status == IPP_STATUS_ERROR_NOT_AUTHORIZED ||
1684 ipp_status == IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES ||
1685 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED ||
1686 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED ||
1687 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED ||
1688 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED)
1689 goto cleanup;
1690 else
1691 {
1692 /*
1693 * Update auth-info-required as needed...
1694 */
1695
1696 _cupsLangPrintFilter(stderr, "ERROR",
1697 _("Print job was not accepted."));
1698
1699 if (ipp_status == IPP_STATUS_ERROR_FORBIDDEN ||
1700 ipp_status == IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED)
1701 {
1702 const char *www_auth = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE);
1703 /* WWW-Authenticate field value */
1704
1705 if (!strncmp(www_auth, "Negotiate", 9))
1706 auth_info_required = "negotiate";
1707 else if (www_auth[0])
1708 auth_info_required = "username,password";
1709 }
1710 else if (ipp_status == IPP_REQUEST_VALUE)
1711 {
1712 /*
1713 * Print file is too large, abort this job...
1714 */
1715
1716 goto cleanup;
1717 }
1718 else
1719 sleep(10);
1720
1721 if (num_files == 0)
1722 {
1723 /*
1724 * We can't re-submit when we have no files to print, so exit
1725 * immediately with the right status code...
1726 */
1727
1728 goto cleanup;
1729 }
1730 }
1731 }
1732 else if ((job_id_attr = ippFindAttribute(response, "job-id",
1733 IPP_TAG_INTEGER)) == NULL)
1734 {
1735 fputs("DEBUG: Print job accepted - job ID unknown.\n", stderr);
1736 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1737 "cups-ipp-missing-job-id");
1738 job_id = 0;
1739 }
1740 else
1741 {
1742 password_tries = 0;
1743 monitor.job_id = job_id = job_id_attr->values[0].integer;
1744 fprintf(stderr, "DEBUG: Print job accepted - job ID %d.\n", job_id);
1745 }
1746
1747 ippDelete(response);
1748
1749 if (job_canceled)
1750 break;
1751
1752 if (job_id && (num_files > 1 || create_job))
1753 {
1754 for (i = 0; num_files == 0 || i < num_files; i ++)
1755 {
1756 /*
1757 * Check for side-channel requests...
1758 */
1759
1760 backendCheckSideChannel(snmp_fd, http->hostaddr);
1761
1762 /*
1763 * Send the next file in the job...
1764 */
1765
1766 request = ippNewRequest(IPP_SEND_DOCUMENT);
1767 ippSetVersion(request, version / 10, version % 10);
1768
1769 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1770 NULL, uri);
1771
1772 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1773 job_id);
1774
1775 if (argv[2][0])
1776 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1777 "requesting-user-name", NULL, argv[2]);
1778
1779 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document",
1780 (i + 1) >= num_files);
1781
1782 if (document_format)
1783 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
1784 "document-format", NULL, document_format);
1785
1786 if (compression)
1787 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1788 "compression", NULL, compression);
1789
1790 fprintf(stderr, "DEBUG: Sending file %d using chunking...\n", i + 1);
1791 fprintf(stderr, "DEBUG: IPP/%d.%d %s #%d\n", version / 10, version % 10, ippOpString(ippGetOperation(request)), ippGetRequestId(request));
1792 debug_attributes(request);
1793
1794 http_status = cupsSendRequest(http, request, resource, 0);
1795 if (http_status == HTTP_CONTINUE && request->state == IPP_DATA)
1796 {
1797 if (compression && strcmp(compression, "none"))
1798 httpSetField(http, HTTP_FIELD_CONTENT_ENCODING, compression);
1799
1800 if (num_files == 0)
1801 {
1802 fd = 0;
1803 http_status = cupsWriteRequestData(http, buffer, (size_t)bytes);
1804 }
1805 else
1806 {
1807 if ((fd = open(files[i], O_RDONLY)) < 0)
1808 {
1809 _cupsLangPrintError("ERROR", _("Unable to open print file"));
1810 return (CUPS_BACKEND_FAILED);
1811 }
1812 }
1813 }
1814 else
1815 fd = -1;
1816
1817 if (fd >= 0)
1818 {
1819 while (!job_canceled && http_status == HTTP_CONTINUE &&
1820 (bytes = read(fd, buffer, sizeof(buffer))) > 0)
1821 {
1822 if ((http_status = cupsWriteRequestData(http, buffer, (size_t)bytes))
1823 != HTTP_CONTINUE)
1824 break;
1825 else
1826 {
1827 /*
1828 * Check for side-channel requests...
1829 */
1830
1831 backendCheckSideChannel(snmp_fd, http->hostaddr);
1832 }
1833 }
1834
1835 if (fd > 0)
1836 close(fd);
1837 }
1838
1839 if (http_status == HTTP_ERROR)
1840 fprintf(stderr, "DEBUG: Error writing document data for "
1841 "Send-Document: %s\n", strerror(httpError(http)));
1842
1843 response = cupsGetResponse(http, resource);
1844 ippDelete(request);
1845
1846 fprintf(stderr, "DEBUG: Send-Document: %s (%s)\n",
1847 ippErrorString(cupsLastError()), cupsLastErrorString());
1848 debug_attributes(response);
1849 ippDelete(response);
1850
1851 if (cupsLastError() > IPP_OK_CONFLICT && !job_canceled)
1852 {
1853 ipp_status = cupsLastError();
1854
1855 _cupsLangPrintFilter(stderr, "ERROR",
1856 _("Unable to add document to print job."));
1857 break;
1858 }
1859 else
1860 {
1861 password_tries = 0;
1862
1863 if (num_files == 0 || fd < 0)
1864 break;
1865 }
1866 }
1867 }
1868
1869 if (job_canceled)
1870 break;
1871
1872 if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
1873 {
1874 fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
1875 copies_remaining --;
1876 }
1877 else if ((ipp_status == IPP_STATUS_ERROR_DOCUMENT_FORMAT_ERROR || ipp_status == IPP_STATUS_ERROR_DOCUMENT_UNPRINTABLE) &&
1878 argc == 6 &&
1879 document_format && strcmp(document_format, "image/pwg-raster") && strcmp(document_format, "image/urf"))
1880 {
1881 /*
1882 * Need to reprocess the job as raster...
1883 */
1884
1885 fputs("JOBSTATE: cups-retry-as-raster\n", stderr);
1886 if (job_id > 0)
1887 cancel_job(http, uri, job_id, resource, argv[2], version);
1888
1889 goto cleanup;
1890 }
1891 else if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1892 ipp_status == IPP_NOT_POSSIBLE ||
1893 ipp_status == IPP_PRINTER_BUSY)
1894 {
1895 if (argc == 6)
1896 {
1897 /*
1898 * Need to reprocess the entire job; if we have a job ID, cancel the
1899 * job first...
1900 */
1901
1902 if (job_id > 0)
1903 cancel_job(http, uri, job_id, resource, argv[2], version);
1904
1905 goto cleanup;
1906 }
1907 continue;
1908 }
1909 else if (ipp_status == IPP_REQUEST_VALUE ||
1910 ipp_status == IPP_ERROR_JOB_CANCELED ||
1911 ipp_status == IPP_NOT_AUTHORIZED ||
1912 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED ||
1913 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED ||
1914 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED ||
1915 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED ||
1916 ipp_status == IPP_INTERNAL_ERROR)
1917 {
1918 /*
1919 * Print file is too large, job was canceled, we need new
1920 * authentication data, or we had some sort of error...
1921 */
1922
1923 goto cleanup;
1924 }
1925 else if (ipp_status == IPP_STATUS_ERROR_CUPS_UPGRADE_REQUIRED)
1926 {
1927 /*
1928 * Server is configured incorrectly; the policy for Create-Job and
1929 * Send-Document has to be the same (auth or no auth, encryption or
1930 * no encryption). Force the queue to stop since printing will never
1931 * work.
1932 */
1933
1934 fputs("DEBUG: The server or printer is configured incorrectly.\n",
1935 stderr);
1936 fputs("DEBUG: The policy for Create-Job and Send-Document must have the "
1937 "same authentication and encryption requirements.\n", stderr);
1938
1939 ipp_status = IPP_STATUS_ERROR_INTERNAL;
1940
1941 if (job_id > 0)
1942 cancel_job(http, uri, job_id, resource, argv[2], version);
1943
1944 goto cleanup;
1945 }
1946 else if (ipp_status == IPP_NOT_FOUND)
1947 {
1948 /*
1949 * Printer does not actually implement support for Create-Job/
1950 * Send-Document, so log the conformance issue and stop the printer.
1951 */
1952
1953 fputs("DEBUG: This printer claims to support Create-Job and "
1954 "Send-Document, but those operations failed.\n", stderr);
1955 fputs("DEBUG: Add '?version=1.0' to the device URI to use legacy "
1956 "compatibility mode.\n", stderr);
1957 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1958 "cups-ipp-missing-send-document");
1959
1960 ipp_status = IPP_INTERNAL_ERROR; /* Force queue to stop */
1961
1962 goto cleanup;
1963 }
1964 else
1965 copies_remaining --;
1966
1967 /*
1968 * Wait for the job to complete...
1969 */
1970
1971 if (!job_id || !waitjob || !get_job_attrs)
1972 continue;
1973
1974 fputs("STATE: +cups-waiting-for-job-completed\n", stderr);
1975
1976 _cupsLangPrintFilter(stderr, "INFO", _("Waiting for job to complete."));
1977
1978 for (delay = _cupsNextDelay(0, &prev_delay), waittime = time(NULL) + 30; !job_canceled;)
1979 {
1980 /*
1981 * Check for side-channel requests...
1982 */
1983
1984 backendCheckSideChannel(snmp_fd, http->hostaddr);
1985
1986 /*
1987 * Check printer state...
1988 */
1989
1990 check_printer_state(http, uri, resource, argv[2], version);
1991
1992 if (cupsLastError() <= IPP_OK_CONFLICT)
1993 password_tries = 0;
1994
1995 /*
1996 * Build an IPP_GET_JOB_ATTRIBUTES request...
1997 */
1998
1999 request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
2000 ippSetVersion(request, version / 10, version % 10);
2001
2002 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2003 NULL, uri);
2004
2005 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
2006 job_id);
2007
2008 if (argv[2][0])
2009 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
2010 "requesting-user-name", NULL, argv[2]);
2011
2012 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2013 "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
2014 NULL, jattrs);
2015
2016 fprintf(stderr, "DEBUG: IPP/%d.%d %s #%d\n", version / 10, version % 10, ippOpString(ippGetOperation(request)), ippGetRequestId(request));
2017 debug_attributes(request);
2018
2019 /*
2020 * Do the request...
2021 */
2022
2023 httpReconnect(http);
2024 response = cupsDoRequest(http, request, resource);
2025 ipp_status = cupsLastError();
2026
2027 if (ipp_status == IPP_NOT_FOUND || ipp_status == IPP_NOT_POSSIBLE)
2028 {
2029 /*
2030 * Job has gone away and/or the server has no job history...
2031 */
2032
2033 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
2034 "cups-ipp-missing-job-history");
2035 ippDelete(response);
2036
2037 ipp_status = IPP_OK;
2038 break;
2039 }
2040
2041 fprintf(stderr, "DEBUG: Get-Job-Attributes: %s (%s)\n",
2042 ippErrorString(ipp_status), cupsLastErrorString());
2043 debug_attributes(response);
2044
2045 if (ipp_status <= IPP_OK_CONFLICT)
2046 password_tries = 0;
2047 else
2048 {
2049 if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
2050 ipp_status != IPP_PRINTER_BUSY)
2051 {
2052 ippDelete(response);
2053 ipp_status = IPP_OK;
2054 break;
2055 }
2056 else if (ipp_status == IPP_INTERNAL_ERROR)
2057 {
2058 waitjob_tries ++;
2059
2060 if (waitjob_tries > 4)
2061 {
2062 ippDelete(response);
2063 ipp_status = IPP_OK;
2064 break;
2065 }
2066 }
2067 }
2068
2069 if (response)
2070 {
2071 if ((job_state = ippFindAttribute(response, "job-state",
2072 IPP_TAG_ENUM)) != NULL)
2073 {
2074 /*
2075 * Reflect the remote job state in the local queue...
2076 */
2077
2078 if (cups_version &&
2079 job_state->values[0].integer >= IPP_JOB_PENDING &&
2080 job_state->values[0].integer <= IPP_JOB_COMPLETED)
2081 update_reasons(NULL,
2082 remote_job_states[job_state->values[0].integer -
2083 IPP_JOB_PENDING]);
2084
2085 if ((job_sheets = ippFindAttribute(response, "job-impressions-completed", IPP_TAG_INTEGER)) == NULL)
2086 job_sheets = ippFindAttribute(response, "job-media-sheets-completed", IPP_TAG_INTEGER);
2087
2088 if (job_sheets)
2089 fprintf(stderr, "PAGE: total %d\n",
2090 job_sheets->values[0].integer);
2091
2092 /*
2093 * Stop polling if the job is finished or pending-held for 30 seconds...
2094 */
2095
2096 if (job_state->values[0].integer > IPP_JSTATE_STOPPED ||
2097 (job_state->values[0].integer == IPP_JSTATE_HELD && time(NULL) > waittime))
2098 {
2099 ippDelete(response);
2100 break;
2101 }
2102 }
2103 else if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
2104 ipp_status != IPP_NOT_POSSIBLE &&
2105 ipp_status != IPP_PRINTER_BUSY)
2106 {
2107 /*
2108 * If the printer does not return a job-state attribute, it does not
2109 * conform to the IPP specification - break out immediately and fail
2110 * the job...
2111 */
2112
2113 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
2114 "cups-ipp-missing-job-state");
2115 ipp_status = IPP_INTERNAL_ERROR;
2116 break;
2117 }
2118 }
2119
2120 ippDelete(response);
2121
2122 /*
2123 * Wait before polling again...
2124 */
2125
2126 sleep((unsigned)delay);
2127
2128 delay = _cupsNextDelay(delay, &prev_delay);
2129 }
2130 }
2131
2132 /*
2133 * Cancel the job as needed...
2134 */
2135
2136 if (job_canceled > 0 && job_id > 0)
2137 {
2138 cancel_job(http, uri, job_id, resource, argv[2], version);
2139
2140 if (cupsLastError() > IPP_OK_CONFLICT)
2141 _cupsLangPrintFilter(stderr, "ERROR", _("Unable to cancel print job."));
2142 }
2143
2144 /*
2145 * Check the printer state and report it if necessary...
2146 */
2147
2148 check_printer_state(http, uri, resource, argv[2], version);
2149
2150 if (cupsLastError() <= IPP_OK_CONFLICT)
2151 password_tries = 0;
2152
2153 /*
2154 * Collect the final page count as needed...
2155 */
2156
2157 if (have_supplies &&
2158 !backendSNMPSupplies(snmp_fd, &(http->addrlist->addr), &page_count,
2159 NULL) &&
2160 page_count > start_count)
2161 fprintf(stderr, "PAGE: total %d\n", page_count - start_count);
2162
2163 #ifdef HAVE_GSSAPI
2164 /*
2165 * See if we used Kerberos at all...
2166 */
2167
2168 if (http->gssctx)
2169 auth_info_required = "negotiate";
2170 #endif /* HAVE_GSSAPI */
2171
2172 /*
2173 * Free memory...
2174 */
2175
2176 cleanup:
2177
2178 cupsFreeOptions(num_options, options);
2179 _ppdCacheDestroy(pc);
2180 ppdClose(ppd);
2181
2182 httpClose(http);
2183
2184 ippDelete(supported);
2185
2186 /*
2187 * Remove the temporary file(s) if necessary...
2188 */
2189
2190 if (tmpfilename[0])
2191 unlink(tmpfilename);
2192
2193 /*
2194 * Return the queue status...
2195 */
2196
2197 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
2198 ipp_status == IPP_AUTHENTICATION_CANCELED ||
2199 ipp_status <= IPP_OK_CONFLICT)
2200 fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
2201
2202 if (ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED)
2203 fputs("JOBSTATE: account-info-needed\n", stderr);
2204 else if (ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED)
2205 fputs("JOBSTATE: account-closed\n", stderr);
2206 else if (ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED)
2207 fputs("JOBSTATE: account-limit-reached\n", stderr);
2208 else if (ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED)
2209 fputs("JOBSTATE: account-authorization-failed\n", stderr);
2210
2211 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
2212 ipp_status == IPP_AUTHENTICATION_CANCELED)
2213 return (CUPS_BACKEND_AUTH_REQUIRED);
2214 else if (ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED ||
2215 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED ||
2216 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED ||
2217 ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED)
2218 return (CUPS_BACKEND_HOLD);
2219 else if (ipp_status == IPP_INTERNAL_ERROR)
2220 return (CUPS_BACKEND_STOP);
2221 else if (ipp_status == IPP_CONFLICT || ipp_status == IPP_STATUS_ERROR_REQUEST_ENTITY || ipp_status == IPP_STATUS_ERROR_REQUEST_VALUE)
2222 return (CUPS_BACKEND_FAILED);
2223 else if (ipp_status == IPP_REQUEST_VALUE ||
2224 ipp_status == IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES ||
2225 ipp_status == IPP_DOCUMENT_FORMAT || job_canceled < 0)
2226 {
2227 if (ipp_status == IPP_REQUEST_VALUE)
2228 _cupsLangPrintFilter(stderr, "ERROR", _("Print job too large."));
2229 else if (ipp_status == IPP_DOCUMENT_FORMAT)
2230 _cupsLangPrintFilter(stderr, "ERROR",
2231 _("Printer cannot print supplied content."));
2232 else if (ipp_status == IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES)
2233 _cupsLangPrintFilter(stderr, "ERROR",
2234 _("Printer cannot print with supplied options."));
2235 else
2236 _cupsLangPrintFilter(stderr, "ERROR", _("Print job canceled at printer."));
2237
2238 return (CUPS_BACKEND_CANCEL);
2239 }
2240 else if (ipp_status > IPP_OK_CONFLICT && ipp_status != IPP_ERROR_JOB_CANCELED)
2241 return (CUPS_BACKEND_RETRY_CURRENT);
2242 else
2243 return (CUPS_BACKEND_OK);
2244 }
2245
2246
2247 /*
2248 * 'cancel_job()' - Cancel a print job.
2249 */
2250
2251 static void
2252 cancel_job(http_t *http, /* I - HTTP connection */
2253 const char *uri, /* I - printer-uri */
2254 int id, /* I - job-id */
2255 const char *resource, /* I - Resource path */
2256 const char *user, /* I - requesting-user-name */
2257 int version) /* I - IPP version */
2258 {
2259 ipp_t *request; /* Cancel-Job request */
2260
2261
2262 _cupsLangPrintFilter(stderr, "INFO", _("Canceling print job."));
2263
2264 request = ippNewRequest(IPP_CANCEL_JOB);
2265 ippSetVersion(request, version / 10, version % 10);
2266
2267 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2268 NULL, uri);
2269 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
2270
2271 if (user && user[0])
2272 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
2273 "requesting-user-name", NULL, user);
2274
2275 /*
2276 * Do the request...
2277 */
2278
2279 ippDelete(cupsDoRequest(http, request, resource));
2280 }
2281
2282
2283 /*
2284 * 'check_printer_state()' - Check the printer state.
2285 */
2286
2287 static ipp_pstate_t /* O - Current printer-state */
2288 check_printer_state(
2289 http_t *http, /* I - HTTP connection */
2290 const char *uri, /* I - Printer URI */
2291 const char *resource, /* I - Resource path */
2292 const char *user, /* I - Username, if any */
2293 int version) /* I - IPP version */
2294 {
2295 ipp_t *request, /* IPP request */
2296 *response; /* IPP response */
2297 ipp_attribute_t *attr; /* Attribute in response */
2298 ipp_pstate_t printer_state = IPP_PRINTER_STOPPED;
2299 /* Current printer-state */
2300
2301
2302 /*
2303 * Send a Get-Printer-Attributes request and log the results...
2304 */
2305
2306 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
2307 ippSetVersion(request, version / 10, version % 10);
2308
2309 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2310 NULL, uri);
2311
2312 if (user && user[0])
2313 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
2314 "requesting-user-name", NULL, user);
2315
2316 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2317 "requested-attributes",
2318 (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
2319
2320 fprintf(stderr, "DEBUG: IPP/%d.%d %s #%d\n", version / 10, version % 10, ippOpString(ippGetOperation(request)), ippGetRequestId(request));
2321 debug_attributes(request);
2322
2323 if ((response = cupsDoRequest(http, request, resource)) != NULL)
2324 {
2325 report_printer_state(response);
2326
2327 if ((attr = ippFindAttribute(response, "printer-state",
2328 IPP_TAG_ENUM)) != NULL)
2329 printer_state = (ipp_pstate_t)attr->values[0].integer;
2330 }
2331
2332 fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
2333 ippErrorString(cupsLastError()), cupsLastErrorString());
2334 debug_attributes(response);
2335 ippDelete(response);
2336
2337 /*
2338 * Return the printer-state value...
2339 */
2340
2341 return (printer_state);
2342 }
2343
2344
2345 /*
2346 * 'debug_attributes()' - Print out the request or response attributes as DEBUG
2347 * messages...
2348 */
2349
2350 static void
2351 debug_attributes(ipp_t *ipp) /* I - Request or response message */
2352 {
2353 ipp_tag_t group; /* Current group */
2354 ipp_attribute_t *attr; /* Current attribute */
2355 char buffer[1024]; /* Value buffer */
2356
2357
2358 for (group = IPP_TAG_ZERO, attr = ippFirstAttribute(ipp);
2359 attr;
2360 attr = ippNextAttribute(ipp))
2361 {
2362 const char *name = ippGetName(attr);
2363
2364 if (!name)
2365 {
2366 group = IPP_TAG_ZERO;
2367 continue;
2368 }
2369
2370 if (group != ippGetGroupTag(attr))
2371 {
2372 group = ippGetGroupTag(attr);
2373 fprintf(stderr, "DEBUG: ---- %s ----\n", ippTagString(group));
2374 }
2375
2376 if (!strcmp(name, "job-password"))
2377 strlcpy(buffer, "---", sizeof(buffer));
2378 else
2379 ippAttributeString(attr, buffer, sizeof(buffer));
2380
2381 fprintf(stderr, "DEBUG: %s %s%s %s\n", name,
2382 ippGetCount(attr) > 1 ? "1setOf " : "",
2383 ippTagString(ippGetValueTag(attr)), buffer);
2384 }
2385
2386 fprintf(stderr, "DEBUG: ---- %s ----\n", ippTagString(IPP_TAG_END));
2387 }
2388
2389
2390 /*
2391 * 'monitor_printer()' - Monitor the printer state.
2392 */
2393
2394 static void * /* O - Thread exit code */
2395 monitor_printer(
2396 _cups_monitor_t *monitor) /* I - Monitoring data */
2397 {
2398 http_t *http; /* Connection to printer */
2399 ipp_t *request, /* IPP request */
2400 *response; /* IPP response */
2401 ipp_attribute_t *attr; /* Attribute in response */
2402 int delay, /* Current delay */
2403 prev_delay; /* Previous delay */
2404 ipp_op_t job_op; /* Operation to use */
2405 int job_id; /* Job ID */
2406 const char *job_name; /* Job name */
2407 ipp_jstate_t job_state; /* Job state */
2408 const char *job_user; /* Job originating user name */
2409 int password_tries = 0; /* Password tries */
2410
2411
2412 /*
2413 * Make a copy of the printer connection...
2414 */
2415
2416 http = httpConnect2(monitor->hostname, monitor->port, NULL, AF_UNSPEC,
2417 monitor->encryption, 1, 0, NULL);
2418 httpSetTimeout(http, 30.0, timeout_cb, NULL);
2419 if (username[0])
2420 cupsSetUser(username);
2421
2422 cupsSetPasswordCB2((cups_password_cb2_t)password_cb, &password_tries);
2423
2424 /*
2425 * Loop until the job is canceled, aborted, or completed.
2426 */
2427
2428 delay = _cupsNextDelay(0, &prev_delay);
2429
2430 monitor->job_reasons = 0;
2431
2432 while (monitor->job_state < IPP_JOB_CANCELED && !job_canceled)
2433 {
2434 /*
2435 * Reconnect to the printer as needed...
2436 */
2437
2438 if (httpGetFd(http) < 0)
2439 httpReconnect(http);
2440
2441 if (httpGetFd(http) >= 0)
2442 {
2443 /*
2444 * Connected, so check on the printer state...
2445 */
2446
2447 monitor->printer_state = check_printer_state(http, monitor->uri,
2448 monitor->resource,
2449 monitor->user,
2450 monitor->version);
2451 if (cupsLastError() <= IPP_OK_CONFLICT)
2452 password_tries = 0;
2453
2454 if (monitor->job_id == 0 && monitor->create_job)
2455 {
2456 /*
2457 * No job-id yet, so continue...
2458 */
2459
2460 goto monitor_sleep;
2461 }
2462
2463 /*
2464 * Check the status of the job itself...
2465 */
2466
2467 job_op = (monitor->job_id > 0 && monitor->get_job_attrs) ?
2468 IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS;
2469 request = ippNewRequest(job_op);
2470 ippSetVersion(request, monitor->version / 10, monitor->version % 10);
2471
2472 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2473 NULL, monitor->uri);
2474 if (job_op == IPP_GET_JOB_ATTRIBUTES)
2475 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
2476 monitor->job_id);
2477
2478 if (monitor->user && monitor->user[0])
2479 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
2480 "requesting-user-name", NULL, monitor->user);
2481
2482 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2483 "requested-attributes",
2484 (int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs);
2485
2486 /*
2487 * Do the request...
2488 */
2489
2490 response = cupsDoRequest(http, request, monitor->resource);
2491
2492 fprintf(stderr, "DEBUG: (monitor) %s: %s (%s)\n", ippOpString(job_op),
2493 ippErrorString(cupsLastError()), cupsLastErrorString());
2494
2495 if (cupsLastError() <= IPP_OK_CONFLICT)
2496 password_tries = 0;
2497
2498 if (job_op == IPP_GET_JOB_ATTRIBUTES)
2499 {
2500 if ((attr = ippFindAttribute(response, "job-state",
2501 IPP_TAG_ENUM)) != NULL)
2502 monitor->job_state = (ipp_jstate_t)attr->values[0].integer;
2503 else
2504 monitor->job_state = IPP_JOB_COMPLETED;
2505 }
2506 else if (response)
2507 {
2508 for (attr = response->attrs; attr; attr = attr->next)
2509 {
2510 job_id = 0;
2511 job_name = NULL;
2512 job_state = IPP_JOB_PENDING;
2513 job_user = NULL;
2514
2515 while (attr && attr->group_tag != IPP_TAG_JOB)
2516 attr = attr->next;
2517
2518 if (!attr)
2519 break;
2520
2521 while (attr && attr->group_tag == IPP_TAG_JOB)
2522 {
2523 if (!strcmp(attr->name, "job-id") &&
2524 attr->value_tag == IPP_TAG_INTEGER)
2525 job_id = attr->values[0].integer;
2526 else if (!strcmp(attr->name, "job-name") &&
2527 (attr->value_tag == IPP_TAG_NAME ||
2528 attr->value_tag == IPP_TAG_NAMELANG))
2529 job_name = attr->values[0].string.text;
2530 else if (!strcmp(attr->name, "job-state") &&
2531 attr->value_tag == IPP_TAG_ENUM)
2532 job_state = (ipp_jstate_t)attr->values[0].integer;
2533 else if (!strcmp(attr->name, "job-originating-user-name") &&
2534 (attr->value_tag == IPP_TAG_NAME ||
2535 attr->value_tag == IPP_TAG_NAMELANG))
2536 job_user = attr->values[0].string.text;
2537
2538 attr = attr->next;
2539 }
2540
2541 if (job_id > 0 && job_name && !strcmp(job_name, monitor->job_name) &&
2542 job_user && monitor->user && !strcmp(job_user, monitor->user))
2543 {
2544 monitor->job_id = job_id;
2545 monitor->job_state = job_state;
2546 break;
2547 }
2548
2549 if (!attr)
2550 break;
2551 }
2552 }
2553
2554 fprintf(stderr, "DEBUG: (monitor) job-state = %s\n",
2555 ippEnumString("job-state", monitor->job_state));
2556
2557 if (!job_canceled &&
2558 (monitor->job_state == IPP_JOB_CANCELED ||
2559 monitor->job_state == IPP_JOB_ABORTED))
2560 {
2561 job_canceled = -1;
2562 fprintf(stderr, "DEBUG: (monitor) job_canceled = -1\n");
2563 }
2564
2565 if ((attr = ippFindAttribute(response, "job-state-reasons",
2566 IPP_TAG_KEYWORD)) != NULL)
2567 {
2568 int i, new_reasons = 0; /* Looping var, new reasons */
2569
2570 for (i = 0; i < attr->num_values; i ++)
2571 {
2572 if (!strcmp(attr->values[i].string.text,
2573 "account-authorization-failed"))
2574 new_reasons |= _CUPS_JSR_ACCOUNT_AUTHORIZATION_FAILED;
2575 else if (!strcmp(attr->values[i].string.text, "account-closed"))
2576 new_reasons |= _CUPS_JSR_ACCOUNT_CLOSED;
2577 else if (!strcmp(attr->values[i].string.text, "account-info-needed"))
2578 new_reasons |= _CUPS_JSR_ACCOUNT_INFO_NEEDED;
2579 else if (!strcmp(attr->values[i].string.text,
2580 "account-limit-reached"))
2581 new_reasons |= _CUPS_JSR_ACCOUNT_LIMIT_REACHED;
2582 else if (!strcmp(attr->values[i].string.text, "job-password-wait"))
2583 new_reasons |= _CUPS_JSR_JOB_PASSWORD_WAIT;
2584 else if (!strcmp(attr->values[i].string.text, "job-release-wait"))
2585 new_reasons |= _CUPS_JSR_JOB_RELEASE_WAIT;
2586 if (!job_canceled &&
2587 (!strncmp(attr->values[i].string.text, "job-canceled-", 13) || !strcmp(attr->values[i].string.text, "aborted-by-system")))
2588 job_canceled = 1;
2589 }
2590
2591 if (new_reasons != monitor->job_reasons)
2592 {
2593 if (new_reasons & _CUPS_JSR_ACCOUNT_AUTHORIZATION_FAILED)
2594 fputs("JOBSTATE: account-authorization-failed\n", stderr);
2595 else if (new_reasons & _CUPS_JSR_ACCOUNT_CLOSED)
2596 fputs("JOBSTATE: account-closed\n", stderr);
2597 else if (new_reasons & _CUPS_JSR_ACCOUNT_INFO_NEEDED)
2598 fputs("JOBSTATE: account-info-needed\n", stderr);
2599 else if (new_reasons & _CUPS_JSR_ACCOUNT_LIMIT_REACHED)
2600 fputs("JOBSTATE: account-limit-reached\n", stderr);
2601 else if (new_reasons & _CUPS_JSR_JOB_PASSWORD_WAIT)
2602 fputs("JOBSTATE: job-password-wait\n", stderr);
2603 else if (new_reasons & _CUPS_JSR_JOB_RELEASE_WAIT)
2604 fputs("JOBSTATE: job-release-wait\n", stderr);
2605 else
2606 fputs("JOBSTATE: job-printing\n", stderr);
2607
2608 monitor->job_reasons = new_reasons;
2609 }
2610 }
2611
2612 ippDelete(response);
2613
2614 fprintf(stderr, "DEBUG: (monitor) job-state = %s\n",
2615 ippEnumString("job-state", monitor->job_state));
2616
2617 if (!job_canceled &&
2618 (monitor->job_state == IPP_JOB_CANCELED ||
2619 monitor->job_state == IPP_JOB_ABORTED))
2620 job_canceled = -1;
2621 }
2622
2623 /*
2624 * Sleep for N seconds...
2625 */
2626
2627 monitor_sleep:
2628
2629 sleep((unsigned)delay);
2630
2631 delay = _cupsNextDelay(delay, &prev_delay);
2632 }
2633
2634 /*
2635 * Cancel the job if necessary...
2636 */
2637
2638 if (job_canceled > 0 && monitor->job_id > 0)
2639 {
2640 if (httpGetFd(http) < 0)
2641 httpReconnect(http);
2642
2643 if (httpGetFd(http) >= 0)
2644 {
2645 cancel_job(http, monitor->uri, monitor->job_id, monitor->resource,
2646 monitor->user, monitor->version);
2647
2648 if (cupsLastError() > IPP_OK_CONFLICT)
2649 {
2650 fprintf(stderr, "DEBUG: (monitor) cancel_job() = %s\n", cupsLastErrorString());
2651 _cupsLangPrintFilter(stderr, "ERROR", _("Unable to cancel print job."));
2652 }
2653 }
2654 }
2655
2656 /*
2657 * Cleanup and return...
2658 */
2659
2660 httpClose(http);
2661
2662 return (NULL);
2663 }
2664
2665
2666 /*
2667 * 'new_request()' - Create a new print creation or validation request.
2668 */
2669
2670 static ipp_t * /* O - Request data */
2671 new_request(
2672 ipp_op_t op, /* I - IPP operation code */
2673 int version, /* I - IPP version number */
2674 const char *uri, /* I - printer-uri value */
2675 const char *user, /* I - requesting-user-name value */
2676 const char *title, /* I - job-name value */
2677 int num_options, /* I - Number of options to send */
2678 cups_option_t *options, /* I - Options to send */
2679 const char *compression, /* I - compression value or NULL */
2680 int copies, /* I - copies value or 0 */
2681 const char *format, /* I - document-format value or NULL */
2682 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
2683 ppd_file_t *ppd, /* I - PPD file data */
2684 ipp_attribute_t *media_col_sup, /* I - media-col-supported values */
2685 ipp_attribute_t *doc_handling_sup, /* I - multiple-document-handling-supported values */
2686 ipp_attribute_t *print_color_mode_sup)
2687 /* I - Printer supports print-color-mode */
2688 {
2689 ipp_t *request; /* Request data */
2690 const char *keyword; /* PWG keyword */
2691
2692
2693 /*
2694 * Create the IPP request...
2695 */
2696
2697 request = ippNewRequest(op);
2698 ippSetVersion(request, version / 10, version % 10);
2699
2700 fprintf(stderr, "DEBUG: %s IPP/%d.%d\n",
2701 ippOpString(request->request.op.operation_id),
2702 request->request.op.version[0],
2703 request->request.op.version[1]);
2704
2705 /*
2706 * Add standard attributes...
2707 */
2708
2709 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
2710 fprintf(stderr, "DEBUG: printer-uri=\"%s\"\n", uri);
2711
2712 if (user && *user)
2713 {
2714 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, user);
2715 fprintf(stderr, "DEBUG: requesting-user-name=\"%s\"\n", user);
2716 }
2717
2718 if (title && *title)
2719 {
2720 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, title);
2721 fprintf(stderr, "DEBUG: job-name=\"%s\"\n", title);
2722 }
2723
2724 if (format && op != IPP_CREATE_JOB)
2725 {
2726 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, format);
2727 fprintf(stderr, "DEBUG: document-format=\"%s\"\n", format);
2728 }
2729
2730 #ifdef HAVE_LIBZ
2731 if (compression && op != IPP_OP_CREATE_JOB && op != IPP_OP_VALIDATE_JOB)
2732 {
2733 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "compression", NULL, compression);
2734 fprintf(stderr, "DEBUG: compression=\"%s\"\n", compression);
2735 }
2736 #endif /* HAVE_LIBZ */
2737
2738 /*
2739 * Handle options on the command-line...
2740 */
2741
2742 if (num_options > 0)
2743 {
2744 if (pc)
2745 {
2746 /*
2747 * Send standard IPP attributes...
2748 */
2749
2750 fputs("DEBUG: Adding standard IPP operation/job attributes.\n", stderr);
2751
2752 copies = _cupsConvertOptions(request, ppd, pc, media_col_sup, doc_handling_sup, print_color_mode_sup, user, format, copies, num_options, options);
2753
2754 /*
2755 * Map FaxOut options...
2756 */
2757
2758 if ((keyword = cupsGetOption("phone", num_options, options)) != NULL)
2759 {
2760 ipp_t *destination; /* destination collection */
2761 char phone[1024], /* Phone number string */
2762 *ptr, /* Pointer into string */
2763 tel_uri[1024]; /* tel: URI */
2764 static const char * const allowed = "0123456789#*-+.()";
2765 /* Allowed characters */
2766
2767 destination = ippNew();
2768
2769 /*
2770 * Unescape and filter out spaces and other characters that are not
2771 * allowed in a tel: URI.
2772 */
2773
2774 _httpDecodeURI(phone, keyword, sizeof(phone));
2775 for (ptr = phone; *ptr;)
2776 {
2777 if (!strchr(allowed, *ptr))
2778 _cups_strcpy(ptr, ptr + 1);
2779 else
2780 ptr ++;
2781 }
2782
2783 httpAssembleURI(HTTP_URI_CODING_ALL, tel_uri, sizeof(tel_uri), "tel", NULL, NULL, 0, phone);
2784 ippAddString(destination, IPP_TAG_JOB, IPP_TAG_URI, "destination-uri", NULL, tel_uri);
2785
2786 if ((keyword = cupsGetOption("faxPrefix", num_options,
2787 options)) != NULL && *keyword)
2788 {
2789 char predial[1024]; /* Pre-dial string */
2790
2791 _httpDecodeURI(predial, keyword, sizeof(predial));
2792 ippAddString(destination, IPP_TAG_JOB, IPP_TAG_TEXT, "pre-dial-string", NULL, predial);
2793 }
2794
2795 ippAddCollection(request, IPP_TAG_JOB, "destination-uris", destination);
2796 ippDelete(destination);
2797 }
2798 }
2799 else
2800 {
2801 /*
2802 * When talking to another CUPS server, send all options...
2803 */
2804
2805 fputs("DEBUG: Adding all operation/job attributes.\n", stderr);
2806 cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
2807 cupsEncodeOptions2(request, num_options, options, IPP_TAG_JOB);
2808 }
2809
2810 if (copies > 1 && (!pc || copies <= pc->max_copies))
2811 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies", copies);
2812 }
2813
2814 fprintf(stderr, "DEBUG: IPP/%d.%d %s #%d\n", version / 10, version % 10, ippOpString(ippGetOperation(request)), ippGetRequestId(request));
2815 debug_attributes(request);
2816
2817 return (request);
2818 }
2819
2820
2821 /*
2822 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
2823 */
2824
2825 static const char * /* O - Password */
2826 password_cb(const char *prompt, /* I - Prompt (not used) */
2827 http_t *http, /* I - Connection */
2828 const char *method, /* I - Request method (not used) */
2829 const char *resource, /* I - Resource path (not used) */
2830 int *password_tries) /* I - Password tries */
2831 {
2832 char def_username[HTTP_MAX_VALUE]; /* Default username */
2833
2834
2835 fprintf(stderr, "DEBUG: password_cb(prompt=\"%s\", http=%p, method=\"%s\", "
2836 "resource=\"%s\", password_tries=%p(%d)), password=%p\n",
2837 prompt, http, method, resource, password_tries, *password_tries,
2838 password);
2839
2840 (void)prompt;
2841 (void)method;
2842 (void)resource;
2843
2844 if (!uri_credentials)
2845 {
2846 /*
2847 * Remember that we need to authenticate...
2848 */
2849
2850 auth_info_required = "username,password";
2851
2852 if (httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "username",
2853 def_username))
2854 {
2855 char quoted[HTTP_MAX_VALUE * 2 + 4];
2856 /* Quoted string */
2857
2858 fprintf(stderr, "ATTR: auth-info-default=%s,\n",
2859 quote_string(def_username, quoted, sizeof(quoted)));
2860 }
2861 }
2862
2863 if (password && *password && *password_tries < 3)
2864 {
2865 (*password_tries) ++;
2866
2867 cupsSetUser(username);
2868
2869 return (password);
2870 }
2871 else
2872 {
2873 /*
2874 * Give up after 3 tries or if we don't have a password to begin with...
2875 */
2876
2877 return (NULL);
2878 }
2879 }
2880
2881
2882 /*
2883 * 'quote_string()' - Quote a string value.
2884 */
2885
2886 static const char * /* O - Quoted string */
2887 quote_string(const char *s, /* I - String */
2888 char *q, /* I - Quoted string buffer */
2889 size_t qsize) /* I - Size of quoted string buffer */
2890 {
2891 char *qptr, /* Pointer into string buffer */
2892 *qend; /* End of string buffer */
2893
2894
2895 qptr = q;
2896 qend = q + qsize - 5;
2897
2898 if (qend < q)
2899 {
2900 *q = '\0';
2901 return (q);
2902 }
2903
2904 *qptr++ = '\'';
2905 *qptr++ = '\"';
2906
2907 while (*s && qptr < qend)
2908 {
2909 if (*s == '\\' || *s == '\"' || *s == '\'')
2910 {
2911 if (qptr < (qend - 4))
2912 {
2913 *qptr++ = '\\';
2914 *qptr++ = '\\';
2915 *qptr++ = '\\';
2916 }
2917 else
2918 break;
2919 }
2920
2921 *qptr++ = *s++;
2922 }
2923
2924 *qptr++ = '\"';
2925 *qptr++ = '\'';
2926 *qptr = '\0';
2927
2928 return (q);
2929 }
2930
2931
2932 /*
2933 * 'report_attr()' - Report an IPP attribute value.
2934 */
2935
2936 static void
2937 report_attr(ipp_attribute_t *attr) /* I - Attribute */
2938 {
2939 int i; /* Looping var */
2940 char value[1024], /* Value string */
2941 *valptr; /* Pointer into value string */
2942 const char *cached; /* Cached attribute */
2943
2944
2945 /*
2946 * Convert the attribute values into quoted strings...
2947 */
2948
2949 for (i = 0, valptr = value;
2950 i < attr->num_values && valptr < (value + sizeof(value) - 10);
2951 i ++)
2952 {
2953 if (i > 0)
2954 *valptr++ = ',';
2955
2956 switch (attr->value_tag)
2957 {
2958 case IPP_TAG_INTEGER :
2959 case IPP_TAG_ENUM :
2960 snprintf(valptr, sizeof(value) - (size_t)(valptr - value), "%d", attr->values[i].integer);
2961 valptr += strlen(valptr);
2962 break;
2963
2964 case IPP_TAG_TEXT :
2965 case IPP_TAG_NAME :
2966 case IPP_TAG_KEYWORD :
2967 quote_string(attr->values[i].string.text, valptr, (size_t)(value + sizeof(value) - valptr));
2968 valptr += strlen(valptr);
2969 break;
2970
2971 default :
2972 /*
2973 * Unsupported value type...
2974 */
2975
2976 return;
2977 }
2978 }
2979
2980 *valptr = '\0';
2981
2982 _cupsMutexLock(&report_mutex);
2983
2984 if ((cached = cupsGetOption(attr->name, num_attr_cache,
2985 attr_cache)) == NULL || strcmp(cached, value))
2986 {
2987 /*
2988 * Tell the scheduler about the new values...
2989 */
2990
2991 num_attr_cache = cupsAddOption(attr->name, value, num_attr_cache,
2992 &attr_cache);
2993 fprintf(stderr, "ATTR: %s=%s\n", attr->name, value);
2994 }
2995
2996 _cupsMutexUnlock(&report_mutex);
2997 }
2998
2999
3000 /*
3001 * 'report_printer_state()' - Report the printer state.
3002 */
3003
3004 static void
3005 report_printer_state(ipp_t *ipp) /* I - IPP response */
3006 {
3007 ipp_attribute_t *pa, /* printer-alert */
3008 *pam, /* printer-alert-message */
3009 *pmja, /* printer-mandatory-job-attributes */
3010 *psm, /* printer-state-message */
3011 *reasons, /* printer-state-reasons */
3012 *marker; /* marker-* attributes */
3013 char value[1024], /* State/message string */
3014 *valptr; /* Pointer into string */
3015 static int ipp_supplies = -1;
3016 /* Report supply levels? */
3017
3018
3019 /*
3020 * Report alerts and messages...
3021 */
3022
3023 if ((pa = ippFindAttribute(ipp, "printer-alert", IPP_TAG_TEXT)) != NULL)
3024 report_attr(pa);
3025
3026 if ((pam = ippFindAttribute(ipp, "printer-alert-message",
3027 IPP_TAG_TEXT)) != NULL)
3028 report_attr(pam);
3029
3030 if ((pmja = ippFindAttribute(ipp, "printer-mandatory-job-attributes", IPP_TAG_KEYWORD)) != NULL)
3031 {
3032 int i, /* Looping var */
3033 count = ippGetCount(pmja); /* Number of values */
3034
3035 for (i = 0, valptr = value; i < count; i ++, valptr += strlen(valptr))
3036 {
3037 if (i)
3038 snprintf(valptr, sizeof(value) - (size_t)(valptr - value), " %s", ippGetString(pmja, i, NULL));
3039 else
3040 strlcpy(value, ippGetString(pmja, i, NULL), sizeof(value));
3041 }
3042
3043 if (strcmp(value, mandatory_attrs))
3044 {
3045 strlcpy(mandatory_attrs, value, sizeof(mandatory_attrs));
3046 fprintf(stderr, "PPD: cupsMandatory=\"%s\"\n", value);
3047 }
3048 }
3049
3050 if ((psm = ippFindAttribute(ipp, "printer-state-message",
3051 IPP_TAG_TEXT)) != NULL)
3052 {
3053 char *ptr; /* Pointer into message */
3054
3055
3056 strlcpy(value, "INFO: ", sizeof(value));
3057 for (ptr = psm->values[0].string.text, valptr = value + 6;
3058 *ptr && valptr < (value + sizeof(value) - 6);
3059 ptr ++)
3060 {
3061 if (*ptr < ' ' && *ptr > 0 && *ptr != '\t')
3062 {
3063 /*
3064 * Substitute "<XX>" for the control character; sprintf is safe because
3065 * we always leave 6 chars free at the end...
3066 */
3067
3068 sprintf(valptr, "<%02X>", *ptr);
3069 valptr += 4;
3070 }
3071 else
3072 *valptr++ = *ptr;
3073 }
3074
3075 *valptr++ = '\n';
3076 *valptr = '\0';
3077
3078 fputs(value, stderr);
3079 }
3080
3081 /*
3082 * Now report printer-state-reasons, filtering out some of the reasons we never
3083 * want to set...
3084 */
3085
3086 if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
3087 IPP_TAG_KEYWORD)) == NULL)
3088 return;
3089
3090 update_reasons(reasons, NULL);
3091
3092 /*
3093 * Relay the current marker-* attribute values...
3094 */
3095
3096 if (ipp_supplies < 0)
3097 {
3098 ppd_file_t *ppd; /* PPD file */
3099 ppd_attr_t *ppdattr; /* Attribute in PPD file */
3100
3101 if ((ppd = ppdOpenFile(getenv("PPD"))) != NULL &&
3102 (ppdattr = ppdFindAttr(ppd, "cupsIPPSupplies", NULL)) != NULL &&
3103 ppdattr->value && _cups_strcasecmp(ppdattr->value, "true"))
3104 ipp_supplies = 0;
3105 else
3106 ipp_supplies = 1;
3107
3108 ppdClose(ppd);
3109 }
3110
3111 if (ipp_supplies > 0)
3112 {
3113 if ((marker = ippFindAttribute(ipp, "marker-colors", IPP_TAG_NAME)) != NULL)
3114 report_attr(marker);
3115 if ((marker = ippFindAttribute(ipp, "marker-high-levels",
3116 IPP_TAG_INTEGER)) != NULL)
3117 report_attr(marker);
3118 if ((marker = ippFindAttribute(ipp, "marker-levels",
3119 IPP_TAG_INTEGER)) != NULL)
3120 report_attr(marker);
3121 if ((marker = ippFindAttribute(ipp, "marker-low-levels",
3122 IPP_TAG_INTEGER)) != NULL)
3123 report_attr(marker);
3124 if ((marker = ippFindAttribute(ipp, "marker-message",
3125 IPP_TAG_TEXT)) != NULL)
3126 report_attr(marker);
3127 if ((marker = ippFindAttribute(ipp, "marker-names", IPP_TAG_NAME)) != NULL)
3128 report_attr(marker);
3129 if ((marker = ippFindAttribute(ipp, "marker-types",
3130 IPP_TAG_KEYWORD)) != NULL)
3131 report_attr(marker);
3132 }
3133 }
3134
3135
3136 #if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
3137 /*
3138 * 'run_as_user()' - Run the IPP backend as the printing user.
3139 *
3140 * This function uses an XPC-based user agent to run the backend as the printing
3141 * user. We need to do this in order to have access to the user's Kerberos
3142 * credentials.
3143 */
3144
3145 static int /* O - Exit status */
3146 run_as_user(char *argv[], /* I - Command-line arguments */
3147 uid_t uid, /* I - User ID */
3148 const char *device_uri, /* I - Device URI */
3149 int fd) /* I - File to print */
3150 {
3151 const char *auth_negotiate;/* AUTH_NEGOTIATE env var */
3152 xpc_connection_t conn; /* Connection to XPC service */
3153 xpc_object_t request; /* Request message dictionary */
3154 __block xpc_object_t response; /* Response message dictionary */
3155 dispatch_semaphore_t sem; /* Semaphore for waiting for response */
3156 int status = CUPS_BACKEND_FAILED;
3157 /* Status of request */
3158
3159
3160 fprintf(stderr, "DEBUG: Running IPP backend as UID %d.\n", (int)uid);
3161
3162 /*
3163 * Connect to the user agent for the specified UID...
3164 */
3165
3166 conn = xpc_connection_create_mach_service(kPMPrintUIToolAgent,
3167 dispatch_get_global_queue(0, 0), 0);
3168 if (!conn)
3169 {
3170 _cupsLangPrintFilter(stderr, "ERROR",
3171 _("Unable to start backend process."));
3172 fputs("DEBUG: Unable to create connection to agent.\n", stderr);
3173 goto cleanup;
3174 }
3175
3176 xpc_connection_set_event_handler(conn,
3177 ^(xpc_object_t event)
3178 {
3179 xpc_type_t messageType = xpc_get_type(event);
3180
3181 if (messageType == XPC_TYPE_ERROR)
3182 {
3183 if (event == XPC_ERROR_CONNECTION_INTERRUPTED)
3184 fprintf(stderr, "DEBUG: Interrupted connection to service %s.\n",
3185 xpc_connection_get_name(conn));
3186 else if (event == XPC_ERROR_CONNECTION_INVALID)
3187 fprintf(stderr, "DEBUG: Connection invalid for service %s.\n",
3188 xpc_connection_get_name(conn));
3189 else
3190 fprintf(stderr, "DEBUG: Unxpected error for service %s: %s\n",
3191 xpc_connection_get_name(conn),
3192 xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
3193 }
3194 });
3195 xpc_connection_set_target_uid(conn, uid);
3196 xpc_connection_resume(conn);
3197
3198 /*
3199 * Try starting the backend...
3200 */
3201
3202 request = xpc_dictionary_create(NULL, NULL, 0);
3203 xpc_dictionary_set_int64(request, "command", kPMStartJob);
3204 xpc_dictionary_set_string(request, "device-uri", device_uri);
3205 xpc_dictionary_set_string(request, "job-id", argv[1]);
3206 xpc_dictionary_set_string(request, "user", argv[2]);
3207 xpc_dictionary_set_string(request, "title", argv[3]);
3208 xpc_dictionary_set_string(request, "copies", argv[4]);
3209 xpc_dictionary_set_string(request, "options", argv[5]);
3210 xpc_dictionary_set_string(request, "auth-info-required",
3211 getenv("AUTH_INFO_REQUIRED"));
3212 if ((auth_negotiate = getenv("AUTH_NEGOTIATE")) != NULL)
3213 xpc_dictionary_set_string(request, "auth-negotiate", auth_negotiate);
3214 xpc_dictionary_set_fd(request, "stdin", fd);
3215 xpc_dictionary_set_fd(request, "stderr", 2);
3216 xpc_dictionary_set_fd(request, "side-channel", CUPS_SC_FD);
3217
3218 sem = dispatch_semaphore_create(0);
3219 response = NULL;
3220
3221 xpc_connection_send_message_with_reply(conn, request,
3222 dispatch_get_global_queue(0,0),
3223 ^(xpc_object_t reply)
3224 {
3225 /* Save the response and wake up */
3226 if (xpc_get_type(reply)
3227 == XPC_TYPE_DICTIONARY)
3228 response = xpc_retain(reply);
3229
3230 dispatch_semaphore_signal(sem);
3231 });
3232
3233 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
3234 xpc_release(request);
3235 dispatch_release(sem);
3236
3237 if (response)
3238 {
3239 child_pid = (pid_t)xpc_dictionary_get_int64(response, "child-pid");
3240
3241 xpc_release(response);
3242
3243 if (child_pid)
3244 fprintf(stderr, "DEBUG: Child PID=%d.\n", (int)child_pid);
3245 else
3246 {
3247 _cupsLangPrintFilter(stderr, "ERROR",
3248 _("Unable to start backend process."));
3249 fputs("DEBUG: No child PID.\n", stderr);
3250 goto cleanup;
3251 }
3252 }
3253 else
3254 {
3255 _cupsLangPrintFilter(stderr, "ERROR",
3256 _("Unable to start backend process."));
3257 fputs("DEBUG: No reply from agent.\n", stderr);
3258 goto cleanup;
3259 }
3260
3261 /*
3262 * Then wait for the backend to finish...
3263 */
3264
3265 request = xpc_dictionary_create(NULL, NULL, 0);
3266 xpc_dictionary_set_int64(request, "command", kPMWaitForJob);
3267 xpc_dictionary_set_fd(request, "stderr", 2);
3268
3269 sem = dispatch_semaphore_create(0);
3270 response = NULL;
3271
3272 xpc_connection_send_message_with_reply(conn, request,
3273 dispatch_get_global_queue(0,0),
3274 ^(xpc_object_t reply)
3275 {
3276 /* Save the response and wake up */
3277 if (xpc_get_type(reply)
3278 == XPC_TYPE_DICTIONARY)
3279 response = xpc_retain(reply);
3280
3281 dispatch_semaphore_signal(sem);
3282 });
3283
3284 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
3285 xpc_release(request);
3286 dispatch_release(sem);
3287
3288 if (response)
3289 {
3290 status = (int)xpc_dictionary_get_int64(response, "status");
3291
3292 if (status == SIGTERM || status == SIGKILL || status == SIGPIPE)
3293 {
3294 fprintf(stderr, "DEBUG: Child terminated on signal %d.\n", status);
3295 status = CUPS_BACKEND_FAILED;
3296 }
3297 else if (WIFSIGNALED(status))
3298 {
3299 fprintf(stderr, "DEBUG: Child crashed on signal %d.\n", status);
3300 status = CUPS_BACKEND_STOP;
3301 }
3302 else if (WIFEXITED(status))
3303 {
3304 status = WEXITSTATUS(status);
3305 fprintf(stderr, "DEBUG: Child exited with status %d.\n", status);
3306 }
3307
3308 xpc_release(response);
3309 }
3310 else
3311 _cupsLangPrintFilter(stderr, "ERROR",
3312 _("Unable to get backend exit status."));
3313
3314 cleanup:
3315
3316 if (conn)
3317 {
3318 xpc_connection_cancel(conn);
3319 xpc_release(conn);
3320 }
3321
3322 return (status);
3323 }
3324 #endif /* HAVE_GSSAPI && HAVE_XPC */
3325
3326
3327 /*
3328 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
3329 */
3330
3331 static void
3332 sigterm_handler(int sig) /* I - Signal */
3333 {
3334 (void)sig; /* remove compiler warnings... */
3335
3336 write(2, "DEBUG: Got SIGTERM.\n", 20);
3337
3338 #if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
3339 if (child_pid)
3340 {
3341 kill(child_pid, sig);
3342 child_pid = 0;
3343 }
3344 #endif /* HAVE_GSSAPI && HAVE_XPC */
3345
3346 if (!job_canceled)
3347 {
3348 /*
3349 * Flag that the job should be canceled...
3350 */
3351
3352 write(2, "DEBUG: sigterm_handler: job_canceled = 1.\n", 25);
3353
3354 job_canceled = 1;
3355 return;
3356 }
3357
3358 /*
3359 * The scheduler already tried to cancel us once, now just terminate
3360 * after removing our temp file!
3361 */
3362
3363 if (tmpfilename[0])
3364 unlink(tmpfilename);
3365
3366 _exit(1);
3367 }
3368
3369
3370 /*
3371 * 'timeout_cb()' - Handle HTTP timeouts.
3372 */
3373
3374 static int /* O - 1 to continue, 0 to cancel */
3375 timeout_cb(http_t *http, /* I - Connection to server (unused) */
3376 void *user_data) /* I - User data (unused) */
3377 {
3378 (void)http;
3379 (void)user_data;
3380
3381 return (!job_canceled);
3382 }
3383
3384
3385 /*
3386 * 'update_reasons()' - Update the printer-state-reasons values.
3387 */
3388
3389 static void
3390 update_reasons(ipp_attribute_t *attr, /* I - printer-state-reasons or NULL */
3391 const char *s) /* I - STATE: string or NULL */
3392 {
3393 char op; /* Add (+), remove (-), replace (\0) */
3394 cups_array_t *new_reasons; /* New reasons array */
3395 char *reason, /* Current reason */
3396 add[2048], /* Reasons added string */
3397 *addptr, /* Pointer into add string */
3398 rem[2048], /* Reasons removed string */
3399 *remptr; /* Pointer into remove string */
3400 const char *addprefix, /* Current add string prefix */
3401 *remprefix; /* Current remove string prefix */
3402
3403
3404 fprintf(stderr, "DEBUG: update_reasons(attr=%d(%s%s), s=\"%s\")\n",
3405 attr ? attr->num_values : 0, attr ? attr->values[0].string.text : "",
3406 attr && attr->num_values > 1 ? ",..." : "", s ? s : "(null)");
3407
3408 /*
3409 * Create an array of new reason keyword strings...
3410 */
3411
3412 if (attr)
3413 {
3414 int i; /* Looping var */
3415
3416 new_reasons = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3417 op = '\0';
3418
3419 for (i = 0; i < attr->num_values; i ++)
3420 {
3421 reason = attr->values[i].string.text;
3422
3423 if (strcmp(reason, "none") &&
3424 strcmp(reason, "none-report") &&
3425 strcmp(reason, "paused") &&
3426 strncmp(reason, "spool-area-full", 15) &&
3427 strcmp(reason, "com.apple.print.recoverable-warning") &&
3428 strncmp(reason, "cups-", 5))
3429 cupsArrayAdd(new_reasons, reason);
3430 }
3431 }
3432 else if (s)
3433 {
3434 if (*s == '+' || *s == '-')
3435 op = *s++;
3436 else
3437 op = '\0';
3438
3439 new_reasons = _cupsArrayNewStrings(s, ',');
3440 }
3441 else
3442 return;
3443
3444 /*
3445 * Compute the changes...
3446 */
3447
3448 add[0] = '\0';
3449 addprefix = "STATE: +";
3450 addptr = add;
3451 rem[0] = '\0';
3452 remprefix = "STATE: -";
3453 remptr = rem;
3454
3455 fprintf(stderr, "DEBUG2: op='%c', new_reasons=%d, state_reasons=%d\n",
3456 op ? op : ' ', cupsArrayCount(new_reasons),
3457 cupsArrayCount(state_reasons));
3458
3459 _cupsMutexLock(&report_mutex);
3460
3461 if (op == '+')
3462 {
3463 /*
3464 * Add reasons...
3465 */
3466
3467 for (reason = (char *)cupsArrayFirst(new_reasons);
3468 reason;
3469 reason = (char *)cupsArrayNext(new_reasons))
3470 {
3471 if (!cupsArrayFind(state_reasons, reason))
3472 {
3473 if (!strncmp(reason, "cups-remote-", 12))
3474 {
3475 /*
3476 * If we are setting cups-remote-xxx, remove all other cups-remote-xxx
3477 * keywords...
3478 */
3479
3480 char *temp; /* Current reason in state_reasons */
3481
3482 cupsArraySave(state_reasons);
3483
3484 for (temp = (char *)cupsArrayFirst(state_reasons);
3485 temp;
3486 temp = (char *)cupsArrayNext(state_reasons))
3487 if (!strncmp(temp, "cups-remote-", 12))
3488 {
3489 snprintf(remptr, sizeof(rem) - (size_t)(remptr - rem), "%s%s", remprefix, temp);
3490 remptr += strlen(remptr);
3491 remprefix = ",";
3492
3493 cupsArrayRemove(state_reasons, temp);
3494 break;
3495 }
3496
3497 cupsArrayRestore(state_reasons);
3498 }
3499
3500 cupsArrayAdd(state_reasons, reason);
3501
3502 snprintf(addptr, sizeof(add) - (size_t)(addptr - add), "%s%s", addprefix, reason);
3503 addptr += strlen(addptr);
3504 addprefix = ",";
3505 }
3506 }
3507 }
3508 else if (op == '-')
3509 {
3510 /*
3511 * Remove reasons...
3512 */
3513
3514 for (reason = (char *)cupsArrayFirst(new_reasons);
3515 reason;
3516 reason = (char *)cupsArrayNext(new_reasons))
3517 {
3518 if (cupsArrayFind(state_reasons, reason))
3519 {
3520 snprintf(remptr, sizeof(rem) - (size_t)(remptr - rem), "%s%s", remprefix, reason);
3521 remptr += strlen(remptr);
3522 remprefix = ",";
3523
3524 cupsArrayRemove(state_reasons, reason);
3525 }
3526 }
3527 }
3528 else
3529 {
3530 /*
3531 * Replace reasons...
3532 */
3533
3534 for (reason = (char *)cupsArrayFirst(state_reasons);
3535 reason;
3536 reason = (char *)cupsArrayNext(state_reasons))
3537 {
3538 if (strncmp(reason, "cups-", 5) && !cupsArrayFind(new_reasons, reason))
3539 {
3540 snprintf(remptr, sizeof(rem) - (size_t)(remptr - rem), "%s%s", remprefix, reason);
3541 remptr += strlen(remptr);
3542 remprefix = ",";
3543
3544 cupsArrayRemove(state_reasons, reason);
3545 }
3546 }
3547
3548 for (reason = (char *)cupsArrayFirst(new_reasons);
3549 reason;
3550 reason = (char *)cupsArrayNext(new_reasons))
3551 {
3552 if (!cupsArrayFind(state_reasons, reason))
3553 {
3554 cupsArrayAdd(state_reasons, reason);
3555
3556 snprintf(addptr, sizeof(add) - (size_t)(addptr - add), "%s%s", addprefix, reason);
3557 addptr += strlen(addptr);
3558 addprefix = ",";
3559 }
3560 }
3561 }
3562
3563 _cupsMutexUnlock(&report_mutex);
3564
3565 /*
3566 * Report changes and return...
3567 */
3568
3569 if (add[0] && rem[0])
3570 fprintf(stderr, "%s\n%s\n", add, rem);
3571 else if (add[0])
3572 fprintf(stderr, "%s\n", add);
3573 else if (rem[0])
3574 fprintf(stderr, "%s\n", rem);
3575 }