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