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