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