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