]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/ipp.c
Fix IPP backend so that it can recover from failed job submissions that are
[thirdparty/cups.git] / backend / ipp.c
CommitLineData
c8f9565c 1/*
c9d3f842 2 * "$Id$"
c8f9565c 3 *
6875feac 4 * IPP backend for CUPS.
c8f9565c 5 *
04c7bec1 6 * Copyright 2007-2011 by Apple Inc.
bb3ff448 7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
c8f9565c 8 *
9 * These coded instructions, statements, and computer programs are the
4e8d321f 10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
c8f9565c 12 * "LICENSE" which should have been included with this file. If this
4e8d321f 13 * file is missing or damaged, see the license at "http://www.cups.org/".
c8f9565c 14 *
dab1a4d8 15 * This file is subject to the Apple OS-Developed Software exception.
16 *
c8f9565c 17 * Contents:
18 *
d1c2727f 19 * main() - Send a file to the printer or server.
1230a8a0 20 * cancel_job() - Cancel a print job.
b9738d7c 21 * check_printer_state() - Check the printer state.
1230a8a0 22 * compress_files() - Compress print files...
b9738d7c 23 * monitor_printer() - Monitor the printer state...
2ec940b5 24 * new_request() - Create a new print creation or validation request.
d1c2727f 25 * password_cb() - Disable the password prompt for
26 * cupsDoFileRequest().
7c7997c3 27 * report_attr() - Report an IPP attribute value.
d1c2727f 28 * report_printer_state() - Report the printer state.
8f4595eb 29 * sigterm_handler() - Handle 'terminate' signals that stop the backend.
c8f9565c 30 */
31
32/*
33 * Include necessary headers.
34 */
35
eab5b04e 36#include "backend-private.h"
c8f9565c 37#include <sys/types.h>
38#include <sys/stat.h>
1c85e5c0 39#include <sys/wait.h>
c8f9565c 40
b9738d7c 41
42/*
43 * Types...
44 */
45
46typedef struct _cups_monitor_s /**** Monitoring data ****/
47{
48 const char *uri, /* Printer URI */
49 *hostname, /* Hostname */
50 *user, /* Username */
51 *resource; /* Resource path */
52 int port, /* Port number */
53 version, /* IPP version */
54 job_id; /* Job ID for submitted job */
55 http_encryption_t encryption; /* Use encryption? */
56 ipp_jstate_t job_state; /* Current job state */
57 ipp_pstate_t printer_state; /* Current printer state */
58} _cups_monitor_t;
59
60
8f4595eb 61/*
62 * Globals...
63 */
64
4233257e 65static const char *auth_info_required = "none";
66 /* New auth-info-required value */
b9738d7c 67static const char * const jattrs[] = /* Job attributes we want */
68{
69 "job-media-sheets-completed",
70 "job-state",
71 "job-state-reasons"
72};
6875feac 73static int job_canceled = 0; /* Job cancelled? */
b9738d7c 74static char *password = NULL; /* Password for device URI */
75static int password_tries = 0; /* Password tries */
76static const char * const pattrs[] = /* Printer attributes we want */
77{
78 "copies-supported",
79 "cups-version",
80 "document-format-supported",
81 "marker-colors",
82 "marker-high-levels",
83 "marker-levels",
84 "marker-low-levels",
85 "marker-message",
86 "marker-names",
87 "marker-types",
88 "media-col-supported",
adbcb02a 89 "multiple-document-handling-supported",
e2fe9bbd 90 "operations-supported",
b9738d7c 91 "printer-alert",
92 "printer-alert-description",
93 "printer-is-accepting-jobs",
94 "printer-state",
95 "printer-state-message",
96 "printer-state-reasons",
97};
6c1b8723 98static const char * const remote_job_states[] =
99{ /* Remote job state keywords */
100 "cups-remote-pending",
101 "cups-remote-pending-held",
102 "cups-remote-processing",
103 "cups-remote-stopped",
104 "cups-remote-canceled",
105 "cups-remote-aborted",
106 "cups-remote-completed"
107};
940afb4b 108static char tmpfilename[1024] = ""; /* Temporary spool file name */
8f4595eb 109
110
b5cb0608 111/*
112 * Local functions...
113 */
114
b9738d7c 115static void cancel_job(http_t *http, const char *uri, int id,
116 const char *resource, const char *user,
117 int version);
118static ipp_pstate_t check_printer_state(http_t *http, const char *uri,
119 const char *resource,
120 const char *user, int version,
121 int job_id);
1230a8a0 122#ifdef HAVE_LIBZ
b9738d7c 123static void compress_files(int num_files, char **files);
1230a8a0 124#endif /* HAVE_LIBZ */
b9738d7c 125static void *monitor_printer(_cups_monitor_t *monitor);
2ec940b5 126static ipp_t *new_request(ipp_op_t op, int version, const char *uri,
127 const char *user, const char *title,
128 int num_options, cups_option_t *options,
129 const char *compression, int copies,
8211508e 130 const char *format, _ppd_cache_t *pc,
adbcb02a 131 ipp_attribute_t *media_col_sup,
132 ipp_attribute_t *doc_handling_sup);
b9738d7c 133static const char *password_cb(const char *);
134static void report_attr(ipp_attribute_t *attr);
135static int report_printer_state(ipp_t *ipp, int job_id);
136static void sigterm_handler(int sig);
a684a3b0 137
b5cb0608 138
c8f9565c 139/*
140 * 'main()' - Send a file to the printer or server.
141 *
142 * Usage:
143 *
144 * printer-uri job-id user title copies options [file]
145 */
146
74ef1ffb 147int /* O - Exit status */
072c4872 148main(int argc, /* I - Number of command-line args */
74ef1ffb 149 char *argv[]) /* I - Command-line arguments */
c8f9565c 150{
74ef1ffb 151 int i; /* Looping var */
cad2b75f 152 int send_options; /* Send job options? */
74ef1ffb 153 int num_options; /* Number of printer options */
154 cups_option_t *options; /* Printer options */
cda2b561 155 const char *device_uri; /* Device URI */
9fa9ba8f 156 char scheme[255], /* Scheme in URI */
74ef1ffb 157 hostname[1024], /* Hostname */
158 username[255], /* Username info */
159 resource[1024], /* Resource info (printer name) */
a0c0bbe7 160 addrname[256], /* Address name */
74ef1ffb 161 *optptr, /* Pointer to URI options */
abd1f85f 162 *name, /* Name of option */
163 *value, /* Value of option */
164 sep; /* Separator character */
cce0044f 165 http_addrlist_t *addrlist; /* Address of printer */
eab5b04e 166 int snmp_fd, /* SNMP socket */
167 start_count, /* Page count via SNMP at start */
6bffe4a6 168 page_count, /* Page count via SNMP */
169 have_supplies; /* Printer supports supply levels? */
072c4872 170 int num_files; /* Number of files to print */
940afb4b 171 char **files, /* Files to print */
172 *compatfile = NULL; /* Compatibility filename */
173 off_t compatsize = 0; /* Size of compatibility file */
74ef1ffb 174 int port; /* Port number (not used) */
cce0044f 175 char portname[255]; /* Port name */
74ef1ffb 176 char uri[HTTP_MAX_URI]; /* Updated URI without user/pass */
6875feac 177 http_status_t http_status; /* Status of HTTP request */
74ef1ffb 178 ipp_status_t ipp_status; /* Status of IPP request */
179 http_t *http; /* HTTP connection */
180 ipp_t *request, /* IPP request */
181 *response, /* IPP response */
182 *supported; /* get-printer-attributes response */
ff0295f0 183 time_t start_time; /* Time of first connect */
ff0295f0 184 int contimeout; /* Connection timeout */
fc596e79 185 int delay, /* Delay for retries */
186 prev_delay; /* Previous delay */
2ec940b5 187 const char *compression; /* Compression mode */
188 int waitjob, /* Wait for job complete? */
74ef1ffb 189 waitprinter; /* Wait for printer ready? */
b9738d7c 190 _cups_monitor_t monitor; /* Monitoring data */
74ef1ffb 191 ipp_attribute_t *job_id_attr; /* job-id attribute */
192 int job_id; /* job-id value */
072c4872 193 ipp_attribute_t *job_sheets; /* job-media-sheets-completed */
194 ipp_attribute_t *job_state; /* job-state */
195 ipp_attribute_t *copies_sup; /* copies-supported */
6875feac 196 ipp_attribute_t *cups_version; /* cups-version */
072c4872 197 ipp_attribute_t *format_sup; /* document-format-supported */
6875feac 198 ipp_attribute_t *media_col_sup; /* media-col-supported */
2ec940b5 199 ipp_attribute_t *operations_sup; /* operations-supported */
adbcb02a 200 ipp_attribute_t *doc_handling_sup; /* multiple-document-handling-supported */
74ef1ffb 201 ipp_attribute_t *printer_state; /* printer-state attribute */
072c4872 202 ipp_attribute_t *printer_accepting; /* printer-is-accepting-jobs */
2ec940b5 203 int validate_job; /* Does printer support Validate-Job? */
37de726f 204 int copies, /* Number of copies for job */
205 copies_remaining; /* Number of copies remaining */
b43a3371 206 const char *content_type, /* CONTENT_TYPE environment variable */
2ec940b5 207 *final_content_type, /* FINAL_CONTENT_TYPE environment var */
208 *document_format; /* document-format value */
6875feac 209 int fd; /* File descriptor */
210 off_t bytes; /* Bytes copied */
211 char buffer[16384]; /* Copy buffer */
4ff40357 212#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
74ef1ffb 213 struct sigaction action; /* Actions for POSIX signals */
4ff40357 214#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
74ef1ffb 215 int version; /* IPP version */
2ec940b5 216 ppd_file_t *ppd; /* PPD file */
8211508e 217 _ppd_cache_t *pc; /* PPD cache and mapping data */
c8f9565c 218
4b23f3b3 219
220 /*
221 * Make sure status messages are not buffered...
222 */
223
2922de55 224 setbuf(stderr, NULL);
c8f9565c 225
98904cd6 226 /*
8f4595eb 227 * Ignore SIGPIPE and catch SIGTERM signals...
98904cd6 228 */
229
230#ifdef HAVE_SIGSET
231 sigset(SIGPIPE, SIG_IGN);
8f4595eb 232 sigset(SIGTERM, sigterm_handler);
98904cd6 233#elif defined(HAVE_SIGACTION)
234 memset(&action, 0, sizeof(action));
235 action.sa_handler = SIG_IGN;
236 sigaction(SIGPIPE, &action, NULL);
8f4595eb 237
238 sigemptyset(&action.sa_mask);
239 sigaddset(&action.sa_mask, SIGTERM);
240 action.sa_handler = sigterm_handler;
241 sigaction(SIGTERM, &action, NULL);
98904cd6 242#else
243 signal(SIGPIPE, SIG_IGN);
8f4595eb 244 signal(SIGTERM, sigterm_handler);
98904cd6 245#endif /* HAVE_SIGSET */
246
4b23f3b3 247 /*
248 * Check command-line...
249 */
250
68edc300 251 if (argc == 1)
252 {
183914a3 253 char *s;
254
d4c438d4 255 if ((s = strrchr(argv[0], '/')) != NULL)
256 s ++;
257 else
258 s = argv[0];
259
3037604c 260 printf("network %s \"Unknown\" \"%s (%s)\"\n",
261 s, _cupsLangString(cupsLangDefault(),
262 _("Internet Printing Protocol")), s);
6248387b 263 return (CUPS_BACKEND_OK);
68edc300 264 }
072c4872 265 else if (argc < 6)
c8f9565c 266 {
472af6f3 267 _cupsLangPrintf(stderr,
4c4eea89 268 _("Usage: %s job-id user title copies options [file]"),
472af6f3 269 argv[0]);
6248387b 270 return (CUPS_BACKEND_STOP);
c8f9565c 271 }
272
a684a3b0 273 /*
072c4872 274 * Get the (final) content type...
a684a3b0 275 */
276
b43a3371 277 if ((content_type = getenv("CONTENT_TYPE")) == NULL)
278 content_type = "application/octet-stream";
a684a3b0 279
b43a3371 280 if ((final_content_type = getenv("FINAL_CONTENT_TYPE")) == NULL)
281 {
282 final_content_type = content_type;
283
284 if (!strncmp(final_content_type, "printer/", 8))
285 final_content_type = "application/vnd.cups-raw";
286 }
12d8a513 287
7abb7137 288 /*
289 * Extract the hostname and printer name from the URI...
290 */
291
04c7bec1 292 while ((device_uri = cupsBackendDeviceURI(argv)) == NULL)
293 {
294 _cupsLangPrintFilter(stderr, "INFO", _("Unable to locate printer."));
295 sleep(10);
296
297 if (getenv("CLASS") != NULL)
298 return (CUPS_BACKEND_FAILED);
299 }
cda2b561 300
9fa9ba8f 301 httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme),
302 username, sizeof(username), hostname, sizeof(hostname), &port,
303 resource, sizeof(resource));
7abb7137 304
6df6deaf 305 if (!port)
306 port = IPP_PORT; /* Default to port 631 */
307
9fa9ba8f 308 if (!strcmp(scheme, "https"))
7abb7137 309 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
692bbbae 310 else
311 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
7abb7137 312
897922a9 313 /*
314 * See if there are any options...
315 */
316
2ec940b5 317 compression = NULL;
6875feac 318 version = 20;
897922a9 319 waitjob = 1;
320 waitprinter = 1;
ff0295f0 321 contimeout = 7 * 24 * 60 * 60;
897922a9 322
323 if ((optptr = strchr(resource, '?')) != NULL)
324 {
325 /*
326 * Yup, terminate the device name string and move to the first
327 * character of the optptr...
328 */
329
330 *optptr++ = '\0';
331
332 /*
333 * Then parse the optptr...
334 */
335
336 while (*optptr)
337 {
338 /*
339 * Get the name...
340 */
341
abd1f85f 342 name = optptr;
897922a9 343
abd1f85f 344 while (*optptr && *optptr != '=' && *optptr != '+' && *optptr != '&')
345 optptr ++;
346
347 if ((sep = *optptr) != '\0')
348 *optptr++ = '\0';
349
350 if (sep == '=')
897922a9 351 {
352 /*
353 * Get the value...
354 */
355
abd1f85f 356 value = optptr;
897922a9 357
abd1f85f 358 while (*optptr && *optptr != '+' && *optptr != '&')
897922a9 359 optptr ++;
abd1f85f 360
361 if (*optptr)
362 *optptr++ = '\0';
897922a9 363 }
364 else
abd1f85f 365 value = (char *)"";
897922a9 366
367 /*
368 * Process the option...
369 */
370
371 if (!strcasecmp(name, "waitjob"))
372 {
373 /*
374 * Wait for job completion?
375 */
376
377 waitjob = !strcasecmp(value, "on") ||
378 !strcasecmp(value, "yes") ||
379 !strcasecmp(value, "true");
380 }
381 else if (!strcasecmp(name, "waitprinter"))
382 {
383 /*
384 * Wait for printer idle?
385 */
386
387 waitprinter = !strcasecmp(value, "on") ||
388 !strcasecmp(value, "yes") ||
389 !strcasecmp(value, "true");
390 }
391 else if (!strcasecmp(name, "encryption"))
392 {
393 /*
394 * Enable/disable encryption?
395 */
396
397 if (!strcasecmp(value, "always"))
398 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
399 else if (!strcasecmp(value, "required"))
400 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
401 else if (!strcasecmp(value, "never"))
402 cupsSetEncryption(HTTP_ENCRYPT_NEVER);
403 else if (!strcasecmp(value, "ifrequested"))
404 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
405 else
406 {
4c4eea89 407 _cupsLangPrintFilter(stderr, "ERROR",
408 _("Unknown encryption option value: \"%s\"."),
409 value);
897922a9 410 }
411 }
a708af2a 412 else if (!strcasecmp(name, "version"))
413 {
414 if (!strcmp(value, "1.0"))
22d59a30 415 version = 10;
a708af2a 416 else if (!strcmp(value, "1.1"))
22d59a30 417 version = 11;
418 else if (!strcmp(value, "2.0"))
419 version = 20;
420 else if (!strcmp(value, "2.1"))
421 version = 21;
4c4eea89 422 else if (!strcmp(value, "2.2"))
423 version = 22;
a708af2a 424 else
425 {
4c4eea89 426 _cupsLangPrintFilter(stderr, "ERROR",
427 _("Unknown version option value: \"%s\"."),
428 value);
a708af2a 429 }
430 }
1230a8a0 431#ifdef HAVE_LIBZ
432 else if (!strcasecmp(name, "compression"))
433 {
2ec940b5 434 if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") ||
435 !strcasecmp(value, "on") || !strcasecmp(value, "gzip"))
436 compression = "gzip";
1230a8a0 437 }
438#endif /* HAVE_LIBZ */
ff0295f0 439 else if (!strcasecmp(name, "contimeout"))
440 {
441 /*
442 * Set the connection timeout...
443 */
444
445 if (atoi(value) > 0)
446 contimeout = atoi(value);
447 }
897922a9 448 else
449 {
450 /*
451 * Unknown option...
452 */
453
4c4eea89 454 _cupsLangPrintFilter(stderr, "ERROR",
455 _("Unknown option \"%s\" with value \"%s\"."),
456 name, value);
897922a9 457 }
458 }
459 }
460
1230a8a0 461 /*
462 * If we have 7 arguments, print the file named on the command-line.
463 * Otherwise, copy stdin to a temporary file and print the temporary
464 * file.
465 */
466
467 if (argc == 6)
468 {
6875feac 469 num_files = 0;
470 send_options = !strcasecmp(final_content_type, "application/pdf") ||
471 !strcasecmp(final_content_type, "application/vnd.cups-pdf") ||
2ec940b5 472 !strncasecmp(final_content_type, "image/", 6);
6875feac 473
474 fputs("DEBUG: Sending stdin for job...\n", stderr);
1230a8a0 475 }
476 else
477 {
478 /*
479 * Point to the files on the command-line...
480 */
481
b43a3371 482 num_files = argc - 6;
483 files = argv + 6;
484 send_options = 1;
cad2b75f 485
1230a8a0 486#ifdef HAVE_LIBZ
487 if (compression)
488 compress_files(num_files, files);
489#endif /* HAVE_LIBZ */
1230a8a0 490
6875feac 491 fprintf(stderr, "DEBUG: %d files to send in job...\n", num_files);
492 }
1230a8a0 493
c8f9565c 494 /*
b5cb0608 495 * Set the authentication info, if any...
c8f9565c 496 */
497
b5cb0608 498 cupsSetPasswordCB(password_cb);
499
500 if (username[0])
501 {
48e211f3 502 /*
503 * Use authenticaion information in the device URI...
504 */
505
b5cb0608 506 if ((password = strchr(username, ':')) != NULL)
507 *password++ = '\0';
508
509 cupsSetUser(username);
510 }
cbedee47 511 else
48e211f3 512 {
513 /*
fa1fc704 514 * Try loading authentication information from the environment.
48e211f3 515 */
516
abd1f85f 517 const char *ptr = getenv("AUTH_USERNAME");
518
519 if (ptr)
fa1fc704 520 cupsSetUser(ptr);
48e211f3 521
fa1fc704 522 password = getenv("AUTH_PASSWORD");
48e211f3 523 }
c8f9565c 524
ad142540 525#ifdef HAVE_GSSAPI
526 /*
527 * For Kerberos, become the printing user (if we can) to get the credentials
528 * that way.
529 */
530
531 if (!getuid() && (value = getenv("AUTH_UID")) != NULL)
532 seteuid(atoi(value));
533#endif /* HAVE_GSSAPI */
534
c8f9565c 535 /*
cce0044f 536 * Try finding the remote server...
c8f9565c 537 */
538
f831f3d8 539 start_time = time(NULL);
ff0295f0 540
cce0044f 541 sprintf(portname, "%d", port);
542
22a980cc 543 fputs("STATE: +connecting-to-device\n", stderr);
cce0044f 544 fprintf(stderr, "DEBUG: Looking up \"%s\"...\n", hostname);
545
546 while ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portname)) == NULL)
547 {
548 _cupsLangPrintFilter(stderr, "INFO",
549 _("Unable to locate printer \"%s\"."), hostname);
550 sleep(10);
551
552 if (getenv("CLASS") != NULL)
553 {
554 fputs("STATE: -connecting-to-device\n", stderr);
555 return (CUPS_BACKEND_STOP);
556 }
557 }
558
559 http = _httpCreate(hostname, port, addrlist, cupsEncryption(), AF_UNSPEC);
560
561 /*
562 * See if the printer supports SNMP...
563 */
564
565 if ((snmp_fd = _cupsSNMPOpen(addrlist->addr.addr.sa_family)) >= 0)
566 {
567 have_supplies = !backendSNMPSupplies(snmp_fd, &(addrlist->addr),
568 &start_count, NULL);
569 }
570 else
571 have_supplies = start_count = 0;
572
573 /*
574 * Wait for data from the filter...
575 */
576
577 if (num_files == 0)
ad6d7ec9 578 if (!backendWaitLoop(snmp_fd, &(addrlist->addr), 0, backendNetworkSideCB))
cce0044f 579 return (CUPS_BACKEND_OK);
580
581 /*
582 * Try connecting to the remote server...
583 */
584
fc596e79 585 delay = _cupsNextDelay(0, &prev_delay);
22a980cc 586
97fcaf92 587 do
c8f9565c 588 {
9fa9ba8f 589 fprintf(stderr, "DEBUG: Connecting to %s:%d\n", hostname, port);
4c4eea89 590 _cupsLangPrintFilter(stderr, "INFO", _("Connecting to printer."));
c8f9565c 591
cce0044f 592 if (httpReconnect(http))
d21a7597 593 {
03a8680a 594 int error = errno; /* Connection error */
595
cce0044f 596 if (http->status == HTTP_PKI_ERROR)
597 fputs("STATE: +cups-certificate-error\n", stderr);
598
6875feac 599 if (job_canceled)
bb3ff448 600 break;
601
997cf8b0 602 if (getenv("CLASS") != NULL)
603 {
604 /*
605 * If the CLASS environment variable is set, the job was submitted
606 * to a class and not to a specific queue. In this case, we want
607 * to abort immediately so that the job can be requeued on the next
608 * available printer in the class.
609 */
610
4c4eea89 611 _cupsLangPrintFilter(stderr, "INFO",
612 _("Unable to contact printer, queuing on next "
613 "printer in class."));
997cf8b0 614
997cf8b0 615 /*
616 * Sleep 5 seconds to keep the job from requeuing too rapidly...
617 */
618
619 sleep(5);
620
cce0044f 621 fputs("STATE: -connecting-to-device\n", stderr);
622
6248387b 623 return (CUPS_BACKEND_FAILED);
997cf8b0 624 }
625
03a8680a 626 fprintf(stderr, "DEBUG: Connection error: %s\n", strerror(errno));
627
4c2096b8 628 if (errno == ECONNREFUSED || errno == EHOSTDOWN ||
629 errno == EHOSTUNREACH)
97fcaf92 630 {
ff0295f0 631 if (contimeout && (time(NULL) - start_time) > contimeout)
632 {
4c4eea89 633 _cupsLangPrintFilter(stderr, "ERROR",
634 _("The printer is not responding."));
cce0044f 635 fputs("STATE: -connecting-to-device\n", stderr);
ff0295f0 636 return (CUPS_BACKEND_FAILED);
637 }
638
03a8680a 639 switch (error)
640 {
641 case EHOSTDOWN :
4c4eea89 642 _cupsLangPrintFilter(stderr, "WARNING",
7cfa02ab 643 _("The printer may not exist or "
644 "is unavailable at this time."));
03a8680a 645 break;
646
647 case EHOSTUNREACH :
4c4eea89 648 _cupsLangPrintFilter(stderr, "WARNING",
7cfa02ab 649 _("The printer is unreachable at this "
650 "time."));
03a8680a 651 break;
652
653 case ECONNREFUSED :
654 default :
4c4eea89 655 _cupsLangPrintFilter(stderr, "WARNING",
7cfa02ab 656 _("The printer is busy."));
03a8680a 657 break;
658 }
ff0295f0 659
660 sleep(delay);
661
fc596e79 662 delay = _cupsNextDelay(delay, &prev_delay);
97fcaf92 663 }
664 else
665 {
4c4eea89 666 _cupsLangPrintFilter(stderr, "ERROR",
7cfa02ab 667 _("The printer is not responding."));
2cc18dd2 668 sleep(30);
97fcaf92 669 }
bb3ff448 670
6875feac 671 if (job_canceled)
bb3ff448 672 break;
d21a7597 673 }
cce0044f 674 else
675 fputs("STATE: -cups-certificate-error\n", stderr);
c8f9565c 676 }
cce0044f 677 while (http->fd < 0);
c8f9565c 678
6875feac 679 if (job_canceled || !http)
bb3ff448 680 return (CUPS_BACKEND_FAILED);
bb3ff448 681
22a980cc 682 fputs("STATE: -connecting-to-device\n", stderr);
4c4eea89 683 _cupsLangPrintFilter(stderr, "INFO", _("Connected to printer."));
a4e23897 684
f0aa54a1 685 fprintf(stderr, "DEBUG: Connected to %s:%d...\n",
686 httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
687 _httpAddrPort(http->hostaddr));
a0c0bbe7 688
c8f9565c 689 /*
690 * Build a URI for the printer and fill the standard IPP attributes for
8ce7c000 691 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
692 * might contain username:password information...
c8f9565c 693 */
694
11fdb546 695 httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), scheme, NULL, hostname,
696 port, resource);
c8f9565c 697
698 /*
97fcaf92 699 * First validate the destination and see if the device supports multiple
6875feac 700 * copies...
c8f9565c 701 */
702
adbcb02a 703 copies_sup = NULL;
704 cups_version = NULL;
705 format_sup = NULL;
706 media_col_sup = NULL;
707 supported = NULL;
708 operations_sup = NULL;
709 doc_handling_sup = NULL;
710 validate_job = 0;
c8f9565c 711
97fcaf92 712 do
c8f9565c 713 {
56f7a531 714 /*
715 * Check for side-channel requests...
716 */
717
718 backendCheckSideChannel(snmp_fd, http->hostaddr);
719
c8f9565c 720 /*
97fcaf92 721 * Build the IPP request...
c8f9565c 722 */
723
e12df069 724 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
22d59a30 725 request->request.op.version[0] = version / 10;
726 request->request.op.version[1] = version % 10;
c8f9565c 727
97fcaf92 728 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
729 NULL, uri);
c8f9565c 730
8f4595eb 731 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
732 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
733 NULL, pattrs);
734
97fcaf92 735 /*
736 * Do the request...
737 */
c8f9565c 738
2054e655 739 fputs("DEBUG: Getting supported attributes...\n", stderr);
a4e23897 740
acd7eb22 741 if (http->version < HTTP_1_1)
6875feac 742 {
8962df79 743 fprintf(stderr, "DEBUG: Printer responded with HTTP version %d.%d.\n",
744 http->version / 100, http->version % 100);
745
4c4eea89 746 _cupsLangPrintFilter(stderr, "ERROR",
940afb4b 747 _("This printer does not conform to the IPP "
748 "standard. Please contact the manufacturer of "
749 "your printer for assistance."));
6875feac 750 }
acd7eb22 751
b9738d7c 752 supported = cupsDoRequest(http, request, resource);
753 ipp_status = cupsLastError();
b5cb0608 754
755 if (ipp_status > IPP_OK_CONFLICT)
c8f9565c 756 {
b9738d7c 757 fprintf(stderr, "DEBUG: Get-Printer-Attributes returned %s.\n",
758 ippErrorString(ipp_status));
759
b5cb0608 760 if (ipp_status == IPP_PRINTER_BUSY ||
761 ipp_status == IPP_SERVICE_UNAVAILABLE)
c8f9565c 762 {
ff0295f0 763 if (contimeout && (time(NULL) - start_time) > contimeout)
764 {
4c4eea89 765 _cupsLangPrintFilter(stderr, "ERROR",
766 _("The printer is not responding."));
ff0295f0 767 return (CUPS_BACKEND_FAILED);
768 }
769
7cfa02ab 770 _cupsLangPrintFilter(stderr, "WARNING", _("The printer is busy."));
ff0295f0 771
41368129 772 report_printer_state(supported, 0);
ff0295f0 773
774 sleep(delay);
775
fc596e79 776 delay = _cupsNextDelay(delay, &prev_delay);
97fcaf92 777 }
b5cb0608 778 else if ((ipp_status == IPP_BAD_REQUEST ||
22d59a30 779 ipp_status == IPP_VERSION_NOT_SUPPORTED) && version > 10)
c8f9565c 780 {
b5cb0608 781 /*
6875feac 782 * Switch to IPP/1.1 or IPP/1.0...
b5cb0608 783 */
97fcaf92 784
6875feac 785 if (version >= 20)
786 {
4c4eea89 787 _cupsLangPrintFilter(stderr, "INFO",
788 _("Printer does not support IPP/%d.%d, trying "
789 "IPP/%s."), version / 10, version % 10, "1.1");
6875feac 790 version = 11;
791 }
792 else
793 {
4c4eea89 794 _cupsLangPrintFilter(stderr, "INFO",
795 _("Printer does not support IPP/%d.%d, trying "
796 "IPP/%s."), version / 10, version % 10, "1.0");
6875feac 797 version = 10;
798 }
799
a4e23897 800 httpReconnect(http);
c8f9565c 801 }
67474245 802 else if (ipp_status == IPP_NOT_FOUND)
803 {
4c4eea89 804 _cupsLangPrintFilter(stderr, "ERROR",
805 _("The printer URI is incorrect or no longer "
806 "exists."));
67474245 807
808 if (supported)
809 ippDelete(supported);
810
6248387b 811 return (CUPS_BACKEND_STOP);
67474245 812 }
fc7e68cf 813 else if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
814 {
815 if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
816 "Negotiate", 9))
817 auth_info_required = "negotiate";
818
819 fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
820 return (CUPS_BACKEND_AUTH_REQUIRED);
821 }
97fcaf92 822 else
67474245 823 {
4c4eea89 824 _cupsLangPrintFilter(stderr, "ERROR",
825 _("Unable to get printer status: %s"),
826 cupsLastErrorString());
67474245 827 sleep(10);
828 }
d1c2727f 829
7cfa02ab 830 ippDelete(supported);
d1c2727f 831 continue;
97fcaf92 832 }
6875feac 833
7cfa02ab 834 /*
835 * Check printer-state-reasons for the "spool-area-full" keyword...
836 */
837
838 if ((printer_state = ippFindAttribute(supported, "printer-state-reasons",
839 IPP_TAG_KEYWORD)) != NULL)
840 {
841 for (i = 0; i < printer_state->num_values; i ++)
842 if (!strcmp(printer_state->values[0].string.text, "spool-area-full") ||
843 !strncmp(printer_state->values[0].string.text, "spool-area-full-",
844 16))
845 break;
846
847 if (i < printer_state->num_values)
848 {
849 _cupsLangPrintFilter(stderr, "WARNING", _("The printer is busy."));
850
851 report_printer_state(supported, 0);
852
853 sleep(delay);
854
855 delay = _cupsNextDelay(delay, &prev_delay);
856
857 ippDelete(supported);
858 continue;
859 }
860 }
861 else
862 _cupsLangPrintFilter(stderr, "ERROR",
863 _("This printer does not conform to the IPP "
864 "standard. Please contact the manufacturer of "
865 "your printer for assistance."));
866
6875feac 867 /*
868 * Check for supported attributes...
869 */
870
871 if ((copies_sup = ippFindAttribute(supported, "copies-supported",
872 IPP_TAG_RANGE)) != NULL)
97fcaf92 873 {
b5cb0608 874 /*
875 * Has the "copies-supported" attribute - does it have an upper
876 * bound > 1?
877 */
97fcaf92 878
6875feac 879 fprintf(stderr, "DEBUG: copies-supported=%d-%d\n",
880 copies_sup->values[0].range.lower,
881 copies_sup->values[0].range.upper);
882
b5cb0608 883 if (copies_sup->values[0].range.upper <= 1)
884 copies_sup = NULL; /* No */
885 }
97fcaf92 886
6875feac 887 cups_version = ippFindAttribute(supported, "cups-version", IPP_TAG_TEXT);
b5cb0608 888
6875feac 889 if ((format_sup = ippFindAttribute(supported, "document-format-supported",
890 IPP_TAG_MIMETYPE)) != NULL)
b5cb0608 891 {
892 fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
893 format_sup->num_values);
894 for (i = 0; i < format_sup->num_values; i ++)
895 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
896 format_sup->values[i].string.text);
c8f9565c 897 }
d1c2727f 898
6875feac 899 if ((media_col_sup = ippFindAttribute(supported, "media-col-supported",
900 IPP_TAG_KEYWORD)) != NULL)
901 {
902 fprintf(stderr, "DEBUG: media-col-supported (%d values)\n",
903 media_col_sup->num_values);
904 for (i = 0; i < media_col_sup->num_values; i ++)
905 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
906 media_col_sup->values[i].string.text);
907 }
908
2ec940b5 909 if ((operations_sup = ippFindAttribute(supported, "operations-supported",
910 IPP_TAG_ENUM)) != NULL)
911 {
912 for (i = 0; i < operations_sup->num_values; i ++)
913 if (operations_sup->values[i].integer == IPP_VALIDATE_JOB)
914 {
915 validate_job = 1;
916 break;
917 }
918
919 if (!validate_job)
920 {
921 _cupsLangPrintFilter(stderr, "WARNING",
922 _("This printer does not conform to the IPP "
923 "standard and may not work."));
924 fputs("DEBUG: operations-supported does not list Validate-Job.\n",
925 stderr);
926 }
927 }
928 else
929 {
930 _cupsLangPrintFilter(stderr, "WARNING",
931 _("This printer does not conform to the IPP "
932 "standard and may not work."));
933 fputs("DEBUG: operations-supported not returned in "
934 "Get-Printer-Attributes request.\n", stderr);
935 }
936
adbcb02a 937 doc_handling_sup = ippFindAttribute(supported,
938 "multiple-document-handling-supported",
939 IPP_TAG_KEYWORD);
940
41368129 941 report_printer_state(supported, 0);
c8f9565c 942 }
97fcaf92 943 while (ipp_status > IPP_OK_CONFLICT);
c8f9565c 944
997cf8b0 945 /*
946 * See if the printer is accepting jobs and is not stopped; if either
947 * condition is true and we are printing to a class, requeue the job...
948 */
949
950 if (getenv("CLASS") != NULL)
951 {
952 printer_state = ippFindAttribute(supported, "printer-state",
953 IPP_TAG_ENUM);
954 printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs",
955 IPP_TAG_BOOLEAN);
956
957 if (printer_state == NULL ||
072c4872 958 (printer_state->values[0].integer > IPP_PRINTER_PROCESSING &&
959 waitprinter) ||
997cf8b0 960 printer_accepting == NULL ||
961 !printer_accepting->values[0].boolean)
962 {
963 /*
964 * If the CLASS environment variable is set, the job was submitted
965 * to a class and not to a specific queue. In this case, we want
966 * to abort immediately so that the job can be requeued on the next
967 * available printer in the class.
968 */
969
4c4eea89 970 _cupsLangPrintFilter(stderr, "INFO",
971 _("Unable to contact printer, queuing on next "
972 "printer in class."));
997cf8b0 973
974 ippDelete(supported);
975 httpClose(http);
976
997cf8b0 977 /*
978 * Sleep 5 seconds to keep the job from requeuing too rapidly...
979 */
980
981 sleep(5);
982
6248387b 983 return (CUPS_BACKEND_FAILED);
997cf8b0 984 }
985 }
986
c8f9565c 987 /*
97fcaf92 988 * See if the printer supports multiple copies...
c8f9565c 989 */
990
37de726f 991 copies = atoi(argv[4]);
992
d1c2727f 993 if (copies_sup || argc < 7)
7d52601e 994 {
37de726f 995 copies_remaining = 1;
7d52601e 996
5358d337 997 if (argc < 7 && !send_options)
7d52601e 998 copies = 1;
999 }
0c5b0932 1000 else
37de726f 1001 copies_remaining = copies;
0c5b0932 1002
2ec940b5 1003 /*
1004 * Prepare remaining printing options...
1005 */
1006
1007 options = NULL;
8211508e 1008 pc = NULL;
2ec940b5 1009
1010 if (send_options)
1011 {
1012 num_options = cupsParseOptions(argv[5], 0, &options);
1013
1014 if (!cups_version && media_col_sup)
1015 {
1016 /*
1017 * Load the PPD file and generate PWG attribute mapping information...
1018 */
1019
1020 ppd = ppdOpenFile(getenv("PPD"));
8211508e 1021 pc = _ppdCacheCreateWithPPD(ppd);
2ec940b5 1022
1023 ppdClose(ppd);
1024 }
1025 }
1026 else
1027 num_options = 0;
1028
1029 document_format = NULL;
1030
1031 if (format_sup != NULL)
1032 {
1033 for (i = 0; i < format_sup->num_values; i ++)
1034 if (!strcasecmp(final_content_type, format_sup->values[i].string.text))
1035 {
1036 document_format = final_content_type;
1037 break;
1038 }
cce0044f 1039
1040 if (!document_format)
1041 {
1042 for (i = 0; i < format_sup->num_values; i ++)
1043 if (!strcasecmp("application/octet-stream",
1044 format_sup->values[i].string.text))
1045 {
1046 document_format = "application/octet-stream";
1047 break;
1048 }
1049 }
2ec940b5 1050 }
1051
940afb4b 1052 /*
1053 * If the printer does not support HTTP/1.1 (which IPP requires), copy stdin
1054 * to a temporary file so that we can do a HTTP/1.0 submission...
1055 *
1056 * (I hate compatibility hacks!)
1057 */
1058
1059 if (http->version < HTTP_1_1 && num_files == 0)
1060 {
1061 if ((fd = cupsTempFd(tmpfilename, sizeof(tmpfilename))) < 0)
1062 {
1063 perror("DEBUG: Unable to create temporary file");
1064 return (CUPS_BACKEND_FAILED);
1065 }
1066
1067 _cupsLangPrintFilter(stderr, "INFO", _("Copying print data."));
1068
1069 compatsize = backendRunLoop(-1, fd, snmp_fd, &(addrlist->addr), 0, 0,
1070 backendNetworkSideCB);
1071
1072 close(fd);
1073
1074 compatfile = tmpfilename;
1075 files = &compatfile;
1076 num_files = 1;
1077 }
1078 else if (http->version < HTTP_1_1 && num_files == 1)
1079 {
1080 struct stat fileinfo; /* File information */
1081
1082 if (!stat(files[0], &fileinfo))
1083 compatsize = fileinfo.st_size;
1084 }
1085
b9738d7c 1086 /*
1087 * Start monitoring the printer in the background...
1088 */
1089
1090 monitor.uri = uri;
1091 monitor.hostname = hostname;
1092 monitor.user = argv[2];
1093 monitor.resource = resource;
1094 monitor.port = port;
1095 monitor.version = version;
1096 monitor.job_id = 0;
1097 monitor.encryption = cupsEncryption();
1098 monitor.job_state = IPP_JOB_PENDING;
1099 monitor.printer_state = IPP_PRINTER_IDLE;
1100
1101 _cupsThreadCreate((_cups_thread_func_t)monitor_printer, &monitor);
1102
c8f9565c 1103 /*
2ec940b5 1104 * Validate access to the printer...
c8f9565c 1105 */
1106
2ec940b5 1107 while (!job_canceled)
c8f9565c 1108 {
2ec940b5 1109 request = new_request(IPP_VALIDATE_JOB, version, uri, argv[2], argv[3],
1110 num_options, options, compression,
8211508e 1111 copies_sup ? copies : 1, document_format, pc,
adbcb02a 1112 media_col_sup, doc_handling_sup);
97fcaf92 1113
2ec940b5 1114 ippDelete(cupsDoRequest(http, request, resource));
8ce7c000 1115
2ec940b5 1116 ipp_status = cupsLastError();
97fcaf92 1117
cce0044f 1118 if (ipp_status > IPP_OK_CONFLICT &&
1119 ipp_status != IPP_OPERATION_NOT_SUPPORTED)
6875feac 1120 {
2ec940b5 1121 if (job_canceled)
1122 break;
6875feac 1123
2ec940b5 1124 if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1125 ipp_status == IPP_PRINTER_BUSY)
e7186d00 1126 {
2ec940b5 1127 _cupsLangPrintFilter(stderr, "INFO",
1128 _("Printer busy; will retry in 10 seconds."));
1129 sleep(10);
6875feac 1130 }
1131 else
e7186d00 1132 {
1133 /*
2ec940b5 1134 * Update auth-info-required as needed...
e7186d00 1135 */
1136
2ec940b5 1137 _cupsLangPrintFilter(stderr, "ERROR", "%s", cupsLastErrorString());
b8f3410f 1138
2ec940b5 1139 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
6875feac 1140 {
2ec940b5 1141 fprintf(stderr, "DEBUG: WWW-Authenticate=\"%s\"\n",
1142 httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE));
97fcaf92 1143
2ec940b5 1144 /*
1145 * Normal authentication goes through the password callback, which sets
1146 * auth_info_required to "username,password". Kerberos goes directly
1147 * through GSSAPI, so look for Negotiate in the WWW-Authenticate header
1148 * here and set auth_info_required as needed...
1149 */
6875feac 1150
2ec940b5 1151 if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
1152 "Negotiate", 9))
1153 auth_info_required = "negotiate";
1154 }
753453e4 1155
2ec940b5 1156 goto cleanup;
1157 }
1158 }
1159 else
1160 break;
1161 }
37de726f 1162
2ec940b5 1163 /*
1164 * Then issue the print-job request...
1165 */
6875feac 1166
2ec940b5 1167 job_id = 0;
6875feac 1168
2ec940b5 1169 while (!job_canceled && copies_remaining > 0)
1170 {
1171 /*
1172 * Check for side-channel requests...
1173 */
b9738d7c 1174
2ec940b5 1175 backendCheckSideChannel(snmp_fd, http->hostaddr);
b9738d7c 1176
2ec940b5 1177 /*
1178 * Build the IPP job creation request...
1179 */
6875feac 1180
2ec940b5 1181 if (job_canceled)
1182 break;
753453e4 1183
2ec940b5 1184 request = new_request(num_files > 1 ? IPP_CREATE_JOB : IPP_PRINT_JOB,
1185 version, uri, argv[2], argv[3], num_options, options,
1186 compression, copies_sup ? copies : 1, document_format,
adbcb02a 1187 pc, media_col_sup, doc_handling_sup);
c8f9565c 1188
cb555bcf 1189 /*
b5cb0608 1190 * Do the request...
cb555bcf 1191 */
1192
072c4872 1193 if (num_files > 1)
1194 response = cupsDoRequest(http, request, resource);
b5cb0608 1195 else
6875feac 1196 {
940afb4b 1197 size_t length = 0; /* Length of request */
1198
1199 if (compatsize > 0)
1200 {
1201 fputs("DEBUG: Sending file using HTTP/1.0 Content-Length...\n", stderr);
1202 length = ippLength(request) + (size_t)compatsize;
1203 }
1204 else
1205 fputs("DEBUG: Sending file using HTTP/1.1 chunking...\n", stderr);
1206
1207 http_status = cupsSendRequest(http, request, resource, length);
6875feac 1208 if (http_status == HTTP_CONTINUE && request->state == IPP_DATA)
1209 {
1210 if (num_files == 1)
1211 fd = open(files[0], O_RDONLY);
1212 else
1213 fd = 0;
1214
d2355476 1215 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
6875feac 1216 {
d2355476 1217 fprintf(stderr, "DEBUG: Read %d bytes...\n", (int)bytes);
1218
e6af41cc 1219 if (cupsWriteRequestData(http, buffer, bytes) != HTTP_CONTINUE)
6875feac 1220 break;
1221 else
1222 {
1223 /*
1224 * Check for side-channel requests...
1225 */
1226
1227 backendCheckSideChannel(snmp_fd, http->hostaddr);
1228 }
1229 }
1230
1231 if (num_files == 1)
1232 close(fd);
1233 }
1234
1235 response = cupsGetResponse(http, resource);
1236 ippDelete(request);
1237 }
072c4872 1238
1239 ipp_status = cupsLastError();
b5cb0608 1240
1241 if (ipp_status > IPP_OK_CONFLICT)
1242 {
753453e4 1243 job_id = 0;
1244
6875feac 1245 if (job_canceled)
072c4872 1246 break;
1247
b5cb0608 1248 if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1249 ipp_status == IPP_PRINTER_BUSY)
1250 {
4c4eea89 1251 _cupsLangPrintFilter(stderr, "INFO",
1252 _("Printer busy; will retry in 10 seconds."));
b5cb0608 1253 sleep(10);
7cfa02ab 1254
1255 if (num_files == 0)
1256 {
1257 /*
1258 * We can't re-submit when we have no files to print, so exit
1259 * immediately with the right status code...
1260 */
1261
1262 goto cleanup;
1263 }
b5cb0608 1264 }
1265 else
ebac5c9b 1266 {
1267 /*
1268 * Update auth-info-required as needed...
1269 */
1270
4c4eea89 1271 _cupsLangPrintFilter(stderr, "ERROR",
1272 _("Print file was not accepted: %s"),
1273 cupsLastErrorString());
ebac5c9b 1274
4233257e 1275 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
ebac5c9b 1276 {
1277 fprintf(stderr, "DEBUG: WWW-Authenticate=\"%s\"\n",
1278 httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE));
1279
4233257e 1280 /*
1281 * Normal authentication goes through the password callback, which sets
1282 * auth_info_required to "username,password". Kerberos goes directly
1283 * through GSSAPI, so look for Negotiate in the WWW-Authenticate header
1284 * here and set auth_info_required as needed...
1285 */
1286
ebac5c9b 1287 if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
1288 "Negotiate", 9))
4233257e 1289 auth_info_required = "negotiate";
ebac5c9b 1290 }
1291 }
b5cb0608 1292 }
1293 else if ((job_id_attr = ippFindAttribute(response, "job-id",
1294 IPP_TAG_INTEGER)) == NULL)
97fcaf92 1295 {
4c4eea89 1296 _cupsLangPrintFilter(stderr, "INFO",
1297 _("Print file accepted - job ID unknown."));
b5cb0608 1298 job_id = 0;
1299 }
1300 else
1301 {
b9738d7c 1302 monitor.job_id = job_id = job_id_attr->values[0].integer;
4c4eea89 1303 _cupsLangPrintFilter(stderr, "INFO",
1304 _("Print file accepted - job ID %d."), job_id);
97fcaf92 1305 }
cb555bcf 1306
072c4872 1307 ippDelete(response);
1308
6875feac 1309 if (job_canceled)
072c4872 1310 break;
1311
1312 if (job_id && num_files > 1)
1313 {
1314 for (i = 0; i < num_files; i ++)
1315 {
56f7a531 1316 /*
1317 * Check for side-channel requests...
1318 */
1319
1320 backendCheckSideChannel(snmp_fd, http->hostaddr);
1321
1322 /*
1323 * Send the next file in the job...
1324 */
1325
072c4872 1326 request = ippNewRequest(IPP_SEND_DOCUMENT);
22d59a30 1327 request->request.op.version[0] = version / 10;
1328 request->request.op.version[1] = version % 10;
072c4872 1329
1330 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1331 NULL, uri);
1332
1333 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1334 job_id);
1335
1336 if (argv[2][0])
1337 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1338 "requesting-user-name", NULL, argv[2]);
1339
1340 if ((i + 1) == num_files)
1341 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
1342
1343 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
1344 "document-format", NULL, content_type);
1345
6875feac 1346 fprintf(stderr, "DEBUG: Sending file %d using chunking...\n", i + 1);
1347 http_status = cupsSendRequest(http, request, resource, 0);
1348 if (http_status == HTTP_CONTINUE && request->state == IPP_DATA &&
1349 (fd = open(files[i], O_RDONLY)) >= 0)
1350 {
1351 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
1352 {
e6af41cc 1353 if (cupsWriteRequestData(http, buffer, bytes) != HTTP_CONTINUE)
6875feac 1354 break;
1355 else
1356 {
1357 /*
1358 * Check for side-channel requests...
1359 */
1360
1361 backendCheckSideChannel(snmp_fd, http->hostaddr);
1362 }
1363 }
1364
1365 close(fd);
1366 }
acd7eb22 1367
6875feac 1368 ippDelete(cupsGetResponse(http, resource));
1369 ippDelete(request);
072c4872 1370
1371 if (cupsLastError() > IPP_OK_CONFLICT)
1372 {
1373 ipp_status = cupsLastError();
1374
4c4eea89 1375 _cupsLangPrintFilter(stderr, "ERROR",
1376 _("Unable to add file to job: %s"),
1377 cupsLastErrorString());
072c4872 1378 break;
1379 }
1380 }
1381 }
b5cb0608 1382
753453e4 1383 if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
b5cb0608 1384 {
1385 fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
37de726f 1386 copies_remaining --;
b5cb0608 1387 }
ab827512 1388 else if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1389 ipp_status == IPP_PRINTER_BUSY)
a04d2365 1390 continue;
ab827512 1391 else
37de726f 1392 copies_remaining --;
8ce7c000 1393
c8f9565c 1394 /*
b5cb0608 1395 * Wait for the job to complete...
c8f9565c 1396 */
1397
897922a9 1398 if (!job_id || !waitjob)
b5cb0608 1399 continue;
1400
4c4eea89 1401 _cupsLangPrintFilter(stderr, "INFO", _("Waiting for job to complete."));
b5cb0608 1402
fc596e79 1403 for (delay = _cupsNextDelay(0, &prev_delay); !job_canceled;)
c8f9565c 1404 {
56f7a531 1405 /*
1406 * Check for side-channel requests...
1407 */
1408
1409 backendCheckSideChannel(snmp_fd, http->hostaddr);
1410
97fcaf92 1411 /*
b5cb0608 1412 * Build an IPP_GET_JOB_ATTRIBUTES request...
97fcaf92 1413 */
1414
e12df069 1415 request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
22d59a30 1416 request->request.op.version[0] = version / 10;
1417 request->request.op.version[1] = version % 10;
97fcaf92 1418
b5cb0608 1419 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1420 NULL, uri);
3f9cb6c6 1421
b5cb0608 1422 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1423 job_id);
c8f9565c 1424
ee8b7dd3 1425 if (argv[2][0])
897922a9 1426 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1427 "requesting-user-name", NULL, argv[2]);
ee8b7dd3 1428
8f4595eb 1429 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1430 "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
1431 NULL, jattrs);
97fcaf92 1432
1433 /*
b5cb0608 1434 * Do the request...
97fcaf92 1435 */
1436
fc596e79 1437 httpReconnect(http);
072c4872 1438 response = cupsDoRequest(http, request, resource);
1439 ipp_status = cupsLastError();
97fcaf92 1440
b5cb0608 1441 if (ipp_status == IPP_NOT_FOUND)
97fcaf92 1442 {
b5cb0608 1443 /*
d1c2727f 1444 * Job has gone away and/or the server has no job history...
b5cb0608 1445 */
97fcaf92 1446
b5cb0608 1447 ippDelete(response);
d1c2727f 1448
1449 ipp_status = IPP_OK;
b5cb0608 1450 break;
97fcaf92 1451 }
1452
b5cb0608 1453 if (ipp_status > IPP_OK_CONFLICT)
6a536282 1454 {
b5cb0608 1455 if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
1456 ipp_status != IPP_PRINTER_BUSY)
97fcaf92 1457 {
072c4872 1458 ippDelete(response);
b5cb0608 1459
4c4eea89 1460 _cupsLangPrintFilter(stderr, "ERROR",
1461 _("Unable to get job attributes: %s"),
1462 cupsLastErrorString());
b5cb0608 1463 break;
97fcaf92 1464 }
6a536282 1465 }
8f4595eb 1466
072c4872 1467 if (response)
97fcaf92 1468 {
8f4595eb 1469 if ((job_state = ippFindAttribute(response, "job-state",
1470 IPP_TAG_ENUM)) != NULL)
97fcaf92 1471 {
6c1b8723 1472 /*
1473 * Reflect the remote job state in the local queue...
1474 */
1475
1476 fputs("STATE: -cups-remote-pending,"
1477 "cups-remote-pending-held,"
1478 "cups-remote-processing,"
1479 "cups-remote-stopped,"
1480 "cups-remote-canceled,"
1481 "cups-remote-aborted,"
1482 "cups-remote-completed\n", stderr);
1483 if (job_state->values[0].integer >= IPP_JOB_PENDING &&
1484 job_state->values[0].integer <= IPP_JOB_COMPLETED)
1485 fprintf(stderr, "STATE: +%s\n",
1486 remote_job_states[job_state->values[0].integer -
1487 IPP_JOB_PENDING]);
1488
8f4595eb 1489 /*
1490 * Stop polling if the job is finished or pending-held...
1491 */
1492
7951315f 1493 if (job_state->values[0].integer > IPP_JOB_STOPPED)
8f4595eb 1494 {
aa141f0d 1495 if ((job_sheets = ippFindAttribute(response,
e12df069 1496 "job-media-sheets-completed",
1497 IPP_TAG_INTEGER)) != NULL)
072c4872 1498 fprintf(stderr, "PAGE: total %d\n",
1499 job_sheets->values[0].integer);
ab827512 1500
8f4595eb 1501 ippDelete(response);
1502 break;
1503 }
97fcaf92 1504 }
f3fdb6b2 1505 else
1506 {
1507 /*
1508 * If the printer does not return a job-state attribute, it does not
1509 * conform to the IPP specification - break out immediately and fail
1510 * the job...
1511 */
1512
7a43b482 1513 fputs("DEBUG: No job-state available from printer - stopping queue.\n",
1514 stderr);
f3fdb6b2 1515 ipp_status = IPP_INTERNAL_ERROR;
1516 break;
1517 }
97fcaf92 1518 }
1519
072c4872 1520 ippDelete(response);
d1c2727f 1521
b5cb0608 1522 /*
fc596e79 1523 * Wait before polling again...
d1c2727f 1524 */
97fcaf92 1525
2ce8588b 1526 sleep(delay);
1527
fc596e79 1528 delay = _cupsNextDelay(delay, &prev_delay);
97fcaf92 1529 }
c8f9565c 1530 }
1531
6bb5a528 1532 /*
072c4872 1533 * Cancel the job as needed...
6bb5a528 1534 */
1535
6875feac 1536 if (job_canceled && job_id)
072c4872 1537 cancel_job(http, uri, job_id, resource, argv[2], version);
1538
1539 /*
1540 * Check the printer state and report it if necessary...
1541 */
6bb5a528 1542
41368129 1543 check_printer_state(http, uri, resource, argv[2], version, job_id);
6bb5a528 1544
eab5b04e 1545 /*
1546 * Collect the final page count as needed...
1547 */
1548
aa141f0d 1549 if (have_supplies &&
eab5b04e 1550 !backendSNMPSupplies(snmp_fd, http->hostaddr, &page_count, NULL) &&
1551 page_count > start_count)
1552 fprintf(stderr, "PAGE: total %d\n", page_count - start_count);
1553
4233257e 1554#ifdef HAVE_GSSAPI
1555 /*
1556 * See if we used Kerberos at all...
1557 */
1558
1559 if (http->gssctx)
1560 auth_info_required = "negotiate";
1561#endif /* HAVE_GSSAPI */
1562
c8f9565c 1563 /*
1564 * Free memory...
1565 */
1566
2ec940b5 1567 cleanup:
1568
1569 cupsFreeOptions(num_options, options);
8211508e 1570 _ppdCacheDestroy(pc);
2ec940b5 1571
c8f9565c 1572 httpClose(http);
c8f9565c 1573
072c4872 1574 ippDelete(supported);
2922de55 1575
c8f9565c 1576 /*
b8f3410f 1577 * Remove the temporary file(s) if necessary...
c8f9565c 1578 */
1579
940afb4b 1580 if (tmpfilename[0])
1581 unlink(tmpfilename);
1582
1230a8a0 1583#ifdef HAVE_LIBZ
1584 if (compression)
1585 {
1586 for (i = 0; i < num_files; i ++)
1587 unlink(files[i]);
1588 }
1589#endif /* HAVE_LIBZ */
1590
c8f9565c 1591 /*
1592 * Return the queue status...
1593 */
1594
c55dc24c 1595 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
d0eaf635 1596 ipp_status == IPP_AUTHENTICATION_CANCELED ||
c55dc24c 1597 ipp_status <= IPP_OK_CONFLICT)
1598 fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
4233257e 1599
d0eaf635 1600 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
1601 ipp_status == IPP_AUTHENTICATION_CANCELED)
3fe655a3 1602 return (CUPS_BACKEND_AUTH_REQUIRED);
f3fdb6b2 1603 else if (ipp_status == IPP_INTERNAL_ERROR)
1604 return (CUPS_BACKEND_STOP);
7cfa02ab 1605 else if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1606 ipp_status == IPP_PRINTER_BUSY)
1607 return (CUPS_BACKEND_RETRY_CURRENT);
3fe655a3 1608 else if (ipp_status > IPP_OK_CONFLICT)
1609 return (CUPS_BACKEND_FAILED);
1610 else
83d83f7a 1611 {
4c4eea89 1612 _cupsLangPrintFilter(stderr, "INFO", _("Ready to print."));
3fe655a3 1613 return (CUPS_BACKEND_OK);
83d83f7a 1614 }
b5cb0608 1615}
1616
1617
072c4872 1618/*
1619 * 'cancel_job()' - Cancel a print job.
1620 */
1621
1622static void
1623cancel_job(http_t *http, /* I - HTTP connection */
1624 const char *uri, /* I - printer-uri */
1625 int id, /* I - job-id */
1626 const char *resource, /* I - Resource path */
1627 const char *user, /* I - requesting-user-name */
1628 int version) /* I - IPP version */
1629{
1630 ipp_t *request; /* Cancel-Job request */
1631
1632
4c4eea89 1633 _cupsLangPrintFilter(stderr, "INFO", _("Canceling print job."));
072c4872 1634
1635 request = ippNewRequest(IPP_CANCEL_JOB);
22d59a30 1636 request->request.op.version[0] = version / 10;
1637 request->request.op.version[1] = version % 10;
072c4872 1638
1639 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1640 NULL, uri);
1641 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
1642
1643 if (user && user[0])
1644 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1645 "requesting-user-name", NULL, user);
1646
1647 /*
1648 * Do the request...
1649 */
1650
1651 ippDelete(cupsDoRequest(http, request, resource));
1652
1653 if (cupsLastError() > IPP_OK_CONFLICT)
4c4eea89 1654 _cupsLangPrintFilter(stderr, "ERROR", _("Unable to cancel job: %s"),
1655 cupsLastErrorString());
072c4872 1656}
1657
1658
6bb5a528 1659/*
b9738d7c 1660 * 'check_printer_state()' - Check the printer state.
6bb5a528 1661 */
1662
b9738d7c 1663static ipp_pstate_t /* O - Current printer-state */
e12df069 1664check_printer_state(
1665 http_t *http, /* I - HTTP connection */
1666 const char *uri, /* I - Printer URI */
1667 const char *resource, /* I - Resource path */
1668 const char *user, /* I - Username, if any */
41368129 1669 int version, /* I - IPP version */
b9738d7c 1670 int job_id)
6bb5a528 1671{
b9738d7c 1672 ipp_t *request, /* IPP request */
1673 *response; /* IPP response */
1674 ipp_attribute_t *attr; /* Attribute in response */
1675 ipp_pstate_t printer_state = IPP_PRINTER_STOPPED;
1676 /* Current printer-state */
6bb5a528 1677
1678
1679 /*
b9738d7c 1680 * Send a Get-Printer-Attributes request and log the results...
6bb5a528 1681 */
1682
e12df069 1683 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
22d59a30 1684 request->request.op.version[0] = version / 10;
1685 request->request.op.version[1] = version % 10;
6bb5a528 1686
1687 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
b9738d7c 1688 NULL, uri);
6bb5a528 1689
1690 if (user && user[0])
1691 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
b9738d7c 1692 "requesting-user-name", NULL, user);
6bb5a528 1693
a6ccc6e8 1694 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
b9738d7c 1695 "requested-attributes",
1696 (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
6bb5a528 1697
1698 if ((response = cupsDoRequest(http, request, resource)) != NULL)
1699 {
41368129 1700 report_printer_state(response, job_id);
b9738d7c 1701
1702 if ((attr = ippFindAttribute(response, "printer-state",
1703 IPP_TAG_ENUM)) != NULL)
1704 printer_state = (ipp_pstate_t)attr->values[0].integer;
1705
6bb5a528 1706 ippDelete(response);
1707 }
b9738d7c 1708
1709 /*
1710 * Return the printer-state value...
1711 */
1712
1713 return (printer_state);
6bb5a528 1714}
1715
1716
1230a8a0 1717#ifdef HAVE_LIBZ
1718/*
1719 * 'compress_files()' - Compress print files...
1720 */
1721
1722static void
1723compress_files(int num_files, /* I - Number of files */
1724 char **files) /* I - Files */
1725{
1726 int i, /* Looping var */
1727 fd; /* Temporary file descriptor */
1728 ssize_t bytes; /* Bytes read/written */
1729 size_t total; /* Total bytes read */
1730 cups_file_t *in, /* Input file */
1731 *out; /* Output file */
1732 struct stat outinfo; /* Output file information */
1733 char filename[1024], /* Temporary filename */
1fb9f3e8 1734 buffer[32768]; /* Copy buffer */
1230a8a0 1735
1736
1737 fprintf(stderr, "DEBUG: Compressing %d job files...\n", num_files);
1738 for (i = 0; i < num_files; i ++)
1739 {
1740 if ((fd = cupsTempFd(filename, sizeof(filename))) < 0)
1741 {
4c4eea89 1742 _cupsLangPrintError("ERROR", _("Unable to create compressed print file"));
1230a8a0 1743 exit(CUPS_BACKEND_FAILED);
1744 }
1745
1746 if ((out = cupsFileOpenFd(fd, "w9")) == NULL)
1747 {
4c4eea89 1748 _cupsLangPrintError("ERROR", _("Unable to open compressed print file"));
1230a8a0 1749 exit(CUPS_BACKEND_FAILED);
1750 }
1751
1752 if ((in = cupsFileOpen(files[i], "r")) == NULL)
1753 {
4c4eea89 1754 _cupsLangPrintError("ERROR", _("Unable to open print file"));
1230a8a0 1755 cupsFileClose(out);
1756 exit(CUPS_BACKEND_FAILED);
1757 }
1758
1759 total = 0;
1760 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
1761 if (cupsFileWrite(out, buffer, bytes) < bytes)
1762 {
4c4eea89 1763 _cupsLangPrintError("ERROR",
1764 _("Unable to generate compressed print file"));
1230a8a0 1765 cupsFileClose(in);
1766 cupsFileClose(out);
1767 exit(CUPS_BACKEND_FAILED);
1768 }
1769 else
1770 total += bytes;
1771
1772 cupsFileClose(out);
1773 cupsFileClose(in);
1774
1775 files[i] = strdup(filename);
1776
1777 if (!stat(filename, &outinfo))
ff0295f0 1778 fprintf(stderr,
1779 "DEBUG: File %d compressed to %.1f%% of original size, "
1780 CUPS_LLFMT " bytes...\n",
1781 i + 1, 100.0 * outinfo.st_size / total,
1782 CUPS_LLCAST outinfo.st_size);
1230a8a0 1783 }
1784}
1785#endif /* HAVE_LIBZ */
1786
1787
b9738d7c 1788/*
1789 * 'monitor_printer()' - Monitor the printer state...
1790 */
1791
1792static void * /* O - Thread exit code */
1793monitor_printer(
1794 _cups_monitor_t *monitor) /* I - Monitoring data */
1795{
1796 http_t *http; /* Connection to printer */
1797 ipp_t *request, /* IPP request */
1798 *response; /* IPP response */
1799 ipp_attribute_t *attr; /* Attribute in response */
1800 int delay, /* Current delay */
8211508e 1801 prev_delay; /* Previous delay */
b9738d7c 1802
1803
1804 /*
1805 * Make a copy of the printer connection...
1806 */
1807
cce0044f 1808 http = _httpCreate(monitor->hostname, monitor->port, NULL, monitor->encryption,
16f98e36 1809 AF_UNSPEC);
b9738d7c 1810 cupsSetPasswordCB(password_cb);
1811
1812 /*
1813 * Loop until the job is canceled, aborted, or completed.
1814 */
1815
fc596e79 1816 delay = _cupsNextDelay(0, &prev_delay);
b9738d7c 1817
1818 while (monitor->job_state < IPP_JOB_CANCELED && !job_canceled)
1819 {
1820 /*
1821 * Reconnect to the printer...
1822 */
1823
1824 if (!httpReconnect(http))
1825 {
1826 /*
1827 * Connected, so check on the printer state...
1828 */
1829
1830 monitor->printer_state = check_printer_state(http, monitor->uri,
1831 monitor->resource,
1832 monitor->user,
1833 monitor->version,
1834 monitor->job_id);
1835
1836 if (monitor->job_id > 0)
1837 {
1838 /*
1839 * Check the status of the job itself...
1840 */
1841
1842 request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
1843 request->request.op.version[0] = monitor->version / 10;
1844 request->request.op.version[1] = monitor->version % 10;
1845
1846 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1847 NULL, monitor->uri);
1848 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1849 monitor->job_id);
1850
1851 if (monitor->user && monitor->user[0])
1852 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1853 "requesting-user-name", NULL, monitor->user);
1854
1855 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1856 "requested-attributes",
1857 (int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs);
1858
1859 /*
1860 * Do the request...
1861 */
1862
1863 response = cupsDoRequest(http, request, monitor->resource);
1864
1865 if ((attr = ippFindAttribute(response, "job-state",
1866 IPP_TAG_ENUM)) != NULL)
1867 monitor->job_state = (ipp_jstate_t)attr->values[0].integer;
1868 else
1869 monitor->job_state = IPP_JOB_COMPLETED;
1870
1871 ippDelete(response);
1872 }
1873
1874 /*
1875 * Disconnect from the printer - we'll reconnect on the next poll...
1876 */
1877
1878 _httpDisconnect(http);
1879 }
1880
1881 /*
fc596e79 1882 * Sleep for N seconds...
b9738d7c 1883 */
1884
1885 sleep(delay);
1886
fc596e79 1887 delay = _cupsNextDelay(delay, &prev_delay);
b9738d7c 1888 }
1889
1890 /*
1891 * Cleanup and return...
1892 */
1893
1894 httpClose(http);
1895
1896 return (NULL);
1897}
1898
1899
2ec940b5 1900/*
1901 * 'new_request()' - Create a new print creation or validation request.
1902 */
1903
1904static ipp_t * /* O - Request data */
1905new_request(
1906 ipp_op_t op, /* I - IPP operation code */
1907 int version, /* I - IPP version number */
1908 const char *uri, /* I - printer-uri value */
1909 const char *user, /* I - requesting-user-name value */
1910 const char *title, /* I - job-name value */
1911 int num_options, /* I - Number of options to send */
1912 cups_option_t *options, /* I - Options to send */
1913 const char *compression, /* I - compression value or NULL */
aa141f0d 1914 int copies, /* I - copies value or 0 */
2ec940b5 1915 const char *format, /* I - documet-format value or NULL */
8211508e 1916 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
adbcb02a 1917 ipp_attribute_t *media_col_sup, /* I - media-col-supported values */
1918 ipp_attribute_t *doc_handling_sup) /* I - multiple-document-handling-supported values */
2ec940b5 1919{
1920 int i; /* Looping var */
1921 ipp_t *request; /* Request data */
1922 const char *keyword; /* PWG keyword */
1923 _pwg_size_t *size; /* PWG media size */
1924 ipp_t *media_col, /* media-col value */
1925 *media_size; /* media-size value */
1926 const char *media_source, /* media-source value */
adbcb02a 1927 *media_type, /* media-type value */
1928 *collate_str; /* multiple-document-handling value */
2ec940b5 1929
1930
1931 /*
1932 * Create the IPP request...
1933 */
1934
1935 request = ippNewRequest(op);
1936 request->request.op.version[0] = version / 10;
1937 request->request.op.version[1] = version % 10;
1938
1939 fprintf(stderr, "DEBUG: %s IPP/%d.%d\n",
1940 ippOpString(request->request.op.operation_id),
1941 request->request.op.version[0],
1942 request->request.op.version[1]);
1943
1944 /*
1945 * Add standard attributes...
1946 */
1947
1948 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1949 NULL, uri);
1950 fprintf(stderr, "DEBUG: printer-uri=\"%s\"\n", uri);
1951
1952 if (user && *user)
1953 {
1954 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1955 "requesting-user-name", NULL, user);
1956 fprintf(stderr, "DEBUG: requesting-user-name=\"%s\"\n", user);
1957 }
1958
1959 if (title && *title)
1960 {
1961 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
1962 title);
1963 fprintf(stderr, "DEBUG: job-name=\"%s\"\n", title);
1964 }
1965
1966 if (format)
1967 {
1968 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
1969 "document-format", NULL, format);
1970 fprintf(stderr, "DEBUG: document-format=\"%s\"\n", format);
1971 }
1972
1973#ifdef HAVE_LIBZ
1974 if (compression)
1975 {
1976 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1977 "compression", NULL, compression);
1978 fprintf(stderr, "DEBUG: compression=\"%s\"\n", compression);
1979 }
1980#endif /* HAVE_LIBZ */
1981
1982 /*
1983 * Handle options on the command-line...
1984 */
1985
1986 if (num_options > 0)
1987 {
8211508e 1988 if (pc)
2ec940b5 1989 {
1990 /*
1991 * Send standard IPP attributes...
1992 */
1993
1994 if ((keyword = cupsGetOption("PageSize", num_options, options)) == NULL)
1995 keyword = cupsGetOption("media", num_options, options);
1996
8211508e 1997 if ((size = _ppdCacheGetSize(pc, keyword)) != NULL)
2ec940b5 1998 {
1999 /*
2000 * Add a media-col value...
2001 */
2002
2003 media_size = ippNew();
2004 ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2005 "x-dimension", size->width);
2006 ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2007 "y-dimension", size->length);
2008
2009 media_col = ippNew();
2010 ippAddCollection(media_col, IPP_TAG_ZERO, "media-size", media_size);
2011
8211508e 2012 media_source = _ppdCacheGetSource(pc, cupsGetOption("InputSlot",
2013 num_options,
2014 options));
2015 media_type = _ppdCacheGetType(pc, cupsGetOption("MediaType",
2016 num_options,
2017 options));
2ec940b5 2018
2019 for (i = 0; i < media_col_sup->num_values; i ++)
2020 {
2021 if (!strcmp(media_col_sup->values[i].string.text,
2022 "media-left-margin"))
2023 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2024 "media-left-margin", size->left);
2025 else if (!strcmp(media_col_sup->values[i].string.text,
2026 "media-bottom-margin"))
2027 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2028 "media-bottom-margin", size->left);
2029 else if (!strcmp(media_col_sup->values[i].string.text,
2030 "media-right-margin"))
2031 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2032 "media-right-margin", size->left);
2033 else if (!strcmp(media_col_sup->values[i].string.text,
2034 "media-top-margin"))
2035 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2036 "media-top-margin", size->left);
2037 else if (!strcmp(media_col_sup->values[i].string.text,
2038 "media-source") && media_source)
2039 ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD,
2040 "media-source", NULL, media_source);
2041 else if (!strcmp(media_col_sup->values[i].string.text,
2042 "media-type") && media_type)
2043 ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD,
2044 "media-type", NULL, media_type);
2045 }
2046
2047 ippAddCollection(request, IPP_TAG_JOB, "media-col", media_col);
2048 }
2049
2050 if ((keyword = cupsGetOption("output-bin", num_options,
2051 options)) == NULL)
8211508e 2052 keyword = _ppdCacheGetBin(pc, cupsGetOption("OutputBin", num_options,
2053 options));
2ec940b5 2054
2055 if (keyword)
2056 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-bin",
2057 NULL, keyword);
2058
2059 if ((keyword = cupsGetOption("output-mode", num_options,
2060 options)) != NULL)
2061 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2062 NULL, keyword);
2063 else if ((keyword = cupsGetOption("ColorModel", num_options,
2064 options)) != NULL)
2065 {
2066 if (!strcasecmp(keyword, "Gray"))
2067 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2068 NULL, "monochrome");
2069 else
2070 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2071 NULL, "color");
2072 }
2073
2074 if ((keyword = cupsGetOption("print-quality", num_options,
2075 options)) != NULL)
2076 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2077 atoi(keyword));
2078 else if ((keyword = cupsGetOption("cupsPrintQuality", num_options,
2079 options)) != NULL)
2080 {
2081 if (!strcasecmp(keyword, "draft"))
2082 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2083 IPP_QUALITY_DRAFT);
2084 else if (!strcasecmp(keyword, "normal"))
2085 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2086 IPP_QUALITY_NORMAL);
2087 else if (!strcasecmp(keyword, "high"))
2088 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2089 IPP_QUALITY_HIGH);
2090 }
2091
2092 if ((keyword = cupsGetOption("sides", num_options, options)) != NULL)
2093 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2094 NULL, keyword);
8211508e 2095 else if (pc->sides_option &&
2096 (keyword = cupsGetOption(pc->sides_option, num_options,
2ec940b5 2097 options)) != NULL)
2098 {
8211508e 2099 if (!strcasecmp(keyword, pc->sides_1sided))
2ec940b5 2100 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2101 NULL, "one-sided");
8211508e 2102 else if (!strcasecmp(keyword, pc->sides_2sided_long))
2ec940b5 2103 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2104 NULL, "two-sided-long-edge");
8211508e 2105 if (!strcasecmp(keyword, pc->sides_2sided_short))
2ec940b5 2106 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2107 NULL, "two-sided-short-edge");
2108 }
adbcb02a 2109
2110 if (doc_handling_sup &&
2111 (keyword = cupsGetOption("collate", num_options, options)) != NULL)
2112 {
2113 if (!strcasecmp(keyword, "true"))
2114 collate_str = "separate-documents-collated-copies";
2115 else
2116 collate_str = "separate-documents-uncollated-copies";
2117
2118 for (i = 0; i < doc_handling_sup->num_values; i ++)
2119 if (!strcmp(doc_handling_sup->values[i].string.text, collate_str))
2120 {
2121 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2122 "multiple-document-handling", NULL, collate_str);
2123 break;
2124 }
2125 }
2ec940b5 2126 }
2127 else
2128 {
2129 /*
2130 * When talking to another CUPS server, send all options...
2131 */
2132
2133 cupsEncodeOptions(request, num_options, options);
2134 }
2135
2136 if (copies > 1)
2137 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies", copies);
2138 }
2139
2140 return (request);
2141}
2142
2143
b5cb0608 2144/*
2145 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
2146 */
2147
072c4872 2148static const char * /* O - Password */
e12df069 2149password_cb(const char *prompt) /* I - Prompt (not used) */
b5cb0608 2150{
2151 (void)prompt;
2152
4233257e 2153 /*
2154 * Remember that we need to authenticate...
2155 */
2156
2157 auth_info_required = "username,password";
2158
acbc1533 2159 if (password && *password && password_tries < 3)
9c85dfbf 2160 {
2161 password_tries ++;
2162
6248387b 2163 return (password);
9c85dfbf 2164 }
6248387b 2165 else
2166 {
2167 /*
4233257e 2168 * Give up after 3 tries or if we don't have a password to begin with...
6248387b 2169 */
2170
4233257e 2171 return (NULL);
6248387b 2172 }
c8f9565c 2173}
2174
2175
7c7997c3 2176/*
2177 * 'report_attr()' - Report an IPP attribute value.
2178 */
2179
2180static void
2181report_attr(ipp_attribute_t *attr) /* I - Attribute */
2182{
2183 int i; /* Looping var */
2184 char value[1024], /* Value string */
2185 *valptr, /* Pointer into value string */
2186 *attrptr; /* Pointer into attribute value */
2187
2188
2189 /*
2190 * Convert the attribute values into quoted strings...
2191 */
2192
2193 for (i = 0, valptr = value;
2194 i < attr->num_values && valptr < (value + sizeof(value) - 10);
2195 i ++)
2196 {
2197 if (i > 0)
2198 *valptr++ = ',';
2199
2200 switch (attr->value_tag)
2201 {
2202 case IPP_TAG_INTEGER :
2203 case IPP_TAG_ENUM :
2204 snprintf(valptr, sizeof(value) - (valptr - value), "%d",
2205 attr->values[i].integer);
2206 valptr += strlen(valptr);
2207 break;
2208
2209 case IPP_TAG_TEXT :
2210 case IPP_TAG_NAME :
2211 case IPP_TAG_KEYWORD :
2212 *valptr++ = '\"';
2213 for (attrptr = attr->values[i].string.text;
2214 *attrptr && valptr < (value + sizeof(value) - 10);
2215 attrptr ++)
2216 {
2217 if (*attrptr == '\\' || *attrptr == '\"')
2218 *valptr++ = '\\';
2219
2220 *valptr++ = *attrptr;
2221 }
2222 *valptr++ = '\"';
2223 break;
2224
2225 default :
2226 /*
2227 * Unsupported value type...
2228 */
2229
2230 return;
2231 }
2232 }
2233
2234 *valptr = '\0';
2235
2236 /*
2237 * Tell the scheduler about the new values...
2238 */
2239
2240 fprintf(stderr, "ATTR: %s=%s\n", attr->name, value);
2241}
2242
2243
c8f9565c 2244/*
d1c2727f 2245 * 'report_printer_state()' - Report the printer state.
2246 */
2247
072c4872 2248static int /* O - Number of reasons shown */
41368129 2249report_printer_state(ipp_t *ipp, /* I - IPP response */
2250 int job_id) /* I - Current job ID */
d1c2727f 2251{
2252 int i; /* Looping var */
d8076497 2253 int count; /* Count of reasons shown... */
b9738d7c 2254 ipp_attribute_t *pa, /* printer-alert */
2255 *pam, /* printer-alert-message */
2256 *psm, /* printer-state-message */
7c7997c3 2257 *reasons, /* printer-state-reasons */
2258 *marker; /* marker-* attributes */
8f4595eb 2259 const char *reason; /* Current reason */
8f4595eb 2260 const char *prefix; /* Prefix for STATE: line */
b9738d7c 2261 char value[1024], /* State/message string */
2262 *valptr; /* Pointer into string */
aa141f0d 2263 static int ipp_supplies = -1;
2264 /* Report supply levels? */
d1c2727f 2265
2266
b9738d7c 2267 /*
2268 * Report alerts and messages...
2269 */
2270
2271 if ((pa = ippFindAttribute(ipp, "printer-alert", IPP_TAG_TEXT)) != NULL)
2272 report_attr(pa);
2273
2274 if ((pam = ippFindAttribute(ipp, "printer-alert-message",
2275 IPP_TAG_TEXT)) != NULL)
2276 report_attr(pam);
2277
a6ccc6e8 2278 if ((psm = ippFindAttribute(ipp, "printer-state-message",
2279 IPP_TAG_TEXT)) != NULL)
b9738d7c 2280 {
2281 char *ptr; /* Pointer into message */
2282
2283
2284 strlcpy(value, "INFO: ", sizeof(value));
2285 for (ptr = psm->values[0].string.text, valptr = value + 6;
2286 *ptr && valptr < (value + sizeof(value) - 6);
2287 ptr ++)
2288 {
2289 if (*ptr < ' ' && *ptr > 0 && *ptr != '\t')
2290 {
2291 /*
2292 * Substitute "<XX>" for the control character; sprintf is safe because
2293 * we always leave 6 chars free at the end...
2294 */
2295
2296 sprintf(valptr, "<%02X>", *ptr);
2297 valptr += 4;
2298 }
2299 else
2300 *valptr++ = *ptr;
2301 }
2302
2303 *valptr++ = '\n';
57487c71 2304 *valptr = '\0';
b9738d7c 2305
2306 fputs(value, stderr);
2307 }
2308
2309 /*
2310 * Now report printer-state-reasons, filtering out some of the reasons we never
2311 * want to set...
2312 */
a6ccc6e8 2313
d1c2727f 2314 if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
2315 IPP_TAG_KEYWORD)) == NULL)
2316 return (0);
2317
b9738d7c 2318 value[0] = '\0';
f831f3d8 2319 prefix = "STATE: ";
8f4595eb 2320
b9738d7c 2321 for (i = 0, count = 0, valptr = value; i < reasons->num_values; i ++)
d1c2727f 2322 {
8f4595eb 2323 reason = reasons->values[i].string.text;
2324
f831f3d8 2325 if (strcmp(reason, "paused") &&
2326 strcmp(reason, "com.apple.print.recoverable-warning"))
41368129 2327 {
b9738d7c 2328 strlcpy(valptr, prefix, sizeof(value) - (valptr - value) - 1);
2329 valptr += strlen(valptr);
2330 strlcpy(valptr, reason, sizeof(value) - (valptr - value) - 1);
2331 valptr += strlen(valptr);
41368129 2332
2333 prefix = ",";
2334 }
d1c2727f 2335 }
2336
b9738d7c 2337 if (value[0])
2338 {
2339 *valptr++ = '\n';
57487c71 2340 *valptr = '\0';
b9738d7c 2341 fputs(value, stderr);
2342 }
8f4595eb 2343
7c7997c3 2344 /*
2345 * Relay the current marker-* attribute values...
2346 */
2347
aa141f0d 2348 if (ipp_supplies < 0)
2349 {
2350 ppd_file_t *ppd; /* PPD file */
2351 ppd_attr_t *ppdattr; /* Attribute in PPD file */
2352
2353 if ((ppd = ppdOpenFile(getenv("PPD"))) != NULL &&
2354 (ppdattr = ppdFindAttr(ppd, "cupsIPPSupplies", NULL)) != NULL &&
2355 ppdattr->value && strcasecmp(ppdattr->value, "true"))
2356 ipp_supplies = 0;
2357 else
2358 ipp_supplies = 1;
2359
2360 ppdClose(ppd);
2361 }
2362
2363 if (ipp_supplies > 0)
2364 {
2365 if ((marker = ippFindAttribute(ipp, "marker-colors", IPP_TAG_NAME)) != NULL)
2366 report_attr(marker);
2367 if ((marker = ippFindAttribute(ipp, "marker-high-levels",
2368 IPP_TAG_INTEGER)) != NULL)
2369 report_attr(marker);
2370 if ((marker = ippFindAttribute(ipp, "marker-levels",
2371 IPP_TAG_INTEGER)) != NULL)
2372 report_attr(marker);
2373 if ((marker = ippFindAttribute(ipp, "marker-low-levels",
2374 IPP_TAG_INTEGER)) != NULL)
2375 report_attr(marker);
2376 if ((marker = ippFindAttribute(ipp, "marker-message",
2377 IPP_TAG_TEXT)) != NULL)
2378 report_attr(marker);
2379 if ((marker = ippFindAttribute(ipp, "marker-names", IPP_TAG_NAME)) != NULL)
2380 report_attr(marker);
2381 if ((marker = ippFindAttribute(ipp, "marker-types",
2382 IPP_TAG_KEYWORD)) != NULL)
2383 report_attr(marker);
2384 }
7c7997c3 2385
d8076497 2386 return (count);
d1c2727f 2387}
2388
2389
d1c2727f 2390/*
8f4595eb 2391 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
2392 */
2393
2394static void
2395sigterm_handler(int sig) /* I - Signal */
2396{
2397 (void)sig; /* remove compiler warnings... */
2398
6875feac 2399 if (!job_canceled)
072c4872 2400 {
2401 /*
2402 * Flag that the job should be cancelled...
2403 */
2404
6875feac 2405 job_canceled = 1;
072c4872 2406 return;
2407 }
2408
940afb4b 2409 /*
2410 * The scheduler already tried to cancel us once, now just terminate
2411 * after removing our temp files!
2412 */
2413
2414 if (tmpfilename[0])
2415 unlink(tmpfilename);
2416
8f4595eb 2417 exit(1);
2418}
2419
2420
2421/*
c9d3f842 2422 * End of "$Id$".
c8f9565c 2423 */