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