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