]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/ipp.c
Don't need to load a message catalog when loading a PPD.
[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
5fb23099 755 fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
756 ippErrorString(ipp_status), cupsLastErrorString());
757
b5cb0608 758 if (ipp_status > IPP_OK_CONFLICT)
c8f9565c 759 {
b9738d7c 760 fprintf(stderr, "DEBUG: Get-Printer-Attributes returned %s.\n",
761 ippErrorString(ipp_status));
762
b5cb0608 763 if (ipp_status == IPP_PRINTER_BUSY ||
764 ipp_status == IPP_SERVICE_UNAVAILABLE)
c8f9565c 765 {
ff0295f0 766 if (contimeout && (time(NULL) - start_time) > contimeout)
767 {
4c4eea89 768 _cupsLangPrintFilter(stderr, "ERROR",
769 _("The printer is not responding."));
ff0295f0 770 return (CUPS_BACKEND_FAILED);
771 }
772
5fb23099 773 _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
ff0295f0 774
41368129 775 report_printer_state(supported, 0);
ff0295f0 776
777 sleep(delay);
778
fc596e79 779 delay = _cupsNextDelay(delay, &prev_delay);
97fcaf92 780 }
b5cb0608 781 else if ((ipp_status == IPP_BAD_REQUEST ||
22d59a30 782 ipp_status == IPP_VERSION_NOT_SUPPORTED) && version > 10)
c8f9565c 783 {
b5cb0608 784 /*
6875feac 785 * Switch to IPP/1.1 or IPP/1.0...
b5cb0608 786 */
97fcaf92 787
6875feac 788 if (version >= 20)
789 {
4c4eea89 790 _cupsLangPrintFilter(stderr, "INFO",
791 _("Printer does not support IPP/%d.%d, trying "
792 "IPP/%s."), version / 10, version % 10, "1.1");
6875feac 793 version = 11;
794 }
795 else
796 {
4c4eea89 797 _cupsLangPrintFilter(stderr, "INFO",
798 _("Printer does not support IPP/%d.%d, trying "
799 "IPP/%s."), version / 10, version % 10, "1.0");
6875feac 800 version = 10;
801 }
802
a4e23897 803 httpReconnect(http);
c8f9565c 804 }
67474245 805 else if (ipp_status == IPP_NOT_FOUND)
806 {
4c4eea89 807 _cupsLangPrintFilter(stderr, "ERROR",
808 _("The printer URI is incorrect or no longer "
809 "exists."));
67474245 810
5fb23099 811 ippDelete(supported);
67474245 812
6248387b 813 return (CUPS_BACKEND_STOP);
67474245 814 }
fc7e68cf 815 else if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
816 {
817 if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
818 "Negotiate", 9))
819 auth_info_required = "negotiate";
820
821 fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
822 return (CUPS_BACKEND_AUTH_REQUIRED);
823 }
97fcaf92 824 else
67474245 825 {
4c4eea89 826 _cupsLangPrintFilter(stderr, "ERROR",
5fb23099 827 _("Unable to get printer status."));
67474245 828 sleep(10);
829 }
d1c2727f 830
7cfa02ab 831 ippDelete(supported);
5fb23099 832 supported = NULL;
d1c2727f 833 continue;
97fcaf92 834 }
6875feac 835
7cfa02ab 836 /*
837 * Check printer-state-reasons for the "spool-area-full" keyword...
838 */
839
840 if ((printer_state = ippFindAttribute(supported, "printer-state-reasons",
841 IPP_TAG_KEYWORD)) != NULL)
842 {
843 for (i = 0; i < printer_state->num_values; i ++)
844 if (!strcmp(printer_state->values[0].string.text, "spool-area-full") ||
845 !strncmp(printer_state->values[0].string.text, "spool-area-full-",
846 16))
847 break;
848
849 if (i < printer_state->num_values)
850 {
5fb23099 851 _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
7cfa02ab 852
853 report_printer_state(supported, 0);
854
855 sleep(delay);
856
857 delay = _cupsNextDelay(delay, &prev_delay);
858
859 ippDelete(supported);
5fb23099 860 supported = NULL;
7cfa02ab 861 continue;
862 }
863 }
864 else
865 _cupsLangPrintFilter(stderr, "ERROR",
866 _("This printer does not conform to the IPP "
867 "standard. Please contact the manufacturer of "
868 "your printer for assistance."));
869
6875feac 870 /*
871 * Check for supported attributes...
872 */
873
874 if ((copies_sup = ippFindAttribute(supported, "copies-supported",
875 IPP_TAG_RANGE)) != NULL)
97fcaf92 876 {
b5cb0608 877 /*
878 * Has the "copies-supported" attribute - does it have an upper
879 * bound > 1?
880 */
97fcaf92 881
6875feac 882 fprintf(stderr, "DEBUG: copies-supported=%d-%d\n",
883 copies_sup->values[0].range.lower,
884 copies_sup->values[0].range.upper);
885
b5cb0608 886 if (copies_sup->values[0].range.upper <= 1)
887 copies_sup = NULL; /* No */
888 }
97fcaf92 889
6875feac 890 cups_version = ippFindAttribute(supported, "cups-version", IPP_TAG_TEXT);
b5cb0608 891
6875feac 892 if ((format_sup = ippFindAttribute(supported, "document-format-supported",
893 IPP_TAG_MIMETYPE)) != NULL)
b5cb0608 894 {
895 fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
896 format_sup->num_values);
897 for (i = 0; i < format_sup->num_values; i ++)
898 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
899 format_sup->values[i].string.text);
c8f9565c 900 }
d1c2727f 901
6875feac 902 if ((media_col_sup = ippFindAttribute(supported, "media-col-supported",
903 IPP_TAG_KEYWORD)) != NULL)
904 {
905 fprintf(stderr, "DEBUG: media-col-supported (%d values)\n",
906 media_col_sup->num_values);
907 for (i = 0; i < media_col_sup->num_values; i ++)
908 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
909 media_col_sup->values[i].string.text);
910 }
911
2ec940b5 912 if ((operations_sup = ippFindAttribute(supported, "operations-supported",
913 IPP_TAG_ENUM)) != NULL)
914 {
915 for (i = 0; i < operations_sup->num_values; i ++)
916 if (operations_sup->values[i].integer == IPP_VALIDATE_JOB)
917 {
918 validate_job = 1;
919 break;
920 }
921
922 if (!validate_job)
923 {
924 _cupsLangPrintFilter(stderr, "WARNING",
925 _("This printer does not conform to the IPP "
926 "standard and may not work."));
927 fputs("DEBUG: operations-supported does not list Validate-Job.\n",
928 stderr);
929 }
930 }
931 else
932 {
933 _cupsLangPrintFilter(stderr, "WARNING",
934 _("This printer does not conform to the IPP "
935 "standard and may not work."));
936 fputs("DEBUG: operations-supported not returned in "
937 "Get-Printer-Attributes request.\n", stderr);
938 }
939
adbcb02a 940 doc_handling_sup = ippFindAttribute(supported,
941 "multiple-document-handling-supported",
942 IPP_TAG_KEYWORD);
943
41368129 944 report_printer_state(supported, 0);
c8f9565c 945 }
97fcaf92 946 while (ipp_status > IPP_OK_CONFLICT);
c8f9565c 947
997cf8b0 948 /*
949 * See if the printer is accepting jobs and is not stopped; if either
950 * condition is true and we are printing to a class, requeue the job...
951 */
952
953 if (getenv("CLASS") != NULL)
954 {
955 printer_state = ippFindAttribute(supported, "printer-state",
956 IPP_TAG_ENUM);
957 printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs",
958 IPP_TAG_BOOLEAN);
959
960 if (printer_state == NULL ||
072c4872 961 (printer_state->values[0].integer > IPP_PRINTER_PROCESSING &&
962 waitprinter) ||
997cf8b0 963 printer_accepting == NULL ||
964 !printer_accepting->values[0].boolean)
965 {
966 /*
967 * If the CLASS environment variable is set, the job was submitted
968 * to a class and not to a specific queue. In this case, we want
969 * to abort immediately so that the job can be requeued on the next
970 * available printer in the class.
971 */
972
4c4eea89 973 _cupsLangPrintFilter(stderr, "INFO",
974 _("Unable to contact printer, queuing on next "
975 "printer in class."));
997cf8b0 976
977 ippDelete(supported);
978 httpClose(http);
979
997cf8b0 980 /*
981 * Sleep 5 seconds to keep the job from requeuing too rapidly...
982 */
983
984 sleep(5);
985
6248387b 986 return (CUPS_BACKEND_FAILED);
997cf8b0 987 }
988 }
989
c8f9565c 990 /*
97fcaf92 991 * See if the printer supports multiple copies...
c8f9565c 992 */
993
37de726f 994 copies = atoi(argv[4]);
995
d1c2727f 996 if (copies_sup || argc < 7)
7d52601e 997 {
37de726f 998 copies_remaining = 1;
7d52601e 999
5358d337 1000 if (argc < 7 && !send_options)
7d52601e 1001 copies = 1;
1002 }
0c5b0932 1003 else
37de726f 1004 copies_remaining = copies;
0c5b0932 1005
2ec940b5 1006 /*
1007 * Prepare remaining printing options...
1008 */
1009
1010 options = NULL;
8211508e 1011 pc = NULL;
2ec940b5 1012
1013 if (send_options)
1014 {
1015 num_options = cupsParseOptions(argv[5], 0, &options);
1016
1017 if (!cups_version && media_col_sup)
1018 {
1019 /*
1020 * Load the PPD file and generate PWG attribute mapping information...
1021 */
1022
1023 ppd = ppdOpenFile(getenv("PPD"));
8211508e 1024 pc = _ppdCacheCreateWithPPD(ppd);
2ec940b5 1025
1026 ppdClose(ppd);
1027 }
1028 }
1029 else
1030 num_options = 0;
1031
1032 document_format = NULL;
1033
1034 if (format_sup != NULL)
1035 {
1036 for (i = 0; i < format_sup->num_values; i ++)
1037 if (!strcasecmp(final_content_type, format_sup->values[i].string.text))
1038 {
1039 document_format = final_content_type;
1040 break;
1041 }
cce0044f 1042
1043 if (!document_format)
1044 {
1045 for (i = 0; i < format_sup->num_values; i ++)
1046 if (!strcasecmp("application/octet-stream",
1047 format_sup->values[i].string.text))
1048 {
1049 document_format = "application/octet-stream";
1050 break;
1051 }
1052 }
2ec940b5 1053 }
1054
940afb4b 1055 /*
1056 * If the printer does not support HTTP/1.1 (which IPP requires), copy stdin
1057 * to a temporary file so that we can do a HTTP/1.0 submission...
1058 *
1059 * (I hate compatibility hacks!)
1060 */
1061
1062 if (http->version < HTTP_1_1 && num_files == 0)
1063 {
1064 if ((fd = cupsTempFd(tmpfilename, sizeof(tmpfilename))) < 0)
1065 {
1066 perror("DEBUG: Unable to create temporary file");
1067 return (CUPS_BACKEND_FAILED);
1068 }
1069
1070 _cupsLangPrintFilter(stderr, "INFO", _("Copying print data."));
1071
1072 compatsize = backendRunLoop(-1, fd, snmp_fd, &(addrlist->addr), 0, 0,
1073 backendNetworkSideCB);
1074
1075 close(fd);
1076
1077 compatfile = tmpfilename;
1078 files = &compatfile;
1079 num_files = 1;
1080 }
1081 else if (http->version < HTTP_1_1 && num_files == 1)
1082 {
1083 struct stat fileinfo; /* File information */
1084
1085 if (!stat(files[0], &fileinfo))
1086 compatsize = fileinfo.st_size;
1087 }
1088
b9738d7c 1089 /*
1090 * Start monitoring the printer in the background...
1091 */
1092
1093 monitor.uri = uri;
1094 monitor.hostname = hostname;
1095 monitor.user = argv[2];
1096 monitor.resource = resource;
1097 monitor.port = port;
1098 monitor.version = version;
1099 monitor.job_id = 0;
1100 monitor.encryption = cupsEncryption();
1101 monitor.job_state = IPP_JOB_PENDING;
1102 monitor.printer_state = IPP_PRINTER_IDLE;
1103
1104 _cupsThreadCreate((_cups_thread_func_t)monitor_printer, &monitor);
1105
c8f9565c 1106 /*
2ec940b5 1107 * Validate access to the printer...
c8f9565c 1108 */
1109
5fb23099 1110 while (!job_canceled && validate_job)
c8f9565c 1111 {
2ec940b5 1112 request = new_request(IPP_VALIDATE_JOB, version, uri, argv[2], argv[3],
1113 num_options, options, compression,
8211508e 1114 copies_sup ? copies : 1, document_format, pc,
adbcb02a 1115 media_col_sup, doc_handling_sup);
97fcaf92 1116
2ec940b5 1117 ippDelete(cupsDoRequest(http, request, resource));
8ce7c000 1118
2ec940b5 1119 ipp_status = cupsLastError();
97fcaf92 1120
5fb23099 1121 fprintf(stderr, "DEBUG: Validate-Job: %s (%s)\n",
1122 ippErrorString(ipp_status), cupsLastErrorString());
6875feac 1123
5fb23099 1124 if (job_canceled)
1125 break;
e7186d00 1126
5fb23099 1127 if (ipp_status == IPP_SERVICE_UNAVAILABLE || ipp_status == IPP_PRINTER_BUSY)
1128 {
1129 _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
1130 sleep(10);
1131 }
548f3b66 1132 else if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
1133 ipp_status == IPP_AUTHENTICATION_CANCELED)
5fb23099 1134 {
1135 /*
1136 * Update auth-info-required as needed...
1137 */
b8f3410f 1138
5fb23099 1139 fprintf(stderr, "DEBUG: WWW-Authenticate=\"%s\"\n",
1140 httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE));
97fcaf92 1141
5fb23099 1142 /*
1143 * Normal authentication goes through the password callback, which sets
1144 * auth_info_required to "username,password". Kerberos goes directly
1145 * through GSSAPI, so look for Negotiate in the WWW-Authenticate header
1146 * here and set auth_info_required as needed...
1147 */
6875feac 1148
5fb23099 1149 if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
1150 "Negotiate", 9))
1151 auth_info_required = "negotiate";
753453e4 1152
5fb23099 1153 goto cleanup;
2ec940b5 1154 }
5fb23099 1155 else if (ipp_status == IPP_OPERATION_NOT_SUPPORTED)
1156 {
1157 _cupsLangPrintFilter(stderr, "WARNING",
1158 _("This printer does not conform to the IPP "
1159 "standard and may not work."));
1160 break;
1161 }
1162 else if (ipp_status < IPP_REDIRECTION_OTHER_SITE)
2ec940b5 1163 break;
1164 }
37de726f 1165
2ec940b5 1166 /*
1167 * Then issue the print-job request...
1168 */
6875feac 1169
2ec940b5 1170 job_id = 0;
6875feac 1171
2ec940b5 1172 while (!job_canceled && copies_remaining > 0)
1173 {
1174 /*
1175 * Check for side-channel requests...
1176 */
b9738d7c 1177
2ec940b5 1178 backendCheckSideChannel(snmp_fd, http->hostaddr);
b9738d7c 1179
2ec940b5 1180 /*
1181 * Build the IPP job creation request...
1182 */
6875feac 1183
2ec940b5 1184 if (job_canceled)
1185 break;
753453e4 1186
2ec940b5 1187 request = new_request(num_files > 1 ? IPP_CREATE_JOB : IPP_PRINT_JOB,
1188 version, uri, argv[2], argv[3], num_options, options,
1189 compression, copies_sup ? copies : 1, document_format,
adbcb02a 1190 pc, media_col_sup, doc_handling_sup);
c8f9565c 1191
cb555bcf 1192 /*
b5cb0608 1193 * Do the request...
cb555bcf 1194 */
1195
072c4872 1196 if (num_files > 1)
1197 response = cupsDoRequest(http, request, resource);
b5cb0608 1198 else
6875feac 1199 {
940afb4b 1200 size_t length = 0; /* Length of request */
1201
1202 if (compatsize > 0)
1203 {
1204 fputs("DEBUG: Sending file using HTTP/1.0 Content-Length...\n", stderr);
1205 length = ippLength(request) + (size_t)compatsize;
1206 }
1207 else
1208 fputs("DEBUG: Sending file using HTTP/1.1 chunking...\n", stderr);
1209
1210 http_status = cupsSendRequest(http, request, resource, length);
6875feac 1211 if (http_status == HTTP_CONTINUE && request->state == IPP_DATA)
1212 {
1213 if (num_files == 1)
1214 fd = open(files[0], O_RDONLY);
1215 else
1216 fd = 0;
1217
d2355476 1218 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
6875feac 1219 {
d2355476 1220 fprintf(stderr, "DEBUG: Read %d bytes...\n", (int)bytes);
1221
e6af41cc 1222 if (cupsWriteRequestData(http, buffer, bytes) != HTTP_CONTINUE)
6875feac 1223 break;
1224 else
1225 {
1226 /*
1227 * Check for side-channel requests...
1228 */
1229
1230 backendCheckSideChannel(snmp_fd, http->hostaddr);
1231 }
1232 }
1233
1234 if (num_files == 1)
1235 close(fd);
1236 }
1237
1238 response = cupsGetResponse(http, resource);
1239 ippDelete(request);
1240 }
072c4872 1241
1242 ipp_status = cupsLastError();
b5cb0608 1243
5fb23099 1244 fprintf(stderr, "DEBUG: %s: %s (%s)\n",
1245 num_files > 1 ? "Create-Job" : "Print-Job",
1246 ippErrorString(ipp_status), cupsLastErrorString());
1247
b5cb0608 1248 if (ipp_status > IPP_OK_CONFLICT)
1249 {
753453e4 1250 job_id = 0;
1251
6875feac 1252 if (job_canceled)
072c4872 1253 break;
1254
b5cb0608 1255 if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1256 ipp_status == IPP_PRINTER_BUSY)
1257 {
5fb23099 1258 _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
b5cb0608 1259 sleep(10);
7cfa02ab 1260
1261 if (num_files == 0)
1262 {
1263 /*
1264 * We can't re-submit when we have no files to print, so exit
1265 * immediately with the right status code...
1266 */
1267
1268 goto cleanup;
1269 }
b5cb0608 1270 }
1271 else
ebac5c9b 1272 {
1273 /*
1274 * Update auth-info-required as needed...
1275 */
1276
4c4eea89 1277 _cupsLangPrintFilter(stderr, "ERROR",
5fb23099 1278 _("Print file was not accepted."));
ebac5c9b 1279
4233257e 1280 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
ebac5c9b 1281 {
1282 fprintf(stderr, "DEBUG: WWW-Authenticate=\"%s\"\n",
1283 httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE));
1284
4233257e 1285 /*
1286 * Normal authentication goes through the password callback, which sets
1287 * auth_info_required to "username,password". Kerberos goes directly
1288 * through GSSAPI, so look for Negotiate in the WWW-Authenticate header
1289 * here and set auth_info_required as needed...
1290 */
1291
ebac5c9b 1292 if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
1293 "Negotiate", 9))
4233257e 1294 auth_info_required = "negotiate";
ebac5c9b 1295 }
5fb23099 1296 else
1297 sleep(10);
1298
1299 if (num_files == 0)
1300 {
1301 /*
1302 * We can't re-submit when we have no files to print, so exit
1303 * immediately with the right status code...
1304 */
1305
1306 goto cleanup;
1307 }
ebac5c9b 1308 }
b5cb0608 1309 }
1310 else if ((job_id_attr = ippFindAttribute(response, "job-id",
1311 IPP_TAG_INTEGER)) == NULL)
97fcaf92 1312 {
4c4eea89 1313 _cupsLangPrintFilter(stderr, "INFO",
1314 _("Print file accepted - job ID unknown."));
b5cb0608 1315 job_id = 0;
1316 }
1317 else
1318 {
b9738d7c 1319 monitor.job_id = job_id = job_id_attr->values[0].integer;
4c4eea89 1320 _cupsLangPrintFilter(stderr, "INFO",
1321 _("Print file accepted - job ID %d."), job_id);
97fcaf92 1322 }
cb555bcf 1323
072c4872 1324 ippDelete(response);
1325
6875feac 1326 if (job_canceled)
072c4872 1327 break;
1328
1329 if (job_id && num_files > 1)
1330 {
1331 for (i = 0; i < num_files; i ++)
1332 {
56f7a531 1333 /*
1334 * Check for side-channel requests...
1335 */
1336
1337 backendCheckSideChannel(snmp_fd, http->hostaddr);
1338
1339 /*
1340 * Send the next file in the job...
1341 */
1342
072c4872 1343 request = ippNewRequest(IPP_SEND_DOCUMENT);
22d59a30 1344 request->request.op.version[0] = version / 10;
1345 request->request.op.version[1] = version % 10;
072c4872 1346
1347 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1348 NULL, uri);
1349
1350 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1351 job_id);
1352
1353 if (argv[2][0])
1354 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1355 "requesting-user-name", NULL, argv[2]);
1356
1357 if ((i + 1) == num_files)
1358 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
1359
1360 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
1361 "document-format", NULL, content_type);
1362
6875feac 1363 fprintf(stderr, "DEBUG: Sending file %d using chunking...\n", i + 1);
1364 http_status = cupsSendRequest(http, request, resource, 0);
1365 if (http_status == HTTP_CONTINUE && request->state == IPP_DATA &&
1366 (fd = open(files[i], O_RDONLY)) >= 0)
1367 {
1368 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
1369 {
e6af41cc 1370 if (cupsWriteRequestData(http, buffer, bytes) != HTTP_CONTINUE)
6875feac 1371 break;
1372 else
1373 {
1374 /*
1375 * Check for side-channel requests...
1376 */
1377
1378 backendCheckSideChannel(snmp_fd, http->hostaddr);
1379 }
1380 }
1381
1382 close(fd);
1383 }
acd7eb22 1384
6875feac 1385 ippDelete(cupsGetResponse(http, resource));
1386 ippDelete(request);
072c4872 1387
5fb23099 1388 fprintf(stderr, "DEBUG: Send-Document: %s (%s)\n",
1389 ippErrorString(cupsLastError()), cupsLastErrorString());
1390
072c4872 1391 if (cupsLastError() > IPP_OK_CONFLICT)
1392 {
1393 ipp_status = cupsLastError();
1394
4c4eea89 1395 _cupsLangPrintFilter(stderr, "ERROR",
5fb23099 1396 _("Unable to add document to print job."));
072c4872 1397 break;
1398 }
1399 }
1400 }
b5cb0608 1401
753453e4 1402 if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
b5cb0608 1403 {
1404 fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
37de726f 1405 copies_remaining --;
b5cb0608 1406 }
ab827512 1407 else if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1408 ipp_status == IPP_PRINTER_BUSY)
a04d2365 1409 continue;
ab827512 1410 else
37de726f 1411 copies_remaining --;
8ce7c000 1412
c8f9565c 1413 /*
b5cb0608 1414 * Wait for the job to complete...
c8f9565c 1415 */
1416
897922a9 1417 if (!job_id || !waitjob)
b5cb0608 1418 continue;
1419
4c4eea89 1420 _cupsLangPrintFilter(stderr, "INFO", _("Waiting for job to complete."));
b5cb0608 1421
fc596e79 1422 for (delay = _cupsNextDelay(0, &prev_delay); !job_canceled;)
c8f9565c 1423 {
56f7a531 1424 /*
1425 * Check for side-channel requests...
1426 */
1427
1428 backendCheckSideChannel(snmp_fd, http->hostaddr);
1429
97fcaf92 1430 /*
b5cb0608 1431 * Build an IPP_GET_JOB_ATTRIBUTES request...
97fcaf92 1432 */
1433
e12df069 1434 request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
22d59a30 1435 request->request.op.version[0] = version / 10;
1436 request->request.op.version[1] = version % 10;
97fcaf92 1437
b5cb0608 1438 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1439 NULL, uri);
3f9cb6c6 1440
b5cb0608 1441 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1442 job_id);
c8f9565c 1443
ee8b7dd3 1444 if (argv[2][0])
897922a9 1445 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1446 "requesting-user-name", NULL, argv[2]);
ee8b7dd3 1447
8f4595eb 1448 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1449 "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
1450 NULL, jattrs);
97fcaf92 1451
1452 /*
b5cb0608 1453 * Do the request...
97fcaf92 1454 */
1455
fc596e79 1456 httpReconnect(http);
072c4872 1457 response = cupsDoRequest(http, request, resource);
1458 ipp_status = cupsLastError();
97fcaf92 1459
b5cb0608 1460 if (ipp_status == IPP_NOT_FOUND)
97fcaf92 1461 {
b5cb0608 1462 /*
d1c2727f 1463 * Job has gone away and/or the server has no job history...
b5cb0608 1464 */
97fcaf92 1465
b5cb0608 1466 ippDelete(response);
d1c2727f 1467
1468 ipp_status = IPP_OK;
b5cb0608 1469 break;
97fcaf92 1470 }
1471
5fb23099 1472 fprintf(stderr, "DEBUG: Get-Job-Attributes: %s (%s)\n",
1473 ippErrorString(ipp_status), cupsLastErrorString());
1474
b5cb0608 1475 if (ipp_status > IPP_OK_CONFLICT)
6a536282 1476 {
b5cb0608 1477 if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
1478 ipp_status != IPP_PRINTER_BUSY)
97fcaf92 1479 {
072c4872 1480 ippDelete(response);
b5cb0608 1481
4c4eea89 1482 _cupsLangPrintFilter(stderr, "ERROR",
5fb23099 1483 _("Unable to get print job status."));
b5cb0608 1484 break;
97fcaf92 1485 }
6a536282 1486 }
8f4595eb 1487
072c4872 1488 if (response)
97fcaf92 1489 {
8f4595eb 1490 if ((job_state = ippFindAttribute(response, "job-state",
1491 IPP_TAG_ENUM)) != NULL)
97fcaf92 1492 {
6c1b8723 1493 /*
1494 * Reflect the remote job state in the local queue...
1495 */
1496
1497 fputs("STATE: -cups-remote-pending,"
1498 "cups-remote-pending-held,"
1499 "cups-remote-processing,"
1500 "cups-remote-stopped,"
1501 "cups-remote-canceled,"
1502 "cups-remote-aborted,"
1503 "cups-remote-completed\n", stderr);
1504 if (job_state->values[0].integer >= IPP_JOB_PENDING &&
1505 job_state->values[0].integer <= IPP_JOB_COMPLETED)
1506 fprintf(stderr, "STATE: +%s\n",
1507 remote_job_states[job_state->values[0].integer -
1508 IPP_JOB_PENDING]);
1509
8f4595eb 1510 /*
1511 * Stop polling if the job is finished or pending-held...
1512 */
1513
7951315f 1514 if (job_state->values[0].integer > IPP_JOB_STOPPED)
8f4595eb 1515 {
aa141f0d 1516 if ((job_sheets = ippFindAttribute(response,
e12df069 1517 "job-media-sheets-completed",
1518 IPP_TAG_INTEGER)) != NULL)
072c4872 1519 fprintf(stderr, "PAGE: total %d\n",
1520 job_sheets->values[0].integer);
ab827512 1521
8f4595eb 1522 ippDelete(response);
1523 break;
1524 }
97fcaf92 1525 }
f3fdb6b2 1526 else
1527 {
1528 /*
1529 * If the printer does not return a job-state attribute, it does not
1530 * conform to the IPP specification - break out immediately and fail
1531 * the job...
1532 */
1533
7a43b482 1534 fputs("DEBUG: No job-state available from printer - stopping queue.\n",
1535 stderr);
f3fdb6b2 1536 ipp_status = IPP_INTERNAL_ERROR;
1537 break;
1538 }
97fcaf92 1539 }
1540
072c4872 1541 ippDelete(response);
d1c2727f 1542
b5cb0608 1543 /*
fc596e79 1544 * Wait before polling again...
d1c2727f 1545 */
97fcaf92 1546
2ce8588b 1547 sleep(delay);
1548
fc596e79 1549 delay = _cupsNextDelay(delay, &prev_delay);
97fcaf92 1550 }
c8f9565c 1551 }
1552
6bb5a528 1553 /*
072c4872 1554 * Cancel the job as needed...
6bb5a528 1555 */
1556
6875feac 1557 if (job_canceled && job_id)
072c4872 1558 cancel_job(http, uri, job_id, resource, argv[2], version);
1559
1560 /*
1561 * Check the printer state and report it if necessary...
1562 */
6bb5a528 1563
41368129 1564 check_printer_state(http, uri, resource, argv[2], version, job_id);
6bb5a528 1565
eab5b04e 1566 /*
1567 * Collect the final page count as needed...
1568 */
1569
aa141f0d 1570 if (have_supplies &&
eab5b04e 1571 !backendSNMPSupplies(snmp_fd, http->hostaddr, &page_count, NULL) &&
1572 page_count > start_count)
1573 fprintf(stderr, "PAGE: total %d\n", page_count - start_count);
1574
4233257e 1575#ifdef HAVE_GSSAPI
1576 /*
1577 * See if we used Kerberos at all...
1578 */
1579
1580 if (http->gssctx)
1581 auth_info_required = "negotiate";
1582#endif /* HAVE_GSSAPI */
1583
c8f9565c 1584 /*
1585 * Free memory...
1586 */
1587
2ec940b5 1588 cleanup:
1589
1590 cupsFreeOptions(num_options, options);
8211508e 1591 _ppdCacheDestroy(pc);
2ec940b5 1592
c8f9565c 1593 httpClose(http);
c8f9565c 1594
072c4872 1595 ippDelete(supported);
2922de55 1596
c8f9565c 1597 /*
b8f3410f 1598 * Remove the temporary file(s) if necessary...
c8f9565c 1599 */
1600
940afb4b 1601 if (tmpfilename[0])
1602 unlink(tmpfilename);
1603
1230a8a0 1604#ifdef HAVE_LIBZ
1605 if (compression)
1606 {
1607 for (i = 0; i < num_files; i ++)
1608 unlink(files[i]);
1609 }
1610#endif /* HAVE_LIBZ */
1611
c8f9565c 1612 /*
1613 * Return the queue status...
1614 */
1615
c55dc24c 1616 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
d0eaf635 1617 ipp_status == IPP_AUTHENTICATION_CANCELED ||
c55dc24c 1618 ipp_status <= IPP_OK_CONFLICT)
1619 fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
4233257e 1620
d0eaf635 1621 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
1622 ipp_status == IPP_AUTHENTICATION_CANCELED)
3fe655a3 1623 return (CUPS_BACKEND_AUTH_REQUIRED);
f3fdb6b2 1624 else if (ipp_status == IPP_INTERNAL_ERROR)
1625 return (CUPS_BACKEND_STOP);
623108b2 1626 else if (ipp_status == IPP_DOCUMENT_FORMAT ||
1627 ipp_status == IPP_CONFLICT)
1628 return (CUPS_BACKEND_FAILED);
3fe655a3 1629 else if (ipp_status > IPP_OK_CONFLICT)
5fb23099 1630 return (CUPS_BACKEND_RETRY_CURRENT);
3fe655a3 1631 else
83d83f7a 1632 {
4c4eea89 1633 _cupsLangPrintFilter(stderr, "INFO", _("Ready to print."));
3fe655a3 1634 return (CUPS_BACKEND_OK);
83d83f7a 1635 }
b5cb0608 1636}
1637
1638
072c4872 1639/*
1640 * 'cancel_job()' - Cancel a print job.
1641 */
1642
1643static void
1644cancel_job(http_t *http, /* I - HTTP connection */
1645 const char *uri, /* I - printer-uri */
1646 int id, /* I - job-id */
1647 const char *resource, /* I - Resource path */
1648 const char *user, /* I - requesting-user-name */
1649 int version) /* I - IPP version */
1650{
1651 ipp_t *request; /* Cancel-Job request */
1652
1653
4c4eea89 1654 _cupsLangPrintFilter(stderr, "INFO", _("Canceling print job."));
072c4872 1655
1656 request = ippNewRequest(IPP_CANCEL_JOB);
22d59a30 1657 request->request.op.version[0] = version / 10;
1658 request->request.op.version[1] = version % 10;
072c4872 1659
1660 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1661 NULL, uri);
1662 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
1663
1664 if (user && user[0])
1665 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1666 "requesting-user-name", NULL, user);
1667
1668 /*
1669 * Do the request...
1670 */
1671
1672 ippDelete(cupsDoRequest(http, request, resource));
1673
1674 if (cupsLastError() > IPP_OK_CONFLICT)
5fb23099 1675 _cupsLangPrintFilter(stderr, "ERROR", _("Unable to cancel print job."));
072c4872 1676}
1677
1678
6bb5a528 1679/*
b9738d7c 1680 * 'check_printer_state()' - Check the printer state.
6bb5a528 1681 */
1682
b9738d7c 1683static ipp_pstate_t /* O - Current printer-state */
e12df069 1684check_printer_state(
1685 http_t *http, /* I - HTTP connection */
1686 const char *uri, /* I - Printer URI */
1687 const char *resource, /* I - Resource path */
1688 const char *user, /* I - Username, if any */
41368129 1689 int version, /* I - IPP version */
b9738d7c 1690 int job_id)
6bb5a528 1691{
b9738d7c 1692 ipp_t *request, /* IPP request */
1693 *response; /* IPP response */
1694 ipp_attribute_t *attr; /* Attribute in response */
1695 ipp_pstate_t printer_state = IPP_PRINTER_STOPPED;
1696 /* Current printer-state */
6bb5a528 1697
1698
1699 /*
b9738d7c 1700 * Send a Get-Printer-Attributes request and log the results...
6bb5a528 1701 */
1702
e12df069 1703 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
22d59a30 1704 request->request.op.version[0] = version / 10;
1705 request->request.op.version[1] = version % 10;
6bb5a528 1706
1707 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
b9738d7c 1708 NULL, uri);
6bb5a528 1709
1710 if (user && user[0])
1711 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
b9738d7c 1712 "requesting-user-name", NULL, user);
6bb5a528 1713
a6ccc6e8 1714 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
b9738d7c 1715 "requested-attributes",
1716 (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
6bb5a528 1717
1718 if ((response = cupsDoRequest(http, request, resource)) != NULL)
1719 {
41368129 1720 report_printer_state(response, job_id);
b9738d7c 1721
1722 if ((attr = ippFindAttribute(response, "printer-state",
1723 IPP_TAG_ENUM)) != NULL)
1724 printer_state = (ipp_pstate_t)attr->values[0].integer;
1725
6bb5a528 1726 ippDelete(response);
1727 }
b9738d7c 1728
5fb23099 1729 fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
1730 ippErrorString(cupsLastError()), cupsLastErrorString());
1731
b9738d7c 1732 /*
1733 * Return the printer-state value...
1734 */
1735
1736 return (printer_state);
6bb5a528 1737}
1738
1739
1230a8a0 1740#ifdef HAVE_LIBZ
1741/*
1742 * 'compress_files()' - Compress print files...
1743 */
1744
1745static void
1746compress_files(int num_files, /* I - Number of files */
1747 char **files) /* I - Files */
1748{
1749 int i, /* Looping var */
1750 fd; /* Temporary file descriptor */
1751 ssize_t bytes; /* Bytes read/written */
1752 size_t total; /* Total bytes read */
1753 cups_file_t *in, /* Input file */
1754 *out; /* Output file */
1755 struct stat outinfo; /* Output file information */
1756 char filename[1024], /* Temporary filename */
1fb9f3e8 1757 buffer[32768]; /* Copy buffer */
1230a8a0 1758
1759
1760 fprintf(stderr, "DEBUG: Compressing %d job files...\n", num_files);
1761 for (i = 0; i < num_files; i ++)
1762 {
1763 if ((fd = cupsTempFd(filename, sizeof(filename))) < 0)
1764 {
4c4eea89 1765 _cupsLangPrintError("ERROR", _("Unable to create compressed print file"));
1230a8a0 1766 exit(CUPS_BACKEND_FAILED);
1767 }
1768
1769 if ((out = cupsFileOpenFd(fd, "w9")) == NULL)
1770 {
4c4eea89 1771 _cupsLangPrintError("ERROR", _("Unable to open compressed print file"));
1230a8a0 1772 exit(CUPS_BACKEND_FAILED);
1773 }
1774
1775 if ((in = cupsFileOpen(files[i], "r")) == NULL)
1776 {
4c4eea89 1777 _cupsLangPrintError("ERROR", _("Unable to open print file"));
1230a8a0 1778 cupsFileClose(out);
1779 exit(CUPS_BACKEND_FAILED);
1780 }
1781
1782 total = 0;
1783 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
1784 if (cupsFileWrite(out, buffer, bytes) < bytes)
1785 {
4c4eea89 1786 _cupsLangPrintError("ERROR",
1787 _("Unable to generate compressed print file"));
1230a8a0 1788 cupsFileClose(in);
1789 cupsFileClose(out);
1790 exit(CUPS_BACKEND_FAILED);
1791 }
1792 else
1793 total += bytes;
1794
1795 cupsFileClose(out);
1796 cupsFileClose(in);
1797
1798 files[i] = strdup(filename);
1799
1800 if (!stat(filename, &outinfo))
ff0295f0 1801 fprintf(stderr,
1802 "DEBUG: File %d compressed to %.1f%% of original size, "
1803 CUPS_LLFMT " bytes...\n",
1804 i + 1, 100.0 * outinfo.st_size / total,
1805 CUPS_LLCAST outinfo.st_size);
1230a8a0 1806 }
1807}
1808#endif /* HAVE_LIBZ */
1809
1810
b9738d7c 1811/*
1812 * 'monitor_printer()' - Monitor the printer state...
1813 */
1814
1815static void * /* O - Thread exit code */
1816monitor_printer(
1817 _cups_monitor_t *monitor) /* I - Monitoring data */
1818{
1819 http_t *http; /* Connection to printer */
1820 ipp_t *request, /* IPP request */
1821 *response; /* IPP response */
1822 ipp_attribute_t *attr; /* Attribute in response */
1823 int delay, /* Current delay */
8211508e 1824 prev_delay; /* Previous delay */
b9738d7c 1825
1826
1827 /*
1828 * Make a copy of the printer connection...
1829 */
1830
cce0044f 1831 http = _httpCreate(monitor->hostname, monitor->port, NULL, monitor->encryption,
16f98e36 1832 AF_UNSPEC);
b9738d7c 1833 cupsSetPasswordCB(password_cb);
1834
1835 /*
1836 * Loop until the job is canceled, aborted, or completed.
1837 */
1838
fc596e79 1839 delay = _cupsNextDelay(0, &prev_delay);
b9738d7c 1840
1841 while (monitor->job_state < IPP_JOB_CANCELED && !job_canceled)
1842 {
1843 /*
1844 * Reconnect to the printer...
1845 */
1846
1847 if (!httpReconnect(http))
1848 {
1849 /*
1850 * Connected, so check on the printer state...
1851 */
1852
1853 monitor->printer_state = check_printer_state(http, monitor->uri,
1854 monitor->resource,
1855 monitor->user,
1856 monitor->version,
1857 monitor->job_id);
1858
1859 if (monitor->job_id > 0)
1860 {
1861 /*
1862 * Check the status of the job itself...
1863 */
1864
1865 request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
1866 request->request.op.version[0] = monitor->version / 10;
1867 request->request.op.version[1] = monitor->version % 10;
1868
1869 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1870 NULL, monitor->uri);
1871 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1872 monitor->job_id);
1873
1874 if (monitor->user && monitor->user[0])
1875 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1876 "requesting-user-name", NULL, monitor->user);
1877
1878 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1879 "requested-attributes",
1880 (int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs);
1881
1882 /*
1883 * Do the request...
1884 */
1885
1886 response = cupsDoRequest(http, request, monitor->resource);
1887
5fb23099 1888 fprintf(stderr, "DEBUG: Get-Job-Attributes: %s (%s)\n",
1889 ippErrorString(cupsLastError()), cupsLastErrorString());
1890
b9738d7c 1891 if ((attr = ippFindAttribute(response, "job-state",
1892 IPP_TAG_ENUM)) != NULL)
1893 monitor->job_state = (ipp_jstate_t)attr->values[0].integer;
1894 else
1895 monitor->job_state = IPP_JOB_COMPLETED;
1896
1897 ippDelete(response);
1898 }
1899
1900 /*
1901 * Disconnect from the printer - we'll reconnect on the next poll...
1902 */
1903
1904 _httpDisconnect(http);
1905 }
1906
1907 /*
fc596e79 1908 * Sleep for N seconds...
b9738d7c 1909 */
1910
1911 sleep(delay);
1912
fc596e79 1913 delay = _cupsNextDelay(delay, &prev_delay);
b9738d7c 1914 }
1915
1916 /*
1917 * Cleanup and return...
1918 */
1919
1920 httpClose(http);
1921
1922 return (NULL);
1923}
1924
1925
2ec940b5 1926/*
1927 * 'new_request()' - Create a new print creation or validation request.
1928 */
1929
1930static ipp_t * /* O - Request data */
1931new_request(
1932 ipp_op_t op, /* I - IPP operation code */
1933 int version, /* I - IPP version number */
1934 const char *uri, /* I - printer-uri value */
1935 const char *user, /* I - requesting-user-name value */
1936 const char *title, /* I - job-name value */
1937 int num_options, /* I - Number of options to send */
1938 cups_option_t *options, /* I - Options to send */
1939 const char *compression, /* I - compression value or NULL */
aa141f0d 1940 int copies, /* I - copies value or 0 */
2ec940b5 1941 const char *format, /* I - documet-format value or NULL */
8211508e 1942 _ppd_cache_t *pc, /* I - PPD cache and mapping data */
adbcb02a 1943 ipp_attribute_t *media_col_sup, /* I - media-col-supported values */
1944 ipp_attribute_t *doc_handling_sup) /* I - multiple-document-handling-supported values */
2ec940b5 1945{
1946 int i; /* Looping var */
1947 ipp_t *request; /* Request data */
1948 const char *keyword; /* PWG keyword */
1949 _pwg_size_t *size; /* PWG media size */
1950 ipp_t *media_col, /* media-col value */
1951 *media_size; /* media-size value */
1952 const char *media_source, /* media-source value */
adbcb02a 1953 *media_type, /* media-type value */
1954 *collate_str; /* multiple-document-handling value */
2ec940b5 1955
1956
1957 /*
1958 * Create the IPP request...
1959 */
1960
1961 request = ippNewRequest(op);
1962 request->request.op.version[0] = version / 10;
1963 request->request.op.version[1] = version % 10;
1964
1965 fprintf(stderr, "DEBUG: %s IPP/%d.%d\n",
1966 ippOpString(request->request.op.operation_id),
1967 request->request.op.version[0],
1968 request->request.op.version[1]);
1969
1970 /*
1971 * Add standard attributes...
1972 */
1973
1974 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1975 NULL, uri);
1976 fprintf(stderr, "DEBUG: printer-uri=\"%s\"\n", uri);
1977
1978 if (user && *user)
1979 {
1980 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1981 "requesting-user-name", NULL, user);
1982 fprintf(stderr, "DEBUG: requesting-user-name=\"%s\"\n", user);
1983 }
1984
1985 if (title && *title)
1986 {
1987 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
1988 title);
1989 fprintf(stderr, "DEBUG: job-name=\"%s\"\n", title);
1990 }
1991
1992 if (format)
1993 {
1994 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
1995 "document-format", NULL, format);
1996 fprintf(stderr, "DEBUG: document-format=\"%s\"\n", format);
1997 }
1998
1999#ifdef HAVE_LIBZ
2000 if (compression)
2001 {
2002 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2003 "compression", NULL, compression);
2004 fprintf(stderr, "DEBUG: compression=\"%s\"\n", compression);
2005 }
2006#endif /* HAVE_LIBZ */
2007
2008 /*
2009 * Handle options on the command-line...
2010 */
2011
2012 if (num_options > 0)
2013 {
8211508e 2014 if (pc)
2ec940b5 2015 {
2016 /*
2017 * Send standard IPP attributes...
2018 */
2019
2020 if ((keyword = cupsGetOption("PageSize", num_options, options)) == NULL)
2021 keyword = cupsGetOption("media", num_options, options);
2022
8211508e 2023 if ((size = _ppdCacheGetSize(pc, keyword)) != NULL)
2ec940b5 2024 {
2025 /*
2026 * Add a media-col value...
2027 */
2028
2029 media_size = ippNew();
2030 ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2031 "x-dimension", size->width);
2032 ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2033 "y-dimension", size->length);
2034
2035 media_col = ippNew();
2036 ippAddCollection(media_col, IPP_TAG_ZERO, "media-size", media_size);
2037
8211508e 2038 media_source = _ppdCacheGetSource(pc, cupsGetOption("InputSlot",
2039 num_options,
2040 options));
2041 media_type = _ppdCacheGetType(pc, cupsGetOption("MediaType",
2042 num_options,
2043 options));
2ec940b5 2044
2045 for (i = 0; i < media_col_sup->num_values; i ++)
2046 {
2047 if (!strcmp(media_col_sup->values[i].string.text,
2048 "media-left-margin"))
2049 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2050 "media-left-margin", size->left);
2051 else if (!strcmp(media_col_sup->values[i].string.text,
2052 "media-bottom-margin"))
2053 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2054 "media-bottom-margin", size->left);
2055 else if (!strcmp(media_col_sup->values[i].string.text,
2056 "media-right-margin"))
2057 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2058 "media-right-margin", size->left);
2059 else if (!strcmp(media_col_sup->values[i].string.text,
2060 "media-top-margin"))
2061 ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
2062 "media-top-margin", size->left);
2063 else if (!strcmp(media_col_sup->values[i].string.text,
2064 "media-source") && media_source)
2065 ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD,
2066 "media-source", NULL, media_source);
2067 else if (!strcmp(media_col_sup->values[i].string.text,
2068 "media-type") && media_type)
2069 ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD,
2070 "media-type", NULL, media_type);
2071 }
2072
2073 ippAddCollection(request, IPP_TAG_JOB, "media-col", media_col);
2074 }
2075
2076 if ((keyword = cupsGetOption("output-bin", num_options,
2077 options)) == NULL)
8211508e 2078 keyword = _ppdCacheGetBin(pc, cupsGetOption("OutputBin", num_options,
2079 options));
2ec940b5 2080
2081 if (keyword)
2082 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-bin",
2083 NULL, keyword);
2084
2085 if ((keyword = cupsGetOption("output-mode", num_options,
2086 options)) != NULL)
2087 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2088 NULL, keyword);
2089 else if ((keyword = cupsGetOption("ColorModel", num_options,
2090 options)) != NULL)
2091 {
2092 if (!strcasecmp(keyword, "Gray"))
2093 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2094 NULL, "monochrome");
2095 else
2096 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode",
2097 NULL, "color");
2098 }
2099
2100 if ((keyword = cupsGetOption("print-quality", num_options,
2101 options)) != NULL)
2102 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2103 atoi(keyword));
2104 else if ((keyword = cupsGetOption("cupsPrintQuality", num_options,
2105 options)) != NULL)
2106 {
2107 if (!strcasecmp(keyword, "draft"))
2108 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2109 IPP_QUALITY_DRAFT);
2110 else if (!strcasecmp(keyword, "normal"))
2111 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2112 IPP_QUALITY_NORMAL);
2113 else if (!strcasecmp(keyword, "high"))
2114 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
2115 IPP_QUALITY_HIGH);
2116 }
2117
2118 if ((keyword = cupsGetOption("sides", num_options, options)) != NULL)
2119 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2120 NULL, keyword);
8211508e 2121 else if (pc->sides_option &&
2122 (keyword = cupsGetOption(pc->sides_option, num_options,
2ec940b5 2123 options)) != NULL)
2124 {
8211508e 2125 if (!strcasecmp(keyword, pc->sides_1sided))
2ec940b5 2126 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2127 NULL, "one-sided");
8211508e 2128 else if (!strcasecmp(keyword, pc->sides_2sided_long))
2ec940b5 2129 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2130 NULL, "two-sided-long-edge");
8211508e 2131 if (!strcasecmp(keyword, pc->sides_2sided_short))
2ec940b5 2132 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
2133 NULL, "two-sided-short-edge");
2134 }
adbcb02a 2135
2136 if (doc_handling_sup &&
2137 (keyword = cupsGetOption("collate", num_options, options)) != NULL)
2138 {
2139 if (!strcasecmp(keyword, "true"))
2140 collate_str = "separate-documents-collated-copies";
2141 else
2142 collate_str = "separate-documents-uncollated-copies";
2143
2144 for (i = 0; i < doc_handling_sup->num_values; i ++)
2145 if (!strcmp(doc_handling_sup->values[i].string.text, collate_str))
2146 {
2147 ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2148 "multiple-document-handling", NULL, collate_str);
2149 break;
2150 }
2151 }
2ec940b5 2152 }
2153 else
2154 {
2155 /*
2156 * When talking to another CUPS server, send all options...
2157 */
2158
2159 cupsEncodeOptions(request, num_options, options);
2160 }
2161
2162 if (copies > 1)
2163 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies", copies);
2164 }
2165
2166 return (request);
2167}
2168
2169
b5cb0608 2170/*
2171 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
2172 */
2173
072c4872 2174static const char * /* O - Password */
e12df069 2175password_cb(const char *prompt) /* I - Prompt (not used) */
b5cb0608 2176{
2177 (void)prompt;
2178
4233257e 2179 /*
2180 * Remember that we need to authenticate...
2181 */
2182
2183 auth_info_required = "username,password";
2184
acbc1533 2185 if (password && *password && password_tries < 3)
9c85dfbf 2186 {
2187 password_tries ++;
2188
6248387b 2189 return (password);
9c85dfbf 2190 }
6248387b 2191 else
2192 {
2193 /*
4233257e 2194 * Give up after 3 tries or if we don't have a password to begin with...
6248387b 2195 */
2196
4233257e 2197 return (NULL);
6248387b 2198 }
c8f9565c 2199}
2200
2201
7c7997c3 2202/*
2203 * 'report_attr()' - Report an IPP attribute value.
2204 */
2205
2206static void
2207report_attr(ipp_attribute_t *attr) /* I - Attribute */
2208{
2209 int i; /* Looping var */
2210 char value[1024], /* Value string */
2211 *valptr, /* Pointer into value string */
2212 *attrptr; /* Pointer into attribute value */
2213
2214
2215 /*
2216 * Convert the attribute values into quoted strings...
2217 */
2218
2219 for (i = 0, valptr = value;
2220 i < attr->num_values && valptr < (value + sizeof(value) - 10);
2221 i ++)
2222 {
2223 if (i > 0)
2224 *valptr++ = ',';
2225
2226 switch (attr->value_tag)
2227 {
2228 case IPP_TAG_INTEGER :
2229 case IPP_TAG_ENUM :
2230 snprintf(valptr, sizeof(value) - (valptr - value), "%d",
2231 attr->values[i].integer);
2232 valptr += strlen(valptr);
2233 break;
2234
2235 case IPP_TAG_TEXT :
2236 case IPP_TAG_NAME :
2237 case IPP_TAG_KEYWORD :
2238 *valptr++ = '\"';
2239 for (attrptr = attr->values[i].string.text;
2240 *attrptr && valptr < (value + sizeof(value) - 10);
2241 attrptr ++)
2242 {
2243 if (*attrptr == '\\' || *attrptr == '\"')
2244 *valptr++ = '\\';
2245
2246 *valptr++ = *attrptr;
2247 }
2248 *valptr++ = '\"';
2249 break;
2250
2251 default :
2252 /*
2253 * Unsupported value type...
2254 */
2255
2256 return;
2257 }
2258 }
2259
2260 *valptr = '\0';
2261
2262 /*
2263 * Tell the scheduler about the new values...
2264 */
2265
2266 fprintf(stderr, "ATTR: %s=%s\n", attr->name, value);
2267}
2268
2269
c8f9565c 2270/*
d1c2727f 2271 * 'report_printer_state()' - Report the printer state.
2272 */
2273
072c4872 2274static int /* O - Number of reasons shown */
41368129 2275report_printer_state(ipp_t *ipp, /* I - IPP response */
2276 int job_id) /* I - Current job ID */
d1c2727f 2277{
2278 int i; /* Looping var */
d8076497 2279 int count; /* Count of reasons shown... */
b9738d7c 2280 ipp_attribute_t *pa, /* printer-alert */
2281 *pam, /* printer-alert-message */
2282 *psm, /* printer-state-message */
7c7997c3 2283 *reasons, /* printer-state-reasons */
2284 *marker; /* marker-* attributes */
8f4595eb 2285 const char *reason; /* Current reason */
8f4595eb 2286 const char *prefix; /* Prefix for STATE: line */
b9738d7c 2287 char value[1024], /* State/message string */
2288 *valptr; /* Pointer into string */
aa141f0d 2289 static int ipp_supplies = -1;
2290 /* Report supply levels? */
d1c2727f 2291
2292
b9738d7c 2293 /*
2294 * Report alerts and messages...
2295 */
2296
2297 if ((pa = ippFindAttribute(ipp, "printer-alert", IPP_TAG_TEXT)) != NULL)
2298 report_attr(pa);
2299
2300 if ((pam = ippFindAttribute(ipp, "printer-alert-message",
2301 IPP_TAG_TEXT)) != NULL)
2302 report_attr(pam);
2303
a6ccc6e8 2304 if ((psm = ippFindAttribute(ipp, "printer-state-message",
2305 IPP_TAG_TEXT)) != NULL)
b9738d7c 2306 {
2307 char *ptr; /* Pointer into message */
2308
2309
2310 strlcpy(value, "INFO: ", sizeof(value));
2311 for (ptr = psm->values[0].string.text, valptr = value + 6;
2312 *ptr && valptr < (value + sizeof(value) - 6);
2313 ptr ++)
2314 {
2315 if (*ptr < ' ' && *ptr > 0 && *ptr != '\t')
2316 {
2317 /*
2318 * Substitute "<XX>" for the control character; sprintf is safe because
2319 * we always leave 6 chars free at the end...
2320 */
2321
2322 sprintf(valptr, "<%02X>", *ptr);
2323 valptr += 4;
2324 }
2325 else
2326 *valptr++ = *ptr;
2327 }
2328
2329 *valptr++ = '\n';
57487c71 2330 *valptr = '\0';
b9738d7c 2331
2332 fputs(value, stderr);
2333 }
2334
2335 /*
2336 * Now report printer-state-reasons, filtering out some of the reasons we never
2337 * want to set...
2338 */
a6ccc6e8 2339
d1c2727f 2340 if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
2341 IPP_TAG_KEYWORD)) == NULL)
2342 return (0);
2343
b9738d7c 2344 value[0] = '\0';
f831f3d8 2345 prefix = "STATE: ";
8f4595eb 2346
b9738d7c 2347 for (i = 0, count = 0, valptr = value; i < reasons->num_values; i ++)
d1c2727f 2348 {
8f4595eb 2349 reason = reasons->values[i].string.text;
2350
f831f3d8 2351 if (strcmp(reason, "paused") &&
2352 strcmp(reason, "com.apple.print.recoverable-warning"))
41368129 2353 {
b9738d7c 2354 strlcpy(valptr, prefix, sizeof(value) - (valptr - value) - 1);
2355 valptr += strlen(valptr);
2356 strlcpy(valptr, reason, sizeof(value) - (valptr - value) - 1);
2357 valptr += strlen(valptr);
41368129 2358
2359 prefix = ",";
2360 }
d1c2727f 2361 }
2362
b9738d7c 2363 if (value[0])
2364 {
2365 *valptr++ = '\n';
57487c71 2366 *valptr = '\0';
b9738d7c 2367 fputs(value, stderr);
2368 }
8f4595eb 2369
7c7997c3 2370 /*
2371 * Relay the current marker-* attribute values...
2372 */
2373
aa141f0d 2374 if (ipp_supplies < 0)
2375 {
2376 ppd_file_t *ppd; /* PPD file */
2377 ppd_attr_t *ppdattr; /* Attribute in PPD file */
2378
2379 if ((ppd = ppdOpenFile(getenv("PPD"))) != NULL &&
2380 (ppdattr = ppdFindAttr(ppd, "cupsIPPSupplies", NULL)) != NULL &&
2381 ppdattr->value && strcasecmp(ppdattr->value, "true"))
2382 ipp_supplies = 0;
2383 else
2384 ipp_supplies = 1;
2385
2386 ppdClose(ppd);
2387 }
2388
2389 if (ipp_supplies > 0)
2390 {
2391 if ((marker = ippFindAttribute(ipp, "marker-colors", IPP_TAG_NAME)) != NULL)
2392 report_attr(marker);
2393 if ((marker = ippFindAttribute(ipp, "marker-high-levels",
2394 IPP_TAG_INTEGER)) != NULL)
2395 report_attr(marker);
2396 if ((marker = ippFindAttribute(ipp, "marker-levels",
2397 IPP_TAG_INTEGER)) != NULL)
2398 report_attr(marker);
2399 if ((marker = ippFindAttribute(ipp, "marker-low-levels",
2400 IPP_TAG_INTEGER)) != NULL)
2401 report_attr(marker);
2402 if ((marker = ippFindAttribute(ipp, "marker-message",
2403 IPP_TAG_TEXT)) != NULL)
2404 report_attr(marker);
2405 if ((marker = ippFindAttribute(ipp, "marker-names", IPP_TAG_NAME)) != NULL)
2406 report_attr(marker);
2407 if ((marker = ippFindAttribute(ipp, "marker-types",
2408 IPP_TAG_KEYWORD)) != NULL)
2409 report_attr(marker);
2410 }
7c7997c3 2411
d8076497 2412 return (count);
d1c2727f 2413}
2414
2415
d1c2727f 2416/*
8f4595eb 2417 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
2418 */
2419
2420static void
2421sigterm_handler(int sig) /* I - Signal */
2422{
2423 (void)sig; /* remove compiler warnings... */
2424
6875feac 2425 if (!job_canceled)
072c4872 2426 {
2427 /*
2428 * Flag that the job should be cancelled...
2429 */
2430
6875feac 2431 job_canceled = 1;
072c4872 2432 return;
2433 }
2434
940afb4b 2435 /*
2436 * The scheduler already tried to cancel us once, now just terminate
2437 * after removing our temp files!
2438 */
2439
2440 if (tmpfilename[0])
2441 unlink(tmpfilename);
2442
8f4595eb 2443 exit(1);
2444}
2445
2446
2447/*
c9d3f842 2448 * End of "$Id$".
c8f9565c 2449 */