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