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