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