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