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