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