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