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