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