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