]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/ipp.c
Merge changes from CUPS 1.5svn-r9229.
[thirdparty/cups.git] / backend / ipp.c
CommitLineData
ef416fc2 1/*
b19ccc9e 2 * "$Id: ipp.c 7948 2008-09-17 00:04:12Z mike $"
ef416fc2 3 *
cc754834 4 * IPP backend for CUPS.
ef416fc2 5 *
41681883 6 * Copyright 2007-2010 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.
ef416fc2 21 * check_printer_state() - Check the printer state...
b423cd4c 22 * compress_files() - Compress print files...
ef416fc2 23 * password_cb() - Disable the password prompt for
24 * cupsDoFileRequest().
1f0275e3 25 * report_attr() - Report an IPP attribute value.
ef416fc2 26 * report_printer_state() - Report the printer state.
ef416fc2 27 * sigterm_handler() - Handle 'terminate' signals that stop the backend.
28 */
29
30/*
31 * Include necessary headers.
32 */
33
7a14d768 34#include "backend-private.h"
ef416fc2 35#include <sys/types.h>
36#include <sys/stat.h>
ef416fc2 37#include <sys/wait.h>
38
ef416fc2 39/*
40 * Globals...
41 */
42
43static char *password = NULL; /* Password for device URI */
76cd9e37 44static int password_tries = 0; /* Password tries */
18ecb428
MS
45static const char *auth_info_required = "none";
46 /* New auth-info-required value */
cc754834 47static int job_canceled = 0; /* Job cancelled? */
ef416fc2 48
49
50/*
51 * Local functions...
52 */
53
bd7854cb 54static void cancel_job(http_t *http, const char *uri, int id,
55 const char *resource, const char *user, int version);
56static void check_printer_state(http_t *http, const char *uri,
ef416fc2 57 const char *resource, const char *user,
db1f069b 58 int version, int job_id);
b423cd4c 59#ifdef HAVE_LIBZ
60static void compress_files(int num_files, char **files);
61#endif /* HAVE_LIBZ */
bd7854cb 62static const char *password_cb(const char *);
1f0275e3 63static void report_attr(ipp_attribute_t *attr);
db1f069b 64static int report_printer_state(ipp_t *ipp, int job_id);
ef416fc2 65static void sigterm_handler(int sig);
66
67
68/*
69 * 'main()' - Send a file to the printer or server.
70 *
71 * Usage:
72 *
73 * printer-uri job-id user title copies options [file]
74 */
75
76int /* O - Exit status */
bd7854cb 77main(int argc, /* I - Number of command-line args */
ef416fc2 78 char *argv[]) /* I - Command-line arguments */
79{
80 int i; /* Looping var */
b94498cf 81 int send_options; /* Send job options? */
ef416fc2 82 int num_options; /* Number of printer options */
83 cups_option_t *options; /* Printer options */
1f0275e3 84 const char *device_uri; /* Device URI */
acb056cb 85 char scheme[255], /* Scheme in URI */
ef416fc2 86 hostname[1024], /* Hostname */
87 username[255], /* Username info */
88 resource[1024], /* Resource info (printer name) */
26d47ec6 89 addrname[256], /* Address name */
ef416fc2 90 *optptr, /* Pointer to URI options */
db1f069b
MS
91 *name, /* Name of option */
92 *value, /* Value of option */
93 sep; /* Separator character */
7a14d768
MS
94 int snmp_fd, /* SNMP socket */
95 start_count, /* Page count via SNMP at start */
426c6a59
MS
96 page_count, /* Page count via SNMP */
97 have_supplies; /* Printer supports supply levels? */
bd7854cb 98 int num_files; /* Number of files to print */
cc754834 99 char **files; /* Files to print */
ef416fc2 100 int port; /* Port number (not used) */
101 char uri[HTTP_MAX_URI]; /* Updated URI without user/pass */
cc754834 102 http_status_t http_status; /* Status of HTTP request */
ef416fc2 103 ipp_status_t ipp_status; /* Status of IPP request */
104 http_t *http; /* HTTP connection */
105 ipp_t *request, /* IPP request */
106 *response, /* IPP response */
107 *supported; /* get-printer-attributes response */
c0e1af83 108 time_t start_time; /* Time of first connect */
c0e1af83 109 int contimeout; /* Connection timeout */
110 int delay; /* Delay for retries... */
b423cd4c 111 int compression, /* Do compression of the job data? */
112 waitjob, /* Wait for job complete? */
ef416fc2 113 waitprinter; /* Wait for printer ready? */
114 ipp_attribute_t *job_id_attr; /* job-id attribute */
115 int job_id; /* job-id value */
bd7854cb 116 ipp_attribute_t *job_sheets; /* job-media-sheets-completed */
117 ipp_attribute_t *job_state; /* job-state */
118 ipp_attribute_t *copies_sup; /* copies-supported */
cc754834 119 ipp_attribute_t *cups_version; /* cups-version */
bd7854cb 120 ipp_attribute_t *format_sup; /* document-format-supported */
cc754834 121 ipp_attribute_t *media_col_sup; /* media-col-supported */
ef416fc2 122 ipp_attribute_t *printer_state; /* printer-state attribute */
bd7854cb 123 ipp_attribute_t *printer_accepting; /* printer-is-accepting-jobs */
d09495fa 124 int copies, /* Number of copies for job */
125 copies_remaining; /* Number of copies remaining */
c277e2f8
MS
126 const char *content_type, /* CONTENT_TYPE environment variable */
127 *final_content_type; /* FINAL_CONTENT_TYPE environment var */
cc754834
MS
128 int fd; /* File descriptor */
129 off_t bytes; /* Bytes copied */
130 char buffer[16384]; /* Copy buffer */
ef416fc2 131#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
132 struct sigaction action; /* Actions for POSIX signals */
133#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
134 int version; /* IPP version */
ef416fc2 135 static const char * const pattrs[] =
136 { /* Printer attributes we want */
137 "copies-supported",
ef416fc2 138 "document-format-supported",
1f0275e3 139 "marker-colors",
f11a948a 140 "marker-high-levels",
1f0275e3 141 "marker-levels",
f11a948a 142 "marker-low-levels",
1f0275e3
MS
143 "marker-message",
144 "marker-names",
145 "marker-types",
cc754834 146 "media-col-supported",
ef416fc2 147 "printer-is-accepting-jobs",
148 "printer-state",
323c5de1 149 "printer-state-message",
ef416fc2 150 "printer-state-reasons",
151 };
152 static const char * const jattrs[] =
153 { /* Job attributes we want */
154 "job-media-sheets-completed",
155 "job-state"
156 };
157
158
159 /*
160 * Make sure status messages are not buffered...
161 */
162
163 setbuf(stderr, NULL);
164
165 /*
166 * Ignore SIGPIPE and catch SIGTERM signals...
167 */
168
169#ifdef HAVE_SIGSET
170 sigset(SIGPIPE, SIG_IGN);
171 sigset(SIGTERM, sigterm_handler);
172#elif defined(HAVE_SIGACTION)
173 memset(&action, 0, sizeof(action));
174 action.sa_handler = SIG_IGN;
175 sigaction(SIGPIPE, &action, NULL);
176
177 sigemptyset(&action.sa_mask);
178 sigaddset(&action.sa_mask, SIGTERM);
179 action.sa_handler = sigterm_handler;
180 sigaction(SIGTERM, &action, NULL);
181#else
182 signal(SIGPIPE, SIG_IGN);
183 signal(SIGTERM, sigterm_handler);
184#endif /* HAVE_SIGSET */
185
186 /*
187 * Check command-line...
188 */
189
190 if (argc == 1)
191 {
192 char *s;
193
194 if ((s = strrchr(argv[0], '/')) != NULL)
195 s ++;
196 else
197 s = argv[0];
198
8b450588
MS
199 printf("network %s \"Unknown\" \"%s (%s)\"\n",
200 s, _cupsLangString(cupsLangDefault(),
201 _("Internet Printing Protocol")), s);
ef416fc2 202 return (CUPS_BACKEND_OK);
203 }
bd7854cb 204 else if (argc < 6)
ef416fc2 205 {
db1f069b
MS
206 _cupsLangPrintf(stderr,
207 _("Usage: %s job-id user title copies options [file]\n"),
208 argv[0]);
ef416fc2 209 return (CUPS_BACKEND_STOP);
210 }
211
212 /*
bd7854cb 213 * Get the (final) content type...
ef416fc2 214 */
215
c277e2f8
MS
216 if ((content_type = getenv("CONTENT_TYPE")) == NULL)
217 content_type = "application/octet-stream";
ef416fc2 218
c277e2f8
MS
219 if ((final_content_type = getenv("FINAL_CONTENT_TYPE")) == NULL)
220 {
221 final_content_type = content_type;
222
223 if (!strncmp(final_content_type, "printer/", 8))
224 final_content_type = "application/vnd.cups-raw";
225 }
d09495fa 226
ef416fc2 227 /*
228 * Extract the hostname and printer name from the URI...
229 */
230
1f0275e3
MS
231 if ((device_uri = cupsBackendDeviceURI(argv)) == NULL)
232 return (CUPS_BACKEND_FAILED);
233
acb056cb
MS
234 httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme),
235 username, sizeof(username), hostname, sizeof(hostname), &port,
236 resource, sizeof(resource));
ef416fc2 237
a41f09e2
MS
238 if (!port)
239 port = IPP_PORT; /* Default to port 631 */
240
acb056cb 241 if (!strcmp(scheme, "https"))
ef416fc2 242 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
8ca02f3c 243 else
244 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
ef416fc2 245
ef416fc2 246 /*
247 * See if there are any options...
248 */
249
b423cd4c 250 compression = 0;
cc754834 251 version = 20;
ef416fc2 252 waitjob = 1;
253 waitprinter = 1;
c0e1af83 254 contimeout = 7 * 24 * 60 * 60;
ef416fc2 255
256 if ((optptr = strchr(resource, '?')) != NULL)
257 {
258 /*
259 * Yup, terminate the device name string and move to the first
260 * character of the optptr...
261 */
262
263 *optptr++ = '\0';
264
265 /*
266 * Then parse the optptr...
267 */
268
269 while (*optptr)
270 {
271 /*
272 * Get the name...
273 */
274
db1f069b 275 name = optptr;
ef416fc2 276
db1f069b
MS
277 while (*optptr && *optptr != '=' && *optptr != '+' && *optptr != '&')
278 optptr ++;
279
280 if ((sep = *optptr) != '\0')
281 *optptr++ = '\0';
282
283 if (sep == '=')
ef416fc2 284 {
285 /*
286 * Get the value...
287 */
288
db1f069b 289 value = optptr;
ef416fc2 290
db1f069b 291 while (*optptr && *optptr != '+' && *optptr != '&')
ef416fc2 292 optptr ++;
db1f069b
MS
293
294 if (*optptr)
295 *optptr++ = '\0';
ef416fc2 296 }
297 else
db1f069b 298 value = (char *)"";
ef416fc2 299
300 /*
301 * Process the option...
302 */
303
304 if (!strcasecmp(name, "waitjob"))
305 {
306 /*
307 * Wait for job completion?
308 */
309
310 waitjob = !strcasecmp(value, "on") ||
311 !strcasecmp(value, "yes") ||
312 !strcasecmp(value, "true");
313 }
314 else if (!strcasecmp(name, "waitprinter"))
315 {
316 /*
317 * Wait for printer idle?
318 */
319
320 waitprinter = !strcasecmp(value, "on") ||
321 !strcasecmp(value, "yes") ||
322 !strcasecmp(value, "true");
323 }
324 else if (!strcasecmp(name, "encryption"))
325 {
326 /*
327 * Enable/disable encryption?
328 */
329
330 if (!strcasecmp(value, "always"))
331 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
332 else if (!strcasecmp(value, "required"))
333 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
334 else if (!strcasecmp(value, "never"))
335 cupsSetEncryption(HTTP_ENCRYPT_NEVER);
336 else if (!strcasecmp(value, "ifrequested"))
337 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
338 else
339 {
db1f069b 340 _cupsLangPrintf(stderr,
4d301e69 341 _("ERROR: Unknown encryption option value \"%s\"\n"),
c0e1af83 342 value);
ef416fc2 343 }
344 }
345 else if (!strcasecmp(name, "version"))
346 {
347 if (!strcmp(value, "1.0"))
c168a833 348 version = 10;
ef416fc2 349 else if (!strcmp(value, "1.1"))
c168a833
MS
350 version = 11;
351 else if (!strcmp(value, "2.0"))
352 version = 20;
353 else if (!strcmp(value, "2.1"))
354 version = 21;
ef416fc2 355 else
356 {
db1f069b 357 _cupsLangPrintf(stderr,
4d301e69 358 _("ERROR: Unknown version option value \"%s\"\n"),
c0e1af83 359 value);
ef416fc2 360 }
361 }
b423cd4c 362#ifdef HAVE_LIBZ
363 else if (!strcasecmp(name, "compression"))
364 {
365 compression = !strcasecmp(value, "true") ||
366 !strcasecmp(value, "yes") ||
367 !strcasecmp(value, "on") ||
368 !strcasecmp(value, "gzip");
369 }
370#endif /* HAVE_LIBZ */
c0e1af83 371 else if (!strcasecmp(name, "contimeout"))
372 {
373 /*
374 * Set the connection timeout...
375 */
376
377 if (atoi(value) > 0)
378 contimeout = atoi(value);
379 }
ef416fc2 380 else
381 {
382 /*
383 * Unknown option...
384 */
385
db1f069b 386 _cupsLangPrintf(stderr,
4d301e69 387 _("ERROR: Unknown option \"%s\" with value \"%s\"\n"),
db1f069b 388 name, value);
ef416fc2 389 }
390 }
391 }
392
b423cd4c 393 /*
394 * If we have 7 arguments, print the file named on the command-line.
395 * Otherwise, copy stdin to a temporary file and print the temporary
396 * file.
397 */
398
399 if (argc == 6)
400 {
cc754834
MS
401 num_files = 0;
402 send_options = !strcasecmp(final_content_type, "application/pdf") ||
403 !strcasecmp(final_content_type, "application/vnd.cups-pdf") ||
404 !strcasecmp(final_content_type, "image/jpeg") ||
405 !strcasecmp(final_content_type, "image/png") ||
406 !strcasecmp(final_content_type, "image/urf");
407
408 fputs("DEBUG: Sending stdin for job...\n", stderr);
b423cd4c 409 }
410 else
411 {
412 /*
413 * Point to the files on the command-line...
414 */
415
c277e2f8
MS
416 num_files = argc - 6;
417 files = argv + 6;
418 send_options = 1;
b94498cf 419
b423cd4c 420#ifdef HAVE_LIBZ
421 if (compression)
422 compress_files(num_files, files);
423#endif /* HAVE_LIBZ */
b423cd4c 424
cc754834
MS
425 fprintf(stderr, "DEBUG: %d files to send in job...\n", num_files);
426 }
b423cd4c 427
ef416fc2 428 /*
429 * Set the authentication info, if any...
430 */
431
432 cupsSetPasswordCB(password_cb);
433
434 if (username[0])
435 {
436 /*
437 * Use authenticaion information in the device URI...
438 */
439
440 if ((password = strchr(username, ':')) != NULL)
441 *password++ = '\0';
442
443 cupsSetUser(username);
444 }
445 else if (!getuid())
446 {
447 /*
09a101d6 448 * Try loading authentication information from the environment.
ef416fc2 449 */
450
db1f069b
MS
451 const char *ptr = getenv("AUTH_USERNAME");
452
453 if (ptr)
09a101d6 454 cupsSetUser(ptr);
ef416fc2 455
09a101d6 456 password = getenv("AUTH_PASSWORD");
ef416fc2 457 }
458
459 /*
460 * Try connecting to the remote server...
461 */
462
4d301e69
MS
463 delay = 5;
464 start_time = time(NULL);
c0e1af83 465
757d2cad 466 fputs("STATE: +connecting-to-device\n", stderr);
467
ef416fc2 468 do
469 {
acb056cb 470 fprintf(stderr, "DEBUG: Connecting to %s:%d\n", hostname, port);
8b450588 471 _cupsLangPuts(stderr, _("INFO: Connecting to printer...\n"));
ef416fc2 472
473 if ((http = httpConnectEncrypt(hostname, port, cupsEncryption())) == NULL)
474 {
c7017ecc
MS
475 int error = errno; /* Connection error */
476
cc754834 477 if (job_canceled)
7594b224 478 break;
479
ef416fc2 480 if (getenv("CLASS") != NULL)
481 {
482 /*
483 * If the CLASS environment variable is set, the job was submitted
484 * to a class and not to a specific queue. In this case, we want
485 * to abort immediately so that the job can be requeued on the next
486 * available printer in the class.
487 */
488
db1f069b
MS
489 _cupsLangPuts(stderr,
490 _("INFO: Unable to contact printer, queuing on next "
491 "printer in class...\n"));
ef416fc2 492
ef416fc2 493 /*
494 * Sleep 5 seconds to keep the job from requeuing too rapidly...
495 */
496
497 sleep(5);
498
499 return (CUPS_BACKEND_FAILED);
500 }
501
c7017ecc
MS
502 fprintf(stderr, "DEBUG: Connection error: %s\n", strerror(errno));
503
ef416fc2 504 if (errno == ECONNREFUSED || errno == EHOSTDOWN ||
505 errno == EHOSTUNREACH)
506 {
c0e1af83 507 if (contimeout && (time(NULL) - start_time) > contimeout)
508 {
c7017ecc 509 _cupsLangPuts(stderr, _("ERROR: The printer is not responding.\n"));
c0e1af83 510 return (CUPS_BACKEND_FAILED);
511 }
512
c7017ecc
MS
513 switch (error)
514 {
515 case EHOSTDOWN :
516 _cupsLangPrintf(stderr,
517 _("WARNING: Network printer \'%s\' may not exist "
518 "or is unavailable at this time.\n"),
519 hostname);
520 break;
521
522 case EHOSTUNREACH :
523 _cupsLangPrintf(stderr,
524 _("WARNING: Network printer \'%s\' is "
525 "unreachable at this time.\n"),
526 hostname);
527 break;
528
529 case ECONNREFUSED :
530 default :
531 _cupsLangPrintf(stderr,
532 _("WARNING: Network printer \'%s\' is busy.\n"),
533 hostname);
534 break;
535 }
c0e1af83 536
537 sleep(delay);
538
539 if (delay < 30)
540 delay += 5;
ef416fc2 541 }
542 else if (h_errno)
543 {
c7017ecc
MS
544 _cupsLangPrintf(stderr,
545 _("ERROR: Unable to locate network printer \'%s\'.\n"),
db1f069b 546 hostname);
c0e1af83 547 return (CUPS_BACKEND_STOP);
ef416fc2 548 }
549 else
550 {
c7017ecc
MS
551 _cupsLangPrintf(stderr, _("ERROR: Network printer \'%s\' is not "
552 "responding.\n"), hostname);
ef416fc2 553 sleep(30);
554 }
7594b224 555
cc754834 556 if (job_canceled)
7594b224 557 break;
ef416fc2 558 }
559 }
560 while (http == NULL);
561
cc754834 562 if (job_canceled || !http)
7594b224 563 return (CUPS_BACKEND_FAILED);
7594b224 564
757d2cad 565 fputs("STATE: -connecting-to-device\n", stderr);
8b450588 566 _cupsLangPuts(stderr, _("INFO: Connected to printer...\n"));
ef416fc2 567
26d47ec6 568#ifdef AF_INET6
569 if (http->hostaddr->addr.sa_family == AF_INET6)
c0e1af83 570 fprintf(stderr, "DEBUG: Connected to [%s]:%d (IPv6)...\n",
571 httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
572 ntohs(http->hostaddr->ipv6.sin6_port));
26d47ec6 573 else
574#endif /* AF_INET6 */
575 if (http->hostaddr->addr.sa_family == AF_INET)
576 fprintf(stderr, "DEBUG: Connected to %s:%d (IPv4)...\n",
c0e1af83 577 httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
578 ntohs(http->hostaddr->ipv4.sin_port));
26d47ec6 579
7a14d768
MS
580 /*
581 * See if the printer supports SNMP...
582 */
583
584 if ((snmp_fd = _cupsSNMPOpen(http->hostaddr->addr.sa_family)) >= 0)
426c6a59
MS
585 have_supplies = !backendSNMPSupplies(snmp_fd, http->hostaddr, &start_count,
586 NULL);
1f0275e3 587 else
426c6a59 588 have_supplies = start_count = 0;
7a14d768 589
ef416fc2 590 /*
591 * Build a URI for the printer and fill the standard IPP attributes for
592 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
593 * might contain username:password information...
594 */
595
acb056cb
MS
596 httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), scheme, NULL, hostname,
597 port, resource);
ef416fc2 598
599 /*
600 * First validate the destination and see if the device supports multiple
cc754834 601 * copies...
ef416fc2 602 */
603
cc754834
MS
604 copies_sup = NULL;
605 cups_version = NULL;
606 format_sup = NULL;
607 media_col_sup = NULL;
608 supported = NULL;
ef416fc2 609
610 do
611 {
7a14d768
MS
612 /*
613 * Check for side-channel requests...
614 */
615
616 backendCheckSideChannel(snmp_fd, http->hostaddr);
617
ef416fc2 618 /*
619 * Build the IPP request...
620 */
621
fa73b229 622 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
c168a833
MS
623 request->request.op.version[0] = version / 10;
624 request->request.op.version[1] = version % 10;
ef416fc2 625
626 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
627 NULL, uri);
628
629 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
630 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
631 NULL, pattrs);
632
633 /*
634 * Do the request...
635 */
636
637 fputs("DEBUG: Getting supported attributes...\n", stderr);
638
97c9a8d7 639 if (http->version < HTTP_1_1)
cc754834 640 {
c7017ecc
MS
641 fprintf(stderr, "DEBUG: Printer responded with HTTP version %d.%d.\n",
642 http->version / 100, http->version % 100);
643
cc754834 644 _cupsLangPuts(stderr,
c7017ecc 645 _("ERROR: Unable to print: the printer does not conform to "
cc754834
MS
646 "the IPP standard.\n"));
647 exit(CUPS_BACKEND_STOP);
648 }
97c9a8d7 649
ef416fc2 650 if ((supported = cupsDoRequest(http, request, resource)) == NULL)
651 ipp_status = cupsLastError();
652 else
653 ipp_status = supported->request.status.status_code;
654
655 if (ipp_status > IPP_OK_CONFLICT)
656 {
657 if (ipp_status == IPP_PRINTER_BUSY ||
658 ipp_status == IPP_SERVICE_UNAVAILABLE)
659 {
c0e1af83 660 if (contimeout && (time(NULL) - start_time) > contimeout)
661 {
c7017ecc 662 _cupsLangPuts(stderr, _("ERROR: The printer is not responding.\n"));
c0e1af83 663 return (CUPS_BACKEND_FAILED);
664 }
665
db1f069b 666 _cupsLangPrintf(stderr,
4d301e69
MS
667 _("WARNING: Network host \'%s\' is busy; will retry in "
668 "%d seconds...\n"), hostname, delay);
c0e1af83 669
db1f069b 670 report_printer_state(supported, 0);
c0e1af83 671
672 sleep(delay);
673
674 if (delay < 30)
675 delay += 5;
ef416fc2 676 }
677 else if ((ipp_status == IPP_BAD_REQUEST ||
c168a833 678 ipp_status == IPP_VERSION_NOT_SUPPORTED) && version > 10)
ef416fc2 679 {
680 /*
cc754834 681 * Switch to IPP/1.1 or IPP/1.0...
ef416fc2 682 */
683
cc754834
MS
684 if (version >= 20)
685 {
686 _cupsLangPrintf(stderr,
687 _("INFO: Printer does not support IPP/%d.%d, trying "
688 "IPP/%s...\n"), version / 10, version % 10, "1.1");
689 version = 11;
690 }
691 else
692 {
693 _cupsLangPrintf(stderr,
694 _("INFO: Printer does not support IPP/%d.%d, trying "
695 "IPP/%s...\n"), version / 10, version % 10, "1.0");
696 version = 10;
697 }
698
ef416fc2 699 httpReconnect(http);
700 }
701 else if (ipp_status == IPP_NOT_FOUND)
702 {
c7017ecc 703 _cupsLangPuts(stderr, _("ERROR: The printer URI is incorrect or no longer exists.\n"));
ef416fc2 704
705 if (supported)
706 ippDelete(supported);
707
708 return (CUPS_BACKEND_STOP);
709 }
41681883
MS
710 else if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
711 {
712 if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
713 "Negotiate", 9))
714 auth_info_required = "negotiate";
715
716 fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
717 return (CUPS_BACKEND_AUTH_REQUIRED);
718 }
ef416fc2 719 else
720 {
db1f069b 721 _cupsLangPrintf(stderr,
4d301e69 722 _("ERROR: Unable to get printer status (%s)\n"),
db1f069b 723 cupsLastErrorString());
ef416fc2 724 sleep(10);
725 }
726
727 if (supported)
728 ippDelete(supported);
729
730 continue;
731 }
cc754834
MS
732
733 /*
734 * Check for supported attributes...
735 */
736
737 if ((copies_sup = ippFindAttribute(supported, "copies-supported",
738 IPP_TAG_RANGE)) != NULL)
ef416fc2 739 {
740 /*
741 * Has the "copies-supported" attribute - does it have an upper
742 * bound > 1?
743 */
744
cc754834
MS
745 fprintf(stderr, "DEBUG: copies-supported=%d-%d\n",
746 copies_sup->values[0].range.lower,
747 copies_sup->values[0].range.upper);
748
ef416fc2 749 if (copies_sup->values[0].range.upper <= 1)
750 copies_sup = NULL; /* No */
751 }
752
cc754834 753 cups_version = ippFindAttribute(supported, "cups-version", IPP_TAG_TEXT);
ef416fc2 754
cc754834
MS
755 if ((format_sup = ippFindAttribute(supported, "document-format-supported",
756 IPP_TAG_MIMETYPE)) != NULL)
ef416fc2 757 {
758 fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
759 format_sup->num_values);
760 for (i = 0; i < format_sup->num_values; i ++)
761 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
762 format_sup->values[i].string.text);
763 }
764
cc754834
MS
765 if ((media_col_sup = ippFindAttribute(supported, "media-col-supported",
766 IPP_TAG_KEYWORD)) != NULL)
767 {
768 fprintf(stderr, "DEBUG: media-col-supported (%d values)\n",
769 media_col_sup->num_values);
770 for (i = 0; i < media_col_sup->num_values; i ++)
771 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
772 media_col_sup->values[i].string.text);
773 }
774
db1f069b 775 report_printer_state(supported, 0);
ef416fc2 776 }
777 while (ipp_status > IPP_OK_CONFLICT);
778
779 /*
780 * See if the printer is accepting jobs and is not stopped; if either
781 * condition is true and we are printing to a class, requeue the job...
782 */
783
784 if (getenv("CLASS") != NULL)
785 {
786 printer_state = ippFindAttribute(supported, "printer-state",
787 IPP_TAG_ENUM);
788 printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs",
789 IPP_TAG_BOOLEAN);
790
791 if (printer_state == NULL ||
bd7854cb 792 (printer_state->values[0].integer > IPP_PRINTER_PROCESSING &&
793 waitprinter) ||
ef416fc2 794 printer_accepting == NULL ||
795 !printer_accepting->values[0].boolean)
796 {
797 /*
798 * If the CLASS environment variable is set, the job was submitted
799 * to a class and not to a specific queue. In this case, we want
800 * to abort immediately so that the job can be requeued on the next
801 * available printer in the class.
802 */
803
db1f069b
MS
804 _cupsLangPuts(stderr,
805 _("INFO: Unable to contact printer, queuing on next "
806 "printer in class...\n"));
ef416fc2 807
808 ippDelete(supported);
809 httpClose(http);
810
ef416fc2 811 /*
812 * Sleep 5 seconds to keep the job from requeuing too rapidly...
813 */
814
815 sleep(5);
816
817 return (CUPS_BACKEND_FAILED);
818 }
819 }
820
821 /*
822 * See if the printer supports multiple copies...
823 */
824
d09495fa 825 copies = atoi(argv[4]);
826
ef416fc2 827 if (copies_sup || argc < 7)
d09495fa 828 {
829 copies_remaining = 1;
830
831 if (argc < 7)
832 copies = 1;
833 }
ef416fc2 834 else
d09495fa 835 copies_remaining = copies;
ef416fc2 836
ef416fc2 837 /*
838 * Then issue the print-job request...
839 */
840
bd7854cb 841 job_id = 0;
ef416fc2 842
d09495fa 843 while (copies_remaining > 0)
ef416fc2 844 {
7a14d768
MS
845 /*
846 * Check for side-channel requests...
847 */
848
849 backendCheckSideChannel(snmp_fd, http->hostaddr);
850
ef416fc2 851 /*
852 * Build the IPP request...
853 */
854
cc754834 855 if (job_canceled)
bd7854cb 856 break;
857
858 if (num_files > 1)
859 request = ippNewRequest(IPP_CREATE_JOB);
860 else
861 request = ippNewRequest(IPP_PRINT_JOB);
862
c168a833
MS
863 request->request.op.version[0] = version / 10;
864 request->request.op.version[1] = version % 10;
ef416fc2 865
cc754834
MS
866 fprintf(stderr, "DEBUG: %s IPP/%d.%d\n",
867 ippOpString(request->request.op.operation_id),
868 request->request.op.version[0],
869 request->request.op.version[1]);
870
ef416fc2 871 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
872 NULL, uri);
cc754834 873 fprintf(stderr, "DEBUG: printer-uri=\"%s\"\n", uri);
ef416fc2 874
875 if (argv[2][0])
cc754834 876 {
ef416fc2 877 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
878 "requesting-user-name", NULL, argv[2]);
cc754834
MS
879 fprintf(stderr, "DEBUG: requesting-user-name=\"%s\"\n", argv[2]);
880 }
ef416fc2 881
cc754834
MS
882 if (argv[3][0])
883 {
ef416fc2 884 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
885 argv[3]);
cc754834
MS
886 fprintf(stderr, "DEBUG: job-name=\"%s\"\n", argv[3]);
887 }
ef416fc2 888
b423cd4c 889#ifdef HAVE_LIBZ
890 if (compression)
891 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
892 "compression", NULL, "gzip");
893#endif /* HAVE_LIBZ */
894
ef416fc2 895 /*
896 * Handle options on the command-line...
897 */
898
899 options = NULL;
900 num_options = cupsParseOptions(argv[5], 0, &options);
901
cc754834
MS
902 if (format_sup != NULL)
903 {
904 for (i = 0; i < format_sup->num_values; i ++)
905 if (!strcasecmp(final_content_type, format_sup->values[i].string.text))
906 break;
907
908 if (i < format_sup->num_values)
909 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
910 "document-format", NULL, final_content_type);
911 }
912
913 if (send_options)
ef416fc2 914 {
cc754834 915 if (cups_version || !media_col_sup)
ef416fc2 916 {
cc754834
MS
917 /*
918 * When talking to another CUPS server, send all options...
919 */
ef416fc2 920
cc754834
MS
921 cupsEncodeOptions(request, num_options, options);
922 }
923 else
ef416fc2 924 {
925 /*
cc754834 926 * Otherwise send standard IPP attributes...
ef416fc2 927 */
928
cc754834
MS
929 char cachefile[1024];/* Printer PWG cache file */
930 const char *cups_cachedir; /* Location of cache file */
931 _pwg_t *pwg; /* PWG mapping data */
932 const char *keyword; /* PWG keyword */
933 _pwg_size_t *size; /* PWG media size */
d9bca400 934
d9bca400 935
cc754834
MS
936 if ((cups_cachedir = getenv("CUPS_CACHEDIR")) == NULL)
937 cups_cachedir = CUPS_CACHEDIR;
ef416fc2 938
cc754834
MS
939 snprintf(cachefile, sizeof(cachefile), "%s/%s.pwg", cups_cachedir,
940 getenv("PRINTER"));
ef416fc2 941
cc754834
MS
942 if ((keyword = cupsGetOption("PageSize", num_options, options)) == NULL)
943 keyword = cupsGetOption("media", num_options, options);
ef416fc2 944
cc754834
MS
945 pwg = _pwgCreateWithFile(cachefile);
946 size = _pwgGetSize(pwg, keyword);
ef416fc2 947
cc754834
MS
948 if (media_col_sup && size)
949 {
950 ipp_t *media_col, /* media-col value */
951 *media_size; /* media-size value */
952 const char *media_source, /* media-source value */
953 *media_type; /* media-type value */
954
955 media_size = ippNew();
956 ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
957 "x-dimension", size->width);
958 ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
959 "y-dimension", size->length);
960
961 media_col = ippNew();
962 ippAddCollection(media_col, IPP_TAG_ZERO, "media-size", media_size);
963
964 media_source = _pwgGetSource(pwg, cupsGetOption("InputSlot",
965 num_options,
966 options));
967 media_type = _pwgGetType(pwg, cupsGetOption("MediaType",
968 num_options, options));
969
970 for (i = 0; i < media_col_sup->num_values; i ++)
971 {
972 if (!strcmp(media_col_sup->values[i].string.text,
973 "media-left-margin"))
974 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
975 "media-left-margin", size->left);
976 else if (!strcmp(media_col_sup->values[i].string.text,
977 "media-bottom-margin"))
978 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
979 "media-bottom-margin", size->left);
980 else if (!strcmp(media_col_sup->values[i].string.text,
981 "media-right-margin"))
982 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
983 "media-right-margin", size->left);
984 else if (!strcmp(media_col_sup->values[i].string.text,
985 "media-top-margin"))
986 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
987 "media-top-margin", size->left);
988 else if (!strcmp(media_col_sup->values[i].string.text,
989 "media-source") && media_source)
990 ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD,
991 "media-source", NULL, media_source);
992 else if (!strcmp(media_col_sup->values[i].string.text,
993 "media-type") && media_type)
994 ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD,
995 "media-type", NULL, media_type);
996 }
997
998 ippAddCollection(request, IPP_TAG_JOB, "media-col", media_col);
999 }
1000 else if (size)
1001 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "media",
1002 NULL, size->map.pwg);
1003 else if (keyword)
1004 {
1005 _pwg_media_t *found; /* PWG media */
ef416fc2 1006
ef416fc2 1007
cc754834
MS
1008 if ((found = _pwgMediaForPPD(keyword)) == NULL)
1009 keyword = found->pwg;
1010 else if ((found = _pwgMediaForLegacy(keyword)) == NULL)
1011 keyword = found->pwg;
1012
1013 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "media",
1014 NULL, keyword);
1015 }
ef416fc2 1016
cc754834
MS
1017 if ((keyword = cupsGetOption("output-bin", num_options,
1018 options)) == NULL)
1019 keyword = _pwgGetBin(pwg, cupsGetOption("OutputBin", num_options,
1020 options));
1021
1022 if (keyword)
1023 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-bin",
1024 NULL, keyword);
1025
1026 if ((keyword = cupsGetOption("output-mode", num_options,
1027 options)) != NULL)
1028 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
1029 NULL, keyword);
1030
1031 if ((keyword = cupsGetOption("sides", num_options, options)) != NULL)
1032 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
1033 NULL, keyword);
1034
1035 _pwgDestroy(pwg);
1036 }
d09495fa 1037
cc754834
MS
1038 if (copies_sup && copies > 1)
1039 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies", copies);
ef416fc2 1040 }
1041
1042 cupsFreeOptions(num_options, options);
1043
ef416fc2 1044 /*
1045 * Do the request...
1046 */
1047
bd7854cb 1048 if (num_files > 1)
1049 response = cupsDoRequest(http, request, resource);
ef416fc2 1050 else
cc754834
MS
1051 {
1052 fputs("DEBUG: Sending file using chunking...\n", stderr);
1053 http_status = cupsSendRequest(http, request, resource, 0);
1054 if (http_status == HTTP_CONTINUE && request->state == IPP_DATA)
1055 {
1056 if (num_files == 1)
1057 fd = open(files[0], O_RDONLY);
1058 else
1059 fd = 0;
1060
1061 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
1062 {
1063 fprintf(stderr, "DEBUG: Read %d bytes...\n", (int)bytes);
1064
4220952d 1065 if (cupsWriteRequestData(http, buffer, bytes) != HTTP_CONTINUE)
cc754834
MS
1066 break;
1067 else
1068 {
1069 /*
1070 * Check for side-channel requests...
1071 */
1072
1073 backendCheckSideChannel(snmp_fd, http->hostaddr);
1074 }
1075 }
1076
1077 if (num_files == 1)
1078 close(fd);
1079 }
1080
1081 response = cupsGetResponse(http, resource);
1082 ippDelete(request);
1083 }
bd7854cb 1084
1085 ipp_status = cupsLastError();
ef416fc2 1086
1087 if (ipp_status > IPP_OK_CONFLICT)
1088 {
1089 job_id = 0;
1090
cc754834 1091 if (job_canceled)
bd7854cb 1092 break;
1093
ef416fc2 1094 if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1095 ipp_status == IPP_PRINTER_BUSY)
1096 {
db1f069b
MS
1097 _cupsLangPuts(stderr,
1098 _("INFO: Printer busy; will retry in 10 seconds...\n"));
ef416fc2 1099 sleep(10);
1100 }
1101 else
68b10830
MS
1102 {
1103 /*
1104 * Update auth-info-required as needed...
1105 */
1106
4d301e69 1107 _cupsLangPrintf(stderr, _("ERROR: Print file was not accepted (%s)\n"),
db1f069b 1108 cupsLastErrorString());
68b10830 1109
18ecb428 1110 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
68b10830
MS
1111 {
1112 fprintf(stderr, "DEBUG: WWW-Authenticate=\"%s\"\n",
1113 httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE));
1114
18ecb428
MS
1115 /*
1116 * Normal authentication goes through the password callback, which sets
1117 * auth_info_required to "username,password". Kerberos goes directly
1118 * through GSSAPI, so look for Negotiate in the WWW-Authenticate header
1119 * here and set auth_info_required as needed...
1120 */
1121
68b10830
MS
1122 if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
1123 "Negotiate", 9))
18ecb428 1124 auth_info_required = "negotiate";
68b10830
MS
1125 }
1126 }
ef416fc2 1127 }
1128 else if ((job_id_attr = ippFindAttribute(response, "job-id",
1129 IPP_TAG_INTEGER)) == NULL)
1130 {
db1f069b
MS
1131 _cupsLangPuts(stderr,
1132 _("NOTICE: Print file accepted - job ID unknown.\n"));
ef416fc2 1133 job_id = 0;
1134 }
1135 else
1136 {
1137 job_id = job_id_attr->values[0].integer;
db1f069b
MS
1138 _cupsLangPrintf(stderr, _("NOTICE: Print file accepted - job ID %d.\n"),
1139 job_id);
ef416fc2 1140 }
1141
bd7854cb 1142 ippDelete(response);
1143
cc754834 1144 if (job_canceled)
bd7854cb 1145 break;
1146
1147 if (job_id && num_files > 1)
1148 {
1149 for (i = 0; i < num_files; i ++)
1150 {
7a14d768
MS
1151 /*
1152 * Check for side-channel requests...
1153 */
1154
1155 backendCheckSideChannel(snmp_fd, http->hostaddr);
1156
1157 /*
1158 * Send the next file in the job...
1159 */
1160
bd7854cb 1161 request = ippNewRequest(IPP_SEND_DOCUMENT);
c168a833
MS
1162 request->request.op.version[0] = version / 10;
1163 request->request.op.version[1] = version % 10;
bd7854cb 1164
1165 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1166 NULL, uri);
1167
1168 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1169 job_id);
1170
1171 if (argv[2][0])
1172 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1173 "requesting-user-name", NULL, argv[2]);
1174
1175 if ((i + 1) == num_files)
1176 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
1177
1178 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
1179 "document-format", NULL, content_type);
1180
cc754834
MS
1181 fprintf(stderr, "DEBUG: Sending file %d using chunking...\n", i + 1);
1182 http_status = cupsSendRequest(http, request, resource, 0);
1183 if (http_status == HTTP_CONTINUE && request->state == IPP_DATA &&
1184 (fd = open(files[i], O_RDONLY)) >= 0)
1185 {
1186 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
1187 {
4220952d 1188 if (cupsWriteRequestData(http, buffer, bytes) != HTTP_CONTINUE)
cc754834
MS
1189 break;
1190 else
1191 {
1192 /*
1193 * Check for side-channel requests...
1194 */
1195
1196 backendCheckSideChannel(snmp_fd, http->hostaddr);
1197 }
1198 }
1199
1200 close(fd);
1201 }
97c9a8d7 1202
cc754834
MS
1203 ippDelete(cupsGetResponse(http, resource));
1204 ippDelete(request);
bd7854cb 1205
1206 if (cupsLastError() > IPP_OK_CONFLICT)
1207 {
1208 ipp_status = cupsLastError();
1209
db1f069b
MS
1210 _cupsLangPrintf(stderr,
1211 _("ERROR: Unable to add file %d to job: %s\n"),
1212 job_id, cupsLastErrorString());
bd7854cb 1213 break;
1214 }
1215 }
1216 }
ef416fc2 1217
1218 if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
1219 {
1220 fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
d09495fa 1221 copies_remaining --;
ef416fc2 1222 }
1223 else if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1224 ipp_status == IPP_PRINTER_BUSY)
1f6f3dbc 1225 continue;
ef416fc2 1226 else
d09495fa 1227 copies_remaining --;
ef416fc2 1228
1229 /*
1230 * Wait for the job to complete...
1231 */
1232
1233 if (!job_id || !waitjob)
1234 continue;
1235
db1f069b 1236 _cupsLangPuts(stderr, _("INFO: Waiting for job to complete...\n"));
ef416fc2 1237
cc754834 1238 for (delay = 1; !job_canceled;)
ef416fc2 1239 {
7a14d768
MS
1240 /*
1241 * Check for side-channel requests...
1242 */
1243
1244 backendCheckSideChannel(snmp_fd, http->hostaddr);
1245
ef416fc2 1246 /*
1247 * Build an IPP_GET_JOB_ATTRIBUTES request...
1248 */
1249
fa73b229 1250 request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
c168a833
MS
1251 request->request.op.version[0] = version / 10;
1252 request->request.op.version[1] = version % 10;
ef416fc2 1253
1254 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1255 NULL, uri);
1256
1257 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1258 job_id);
1259
1260 if (argv[2][0])
1261 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1262 "requesting-user-name", NULL, argv[2]);
1263
1264 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1265 "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
1266 NULL, jattrs);
1267
1268 /*
1269 * Do the request...
1270 */
1271
bd7854cb 1272 response = cupsDoRequest(http, request, resource);
1273 ipp_status = cupsLastError();
ef416fc2 1274
1275 if (ipp_status == IPP_NOT_FOUND)
1276 {
1277 /*
1278 * Job has gone away and/or the server has no job history...
1279 */
1280
1281 ippDelete(response);
1282
1283 ipp_status = IPP_OK;
1284 break;
1285 }
1286
1287 if (ipp_status > IPP_OK_CONFLICT)
1288 {
1289 if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
1290 ipp_status != IPP_PRINTER_BUSY)
1291 {
bd7854cb 1292 ippDelete(response);
ef416fc2 1293
db1f069b 1294 _cupsLangPrintf(stderr,
4d301e69 1295 _("ERROR: Unable to get job %d attributes (%s)\n"),
db1f069b 1296 job_id, cupsLastErrorString());
ef416fc2 1297 break;
1298 }
1299 }
1300
bd7854cb 1301 if (response)
ef416fc2 1302 {
1303 if ((job_state = ippFindAttribute(response, "job-state",
1304 IPP_TAG_ENUM)) != NULL)
1305 {
1306 /*
1307 * Stop polling if the job is finished or pending-held...
1308 */
1309
b94498cf 1310 if (job_state->values[0].integer > IPP_JOB_STOPPED)
ef416fc2 1311 {
fa73b229 1312 if ((job_sheets = ippFindAttribute(response,
1313 "job-media-sheets-completed",
1314 IPP_TAG_INTEGER)) != NULL)
bd7854cb 1315 fprintf(stderr, "PAGE: total %d\n",
1316 job_sheets->values[0].integer);
ef416fc2 1317
1318 ippDelete(response);
1319 break;
1320 }
1321 }
7a0cbd5e
MS
1322 else
1323 {
1324 /*
1325 * If the printer does not return a job-state attribute, it does not
1326 * conform to the IPP specification - break out immediately and fail
1327 * the job...
1328 */
1329
1330 fputs("DEBUG: No job-state available from printer - stopping queue.\n",
1331 stderr);
1332 ipp_status = IPP_INTERNAL_ERROR;
1333 break;
1334 }
ef416fc2 1335 }
1336
bd7854cb 1337 ippDelete(response);
ef416fc2 1338
1339 /*
1340 * Check the printer state and report it if necessary...
1341 */
1342
db1f069b 1343 check_printer_state(http, uri, resource, argv[2], version, job_id);
ef416fc2 1344
1345 /*
2e4ff8af 1346 * Wait 1-10 seconds before polling again...
ef416fc2 1347 */
1348
2e4ff8af
MS
1349 sleep(delay);
1350
1351 delay ++;
1352 if (delay > 10)
1353 delay = 1;
ef416fc2 1354 }
1355 }
1356
1357 /*
bd7854cb 1358 * Cancel the job as needed...
ef416fc2 1359 */
1360
cc754834 1361 if (job_canceled && job_id)
bd7854cb 1362 cancel_job(http, uri, job_id, resource, argv[2], version);
1363
1364 /*
1365 * Check the printer state and report it if necessary...
1366 */
ef416fc2 1367
db1f069b 1368 check_printer_state(http, uri, resource, argv[2], version, job_id);
ef416fc2 1369
7a14d768
MS
1370 /*
1371 * Collect the final page count as needed...
1372 */
1373
426c6a59 1374 if (have_supplies &&
7a14d768
MS
1375 !backendSNMPSupplies(snmp_fd, http->hostaddr, &page_count, NULL) &&
1376 page_count > start_count)
1377 fprintf(stderr, "PAGE: total %d\n", page_count - start_count);
1378
18ecb428
MS
1379#ifdef HAVE_GSSAPI
1380 /*
1381 * See if we used Kerberos at all...
1382 */
1383
1384 if (http->gssctx)
1385 auth_info_required = "negotiate";
1386#endif /* HAVE_GSSAPI */
1387
ef416fc2 1388 /*
1389 * Free memory...
1390 */
1391
1392 httpClose(http);
1393
bd7854cb 1394 ippDelete(supported);
ef416fc2 1395
1396 /*
1397 * Remove the temporary file(s) if necessary...
1398 */
1399
b423cd4c 1400#ifdef HAVE_LIBZ
1401 if (compression)
1402 {
1403 for (i = 0; i < num_files; i ++)
1404 unlink(files[i]);
1405 }
1406#endif /* HAVE_LIBZ */
1407
ef416fc2 1408 /*
1409 * Return the queue status...
1410 */
1411
18ecb428
MS
1412 fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
1413
1414 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
7ff4fea9 1415 return (CUPS_BACKEND_AUTH_REQUIRED);
7a0cbd5e
MS
1416 else if (ipp_status == IPP_INTERNAL_ERROR)
1417 return (CUPS_BACKEND_STOP);
7ff4fea9
MS
1418 else if (ipp_status > IPP_OK_CONFLICT)
1419 return (CUPS_BACKEND_FAILED);
1420 else
41681883
MS
1421 {
1422 _cupsLangPuts(stderr, _("INFO: Ready to print.\n"));
7ff4fea9 1423 return (CUPS_BACKEND_OK);
41681883 1424 }
ef416fc2 1425}
1426
1427
bd7854cb 1428/*
1429 * 'cancel_job()' - Cancel a print job.
1430 */
1431
1432static void
1433cancel_job(http_t *http, /* I - HTTP connection */
1434 const char *uri, /* I - printer-uri */
1435 int id, /* I - job-id */
1436 const char *resource, /* I - Resource path */
1437 const char *user, /* I - requesting-user-name */
1438 int version) /* I - IPP version */
1439{
1440 ipp_t *request; /* Cancel-Job request */
1441
1442
db1f069b 1443 _cupsLangPuts(stderr, _("INFO: Canceling print job...\n"));
bd7854cb 1444
1445 request = ippNewRequest(IPP_CANCEL_JOB);
c168a833
MS
1446 request->request.op.version[0] = version / 10;
1447 request->request.op.version[1] = version % 10;
bd7854cb 1448
1449 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1450 NULL, uri);
1451 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
1452
1453 if (user && user[0])
1454 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1455 "requesting-user-name", NULL, user);
1456
1457 /*
1458 * Do the request...
1459 */
1460
1461 ippDelete(cupsDoRequest(http, request, resource));
1462
1463 if (cupsLastError() > IPP_OK_CONFLICT)
db1f069b
MS
1464 _cupsLangPrintf(stderr, _("ERROR: Unable to cancel job %d: %s\n"), id,
1465 cupsLastErrorString());
bd7854cb 1466}
1467
1468
ef416fc2 1469/*
1470 * 'check_printer_state()' - Check the printer state...
1471 */
1472
bd7854cb 1473static void
fa73b229 1474check_printer_state(
1475 http_t *http, /* I - HTTP connection */
1476 const char *uri, /* I - Printer URI */
1477 const char *resource, /* I - Resource path */
1478 const char *user, /* I - Username, if any */
db1f069b
MS
1479 int version, /* I - IPP version */
1480 int job_id) /* I - Current job ID */
ef416fc2 1481{
1482 ipp_t *request, /* IPP request */
1483 *response; /* IPP response */
323c5de1 1484 static const char * const attrs[] = /* Attributes we want */
1485 {
1f0275e3
MS
1486 "marker-colors",
1487 "marker-levels",
1488 "marker-message",
1489 "marker-names",
1490 "marker-types",
323c5de1 1491 "printer-state-message",
1492 "printer-state-reasons"
1493 };
ef416fc2 1494
1495
1496 /*
1497 * Check on the printer state...
1498 */
1499
fa73b229 1500 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
c168a833
MS
1501 request->request.op.version[0] = version / 10;
1502 request->request.op.version[1] = version % 10;
ef416fc2 1503
1504 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1505 NULL, uri);
1506
1507 if (user && user[0])
1508 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1509 "requesting-user-name", NULL, user);
1510
323c5de1 1511 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1512 "requested-attributes",
1513 (int)(sizeof(attrs) / sizeof(attrs[0])), NULL, attrs);
ef416fc2 1514
1515 /*
1516 * Do the request...
1517 */
1518
1519 if ((response = cupsDoRequest(http, request, resource)) != NULL)
1520 {
db1f069b 1521 report_printer_state(response, job_id);
ef416fc2 1522 ippDelete(response);
1523 }
1524}
1525
1526
b423cd4c 1527#ifdef HAVE_LIBZ
1528/*
1529 * 'compress_files()' - Compress print files...
1530 */
1531
1532static void
1533compress_files(int num_files, /* I - Number of files */
1534 char **files) /* I - Files */
1535{
1536 int i, /* Looping var */
1537 fd; /* Temporary file descriptor */
1538 ssize_t bytes; /* Bytes read/written */
1539 size_t total; /* Total bytes read */
1540 cups_file_t *in, /* Input file */
1541 *out; /* Output file */
1542 struct stat outinfo; /* Output file information */
1543 char filename[1024], /* Temporary filename */
a74454a7 1544 buffer[32768]; /* Copy buffer */
b423cd4c 1545
1546
1547 fprintf(stderr, "DEBUG: Compressing %d job files...\n", num_files);
1548 for (i = 0; i < num_files; i ++)
1549 {
1550 if ((fd = cupsTempFd(filename, sizeof(filename))) < 0)
1551 {
db1f069b
MS
1552 _cupsLangPrintf(stderr,
1553 _("ERROR: Unable to create temporary compressed print "
1554 "file: %s\n"), strerror(errno));
b423cd4c 1555 exit(CUPS_BACKEND_FAILED);
1556 }
1557
1558 if ((out = cupsFileOpenFd(fd, "w9")) == NULL)
1559 {
db1f069b
MS
1560 _cupsLangPrintf(stderr,
1561 _("ERROR: Unable to open temporary compressed print "
1562 "file: %s\n"), strerror(errno));
b423cd4c 1563 exit(CUPS_BACKEND_FAILED);
1564 }
1565
1566 if ((in = cupsFileOpen(files[i], "r")) == NULL)
1567 {
db1f069b
MS
1568 _cupsLangPrintf(stderr,
1569 _("ERROR: Unable to open print file \"%s\": %s\n"),
1570 files[i], strerror(errno));
b423cd4c 1571 cupsFileClose(out);
1572 exit(CUPS_BACKEND_FAILED);
1573 }
1574
1575 total = 0;
1576 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
1577 if (cupsFileWrite(out, buffer, bytes) < bytes)
1578 {
db1f069b
MS
1579 _cupsLangPrintf(stderr,
1580 _("ERROR: Unable to write %d bytes to \"%s\": %s\n"),
1581 (int)bytes, filename, strerror(errno));
b423cd4c 1582 cupsFileClose(in);
1583 cupsFileClose(out);
1584 exit(CUPS_BACKEND_FAILED);
1585 }
1586 else
1587 total += bytes;
1588
1589 cupsFileClose(out);
1590 cupsFileClose(in);
1591
1592 files[i] = strdup(filename);
1593
1594 if (!stat(filename, &outinfo))
1595 fprintf(stderr,
1596 "DEBUG: File %d compressed to %.1f%% of original size, "
1597 CUPS_LLFMT " bytes...\n",
e1d6a774 1598 i + 1, 100.0 * outinfo.st_size / total,
1599 CUPS_LLCAST outinfo.st_size);
b423cd4c 1600 }
1601}
1602#endif /* HAVE_LIBZ */
1603
1604
ef416fc2 1605/*
1606 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
1607 */
1608
bd7854cb 1609static const char * /* O - Password */
fa73b229 1610password_cb(const char *prompt) /* I - Prompt (not used) */
ef416fc2 1611{
1612 (void)prompt;
1613
18ecb428
MS
1614 /*
1615 * Remember that we need to authenticate...
1616 */
1617
1618 auth_info_required = "username,password";
1619
3dfe78b3 1620 if (password && *password && password_tries < 3)
76cd9e37
MS
1621 {
1622 password_tries ++;
1623
ef416fc2 1624 return (password);
76cd9e37 1625 }
ef416fc2 1626 else
1627 {
1628 /*
18ecb428 1629 * Give up after 3 tries or if we don't have a password to begin with...
ef416fc2 1630 */
1631
18ecb428 1632 return (NULL);
ef416fc2 1633 }
1634}
1635
1636
1f0275e3
MS
1637/*
1638 * 'report_attr()' - Report an IPP attribute value.
1639 */
1640
1641static void
1642report_attr(ipp_attribute_t *attr) /* I - Attribute */
1643{
1644 int i; /* Looping var */
1645 char value[1024], /* Value string */
1646 *valptr, /* Pointer into value string */
1647 *attrptr; /* Pointer into attribute value */
1648
1649
1650 /*
1651 * Convert the attribute values into quoted strings...
1652 */
1653
1654 for (i = 0, valptr = value;
1655 i < attr->num_values && valptr < (value + sizeof(value) - 10);
1656 i ++)
1657 {
1658 if (i > 0)
1659 *valptr++ = ',';
1660
1661 switch (attr->value_tag)
1662 {
1663 case IPP_TAG_INTEGER :
1664 case IPP_TAG_ENUM :
1665 snprintf(valptr, sizeof(value) - (valptr - value), "%d",
1666 attr->values[i].integer);
1667 valptr += strlen(valptr);
1668 break;
1669
1670 case IPP_TAG_TEXT :
1671 case IPP_TAG_NAME :
1672 case IPP_TAG_KEYWORD :
1673 *valptr++ = '\"';
1674 for (attrptr = attr->values[i].string.text;
1675 *attrptr && valptr < (value + sizeof(value) - 10);
1676 attrptr ++)
1677 {
1678 if (*attrptr == '\\' || *attrptr == '\"')
1679 *valptr++ = '\\';
1680
1681 *valptr++ = *attrptr;
1682 }
1683 *valptr++ = '\"';
1684 break;
1685
1686 default :
1687 /*
1688 * Unsupported value type...
1689 */
1690
1691 return;
1692 }
1693 }
1694
1695 *valptr = '\0';
1696
1697 /*
1698 * Tell the scheduler about the new values...
1699 */
1700
1701 fprintf(stderr, "ATTR: %s=%s\n", attr->name, value);
1702}
1703
1704
ef416fc2 1705/*
1706 * 'report_printer_state()' - Report the printer state.
1707 */
1708
bd7854cb 1709static int /* O - Number of reasons shown */
db1f069b
MS
1710report_printer_state(ipp_t *ipp, /* I - IPP response */
1711 int job_id) /* I - Current job ID */
ef416fc2 1712{
1713 int i; /* Looping var */
1714 int count; /* Count of reasons shown... */
4d301e69 1715 ipp_attribute_t *psm, /* printer-state-message */
1f0275e3
MS
1716 *reasons, /* printer-state-reasons */
1717 *marker; /* marker-* attributes */
ef416fc2 1718 const char *reason; /* Current reason */
ef416fc2 1719 const char *prefix; /* Prefix for STATE: line */
1720 char state[1024]; /* State string */
1721
1722
323c5de1 1723 if ((psm = ippFindAttribute(ipp, "printer-state-message",
1724 IPP_TAG_TEXT)) != NULL)
1725 fprintf(stderr, "INFO: %s\n", psm->values[0].string.text);
1726
ef416fc2 1727 if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
1728 IPP_TAG_KEYWORD)) == NULL)
1729 return (0);
1730
4d301e69
MS
1731 state[0] = '\0';
1732 prefix = "STATE: ";
ef416fc2 1733
1734 for (i = 0, count = 0; i < reasons->num_values; i ++)
1735 {
1736 reason = reasons->values[i].string.text;
1737
4d301e69
MS
1738 if (strcmp(reason, "paused") &&
1739 strcmp(reason, "com.apple.print.recoverable-warning"))
db1f069b
MS
1740 {
1741 strlcat(state, prefix, sizeof(state));
1742 strlcat(state, reason, sizeof(state));
1743
1744 prefix = ",";
1745 }
ef416fc2 1746 }
1747
f11a948a
MS
1748 if (state[0])
1749 fprintf(stderr, "%s\n", state);
ef416fc2 1750
1f0275e3
MS
1751 /*
1752 * Relay the current marker-* attribute values...
1753 */
1754
1755 if ((marker = ippFindAttribute(ipp, "marker-colors", IPP_TAG_NAME)) != NULL)
1756 report_attr(marker);
f11a948a
MS
1757 if ((marker = ippFindAttribute(ipp, "marker-high-levels",
1758 IPP_TAG_INTEGER)) != NULL)
1759 report_attr(marker);
1f0275e3
MS
1760 if ((marker = ippFindAttribute(ipp, "marker-levels",
1761 IPP_TAG_INTEGER)) != NULL)
1762 report_attr(marker);
f11a948a
MS
1763 if ((marker = ippFindAttribute(ipp, "marker-low-levels",
1764 IPP_TAG_INTEGER)) != NULL)
1765 report_attr(marker);
1f0275e3
MS
1766 if ((marker = ippFindAttribute(ipp, "marker-message", IPP_TAG_TEXT)) != NULL)
1767 report_attr(marker);
1768 if ((marker = ippFindAttribute(ipp, "marker-names", IPP_TAG_NAME)) != NULL)
1769 report_attr(marker);
1770 if ((marker = ippFindAttribute(ipp, "marker-types", IPP_TAG_KEYWORD)) != NULL)
1771 report_attr(marker);
1772
ef416fc2 1773 return (count);
1774}
1775
1776
ef416fc2 1777/*
1778 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
1779 */
1780
1781static void
1782sigterm_handler(int sig) /* I - Signal */
1783{
1784 (void)sig; /* remove compiler warnings... */
1785
cc754834 1786 if (!job_canceled)
bd7854cb 1787 {
1788 /*
1789 * Flag that the job should be cancelled...
1790 */
1791
cc754834 1792 job_canceled = 1;
bd7854cb 1793 return;
1794 }
1795
ef416fc2 1796 exit(1);
1797}
1798
1799
1800/*
b19ccc9e 1801 * End of "$Id: ipp.c 7948 2008-09-17 00:04:12Z mike $".
ef416fc2 1802 */