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