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