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