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