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