]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/ipp.c
Check for NULL response.
[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 {
901 if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
902 "Negotiate", 9))
903 auth_info_required = "negotiate";
904
905 fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
906 return (CUPS_BACKEND_AUTH_REQUIRED);
907 }
97fcaf92 908 else
67474245 909 {
4c4eea89 910 _cupsLangPrintFilter(stderr, "ERROR",
5fb23099 911 _("Unable to get printer status."));
67474245 912 sleep(10);
913 }
d1c2727f 914
7cfa02ab 915 ippDelete(supported);
5fb23099 916 supported = NULL;
d1c2727f 917 continue;
97fcaf92 918 }
6875feac 919
f8c0cc95 920 if (!getenv("CLASS"))
7cfa02ab 921 {
f8c0cc95 922 /*
923 * Check printer-is-accepting-jobs = false and printer-state-reasons for the
924 * "spool-area-full" keyword...
925 */
926
927 int busy = 0;
928
929 if ((printer_accepting = ippFindAttribute(supported,
930 "printer-is-accepting-jobs",
931 IPP_TAG_BOOLEAN)) != NULL &&
932 !printer_accepting->values[0].boolean)
933 busy = 1;
9e88f2d2 934 else if (!printer_accepting)
7b1ef0fc 935 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
936 "cups-ipp-missing-printer-is-accepting-jobs");
c6fab96f 937
9e88f2d2 938 if ((printer_state = ippFindAttribute(supported,
939 "printer-state-reasons",
940 IPP_TAG_KEYWORD)) != NULL && !busy)
f8c0cc95 941 {
942 for (i = 0; i < printer_state->num_values; i ++)
9e88f2d2 943 if (!strcmp(printer_state->values[0].string.text,
944 "spool-area-full") ||
f8c0cc95 945 !strncmp(printer_state->values[0].string.text, "spool-area-full-",
946 16))
947 {
948 busy = 1;
949 break;
950 }
951 }
952 else
7b1ef0fc 953 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
954 "cups-ipp-missing-printer-state-reasons");
7cfa02ab 955
f8c0cc95 956 if (busy)
7cfa02ab 957 {
5fb23099 958 _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
7cfa02ab 959
4a241564 960 report_printer_state(supported);
7cfa02ab 961
962 sleep(delay);
963
f8c0cc95 964 delay = _cupsNextDelay(delay, &prev_delay);
7cfa02ab 965
966 ippDelete(supported);
5fb23099 967 supported = NULL;
7cfa02ab 968 continue;
969 }
970 }
7cfa02ab 971
6875feac 972 /*
973 * Check for supported attributes...
974 */
975
976 if ((copies_sup = ippFindAttribute(supported, "copies-supported",
977 IPP_TAG_RANGE)) != NULL)
97fcaf92 978 {
b5cb0608 979 /*
980 * Has the "copies-supported" attribute - does it have an upper
981 * bound > 1?
982 */
97fcaf92 983
6875feac 984 fprintf(stderr, "DEBUG: copies-supported=%d-%d\n",
985 copies_sup->values[0].range.lower,
986 copies_sup->values[0].range.upper);
987
b5cb0608 988 if (copies_sup->values[0].range.upper <= 1)
989 copies_sup = NULL; /* No */
990 }
97fcaf92 991
6875feac 992 cups_version = ippFindAttribute(supported, "cups-version", IPP_TAG_TEXT);
b5cb0608 993
6875feac 994 if ((format_sup = ippFindAttribute(supported, "document-format-supported",
995 IPP_TAG_MIMETYPE)) != NULL)
b5cb0608 996 {
997 fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
998 format_sup->num_values);
999 for (i = 0; i < format_sup->num_values; i ++)
1000 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
1001 format_sup->values[i].string.text);
c8f9565c 1002 }
d1c2727f 1003
6875feac 1004 if ((media_col_sup = ippFindAttribute(supported, "media-col-supported",
1005 IPP_TAG_KEYWORD)) != NULL)
1006 {
1007 fprintf(stderr, "DEBUG: media-col-supported (%d values)\n",
1008 media_col_sup->num_values);
1009 for (i = 0; i < media_col_sup->num_values; i ++)
1010 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
1011 media_col_sup->values[i].string.text);
1012 }
1013
2ec940b5 1014 if ((operations_sup = ippFindAttribute(supported, "operations-supported",
1015 IPP_TAG_ENUM)) != NULL)
1016 {
9e88f2d2 1017 for (i = 0; i < operations_sup->num_values; i ++)
1018 if (operations_sup->values[i].integer == IPP_PRINT_JOB)
1019 break;
1020
1021 if (i >= operations_sup->num_values)
7b1ef0fc 1022 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1023 "cups-ipp-missing-print-job");
9e88f2d2 1024
1025 for (i = 0; i < operations_sup->num_values; i ++)
1026 if (operations_sup->values[i].integer == IPP_CANCEL_JOB)
1027 break;
1028
1029 if (i >= operations_sup->num_values)
7b1ef0fc 1030 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1031 "cups-ipp-missing-cancel-job");
9e88f2d2 1032
1033 for (i = 0; i < operations_sup->num_values; i ++)
1034 if (operations_sup->values[i].integer == IPP_GET_JOB_ATTRIBUTES)
1035 break;
1036
1037 if (i >= operations_sup->num_values)
7b1ef0fc 1038 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1039 "cups-ipp-missing-get-job-attributes");
9e88f2d2 1040
1041 for (i = 0; i < operations_sup->num_values; i ++)
1042 if (operations_sup->values[i].integer == IPP_GET_PRINTER_ATTRIBUTES)
1043 break;
1044
1045 if (i >= operations_sup->num_values)
7b1ef0fc 1046 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1047 "cups-ipp-missing-get-printer-attributes");
9e88f2d2 1048
2ec940b5 1049 for (i = 0; i < operations_sup->num_values; i ++)
5ed93a63 1050 {
2ec940b5 1051 if (operations_sup->values[i].integer == IPP_VALIDATE_JOB)
2ec940b5 1052 validate_job = 1;
5ed93a63 1053 else if (operations_sup->values[i].integer == IPP_CREATE_JOB)
5ed93a63 1054 create_job = 1;
537ee478 1055 else if (operations_sup->values[i].integer == IPP_SEND_DOCUMENT)
1056 send_document = 1;
1057 }
1058
1059 if (!send_document)
1060 {
1061 fputs("DEBUG: Printer supports Create-Job but not Send-Document.\n",
1062 stderr);
1063 create_job = 0;
5ed93a63 1064 }
2ec940b5 1065
1066 if (!validate_job)
7b1ef0fc 1067 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1068 "cups-ipp-missing-validate-job");
2ec940b5 1069 }
1070 else
7b1ef0fc 1071 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1072 "cups-ipp-missing-operations-supported");
2ec940b5 1073
adbcb02a 1074 doc_handling_sup = ippFindAttribute(supported,
1075 "multiple-document-handling-supported",
1076 IPP_TAG_KEYWORD);
1077
4a241564 1078 report_printer_state(supported);
c8f9565c 1079 }
97fcaf92 1080 while (ipp_status > IPP_OK_CONFLICT);
c8f9565c 1081
997cf8b0 1082 /*
1083 * See if the printer is accepting jobs and is not stopped; if either
1084 * condition is true and we are printing to a class, requeue the job...
1085 */
1086
1087 if (getenv("CLASS") != NULL)
1088 {
1089 printer_state = ippFindAttribute(supported, "printer-state",
1090 IPP_TAG_ENUM);
1091 printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs",
1092 IPP_TAG_BOOLEAN);
1093
1094 if (printer_state == NULL ||
072c4872 1095 (printer_state->values[0].integer > IPP_PRINTER_PROCESSING &&
1096 waitprinter) ||
997cf8b0 1097 printer_accepting == NULL ||
1098 !printer_accepting->values[0].boolean)
1099 {
1100 /*
1101 * If the CLASS environment variable is set, the job was submitted
1102 * to a class and not to a specific queue. In this case, we want
1103 * to abort immediately so that the job can be requeued on the next
1104 * available printer in the class.
1105 */
1106
4c4eea89 1107 _cupsLangPrintFilter(stderr, "INFO",
1108 _("Unable to contact printer, queuing on next "
1109 "printer in class."));
997cf8b0 1110
1111 ippDelete(supported);
1112 httpClose(http);
1113
997cf8b0 1114 /*
1115 * Sleep 5 seconds to keep the job from requeuing too rapidly...
1116 */
1117
1118 sleep(5);
1119
6248387b 1120 return (CUPS_BACKEND_FAILED);
997cf8b0 1121 }
1122 }
1123
c8f9565c 1124 /*
97fcaf92 1125 * See if the printer supports multiple copies...
c8f9565c 1126 */
1127
37de726f 1128 copies = atoi(argv[4]);
1129
d1c2727f 1130 if (copies_sup || argc < 7)
7d52601e 1131 {
37de726f 1132 copies_remaining = 1;
7d52601e 1133
982776b0 1134 if (argc < 7 && !_cups_strncasecmp(final_content_type, "image/", 6))
7d52601e 1135 copies = 1;
1136 }
0c5b0932 1137 else
37de726f 1138 copies_remaining = copies;
0c5b0932 1139
2ec940b5 1140 /*
1141 * Prepare remaining printing options...
1142 */
1143
1144 options = NULL;
8211508e 1145 pc = NULL;
2ec940b5 1146
1147 if (send_options)
1148 {
1149 num_options = cupsParseOptions(argv[5], 0, &options);
1150
1151 if (!cups_version && media_col_sup)
1152 {
1153 /*
1154 * Load the PPD file and generate PWG attribute mapping information...
1155 */
1156
1157 ppd = ppdOpenFile(getenv("PPD"));
8211508e 1158 pc = _ppdCacheCreateWithPPD(ppd);
2ec940b5 1159
1160 ppdClose(ppd);
1161 }
1162 }
1163 else
1164 num_options = 0;
1165
1166 document_format = NULL;
1167
1168 if (format_sup != NULL)
1169 {
1170 for (i = 0; i < format_sup->num_values; i ++)
c6fab96f 1171 if (!_cups_strcasecmp(final_content_type, format_sup->values[i].string.text))
2ec940b5 1172 {
1173 document_format = final_content_type;
1174 break;
1175 }
cce0044f 1176
1177 if (!document_format)
1178 {
1179 for (i = 0; i < format_sup->num_values; i ++)
c6fab96f 1180 if (!_cups_strcasecmp("application/octet-stream",
cce0044f 1181 format_sup->values[i].string.text))
1182 {
1183 document_format = "application/octet-stream";
1184 break;
1185 }
1186 }
2ec940b5 1187 }
1188
940afb4b 1189 /*
1190 * If the printer does not support HTTP/1.1 (which IPP requires), copy stdin
1191 * to a temporary file so that we can do a HTTP/1.0 submission...
1192 *
1193 * (I hate compatibility hacks!)
1194 */
1195
1196 if (http->version < HTTP_1_1 && num_files == 0)
1197 {
1198 if ((fd = cupsTempFd(tmpfilename, sizeof(tmpfilename))) < 0)
1199 {
1200 perror("DEBUG: Unable to create temporary file");
1201 return (CUPS_BACKEND_FAILED);
1202 }
1203
1204 _cupsLangPrintFilter(stderr, "INFO", _("Copying print data."));
1205
1206 compatsize = backendRunLoop(-1, fd, snmp_fd, &(addrlist->addr), 0, 0,
1207 backendNetworkSideCB);
1208
1209 close(fd);
1210
1211 compatfile = tmpfilename;
1212 files = &compatfile;
1213 num_files = 1;
1214 }
1215 else if (http->version < HTTP_1_1 && num_files == 1)
1216 {
1217 struct stat fileinfo; /* File information */
1218
1219 if (!stat(files[0], &fileinfo))
1220 compatsize = fileinfo.st_size;
1221 }
1222
b9738d7c 1223 /*
1224 * Start monitoring the printer in the background...
1225 */
1226
1227 monitor.uri = uri;
1228 monitor.hostname = hostname;
1229 monitor.user = argv[2];
1230 monitor.resource = resource;
1231 monitor.port = port;
1232 monitor.version = version;
1233 monitor.job_id = 0;
1234 monitor.encryption = cupsEncryption();
1235 monitor.job_state = IPP_JOB_PENDING;
1236 monitor.printer_state = IPP_PRINTER_IDLE;
1237
5ed93a63 1238 if (create_job)
1239 {
1240 monitor.job_name = argv[3];
1241 }
1242 else
1243 {
1244 snprintf(print_job_name, sizeof(print_job_name), "%s - %s", argv[1],
1245 argv[3]);
1246 monitor.job_name = print_job_name;
1247 }
1248
b9738d7c 1249 _cupsThreadCreate((_cups_thread_func_t)monitor_printer, &monitor);
1250
c8f9565c 1251 /*
2ec940b5 1252 * Validate access to the printer...
c8f9565c 1253 */
1254
5fb23099 1255 while (!job_canceled && validate_job)
c8f9565c 1256 {
5ed93a63 1257 request = new_request(IPP_VALIDATE_JOB, version, uri, argv[2],
1258 monitor.job_name, num_options, options, compression,
8211508e 1259 copies_sup ? copies : 1, document_format, pc,
adbcb02a 1260 media_col_sup, doc_handling_sup);
97fcaf92 1261
2ec940b5 1262 ippDelete(cupsDoRequest(http, request, resource));
8ce7c000 1263
2ec940b5 1264 ipp_status = cupsLastError();
97fcaf92 1265
5fb23099 1266 fprintf(stderr, "DEBUG: Validate-Job: %s (%s)\n",
1267 ippErrorString(ipp_status), cupsLastErrorString());
6875feac 1268
5fb23099 1269 if (job_canceled)
1270 break;
e7186d00 1271
5fb23099 1272 if (ipp_status == IPP_SERVICE_UNAVAILABLE || ipp_status == IPP_PRINTER_BUSY)
1273 {
1274 _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
1275 sleep(10);
1276 }
548f3b66 1277 else if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
1278 ipp_status == IPP_AUTHENTICATION_CANCELED)
5fb23099 1279 {
1280 /*
1281 * Update auth-info-required as needed...
1282 */
b8f3410f 1283
5fb23099 1284 fprintf(stderr, "DEBUG: WWW-Authenticate=\"%s\"\n",
1285 httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE));
97fcaf92 1286
5fb23099 1287 /*
1288 * Normal authentication goes through the password callback, which sets
1289 * auth_info_required to "username,password". Kerberos goes directly
1290 * through GSSAPI, so look for Negotiate in the WWW-Authenticate header
1291 * here and set auth_info_required as needed...
1292 */
6875feac 1293
5fb23099 1294 if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
1295 "Negotiate", 9))
1296 auth_info_required = "negotiate";
753453e4 1297
5fb23099 1298 goto cleanup;
2ec940b5 1299 }
5fb23099 1300 else if (ipp_status == IPP_OPERATION_NOT_SUPPORTED)
1301 {
0663fa1c 1302 /*
1303 * This is all too common...
1304 */
1305
7b1ef0fc 1306 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1307 "cups-ipp-missing-validate-job");
5fb23099 1308 break;
1309 }
1310 else if (ipp_status < IPP_REDIRECTION_OTHER_SITE)
2ec940b5 1311 break;
1312 }
37de726f 1313
2ec940b5 1314 /*
1315 * Then issue the print-job request...
1316 */
6875feac 1317
2ec940b5 1318 job_id = 0;
6875feac 1319
2ec940b5 1320 while (!job_canceled && copies_remaining > 0)
1321 {
1322 /*
1323 * Check for side-channel requests...
1324 */
b9738d7c 1325
2ec940b5 1326 backendCheckSideChannel(snmp_fd, http->hostaddr);
b9738d7c 1327
2ec940b5 1328 /*
1329 * Build the IPP job creation request...
1330 */
6875feac 1331
2ec940b5 1332 if (job_canceled)
1333 break;
753453e4 1334
5ed93a63 1335 request = new_request((num_files > 1 || create_job) ? IPP_CREATE_JOB :
1336 IPP_PRINT_JOB,
1337 version, uri, argv[2], monitor.job_name, num_options,
1338 options, compression, copies_sup ? copies : 1,
1339 document_format, pc, media_col_sup, doc_handling_sup);
c8f9565c 1340
cb555bcf 1341 /*
b5cb0608 1342 * Do the request...
cb555bcf 1343 */
1344
5ed93a63 1345 if (num_files > 1 || create_job)
072c4872 1346 response = cupsDoRequest(http, request, resource);
b5cb0608 1347 else
6875feac 1348 {
940afb4b 1349 size_t length = 0; /* Length of request */
1350
1351 if (compatsize > 0)
1352 {
1353 fputs("DEBUG: Sending file using HTTP/1.0 Content-Length...\n", stderr);
1354 length = ippLength(request) + (size_t)compatsize;
1355 }
1356 else
1357 fputs("DEBUG: Sending file using HTTP/1.1 chunking...\n", stderr);
1358
1359 http_status = cupsSendRequest(http, request, resource, length);
6875feac 1360 if (http_status == HTTP_CONTINUE && request->state == IPP_DATA)
1361 {
1362 if (num_files == 1)
4ed00dc2 1363 {
1364 if ((fd = open(files[0], O_RDONLY)) < 0)
1365 {
1366 _cupsLangPrintError("ERROR", _("Unable to open print file"));
1367 return (CUPS_BACKEND_FAILED);
1368 }
1369 }
6875feac 1370 else
6875feac 1371 {
7b64ad64 1372 fd = 0;
1373 http_status = cupsWriteRequestData(http, buffer, bytes);
1374 }
6875feac 1375
06efaeda 1376 while (http_status == HTTP_CONTINUE &&
1377 (!job_canceled || compatsize > 0))
7b64ad64 1378 {
0663fa1c 1379 /*
1380 * Check for side-channel requests and more print data...
1381 */
1382
1383 FD_ZERO(&input);
1384 FD_SET(fd, &input);
1385 FD_SET(snmp_fd, &input);
1386
1387 while (select(fd > snmp_fd ? fd + 1 : snmp_fd + 1, &input, NULL, NULL,
1388 NULL) <= 0 && !job_canceled);
1389
1390 if (FD_ISSET(snmp_fd, &input))
6875feac 1391 backendCheckSideChannel(snmp_fd, http->hostaddr);
0663fa1c 1392
1393 if (FD_ISSET(fd, &input))
7b64ad64 1394 {
1395 if ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
1396 {
1397 fprintf(stderr, "DEBUG: Read %d bytes...\n", (int)bytes);
1398
1399 if (cupsWriteRequestData(http, buffer, bytes) != HTTP_CONTINUE)
1400 break;
1401 }
1402 else if (bytes == 0 || (errno != EINTR && errno != EAGAIN))
1403 break;
1404 }
6875feac 1405 }
1406
1407 if (num_files == 1)
1408 close(fd);
1409 }
1410
1411 response = cupsGetResponse(http, resource);
1412 ippDelete(request);
1413 }
072c4872 1414
1415 ipp_status = cupsLastError();
b5cb0608 1416
5fb23099 1417 fprintf(stderr, "DEBUG: %s: %s (%s)\n",
5ed93a63 1418 (num_files > 1 || create_job) ? "Create-Job" : "Print-Job",
5fb23099 1419 ippErrorString(ipp_status), cupsLastErrorString());
1420
b5cb0608 1421 if (ipp_status > IPP_OK_CONFLICT)
1422 {
753453e4 1423 job_id = 0;
1424
6875feac 1425 if (job_canceled)
072c4872 1426 break;
1427
b5cb0608 1428 if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
084c8c5c 1429 ipp_status == IPP_NOT_POSSIBLE ||
b5cb0608 1430 ipp_status == IPP_PRINTER_BUSY)
1431 {
5fb23099 1432 _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
b5cb0608 1433 sleep(10);
7cfa02ab 1434
1435 if (num_files == 0)
1436 {
1437 /*
1438 * We can't re-submit when we have no files to print, so exit
1439 * immediately with the right status code...
1440 */
1441
1442 goto cleanup;
1443 }
b5cb0608 1444 }
72bdcfc1 1445 else if (ipp_status == IPP_ERROR_JOB_CANCELED)
1446 goto cleanup;
b5cb0608 1447 else
ebac5c9b 1448 {
1449 /*
1450 * Update auth-info-required as needed...
1451 */
1452
4c4eea89 1453 _cupsLangPrintFilter(stderr, "ERROR",
5fb23099 1454 _("Print file was not accepted."));
ebac5c9b 1455
4233257e 1456 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
ebac5c9b 1457 {
1458 fprintf(stderr, "DEBUG: WWW-Authenticate=\"%s\"\n",
1459 httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE));
1460
4233257e 1461 /*
1462 * Normal authentication goes through the password callback, which sets
1463 * auth_info_required to "username,password". Kerberos goes directly
1464 * through GSSAPI, so look for Negotiate in the WWW-Authenticate header
1465 * here and set auth_info_required as needed...
1466 */
1467
ebac5c9b 1468 if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
1469 "Negotiate", 9))
4233257e 1470 auth_info_required = "negotiate";
ebac5c9b 1471 }
5fb23099 1472 else
1473 sleep(10);
1474
1475 if (num_files == 0)
1476 {
1477 /*
1478 * We can't re-submit when we have no files to print, so exit
1479 * immediately with the right status code...
1480 */
1481
1482 goto cleanup;
1483 }
ebac5c9b 1484 }
b5cb0608 1485 }
1486 else if ((job_id_attr = ippFindAttribute(response, "job-id",
1487 IPP_TAG_INTEGER)) == NULL)
97fcaf92 1488 {
4c4eea89 1489 _cupsLangPrintFilter(stderr, "INFO",
1490 _("Print file accepted - job ID unknown."));
7b1ef0fc 1491 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1492 "cups-ipp-missing-job-id");
b5cb0608 1493 job_id = 0;
1494 }
1495 else
1496 {
b9738d7c 1497 monitor.job_id = job_id = job_id_attr->values[0].integer;
4c4eea89 1498 _cupsLangPrintFilter(stderr, "INFO",
1499 _("Print file accepted - job ID %d."), job_id);
97fcaf92 1500 }
cb555bcf 1501
5ed93a63 1502 fprintf(stderr, "DEBUG: job-id=%d\n", job_id);
072c4872 1503 ippDelete(response);
1504
6875feac 1505 if (job_canceled)
072c4872 1506 break;
1507
5ed93a63 1508 if (job_id && (num_files > 1 || create_job))
072c4872 1509 {
5ed93a63 1510 for (i = 0; num_files == 0 || i < num_files; i ++)
072c4872 1511 {
56f7a531 1512 /*
1513 * Check for side-channel requests...
1514 */
1515
1516 backendCheckSideChannel(snmp_fd, http->hostaddr);
1517
1518 /*
1519 * Send the next file in the job...
1520 */
1521
072c4872 1522 request = ippNewRequest(IPP_SEND_DOCUMENT);
22d59a30 1523 request->request.op.version[0] = version / 10;
1524 request->request.op.version[1] = version % 10;
072c4872 1525
1526 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1527 NULL, uri);
1528
1529 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1530 job_id);
1531
1532 if (argv[2][0])
1533 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1534 "requesting-user-name", NULL, argv[2]);
1535
5ed93a63 1536 if ((i + 1) >= num_files)
072c4872 1537 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
1538
1539 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
5ed93a63 1540 "document-format", NULL, document_format);
072c4872 1541
6875feac 1542 fprintf(stderr, "DEBUG: Sending file %d using chunking...\n", i + 1);
1543 http_status = cupsSendRequest(http, request, resource, 0);
5ed93a63 1544 if (http_status == HTTP_CONTINUE && request->state == IPP_DATA)
1545 {
1546 if (num_files == 0)
1547 {
1548 fd = 0;
1549 http_status = cupsWriteRequestData(http, buffer, bytes);
1550 }
1551 else
4ed00dc2 1552 {
1553 if ((fd = open(files[i], O_RDONLY)) < 0)
1554 {
1555 _cupsLangPrintError("ERROR", _("Unable to open print file"));
1556 return (CUPS_BACKEND_FAILED);
1557 }
1558 }
5ed93a63 1559 }
1560 else
1561 fd = -1;
1562
1563 if (fd >= 0)
6875feac 1564 {
06efaeda 1565 while (!job_canceled &&
1566 (bytes = read(fd, buffer, sizeof(buffer))) > 0)
6875feac 1567 {
e6af41cc 1568 if (cupsWriteRequestData(http, buffer, bytes) != HTTP_CONTINUE)
6875feac 1569 break;
1570 else
1571 {
1572 /*
1573 * Check for side-channel requests...
1574 */
1575
1576 backendCheckSideChannel(snmp_fd, http->hostaddr);
1577 }
1578 }
1579
5ed93a63 1580 if (fd > 0)
1581 close(fd);
6875feac 1582 }
acd7eb22 1583
6875feac 1584 ippDelete(cupsGetResponse(http, resource));
1585 ippDelete(request);
072c4872 1586
5fb23099 1587 fprintf(stderr, "DEBUG: Send-Document: %s (%s)\n",
1588 ippErrorString(cupsLastError()), cupsLastErrorString());
1589
072c4872 1590 if (cupsLastError() > IPP_OK_CONFLICT)
1591 {
1592 ipp_status = cupsLastError();
1593
4c4eea89 1594 _cupsLangPrintFilter(stderr, "ERROR",
5fb23099 1595 _("Unable to add document to print job."));
072c4872 1596 break;
1597 }
5ed93a63 1598 else if (num_files == 0 || fd < 0)
1599 break;
072c4872 1600 }
1601 }
b5cb0608 1602
753453e4 1603 if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
b5cb0608 1604 {
1605 fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
37de726f 1606 copies_remaining --;
b5cb0608 1607 }
ab827512 1608 else if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
084c8c5c 1609 ipp_status == IPP_NOT_POSSIBLE ||
ab827512 1610 ipp_status == IPP_PRINTER_BUSY)
a04d2365 1611 continue;
ab827512 1612 else
37de726f 1613 copies_remaining --;
8ce7c000 1614
c8f9565c 1615 /*
b5cb0608 1616 * Wait for the job to complete...
c8f9565c 1617 */
1618
897922a9 1619 if (!job_id || !waitjob)
b5cb0608 1620 continue;
1621
4c4eea89 1622 _cupsLangPrintFilter(stderr, "INFO", _("Waiting for job to complete."));
b5cb0608 1623
fc596e79 1624 for (delay = _cupsNextDelay(0, &prev_delay); !job_canceled;)
c8f9565c 1625 {
56f7a531 1626 /*
1627 * Check for side-channel requests...
1628 */
1629
1630 backendCheckSideChannel(snmp_fd, http->hostaddr);
1631
97fcaf92 1632 /*
b5cb0608 1633 * Build an IPP_GET_JOB_ATTRIBUTES request...
97fcaf92 1634 */
1635
e12df069 1636 request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
22d59a30 1637 request->request.op.version[0] = version / 10;
1638 request->request.op.version[1] = version % 10;
97fcaf92 1639
b5cb0608 1640 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1641 NULL, uri);
3f9cb6c6 1642
b5cb0608 1643 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1644 job_id);
c8f9565c 1645
ee8b7dd3 1646 if (argv[2][0])
897922a9 1647 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1648 "requesting-user-name", NULL, argv[2]);
ee8b7dd3 1649
8f4595eb 1650 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1651 "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
1652 NULL, jattrs);
97fcaf92 1653
1654 /*
b5cb0608 1655 * Do the request...
97fcaf92 1656 */
1657
fc596e79 1658 httpReconnect(http);
072c4872 1659 response = cupsDoRequest(http, request, resource);
1660 ipp_status = cupsLastError();
97fcaf92 1661
b5cb0608 1662 if (ipp_status == IPP_NOT_FOUND)
97fcaf92 1663 {
b5cb0608 1664 /*
d1c2727f 1665 * Job has gone away and/or the server has no job history...
b5cb0608 1666 */
97fcaf92 1667
7b1ef0fc 1668 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1669 "cups-ipp-missing-job-history");
b5cb0608 1670 ippDelete(response);
d1c2727f 1671
1672 ipp_status = IPP_OK;
b5cb0608 1673 break;
97fcaf92 1674 }
1675
5fb23099 1676 fprintf(stderr, "DEBUG: Get-Job-Attributes: %s (%s)\n",
1677 ippErrorString(ipp_status), cupsLastErrorString());
1678
b5cb0608 1679 if (ipp_status > IPP_OK_CONFLICT)
6a536282 1680 {
b5cb0608 1681 if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
084c8c5c 1682 ipp_status != IPP_NOT_POSSIBLE &&
b5cb0608 1683 ipp_status != IPP_PRINTER_BUSY)
97fcaf92 1684 {
072c4872 1685 ippDelete(response);
b5cb0608 1686
4c4eea89 1687 _cupsLangPrintFilter(stderr, "ERROR",
5fb23099 1688 _("Unable to get print job status."));
b5cb0608 1689 break;
97fcaf92 1690 }
6a536282 1691 }
8f4595eb 1692
072c4872 1693 if (response)
97fcaf92 1694 {
8f4595eb 1695 if ((job_state = ippFindAttribute(response, "job-state",
1696 IPP_TAG_ENUM)) != NULL)
97fcaf92 1697 {
6c1b8723 1698 /*
1699 * Reflect the remote job state in the local queue...
1700 */
1701
7b1ef0fc 1702 if (cups_version &&
1703 job_state->values[0].integer >= IPP_JOB_PENDING &&
1704 job_state->values[0].integer <= IPP_JOB_COMPLETED)
1705 update_reasons(NULL,
1706 remote_job_states[job_state->values[0].integer -
1707 IPP_JOB_PENDING]);
f8c0cc95 1708
1709 if ((job_sheets = ippFindAttribute(response,
1710 "job-media-sheets-completed",
1711 IPP_TAG_INTEGER)) == NULL)
1712 job_sheets = ippFindAttribute(response,
1713 "job-impressions-completed",
1714 IPP_TAG_INTEGER);
1715
1716 if (job_sheets)
1717 fprintf(stderr, "PAGE: total %d\n",
1718 job_sheets->values[0].integer);
6c1b8723 1719
8f4595eb 1720 /*
1721 * Stop polling if the job is finished or pending-held...
1722 */
1723
7951315f 1724 if (job_state->values[0].integer > IPP_JOB_STOPPED)
8f4595eb 1725 {
1726 ippDelete(response);
1727 break;
1728 }
97fcaf92 1729 }
084c8c5c 1730 else if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
1731 ipp_status != IPP_NOT_POSSIBLE &&
1732 ipp_status != IPP_PRINTER_BUSY)
f3fdb6b2 1733 {
1734 /*
1735 * If the printer does not return a job-state attribute, it does not
1736 * conform to the IPP specification - break out immediately and fail
1737 * the job...
1738 */
1739
7b1ef0fc 1740 update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
1741 "cups-ipp-missing-job-state");
f3fdb6b2 1742 ipp_status = IPP_INTERNAL_ERROR;
1743 break;
1744 }
97fcaf92 1745 }
1746
072c4872 1747 ippDelete(response);
d1c2727f 1748
b5cb0608 1749 /*
fc596e79 1750 * Wait before polling again...
d1c2727f 1751 */
97fcaf92 1752
2ce8588b 1753 sleep(delay);
1754
fc596e79 1755 delay = _cupsNextDelay(delay, &prev_delay);
97fcaf92 1756 }
c8f9565c 1757 }
1758
6bb5a528 1759 /*
072c4872 1760 * Cancel the job as needed...
6bb5a528 1761 */
1762
6875feac 1763 if (job_canceled && job_id)
072c4872 1764 cancel_job(http, uri, job_id, resource, argv[2], version);
1765
1766 /*
1767 * Check the printer state and report it if necessary...
1768 */
6bb5a528 1769
4a241564 1770 check_printer_state(http, uri, resource, argv[2], version);
6bb5a528 1771
eab5b04e 1772 /*
1773 * Collect the final page count as needed...
1774 */
1775
aa141f0d 1776 if (have_supplies &&
eab5b04e 1777 !backendSNMPSupplies(snmp_fd, http->hostaddr, &page_count, NULL) &&
1778 page_count > start_count)
1779 fprintf(stderr, "PAGE: total %d\n", page_count - start_count);
1780
4233257e 1781#ifdef HAVE_GSSAPI
1782 /*
1783 * See if we used Kerberos at all...
1784 */
1785
1786 if (http->gssctx)
1787 auth_info_required = "negotiate";
1788#endif /* HAVE_GSSAPI */
1789
c8f9565c 1790 /*
1791 * Free memory...
1792 */
1793
2ec940b5 1794 cleanup:
1795
1796 cupsFreeOptions(num_options, options);
8211508e 1797 _ppdCacheDestroy(pc);
2ec940b5 1798
c8f9565c 1799 httpClose(http);
c8f9565c 1800
072c4872 1801 ippDelete(supported);
2922de55 1802
c8f9565c 1803 /*
b8f3410f 1804 * Remove the temporary file(s) if necessary...
c8f9565c 1805 */
1806
940afb4b 1807 if (tmpfilename[0])
1808 unlink(tmpfilename);
1809
1230a8a0 1810#ifdef HAVE_LIBZ
1811 if (compression)
1812 {
1813 for (i = 0; i < num_files; i ++)
1814 unlink(files[i]);
1815 }
1816#endif /* HAVE_LIBZ */
1817
c8f9565c 1818 /*
1819 * Return the queue status...
1820 */
1821
c55dc24c 1822 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
d0eaf635 1823 ipp_status == IPP_AUTHENTICATION_CANCELED ||
c55dc24c 1824 ipp_status <= IPP_OK_CONFLICT)
1825 fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
4233257e 1826
d0eaf635 1827 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
1828 ipp_status == IPP_AUTHENTICATION_CANCELED)
3fe655a3 1829 return (CUPS_BACKEND_AUTH_REQUIRED);
f3fdb6b2 1830 else if (ipp_status == IPP_INTERNAL_ERROR)
1831 return (CUPS_BACKEND_STOP);
623108b2 1832 else if (ipp_status == IPP_DOCUMENT_FORMAT ||
1833 ipp_status == IPP_CONFLICT)
1834 return (CUPS_BACKEND_FAILED);
e0d12789 1835 else if (ipp_status > IPP_OK_CONFLICT && ipp_status != IPP_ERROR_JOB_CANCELED)
5fb23099 1836 return (CUPS_BACKEND_RETRY_CURRENT);
3fe655a3 1837 else
83d83f7a 1838 {
4c4eea89 1839 _cupsLangPrintFilter(stderr, "INFO", _("Ready to print."));
3fe655a3 1840 return (CUPS_BACKEND_OK);
83d83f7a 1841 }
b5cb0608 1842}
1843
1844
072c4872 1845/*
1846 * 'cancel_job()' - Cancel a print job.
1847 */
1848
1849static void
1850cancel_job(http_t *http, /* I - HTTP connection */
1851 const char *uri, /* I - printer-uri */
1852 int id, /* I - job-id */
1853 const char *resource, /* I - Resource path */
1854 const char *user, /* I - requesting-user-name */
1855 int version) /* I - IPP version */
1856{
1857 ipp_t *request; /* Cancel-Job request */
1858
1859
4c4eea89 1860 _cupsLangPrintFilter(stderr, "INFO", _("Canceling print job."));
072c4872 1861
1862 request = ippNewRequest(IPP_CANCEL_JOB);
22d59a30 1863 request->request.op.version[0] = version / 10;
1864 request->request.op.version[1] = version % 10;
072c4872 1865
1866 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1867 NULL, uri);
1868 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
1869
1870 if (user && user[0])
1871 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1872 "requesting-user-name", NULL, user);
1873
1874 /*
1875 * Do the request...
1876 */
1877
1878 ippDelete(cupsDoRequest(http, request, resource));
1879
1880 if (cupsLastError() > IPP_OK_CONFLICT)
5fb23099 1881 _cupsLangPrintFilter(stderr, "ERROR", _("Unable to cancel print job."));
072c4872 1882}
1883
1884
6bb5a528 1885/*
b9738d7c 1886 * 'check_printer_state()' - Check the printer state.
6bb5a528 1887 */
1888
b9738d7c 1889static ipp_pstate_t /* O - Current printer-state */
e12df069 1890check_printer_state(
1891 http_t *http, /* I - HTTP connection */
1892 const char *uri, /* I - Printer URI */
1893 const char *resource, /* I - Resource path */
1894 const char *user, /* I - Username, if any */
4a241564 1895 int version) /* I - IPP version */
1896 {
b9738d7c 1897 ipp_t *request, /* IPP request */
1898 *response; /* IPP response */
1899 ipp_attribute_t *attr; /* Attribute in response */
1900 ipp_pstate_t printer_state = IPP_PRINTER_STOPPED;
1901 /* Current printer-state */
6bb5a528 1902
1903
1904 /*
b9738d7c 1905 * Send a Get-Printer-Attributes request and log the results...
6bb5a528 1906 */
1907
e12df069 1908 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
22d59a30 1909 request->request.op.version[0] = version / 10;
1910 request->request.op.version[1] = version % 10;
6bb5a528 1911
1912 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
b9738d7c 1913 NULL, uri);
6bb5a528 1914
1915 if (user && user[0])
1916 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
b9738d7c 1917 "requesting-user-name", NULL, user);
6bb5a528 1918
a6ccc6e8 1919 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
b9738d7c 1920 "requested-attributes",
1921 (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
6bb5a528 1922
1923 if ((response = cupsDoRequest(http, request, resource)) != NULL)
1924 {
4a241564 1925 report_printer_state(response);
b9738d7c 1926
1927 if ((attr = ippFindAttribute(response, "printer-state",
1928 IPP_TAG_ENUM)) != NULL)
1929 printer_state = (ipp_pstate_t)attr->values[0].integer;
1930
6bb5a528 1931 ippDelete(response);
1932 }
b9738d7c 1933
5fb23099 1934 fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
1935 ippErrorString(cupsLastError()), cupsLastErrorString());
1936
b9738d7c 1937 /*
1938 * Return the printer-state value...
1939 */
1940
1941 return (printer_state);
6bb5a528 1942}
1943
1944
1230a8a0 1945#ifdef HAVE_LIBZ
1946/*
959a6a28 1947 * 'compress_files()' - Compress print files.
1230a8a0 1948 */
1949
1950static void
1951compress_files(int num_files, /* I - Number of files */
1952 char **files) /* I - Files */
1953{
1954 int i, /* Looping var */
1955 fd; /* Temporary file descriptor */
1956 ssize_t bytes; /* Bytes read/written */
1957 size_t total; /* Total bytes read */
1958 cups_file_t *in, /* Input file */
1959 *out; /* Output file */
1960 struct stat outinfo; /* Output file information */
1961 char filename[1024], /* Temporary filename */
1fb9f3e8 1962 buffer[32768]; /* Copy buffer */
1230a8a0 1963
1964
1965 fprintf(stderr, "DEBUG: Compressing %d job files...\n", num_files);
1966 for (i = 0; i < num_files; i ++)
1967 {
1968 if ((fd = cupsTempFd(filename, sizeof(filename))) < 0)
1969 {
4c4eea89 1970 _cupsLangPrintError("ERROR", _("Unable to create compressed print file"));
1230a8a0 1971 exit(CUPS_BACKEND_FAILED);
1972 }
1973
1974 if ((out = cupsFileOpenFd(fd, "w9")) == NULL)
1975 {
4c4eea89 1976 _cupsLangPrintError("ERROR", _("Unable to open compressed print file"));
1230a8a0 1977 exit(CUPS_BACKEND_FAILED);
1978 }
1979
1980 if ((in = cupsFileOpen(files[i], "r")) == NULL)
1981 {
4c4eea89 1982 _cupsLangPrintError("ERROR", _("Unable to open print file"));
1230a8a0 1983 cupsFileClose(out);
1984 exit(CUPS_BACKEND_FAILED);
1985 }
1986
1987 total = 0;
1988 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
1989 if (cupsFileWrite(out, buffer, bytes) < bytes)
1990 {
4c4eea89 1991 _cupsLangPrintError("ERROR",
1992 _("Unable to generate compressed print file"));
1230a8a0 1993 cupsFileClose(in);
1994 cupsFileClose(out);
1995 exit(CUPS_BACKEND_FAILED);
1996 }
1997 else
1998 total += bytes;
1999
2000 cupsFileClose(out);
2001 cupsFileClose(in);
2002
2003 files[i] = strdup(filename);
2004
2005 if (!stat(filename, &outinfo))
ff0295f0 2006 fprintf(stderr,
2007 "DEBUG: File %d compressed to %.1f%% of original size, "
2008 CUPS_LLFMT " bytes...\n",
2009 i + 1, 100.0 * outinfo.st_size / total,
2010 CUPS_LLCAST outinfo.st_size);
1230a8a0 2011 }
2012}
2013#endif /* HAVE_LIBZ */
2014
2015
b9738d7c 2016/*
959a6a28 2017 * 'monitor_printer()' - Monitor the printer state.
b9738d7c 2018 */
2019
2020static void * /* O - Thread exit code */
2021monitor_printer(
2022 _cups_monitor_t *monitor) /* I - Monitoring data */
2023{
2024 http_t *http; /* Connection to printer */
2025 ipp_t *request, /* IPP request */
2026 *response; /* IPP response */
2027 ipp_attribute_t *attr; /* Attribute in response */
2028 int delay, /* Current delay */
8211508e 2029 prev_delay; /* Previous delay */
5ed93a63 2030 ipp_op_t job_op; /* Operation to use */
2031 int job_id; /* Job ID */
2032 const char *job_name; /* Job name */
2033 ipp_jstate_t job_state; /* Job state */
2034 const char *job_user; /* Job originating user name */
b9738d7c 2035
2036
2037 /*
2038 * Make a copy of the printer connection...
2039 */
2040
cce0044f 2041 http = _httpCreate(monitor->hostname, monitor->port, NULL, monitor->encryption,
16f98e36 2042 AF_UNSPEC);
1179bc03 2043 httpSetTimeout(http, 30.0, timeout_cb, NULL);
b9738d7c 2044 cupsSetPasswordCB(password_cb);
2045
2046 /*
2047 * Loop until the job is canceled, aborted, or completed.
2048 */
2049
fc596e79 2050 delay = _cupsNextDelay(0, &prev_delay);
b9738d7c 2051
2052 while (monitor->job_state < IPP_JOB_CANCELED && !job_canceled)
2053 {
2054 /*
2055 * Reconnect to the printer...
2056 */
2057
2058 if (!httpReconnect(http))
2059 {
2060 /*
2061 * Connected, so check on the printer state...
2062 */
2063
2064 monitor->printer_state = check_printer_state(http, monitor->uri,
2065 monitor->resource,
2066 monitor->user,
4a241564 2067 monitor->version);
b9738d7c 2068
5ed93a63 2069 /*
2070 * Check the status of the job itself...
2071 */
b9738d7c 2072
5ed93a63 2073 job_op = monitor->job_id > 0 ? IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS;
2074 request = ippNewRequest(job_op);
2075 request->request.op.version[0] = monitor->version / 10;
2076 request->request.op.version[1] = monitor->version % 10;
b9738d7c 2077
5ed93a63 2078 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2079 NULL, monitor->uri);
2080 if (job_op == IPP_GET_JOB_ATTRIBUTES)
b9738d7c 2081 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
5ed93a63 2082 monitor->job_id);
b9738d7c 2083
5ed93a63 2084 if (monitor->user && monitor->user[0])
2085 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
2086 "requesting-user-name", NULL, monitor->user);
b9738d7c 2087
5ed93a63 2088 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2089 "requested-attributes",
2090 (int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs);
b9738d7c 2091
5ed93a63 2092 /*
2093 * Do the request...
2094 */
b9738d7c 2095
5ed93a63 2096 response = cupsDoRequest(http, request, monitor->resource);
b9738d7c 2097
5ed93a63 2098 fprintf(stderr, "DEBUG: %s: %s (%s)\n", ippOpString(job_op),
2099 ippErrorString(cupsLastError()), cupsLastErrorString());
5fb23099 2100
5ed93a63 2101 if (job_op == IPP_GET_JOB_ATTRIBUTES)
2102 {
b9738d7c 2103 if ((attr = ippFindAttribute(response, "job-state",
2104 IPP_TAG_ENUM)) != NULL)
2105 monitor->job_state = (ipp_jstate_t)attr->values[0].integer;
2106 else
2107 monitor->job_state = IPP_JOB_COMPLETED;
5ed93a63 2108 }
ceb8f840 2109 else if (response)
5ed93a63 2110 {
2111 for (attr = response->attrs; attr; attr = attr->next)
2112 {
2113 job_id = 0;
2114 job_name = NULL;
2115 job_state = IPP_JOB_PENDING;
2116 job_user = NULL;
2117
2118 while (attr && attr->group_tag != IPP_TAG_JOB)
2119 attr = attr->next;
b9738d7c 2120
5ed93a63 2121 if (!attr)
2122 break;
2123
2124 while (attr && attr->group_tag == IPP_TAG_JOB)
2125 {
2126 if (!strcmp(attr->name, "job-id") &&
2127 attr->value_tag == IPP_TAG_INTEGER)
2128 job_id = attr->values[0].integer;
2129 else if (!strcmp(attr->name, "job-name") &&
2130 (attr->value_tag == IPP_TAG_NAME ||
2131 attr->value_tag == IPP_TAG_NAMELANG))
2132 job_name = attr->values[0].string.text;
2133 else if (!strcmp(attr->name, "job-state") &&
2134 attr->value_tag == IPP_TAG_ENUM)
2135 job_state = attr->values[0].integer;
2136 else if (!strcmp(attr->name, "job-originating-user-name") &&
2137 (attr->value_tag == IPP_TAG_NAME ||
2138 attr->value_tag == IPP_TAG_NAMELANG))
2139 job_user = attr->values[0].string.text;
2140
2141 attr = attr->next;
2142 }
2143
2144 if (job_id > 0 && job_name && !strcmp(job_name, monitor->job_name) &&
2145 job_user && monitor->user && !strcmp(job_user, monitor->user))
2146 {
2147 monitor->job_id = job_id;
2148 monitor->job_state = job_state;
2149 break;
2150 }
2151
2152 if (!attr)
2153 break;
2154 }
b9738d7c 2155 }
2156
5ed93a63 2157 ippDelete(response);
2158
b9738d7c 2159 /*
2160 * Disconnect from the printer - we'll reconnect on the next poll...
2161 */
2162
2163 _httpDisconnect(http);
2164 }
2165
2166 /*
fc596e79 2167 * Sleep for N seconds...
b9738d7c 2168 */
2169
2170 sleep(delay);
2171
fc596e79 2172 delay = _cupsNextDelay(delay, &prev_delay);
b9738d7c 2173 }
2174
5ed93a63 2175 /*
2176 * Cancel the job if necessary...
2177 */
2178
2179 if (job_canceled && monitor->job_id > 0)
2180 if (!httpReconnect(http))
2181 cancel_job(http, monitor->uri, monitor->job_id, monitor->resource,
2182 monitor->user, monitor->version);
2183
b9738d7c 2184 /*
2185 * Cleanup and return...
2186 */
2187
2188 httpClose(http);
2189
2190 return (NULL);
2191}
2192
2193
2ec940b5 2194/*
2195 * 'new_request()' - Create a new print creation or validation request.
2196 */
2197
2198static ipp_t * /* O - Request data */
2199new_request(
2200 ipp_op_t op, /* I - IPP operation code */
2201 int version, /* I - IPP version number */
2202 const char *uri, /* I - printer-uri value */
2203 const char *user, /* I - requesting-user-name value */
2204 const char *title, /* I - job-name value */
2205 int num_options, /* I - Number of options to send */
2206 cups_option_t *options, /* I - Options to send */
2207 const char *compression, /* I - compression value or NULL */
aa141f0d 2208 int copies, /* I - copies value or 0 */
03ab99b4 2209 const char *format, /* I - document-format value or NULL */
8211508e 2210 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
adbcb02a 2211 ipp_attribute_t *media_col_sup, /* I - media-col-supported values */
2212 ipp_attribute_t *doc_handling_sup) /* I - multiple-document-handling-supported values */
2ec940b5 2213{
2214 int i; /* Looping var */
2215 ipp_t *request; /* Request data */
2216 const char *keyword; /* PWG keyword */
2217 _pwg_size_t *size; /* PWG media size */
2218 ipp_t *media_col, /* media-col value */
2219 *media_size; /* media-size value */
2220 const char *media_source, /* media-source value */
adbcb02a 2221 *media_type, /* media-type value */
2222 *collate_str; /* multiple-document-handling value */
2ec940b5 2223
2224
2225 /*
2226 * Create the IPP request...
2227 */
2228
2229 request = ippNewRequest(op);
2230 request->request.op.version[0] = version / 10;
2231 request->request.op.version[1] = version % 10;
2232
2233 fprintf(stderr, "DEBUG: %s IPP/%d.%d\n",
2234 ippOpString(request->request.op.operation_id),
2235 request->request.op.version[0],
2236 request->request.op.version[1]);
2237
2238 /*
2239 * Add standard attributes...
2240 */
2241
2242 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2243 NULL, uri);
2244 fprintf(stderr, "DEBUG: printer-uri=\"%s\"\n", uri);
2245
2246 if (user && *user)
2247 {
2248 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
2249 "requesting-user-name", NULL, user);
2250 fprintf(stderr, "DEBUG: requesting-user-name=\"%s\"\n", user);
2251 }
2252
2253 if (title && *title)
2254 {
2255 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
2256 title);
2257 fprintf(stderr, "DEBUG: job-name=\"%s\"\n", title);
2258 }
2259
2260 if (format)
2261 {
2262 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
2263 "document-format", NULL, format);
2264 fprintf(stderr, "DEBUG: document-format=\"%s\"\n", format);
2265 }
2266
2267#ifdef HAVE_LIBZ
2268 if (compression)
2269 {
2270 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2271 "compression", NULL, compression);
2272 fprintf(stderr, "DEBUG: compression=\"%s\"\n", compression);
2273 }
2274#endif /* HAVE_LIBZ */
2275
2276 /*
2277 * Handle options on the command-line...
2278 */
2279
2280 if (num_options > 0)
2281 {
8211508e 2282 if (pc)
2ec940b5 2283 {
10a541ae 2284 int num_finishings = 0, /* Number of finishing values */
2285 finishings[10]; /* Finishing enum values */
2286
2ec940b5 2287 /*
2288 * Send standard IPP attributes...
2289 */
2290
2291 if ((keyword = cupsGetOption("PageSize", num_options, options)) == NULL)
2292 keyword = cupsGetOption("media", num_options, options);
2293
8211508e 2294 if ((size = _ppdCacheGetSize(pc, keyword)) != NULL)
2ec940b5 2295 {
2296 /*
2297 * Add a media-col value...
2298 */
2299
2300 media_size = ippNew();
2301 ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2302 "x-dimension", size->width);
2303 ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2304 "y-dimension", size->length);
2305
2306 media_col = ippNew();
2307 ippAddCollection(media_col, IPP_TAG_ZERO, "media-size", media_size);
2308
8211508e 2309 media_source = _ppdCacheGetSource(pc, cupsGetOption("InputSlot",
2310 num_options,
2311 options));
2312 media_type = _ppdCacheGetType(pc, cupsGetOption("MediaType",
2313 num_options,
2314 options));
2ec940b5 2315
2316 for (i = 0; i < media_col_sup->num_values; i ++)
2317 {
2318 if (!strcmp(media_col_sup->values[i].string.text,
2319 "media-left-margin"))
2320 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2321 "media-left-margin", size->left);
2322 else if (!strcmp(media_col_sup->values[i].string.text,
2323 "media-bottom-margin"))
2324 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
59075862 2325 "media-bottom-margin", size->bottom);
2ec940b5 2326 else if (!strcmp(media_col_sup->values[i].string.text,
2327 "media-right-margin"))
2328 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
59075862 2329 "media-right-margin", size->right);
2ec940b5 2330 else if (!strcmp(media_col_sup->values[i].string.text,
2331 "media-top-margin"))
2332 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
59075862 2333 "media-top-margin", size->top);
2ec940b5 2334 else if (!strcmp(media_col_sup->values[i].string.text,
2335 "media-source") && media_source)
2336 ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD,
2337 "media-source", NULL, media_source);
2338 else if (!strcmp(media_col_sup->values[i].string.text,
2339 "media-type") && media_type)
2340 ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD,
2341 "media-type", NULL, media_type);
2342 }
2343
2344 ippAddCollection(request, IPP_TAG_JOB, "media-col", media_col);
2345 }
2346
2347 if ((keyword = cupsGetOption("output-bin", num_options,
2348 options)) == NULL)
8211508e 2349 keyword = _ppdCacheGetBin(pc, cupsGetOption("OutputBin", num_options,
2350 options));
2ec940b5 2351
2352 if (keyword)
2353 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-bin",
2354 NULL, keyword);
2355
2356 if ((keyword = cupsGetOption("output-mode", num_options,
2357 options)) != NULL)
2358 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2359 NULL, keyword);
2360 else if ((keyword = cupsGetOption("ColorModel", num_options,
2361 options)) != NULL)
2362 {
c6fab96f 2363 if (!_cups_strcasecmp(keyword, "Gray"))
2ec940b5 2364 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2365 NULL, "monochrome");
2366 else
2367 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2368 NULL, "color");
2369 }
2370
2371 if ((keyword = cupsGetOption("print-quality", num_options,
2372 options)) != NULL)
2373 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2374 atoi(keyword));
2375 else if ((keyword = cupsGetOption("cupsPrintQuality", num_options,
2376 options)) != NULL)
2377 {
c6fab96f 2378 if (!_cups_strcasecmp(keyword, "draft"))
2ec940b5 2379 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2380 IPP_QUALITY_DRAFT);
c6fab96f 2381 else if (!_cups_strcasecmp(keyword, "normal"))
2ec940b5 2382 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2383 IPP_QUALITY_NORMAL);
c6fab96f 2384 else if (!_cups_strcasecmp(keyword, "high"))
2ec940b5 2385 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2386 IPP_QUALITY_HIGH);
2387 }
2388
2389 if ((keyword = cupsGetOption("sides", num_options, options)) != NULL)
2390 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2391 NULL, keyword);
8211508e 2392 else if (pc->sides_option &&
2393 (keyword = cupsGetOption(pc->sides_option, num_options,
2ec940b5 2394 options)) != NULL)
2395 {
c6fab96f 2396 if (!_cups_strcasecmp(keyword, pc->sides_1sided))
2ec940b5 2397 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2398 NULL, "one-sided");
c6fab96f 2399 else if (!_cups_strcasecmp(keyword, pc->sides_2sided_long))
2ec940b5 2400 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2401 NULL, "two-sided-long-edge");
c6fab96f 2402 if (!_cups_strcasecmp(keyword, pc->sides_2sided_short))
2ec940b5 2403 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2404 NULL, "two-sided-short-edge");
2405 }
adbcb02a 2406
2407 if (doc_handling_sup &&
d0cc23b7 2408 (!format || _cups_strncasecmp(format, "image/", 6)) &&
adbcb02a 2409 (keyword = cupsGetOption("collate", num_options, options)) != NULL)
2410 {
c6fab96f 2411 if (!_cups_strcasecmp(keyword, "true"))
adbcb02a 2412 collate_str = "separate-documents-collated-copies";
2413 else
2414 collate_str = "separate-documents-uncollated-copies";
7b64ad64 2415
adbcb02a 2416 for (i = 0; i < doc_handling_sup->num_values; i ++)
2417 if (!strcmp(doc_handling_sup->values[i].string.text, collate_str))
2418 {
2419 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2420 "multiple-document-handling", NULL, collate_str);
2421 break;
2422 }
2423 }
32f5cbae 2424
10a541ae 2425 /*
2426 * Map finishing options...
2427 */
2428
2429 num_finishings = _ppdCacheGetFinishingValues(pc, num_options, options,
2430 (int)(sizeof(finishings) /
2431 sizeof(finishings[0])),
2432 finishings);
2433 if (num_finishings > 0)
2434 ippAddIntegers(request, IPP_TAG_JOB, IPP_TAG_ENUM, "finishings",
2435 num_finishings, finishings);
5a9015da 2436
2437 /*
2438 * Map FaxOut options...
2439 */
2440
2441 if ((keyword = cupsGetOption("phone", num_options, options)) != NULL)
2442 {
2443 ipp_t *destination; /* destination collection */
2444 char tel_uri[1024]; /* tel: URI */
2445
2446 destination = ippNew();
2447
2448 httpAssembleURI(HTTP_URI_CODING_ALL, tel_uri, sizeof(tel_uri), "tel",
2449 NULL, NULL, 0, keyword);
2450 ippAddString(destination, IPP_TAG_JOB, IPP_TAG_URI, "destination-uri",
2451 NULL, tel_uri);
2452
2453 if ((keyword = cupsGetOption("faxPrefix", num_options,
2454 options)) != NULL && *keyword)
2455 ippAddString(destination, IPP_TAG_JOB, IPP_TAG_TEXT,
2456 "pre-dial-string", NULL, keyword);
2457
2458 ippAddCollection(request, IPP_TAG_JOB, "destination-uris", destination);
2459 ippDelete(destination);
2460 }
2ec940b5 2461 }
2462 else
2463 {
2464 /*
2465 * When talking to another CUPS server, send all options...
2466 */
2467
2468 cupsEncodeOptions(request, num_options, options);
2469 }
2470
2471 if (copies > 1)
2472 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies", copies);
2473 }
2474
2475 return (request);
2476}
2477
2478
b5cb0608 2479/*
2480 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
2481 */
2482
072c4872 2483static const char * /* O - Password */
e12df069 2484password_cb(const char *prompt) /* I - Prompt (not used) */
b5cb0608 2485{
2486 (void)prompt;
2487
4233257e 2488 /*
2489 * Remember that we need to authenticate...
2490 */
2491
2492 auth_info_required = "username,password";
2493
acbc1533 2494 if (password && *password && password_tries < 3)
9c85dfbf 2495 {
2496 password_tries ++;
2497
6248387b 2498 return (password);
9c85dfbf 2499 }
6248387b 2500 else
2501 {
2502 /*
4233257e 2503 * Give up after 3 tries or if we don't have a password to begin with...
6248387b 2504 */
2505
4233257e 2506 return (NULL);
6248387b 2507 }
c8f9565c 2508}
2509
2510
7c7997c3 2511/*
2512 * 'report_attr()' - Report an IPP attribute value.
2513 */
2514
2515static void
2516report_attr(ipp_attribute_t *attr) /* I - Attribute */
2517{
7b1ef0fc 2518 int i; /* Looping var */
2519 char value[1024], /* Value string */
2520 *valptr, /* Pointer into value string */
2521 *attrptr; /* Pointer into attribute value */
2522 const char *cached; /* Cached attribute */
7c7997c3 2523
2524
2525 /*
2526 * Convert the attribute values into quoted strings...
2527 */
2528
2529 for (i = 0, valptr = value;
2530 i < attr->num_values && valptr < (value + sizeof(value) - 10);
2531 i ++)
2532 {
2533 if (i > 0)
2534 *valptr++ = ',';
2535
2536 switch (attr->value_tag)
2537 {
2538 case IPP_TAG_INTEGER :
2539 case IPP_TAG_ENUM :
2540 snprintf(valptr, sizeof(value) - (valptr - value), "%d",
2541 attr->values[i].integer);
2542 valptr += strlen(valptr);
2543 break;
2544
2545 case IPP_TAG_TEXT :
2546 case IPP_TAG_NAME :
2547 case IPP_TAG_KEYWORD :
2548 *valptr++ = '\"';
2549 for (attrptr = attr->values[i].string.text;
2550 *attrptr && valptr < (value + sizeof(value) - 10);
2551 attrptr ++)
2552 {
2553 if (*attrptr == '\\' || *attrptr == '\"')
2554 *valptr++ = '\\';
2555
2556 *valptr++ = *attrptr;
2557 }
2558 *valptr++ = '\"';
2559 break;
2560
2561 default :
2562 /*
2563 * Unsupported value type...
2564 */
2565
2566 return;
2567 }
2568 }
2569
2570 *valptr = '\0';
2571
6ef97027 2572 _cupsMutexLock(&report_mutex);
2573
7b1ef0fc 2574 if ((cached = cupsGetOption(attr->name, num_attr_cache,
2575 attr_cache)) == NULL || strcmp(cached, value))
2576 {
2577 /*
2578 * Tell the scheduler about the new values...
2579 */
7c7997c3 2580
7b1ef0fc 2581 num_attr_cache = cupsAddOption(attr->name, value, num_attr_cache,
2582 &attr_cache);
2583 fprintf(stderr, "ATTR: %s=%s\n", attr->name, value);
2584 }
6ef97027 2585
2586 _cupsMutexUnlock(&report_mutex);
7c7997c3 2587}
2588
2589
c8f9565c 2590/*
d1c2727f 2591 * 'report_printer_state()' - Report the printer state.
2592 */
2593
90d0da15 2594static void
4a241564 2595report_printer_state(ipp_t *ipp) /* I - IPP response */
d1c2727f 2596{
b9738d7c 2597 ipp_attribute_t *pa, /* printer-alert */
2598 *pam, /* printer-alert-message */
2599 *psm, /* printer-state-message */
7c7997c3 2600 *reasons, /* printer-state-reasons */
2601 *marker; /* marker-* attributes */
b9738d7c 2602 char value[1024], /* State/message string */
2603 *valptr; /* Pointer into string */
aa141f0d 2604 static int ipp_supplies = -1;
2605 /* Report supply levels? */
d1c2727f 2606
2607
b9738d7c 2608 /*
2609 * Report alerts and messages...
2610 */
2611
2612 if ((pa = ippFindAttribute(ipp, "printer-alert", IPP_TAG_TEXT)) != NULL)
2613 report_attr(pa);
2614
2615 if ((pam = ippFindAttribute(ipp, "printer-alert-message",
2616 IPP_TAG_TEXT)) != NULL)
2617 report_attr(pam);
2618
a6ccc6e8 2619 if ((psm = ippFindAttribute(ipp, "printer-state-message",
2620 IPP_TAG_TEXT)) != NULL)
b9738d7c 2621 {
2622 char *ptr; /* Pointer into message */
2623
2624
2625 strlcpy(value, "INFO: ", sizeof(value));
2626 for (ptr = psm->values[0].string.text, valptr = value + 6;
2627 *ptr && valptr < (value + sizeof(value) - 6);
2628 ptr ++)
2629 {
2630 if (*ptr < ' ' && *ptr > 0 && *ptr != '\t')
2631 {
2632 /*
2633 * Substitute "<XX>" for the control character; sprintf is safe because
2634 * we always leave 6 chars free at the end...
2635 */
2636
2637 sprintf(valptr, "<%02X>", *ptr);
2638 valptr += 4;
2639 }
2640 else
2641 *valptr++ = *ptr;
2642 }
2643
2644 *valptr++ = '\n';
57487c71 2645 *valptr = '\0';
b9738d7c 2646
2647 fputs(value, stderr);
2648 }
2649
2650 /*
2651 * Now report printer-state-reasons, filtering out some of the reasons we never
2652 * want to set...
2653 */
a6ccc6e8 2654
d1c2727f 2655 if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
2656 IPP_TAG_KEYWORD)) == NULL)
90d0da15 2657 return;
d1c2727f 2658
7b1ef0fc 2659 update_reasons(reasons, NULL);
8f4595eb 2660
7c7997c3 2661 /*
2662 * Relay the current marker-* attribute values...
2663 */
2664
aa141f0d 2665 if (ipp_supplies < 0)
2666 {
2667 ppd_file_t *ppd; /* PPD file */
2668 ppd_attr_t *ppdattr; /* Attribute in PPD file */
2669
2670 if ((ppd = ppdOpenFile(getenv("PPD"))) != NULL &&
2671 (ppdattr = ppdFindAttr(ppd, "cupsIPPSupplies", NULL)) != NULL &&
c6fab96f 2672 ppdattr->value && _cups_strcasecmp(ppdattr->value, "true"))
aa141f0d 2673 ipp_supplies = 0;
2674 else
2675 ipp_supplies = 1;
2676
2677 ppdClose(ppd);
2678 }
2679
2680 if (ipp_supplies > 0)
2681 {
2682 if ((marker = ippFindAttribute(ipp, "marker-colors", IPP_TAG_NAME)) != NULL)
2683 report_attr(marker);
2684 if ((marker = ippFindAttribute(ipp, "marker-high-levels",
2685 IPP_TAG_INTEGER)) != NULL)
2686 report_attr(marker);
2687 if ((marker = ippFindAttribute(ipp, "marker-levels",
2688 IPP_TAG_INTEGER)) != NULL)
2689 report_attr(marker);
2690 if ((marker = ippFindAttribute(ipp, "marker-low-levels",
2691 IPP_TAG_INTEGER)) != NULL)
2692 report_attr(marker);
2693 if ((marker = ippFindAttribute(ipp, "marker-message",
2694 IPP_TAG_TEXT)) != NULL)
2695 report_attr(marker);
2696 if ((marker = ippFindAttribute(ipp, "marker-names", IPP_TAG_NAME)) != NULL)
2697 report_attr(marker);
2698 if ((marker = ippFindAttribute(ipp, "marker-types",
2699 IPP_TAG_KEYWORD)) != NULL)
2700 report_attr(marker);
2701 }
d1c2727f 2702}
2703
2704
959a6a28 2705#if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
2706/*
2707 * 'run_as_user()' - Run the IPP backend as the printing user.
2708 *
2709 * This function uses an XPC-based user agent to run the backend as the printing
2710 * user. We need to do this in order to have access to the user's Kerberos
2711 * credentials.
2712 */
2713
2714static int /* O - Exit status */
2715run_as_user(int argc, /* I - Number of command-line args */
2716 char *argv[], /* I - Command-line arguments */
2717 uid_t uid, /* I - User ID */
2718 const char *device_uri, /* I - Device URI */
2719 int fd) /* I - File to print */
2720{
3d5f60d1 2721 const char *auth_negotiate;/* AUTH_NEGOTIATE env var */
959a6a28 2722 xpc_connection_t conn; /* Connection to XPC service */
2723 xpc_object_t request; /* Request message dictionary */
2724 __block xpc_object_t response; /* Response message dictionary */
2725 dispatch_semaphore_t sem; /* Semaphore for waiting for response */
2726 int status = CUPS_BACKEND_FAILED;
2727 /* Status of request */
2728
2729
2730 fprintf(stderr, "DEBUG: Running IPP backend as UID %d.\n", (int)uid);
2731
2732 /*
2733 * Connect to the user agent for the specified UID...
2734 */
2735
2736 conn = xpc_connection_create_mach_service(kPMPrintUIToolAgent,
2737 dispatch_get_global_queue(0, 0), 0);
2738 if (!conn)
2739 {
2740 _cupsLangPrintFilter(stderr, "ERROR",
2741 _("Unable to start backend process."));
2742 fputs("DEBUG: Unable to create connection to agent.\n", stderr);
2743 goto cleanup;
2744 }
2745
2746 xpc_connection_set_event_handler(conn,
2747 ^(xpc_object_t event)
2748 {
2749 xpc_type_t messageType = xpc_get_type(event);
2750
2751 if (messageType == XPC_TYPE_ERROR)
2752 {
2753 if (event == XPC_ERROR_CONNECTION_INTERRUPTED)
2754 fprintf(stderr, "DEBUG: Interrupted connection to service %s.\n",
2755 xpc_connection_get_name(conn));
2756 else if (event == XPC_ERROR_CONNECTION_INVALID)
2757 fprintf(stderr, "DEBUG: Connection invalid for service %s.\n",
2758 xpc_connection_get_name(conn));
2759 else
2760 fprintf(stderr, "DEBUG: Unxpected error for service %s: %s\n",
2761 xpc_connection_get_name(conn),
2762 xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
2763 }
2764 });
2765 xpc_connection_set_target_uid(conn, uid);
2766 xpc_connection_resume(conn);
2767
2768 /*
2769 * Try starting the backend...
2770 */
2771
2772 request = xpc_dictionary_create(NULL, NULL, 0);
2773 xpc_dictionary_set_int64(request, "command", kPMStartJob);
2774 xpc_dictionary_set_string(request, "device-uri", device_uri);
2775 xpc_dictionary_set_string(request, "job-id", argv[1]);
2776 xpc_dictionary_set_string(request, "user", argv[2]);
2777 xpc_dictionary_set_string(request, "title", argv[3]);
2778 xpc_dictionary_set_string(request, "copies", argv[4]);
2779 xpc_dictionary_set_string(request, "options", argv[5]);
2780 xpc_dictionary_set_string(request, "auth-info-required",
2781 getenv("AUTH_INFO_REQUIRED"));
3d5f60d1 2782 if ((auth_negotiate = getenv("AUTH_NEGOTIATE")) != NULL)
2783 xpc_dictionary_set_string(request, "auth-negotiate", auth_negotiate);
959a6a28 2784 xpc_dictionary_set_fd(request, "stdin", fd);
2785 xpc_dictionary_set_fd(request, "stderr", 2);
2786 xpc_dictionary_set_fd(request, "side-channel", CUPS_SC_FD);
2787
2788 sem = dispatch_semaphore_create(0);
2789 response = NULL;
2790
2791 xpc_connection_send_message_with_reply(conn, request,
2792 dispatch_get_global_queue(0,0),
2793 ^(xpc_object_t reply)
2794 {
2795 /* Save the response and wake up */
2796 if (xpc_get_type(reply)
2797 == XPC_TYPE_DICTIONARY)
2798 response = xpc_retain(reply);
2799
2800 dispatch_semaphore_signal(sem);
2801 });
2802
2803 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
2804 xpc_release(request);
2805 dispatch_release(sem);
2806
2807 if (response)
2808 {
2809 child_pid = xpc_dictionary_get_int64(response, "child-pid");
2810
2811 xpc_release(response);
2812
2813 if (child_pid)
2814 fprintf(stderr, "DEBUG: Child PID=%d.\n", child_pid);
2815 else
2816 {
2817 _cupsLangPrintFilter(stderr, "ERROR",
2818 _("Unable to start backend process."));
2819 fputs("DEBUG: No child PID.\n", stderr);
2820 goto cleanup;
2821 }
2822 }
2823 else
2824 {
2825 _cupsLangPrintFilter(stderr, "ERROR",
2826 _("Unable to start backend process."));
2827 fputs("DEBUG: No reply from agent.\n", stderr);
2828 goto cleanup;
2829 }
2830
2831 /*
2832 * Then wait for the backend to finish...
2833 */
2834
2835 request = xpc_dictionary_create(NULL, NULL, 0);
2836 xpc_dictionary_set_int64(request, "command", kPMWaitForJob);
2837 xpc_dictionary_set_fd(request, "stderr", 2);
2838
2839 sem = dispatch_semaphore_create(0);
2840 response = NULL;
2841
2842 xpc_connection_send_message_with_reply(conn, request,
2843 dispatch_get_global_queue(0,0),
2844 ^(xpc_object_t reply)
2845 {
2846 /* Save the response and wake up */
2847 if (xpc_get_type(reply)
2848 == XPC_TYPE_DICTIONARY)
2849 response = xpc_retain(reply);
2850
2851 dispatch_semaphore_signal(sem);
2852 });
2853
2854 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
2855 xpc_release(request);
2856 dispatch_release(sem);
2857
2858 if (response)
2859 {
2860 status = xpc_dictionary_get_int64(response, "status");
2861
2862 if (status == SIGTERM || status == SIGKILL || status == SIGPIPE)
2863 {
2864 fprintf(stderr, "DEBUG: Child terminated on signal %d.\n", status);
2865 status = CUPS_BACKEND_FAILED;
2866 }
2867 else if (WIFSIGNALED(status))
2868 {
2869 fprintf(stderr, "DEBUG: Child crashed on signal %d.\n", status);
2870 status = CUPS_BACKEND_STOP;
2871 }
2872 else if (WIFEXITED(status))
2873 {
2874 status = WEXITSTATUS(status);
2875 fprintf(stderr, "DEBUG: Child exited with status %d.\n", status);
2876 }
2877
2878 xpc_release(response);
2879 }
2880 else
2881 _cupsLangPrintFilter(stderr, "ERROR",
2882 _("Unable to get backend exit status."));
2883
2884 cleanup:
2885
2886 if (conn)
2887 {
2888 xpc_connection_suspend(conn);
2889 xpc_connection_cancel(conn);
2890 xpc_release(conn);
2891 }
2892
2893 return (status);
2894}
2895#endif /* HAVE_GSSAPI && HAVE_XPC */
2896
2897
d1c2727f 2898/*
8f4595eb 2899 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
2900 */
2901
2902static void
2903sigterm_handler(int sig) /* I - Signal */
2904{
2905 (void)sig; /* remove compiler warnings... */
2906
959a6a28 2907#if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
2908 if (child_pid)
2909 {
2910 kill(child_pid, sig);
2911 child_pid = 0;
2912 }
2913#endif /* HAVE_GSSAPI && HAVE_XPC */
2914
6875feac 2915 if (!job_canceled)
072c4872 2916 {
2917 /*
959a6a28 2918 * Flag that the job should be canceled...
072c4872 2919 */
2920
6875feac 2921 job_canceled = 1;
072c4872 2922 return;
2923 }
2924
940afb4b 2925 /*
2926 * The scheduler already tried to cancel us once, now just terminate
7b1ef0fc 2927 * after removing our temp file!
940afb4b 2928 */
2929
2930 if (tmpfilename[0])
2931 unlink(tmpfilename);
2932
8f4595eb 2933 exit(1);
2934}
2935
2936
1179bc03 2937/*
2938 * 'timeout_cb()' - Handle HTTP timeouts.
2939 */
2940
2941static int /* O - 1 to continue, 0 to cancel */
2942timeout_cb(http_t *http, /* I - Connection to server (unused) */
2943 void *user_data) /* I - User data (unused) */
2944{
2945 (void)http;
2946 (void)user_data;
2947
2948 return (!job_canceled);
2949}
2950
2951
7b1ef0fc 2952/*
2953 * 'update_reasons()' - Update the printer-state-reasons values.
2954 */
2955
2956static void
2957update_reasons(ipp_attribute_t *attr, /* I - printer-state-reasons or NULL */
2958 const char *s) /* I - STATE: string or NULL */
2959{
2960 char op; /* Add (+), remove (-), replace (\0) */
2961 cups_array_t *new_reasons; /* New reasons array */
2962 char *reason, /* Current reason */
2963 add[2048], /* Reasons added string */
2964 *addptr, /* Pointer into add string */
2965 rem[2048], /* Reasons removed string */
2966 *remptr; /* Pointer into remove string */
2967 const char *addprefix, /* Current add string prefix */
2968 *remprefix; /* Current remove string prefix */
2969
2970
2971 fprintf(stderr, "DEBUG: update_reasons(attr=%d(%s%s), s=\"%s\")\n",
2972 attr ? attr->num_values : 0, attr ? attr->values[0].string.text : "",
2973 attr && attr->num_values > 1 ? ",..." : "", s ? s : "(null)");
2974
2975 /*
2976 * Create an array of new reason keyword strings...
2977 */
2978
2979 if (attr)
2980 {
2981 int i; /* Looping var */
2982
2983 new_reasons = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2984 op = '\0';
2985
2986 for (i = 0; i < attr->num_values; i ++)
2987 {
2988 reason = attr->values[i].string.text;
2989
2990 if (strcmp(reason, "none") &&
2991 strcmp(reason, "none-report") &&
2992 strcmp(reason, "paused") &&
77dd74a0 2993 strncmp(reason, "spool-area-full", 15) &&
7b1ef0fc 2994 strcmp(reason, "com.apple.print.recoverable-warning") &&
2995 strncmp(reason, "cups-", 5))
2996 cupsArrayAdd(new_reasons, reason);
2997 }
2998 }
2999 else if (s)
3000 {
3001 if (*s == '+' || *s == '-')
3002 op = *s++;
3003 else
3004 op = '\0';
3005
3006 new_reasons = _cupsArrayNewStrings(s);
3007 }
3008 else
3009 return;
3010
3011 /*
3012 * Compute the changes...
3013 */
3014
3015 add[0] = '\0';
3016 addprefix = "STATE: +";
3017 addptr = add;
3018 rem[0] = '\0';
3019 remprefix = "STATE: -";
3020 remptr = rem;
3021
3022 fprintf(stderr, "DEBUG2: op='%c', new_reasons=%d, state_reasons=%d\n",
3023 op ? op : ' ', cupsArrayCount(new_reasons),
3024 cupsArrayCount(state_reasons));
3025
6ef97027 3026 _cupsMutexLock(&report_mutex);
7b1ef0fc 3027
3028 if (op == '+')
3029 {
3030 /*
3031 * Add reasons...
3032 */
3033
3034 for (reason = (char *)cupsArrayFirst(new_reasons);
3035 reason;
3036 reason = (char *)cupsArrayNext(new_reasons))
3037 {
3038 if (!cupsArrayFind(state_reasons, reason))
3039 {
3040 if (!strncmp(reason, "cups-remote-", 12))
3041 {
3042 /*
3043 * If we are setting cups-remote-xxx, remove all other cups-remote-xxx
3044 * keywords...
3045 */
3046
3047 char *temp; /* Current reason in state_reasons */
3048
3049 cupsArraySave(state_reasons);
3050
3051 for (temp = (char *)cupsArrayFirst(state_reasons);
3052 temp;
3053 temp = (char *)cupsArrayNext(state_reasons))
3054 if (!strncmp(temp, "cups-remote-", 12))
3055 {
3056 snprintf(remptr, sizeof(rem) - (remptr - rem), "%s%s", remprefix,
3057 temp);
3058 remptr += strlen(remptr);
3059 remprefix = ",";
3060
3061 cupsArrayRemove(state_reasons, temp);
3062 break;
3063 }
3064
3065 cupsArrayRestore(state_reasons);
3066 }
3067
3068 cupsArrayAdd(state_reasons, reason);
3069
3070 snprintf(addptr, sizeof(add) - (addptr - add), "%s%s", addprefix,
3071 reason);
3072 addptr += strlen(addptr);
3073 addprefix = ",";
3074 }
3075 }
3076 }
3077 else if (op == '-')
3078 {
3079 /*
3080 * Remove reasons...
3081 */
3082
3083 for (reason = (char *)cupsArrayFirst(new_reasons);
3084 reason;
3085 reason = (char *)cupsArrayNext(new_reasons))
3086 {
3087 if (cupsArrayFind(state_reasons, reason))
3088 {
3089 snprintf(remptr, sizeof(rem) - (remptr - rem), "%s%s", remprefix,
3090 reason);
3091 remptr += strlen(remptr);
3092 remprefix = ",";
3093
3094 cupsArrayRemove(state_reasons, reason);
3095 }
3096 }
3097 }
3098 else
3099 {
3100 /*
3101 * Replace reasons...
3102 */
3103
3104 for (reason = (char *)cupsArrayFirst(state_reasons);
3105 reason;
3106 reason = (char *)cupsArrayNext(state_reasons))
3107 {
3108 if (strncmp(reason, "cups-", 5) && !cupsArrayFind(new_reasons, reason))
3109 {
3110 snprintf(remptr, sizeof(rem) - (remptr - rem), "%s%s", remprefix,
3111 reason);
3112 remptr += strlen(remptr);
3113 remprefix = ",";
3114
3115 cupsArrayRemove(state_reasons, reason);
3116 }
3117 }
3118
3119 for (reason = (char *)cupsArrayFirst(new_reasons);
3120 reason;
3121 reason = (char *)cupsArrayNext(new_reasons))
3122 {
3123 if (!cupsArrayFind(state_reasons, reason))
3124 {
3125 cupsArrayAdd(state_reasons, reason);
3126
3127 snprintf(addptr, sizeof(add) - (addptr - add), "%s%s", addprefix,
3128 reason);
3129 addptr += strlen(addptr);
3130 addprefix = ",";
3131 }
3132 }
3133 }
3134
6ef97027 3135 _cupsMutexUnlock(&report_mutex);
7b1ef0fc 3136
3137 /*
3138 * Report changes and return...
3139 */
3140
3141 if (add[0] && rem[0])
3142 fprintf(stderr, "%s\n%s\n", add, rem);
3143 else if (add[0])
3144 fprintf(stderr, "%s\n", add);
3145 else if (rem[0])
3146 fprintf(stderr, "%s\n", rem);
3147}
3148
8f4595eb 3149/*
c9d3f842 3150 * End of "$Id$".
c8f9565c 3151 */