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