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