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