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