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