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