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