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