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