]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/ipp.c
The lpstat program could display garbage date and time values for
[thirdparty/cups.git] / backend / ipp.c
CommitLineData
c8f9565c 1/*
c9d3f842 2 * "$Id$"
c8f9565c 3 *
4 * IPP backend for the Common UNIX Printing System (CUPS).
5 *
c9d3f842 6 * Copyright 1997-2005 by Easy Software Products, all rights reserved.
c8f9565c 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
8784b6a6 17 * 44141 Airport View Drive, Suite 204
c9d3f842 18 * Hollywood, Maryland 20636 USA
c8f9565c 19 *
9639c4de 20 * Voice: (301) 373-9600
c8f9565c 21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
dab1a4d8 24 * This file is subject to the Apple OS-Developed Software exception.
25 *
c8f9565c 26 * Contents:
27 *
d1c2727f 28 * main() - Send a file to the printer or server.
6bb5a528 29 * check_printer_state() - Check the printer state...
d1c2727f 30 * password_cb() - Disable the password prompt for
31 * cupsDoFileRequest().
32 * report_printer_state() - Report the printer state.
8f4595eb 33 * run_pictwps_filter() - Convert PICT files to PostScript when printing
34 * remotely.
35 * sigterm_handler() - Handle 'terminate' signals that stop the backend.
c8f9565c 36 */
37
38/*
39 * Include necessary headers.
40 */
41
42#include <stdio.h>
43#include <stdlib.h>
44#include <errno.h>
45#include <sys/types.h>
46#include <sys/stat.h>
2d417cb3 47#include <cups/http-private.h>
c8f9565c 48#include <cups/cups.h>
49#include <cups/language.h>
50#include <cups/string.h>
4ff40357 51#include <signal.h>
1c85e5c0 52#include <sys/wait.h>
c8f9565c 53
54
8f4595eb 55/*
56 * Globals...
57 */
58
6bb5a528 59static char *password = NULL; /* Password for device URI */
b8f3410f 60#ifdef __APPLE__
61static char pstmpname[1024] = ""; /* Temporary PostScript file name */
62#endif /* __APPLE__ */
6bb5a528 63static char tmpfilename[1024] = ""; /* Temporary spool file name */
8f4595eb 64
65
b5cb0608 66/*
67 * Local functions...
68 */
69
6bb5a528 70void check_printer_state(http_t *http, cups_lang_t *language,
71 const char *charset, const char *uri, /* I - Printer URI */
72 const char *resource, const char *user,
73 int version);
b5cb0608 74const char *password_cb(const char *);
d1c2727f 75int report_printer_state(ipp_t *ipp);
b5cb0608 76
a684a3b0 77#ifdef __APPLE__
b8f3410f 78int run_pictwps_filter(char **argv, const char *filename);
a684a3b0 79#endif /* __APPLE__ */
8f4595eb 80static void sigterm_handler(int sig);
a684a3b0 81
b5cb0608 82
c8f9565c 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
74ef1ffb 91int /* O - Exit status */
92main(int argc, /* I - Number of command-line arguments (6 or 7) */
93 char *argv[]) /* I - Command-line arguments */
c8f9565c 94{
74ef1ffb 95 int i; /* Looping var */
96 int num_options; /* Number of printer options */
97 cups_option_t *options; /* Printer options */
98 char method[255], /* Method in URI */
99 hostname[1024], /* Hostname */
100 username[255], /* Username info */
101 resource[1024], /* Resource info (printer name) */
102 *optptr, /* Pointer to URI options */
103 name[255], /* Name of option */
104 value[255], /* Value of option */
105 *ptr; /* Pointer into name or value */
106 char *filename; /* File to print */
107 int port; /* Port number (not used) */
108 char uri[HTTP_MAX_URI]; /* Updated URI without user/pass */
109 ipp_status_t ipp_status; /* Status of IPP request */
110 http_t *http; /* HTTP connection */
111 ipp_t *request, /* IPP request */
112 *response, /* IPP response */
113 *supported; /* get-printer-attributes response */
114 int waitjob, /* Wait for job complete? */
115 waitprinter; /* Wait for printer ready? */
116 ipp_attribute_t *job_id_attr; /* job-id attribute */
117 int job_id; /* job-id value */
118 ipp_attribute_t *job_sheets; /* job-media-sheets-completed attribute */
119 ipp_attribute_t *job_state; /* job-state attribute */
120 ipp_attribute_t *copies_sup; /* copies-supported attribute */
121 ipp_attribute_t *charset_sup; /* charset-supported attribute */
122 ipp_attribute_t *format_sup; /* document-format-supported attribute */
123 ipp_attribute_t *printer_state; /* printer-state attribute */
124 ipp_attribute_t *printer_accepting; /* printer-is-accepting-jobs attribute */
125 const char *charset; /* Character set to use */
126 cups_lang_t *language; /* Default language */
127 int copies; /* Number of copies remaining */
128 const char *content_type; /* CONTENT_TYPE environment variable */
4ff40357 129#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
74ef1ffb 130 struct sigaction action; /* Actions for POSIX signals */
4ff40357 131#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
74ef1ffb 132 int version; /* IPP version */
133 int reasons; /* Number of printer-state-reasons shown */
8f4595eb 134 static const char * const pattrs[] =
74ef1ffb 135 { /* Printer attributes we want */
8f4595eb 136 "copies-supported",
137 "charset-supported",
138 "document-format-supported",
139 "printer-is-accepting-jobs",
140 "printer-state",
141 "printer-state-reasons",
142 };
143 static const char * const jattrs[] =
74ef1ffb 144 { /* Job attributes we want */
8f4595eb 145 "job-media-sheets-completed",
146 "job-state"
147 };
c8f9565c 148
4b23f3b3 149
150 /*
151 * Make sure status messages are not buffered...
152 */
153
2922de55 154 setbuf(stderr, NULL);
c8f9565c 155
98904cd6 156 /*
8f4595eb 157 * Ignore SIGPIPE and catch SIGTERM signals...
98904cd6 158 */
159
160#ifdef HAVE_SIGSET
161 sigset(SIGPIPE, SIG_IGN);
8f4595eb 162 sigset(SIGTERM, sigterm_handler);
98904cd6 163#elif defined(HAVE_SIGACTION)
164 memset(&action, 0, sizeof(action));
165 action.sa_handler = SIG_IGN;
166 sigaction(SIGPIPE, &action, NULL);
8f4595eb 167
168 sigemptyset(&action.sa_mask);
169 sigaddset(&action.sa_mask, SIGTERM);
170 action.sa_handler = sigterm_handler;
171 sigaction(SIGTERM, &action, NULL);
98904cd6 172#else
173 signal(SIGPIPE, SIG_IGN);
8f4595eb 174 signal(SIGTERM, sigterm_handler);
98904cd6 175#endif /* HAVE_SIGSET */
176
4b23f3b3 177 /*
178 * Check command-line...
179 */
180
68edc300 181 if (argc == 1)
182 {
183914a3 183 char *s;
184
d4c438d4 185 if ((s = strrchr(argv[0], '/')) != NULL)
186 s ++;
187 else
188 s = argv[0];
189
7c587866 190 printf("network %s \"Unknown\" \"Internet Printing Protocol (%s)\"\n", s, s);
68edc300 191 return (0);
192 }
193 else if (argc < 6 || argc > 7)
c8f9565c 194 {
195 fprintf(stderr, "Usage: %s job-id user title copies options [file]\n",
196 argv[0]);
197 return (1);
198 }
199
a684a3b0 200 /*
201 * Get the content type...
202 */
203
204 if (argc > 6)
205 content_type = getenv("CONTENT_TYPE");
206 else
207 content_type = "application/vnd.cups-raw";
208
209 if (content_type == NULL)
210 content_type = "application/octet-stream";
211
7abb7137 212 /*
213 * Extract the hostname and printer name from the URI...
214 */
215
f2197824 216 if (getenv("DEVICE_URI") != NULL)
217 /* authentication information is only available in the env var */
7abb7137 218 httpSeparate(getenv("DEVICE_URI"), method, username, hostname, &port,
219 resource);
f2197824 220 else if (strchr(argv[0], ':') != NULL)
221 httpSeparate(argv[0], method, username, hostname, &port, resource);
7abb7137 222 else
223 {
224 fputs("ERROR: Missing device URI on command-line and no DEVICE_URI environment variable!\n",
225 stderr);
226 return (1);
227 }
228
229 if (!strcmp(method, "https"))
230 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
231
c8f9565c 232 /*
233 * If we have 7 arguments, print the file named on the command-line.
97fcaf92 234 * Otherwise, copy stdin to a temporary file and print the temporary
235 * file.
c8f9565c 236 */
237
238 if (argc == 6)
97fcaf92 239 {
240 /*
241 * Copy stdin to a temporary file...
242 */
243
1b5bf964 244 int fd; /* Temporary file */
97fcaf92 245 char buffer[8192]; /* Buffer for copying */
246 int bytes; /* Number of bytes read */
247
248
8f4595eb 249 if ((fd = cupsTempFd(tmpfilename, sizeof(tmpfilename))) < 0)
97fcaf92 250 {
251 perror("ERROR: unable to create temporary file");
252 return (1);
253 }
254
255 while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
1b5bf964 256 if (write(fd, buffer, bytes) < bytes)
97fcaf92 257 {
258 perror("ERROR: unable to write to temporary file");
1b5bf964 259 close(fd);
8f4595eb 260 unlink(tmpfilename);
97fcaf92 261 return (1);
262 }
263
1b5bf964 264 close(fd);
8f4595eb 265 filename = tmpfilename;
97fcaf92 266 }
267 else
8f4595eb 268 filename = argv[6];
97fcaf92 269
897922a9 270 /*
271 * See if there are any options...
272 */
273
a708af2a 274 version = 1;
897922a9 275 waitjob = 1;
276 waitprinter = 1;
277
278 if ((optptr = strchr(resource, '?')) != NULL)
279 {
280 /*
281 * Yup, terminate the device name string and move to the first
282 * character of the optptr...
283 */
284
285 *optptr++ = '\0';
286
287 /*
288 * Then parse the optptr...
289 */
290
291 while (*optptr)
292 {
293 /*
294 * Get the name...
295 */
296
297 for (ptr = name; *optptr && *optptr != '=';)
298 if (ptr < (name + sizeof(name) - 1))
299 *ptr++ = *optptr++;
300 *ptr = '\0';
301
302 if (*optptr == '=')
303 {
304 /*
305 * Get the value...
306 */
307
308 optptr ++;
309
74ef1ffb 310 for (ptr = value; *optptr && *optptr != '+' && *optptr != '&';)
897922a9 311 if (ptr < (value + sizeof(value) - 1))
312 *ptr++ = *optptr++;
313 *ptr = '\0';
314
315 if (*optptr == '+')
316 optptr ++;
317 }
318 else
319 value[0] = '\0';
320
321 /*
322 * Process the option...
323 */
324
325 if (!strcasecmp(name, "waitjob"))
326 {
327 /*
328 * Wait for job completion?
329 */
330
331 waitjob = !strcasecmp(value, "on") ||
332 !strcasecmp(value, "yes") ||
333 !strcasecmp(value, "true");
334 }
335 else if (!strcasecmp(name, "waitprinter"))
336 {
337 /*
338 * Wait for printer idle?
339 */
340
341 waitprinter = !strcasecmp(value, "on") ||
342 !strcasecmp(value, "yes") ||
343 !strcasecmp(value, "true");
344 }
345 else if (!strcasecmp(name, "encryption"))
346 {
347 /*
348 * Enable/disable encryption?
349 */
350
351 if (!strcasecmp(value, "always"))
352 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
353 else if (!strcasecmp(value, "required"))
354 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
355 else if (!strcasecmp(value, "never"))
356 cupsSetEncryption(HTTP_ENCRYPT_NEVER);
357 else if (!strcasecmp(value, "ifrequested"))
358 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
359 else
360 {
361 fprintf(stderr, "ERROR: Unknown encryption option value \"%s\"!\n",
362 value);
363 }
364 }
a708af2a 365 else if (!strcasecmp(name, "version"))
366 {
367 if (!strcmp(value, "1.0"))
368 version = 0;
369 else if (!strcmp(value, "1.1"))
370 version = 1;
371 else
372 {
373 fprintf(stderr, "ERROR: Unknown version option value \"%s\"!\n",
374 value);
375 }
376 }
897922a9 377 else
378 {
379 /*
380 * Unknown option...
381 */
382
383 fprintf(stderr, "ERROR: Unknown option \"%s\" with value \"%s\"!\n",
384 name, value);
385 }
386 }
387 }
388
c8f9565c 389 /*
b5cb0608 390 * Set the authentication info, if any...
c8f9565c 391 */
392
b5cb0608 393 cupsSetPasswordCB(password_cb);
394
395 if (username[0])
396 {
397 if ((password = strchr(username, ':')) != NULL)
398 *password++ = '\0';
399
400 cupsSetUser(username);
401 }
c8f9565c 402
403 /*
404 * Try connecting to the remote server...
405 */
406
97fcaf92 407 do
c8f9565c 408 {
a4e23897 409 fprintf(stderr, "INFO: Connecting to %s on port %d...\n", hostname, port);
c8f9565c 410
7914a6a1 411 if ((http = httpConnectEncrypt(hostname, port, cupsEncryption())) == NULL)
d21a7597 412 {
997cf8b0 413 if (getenv("CLASS") != NULL)
414 {
415 /*
416 * If the CLASS environment variable is set, the job was submitted
417 * to a class and not to a specific queue. In this case, we want
418 * to abort immediately so that the job can be requeued on the next
419 * available printer in the class.
420 */
421
897922a9 422 fprintf(stderr, "INFO: Unable to connect to %s, queuing on next printer in class...\n",
997cf8b0 423 hostname);
424
a684a3b0 425 if (argc == 6 || strcmp(filename, argv[6]))
997cf8b0 426 unlink(filename);
427
428 /*
429 * Sleep 5 seconds to keep the job from requeuing too rapidly...
430 */
431
432 sleep(5);
433
434 return (1);
435 }
436
4c2096b8 437 if (errno == ECONNREFUSED || errno == EHOSTDOWN ||
438 errno == EHOSTUNREACH)
97fcaf92 439 {
8cf2eef5 440 fprintf(stderr, "INFO: Network host \'%s\' is busy; will retry in 30 seconds...\n",
97fcaf92 441 hostname);
442 sleep(30);
443 }
7e3ba0af 444 else if (h_errno)
445 {
446 fprintf(stderr, "INFO: Unable to lookup host \'%s\' - %s\n",
447 hostname, hstrerror(h_errno));
448 sleep(30);
449 }
97fcaf92 450 else
451 {
452 perror("ERROR: Unable to connect to IPP host");
2cc18dd2 453 sleep(30);
97fcaf92 454 }
d21a7597 455 }
c8f9565c 456 }
97fcaf92 457 while (http == NULL);
c8f9565c 458
a4e23897 459 fprintf(stderr, "INFO: Connected to %s...\n", hostname);
460
c8f9565c 461 /*
462 * Build a URI for the printer and fill the standard IPP attributes for
8ce7c000 463 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
464 * might contain username:password information...
c8f9565c 465 */
466
3f9cb6c6 467 snprintf(uri, sizeof(uri), "%s://%s:%d%s", method, hostname, port, resource);
c8f9565c 468
469 /*
97fcaf92 470 * First validate the destination and see if the device supports multiple
471 * copies. We have to do this because some IPP servers (e.g. HP JetDirect)
472 * don't support the copies attribute...
c8f9565c 473 */
474
25512894 475 language = cupsLangDefault();
476 charset_sup = NULL;
477 copies_sup = NULL;
2922de55 478 format_sup = NULL;
2922de55 479 supported = NULL;
c8f9565c 480
97fcaf92 481 do
c8f9565c 482 {
483 /*
97fcaf92 484 * Build the IPP request...
c8f9565c 485 */
486
97fcaf92 487 request = ippNew();
0a3ac972 488 request->request.op.version[1] = version;
489 request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
490 request->request.op.request_id = 1;
c8f9565c 491
97fcaf92 492 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
67436e05 493 "attributes-charset", NULL, "utf-8");
97fcaf92 494
495 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
496 "attributes-natural-language", NULL,
67436e05 497 language != NULL ? language->language : "en");
c8f9565c 498
97fcaf92 499 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
500 NULL, uri);
c8f9565c 501
8f4595eb 502 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
503 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
504 NULL, pattrs);
505
97fcaf92 506 /*
507 * Do the request...
508 */
c8f9565c 509
a4e23897 510 fputs("DEBUG: Getting supported attributes...\n", stderr);
511
b5cb0608 512 if ((supported = cupsDoRequest(http, request, resource)) == NULL)
513 ipp_status = cupsLastError();
514 else
0a3ac972 515 ipp_status = supported->request.status.status_code;
b5cb0608 516
517 if (ipp_status > IPP_OK_CONFLICT)
c8f9565c 518 {
b5cb0608 519 if (ipp_status == IPP_PRINTER_BUSY ||
520 ipp_status == IPP_SERVICE_UNAVAILABLE)
c8f9565c 521 {
b5cb0608 522 fputs("INFO: Printer busy; will retry in 10 seconds...\n", stderr);
d1c2727f 523 report_printer_state(supported);
97fcaf92 524 sleep(10);
97fcaf92 525 }
b5cb0608 526 else if ((ipp_status == IPP_BAD_REQUEST ||
527 ipp_status == IPP_VERSION_NOT_SUPPORTED) && version == 1)
c8f9565c 528 {
b5cb0608 529 /*
530 * Switch to IPP/1.0...
531 */
97fcaf92 532
b5cb0608 533 fputs("INFO: Printer does not support IPP/1.1, trying IPP/1.0...\n", stderr);
534 version = 0;
a4e23897 535 httpReconnect(http);
c8f9565c 536 }
67474245 537 else if (ipp_status == IPP_NOT_FOUND)
538 {
539 fputs("ERROR: Destination printer does not exist!\n", stderr);
540
541 if (supported)
542 ippDelete(supported);
543
544 return (1);
545 }
97fcaf92 546 else
67474245 547 {
d1c2727f 548 fprintf(stderr, "ERROR: Unable to get printer status (%s)!\n",
b5cb0608 549 ippErrorString(ipp_status));
67474245 550 sleep(10);
551 }
d1c2727f 552
553 if (supported)
554 ippDelete(supported);
555
556 continue;
97fcaf92 557 }
b5cb0608 558 else if ((copies_sup = ippFindAttribute(supported, "copies-supported",
559 IPP_TAG_RANGE)) != NULL)
97fcaf92 560 {
b5cb0608 561 /*
562 * Has the "copies-supported" attribute - does it have an upper
563 * bound > 1?
564 */
97fcaf92 565
b5cb0608 566 if (copies_sup->values[0].range.upper <= 1)
567 copies_sup = NULL; /* No */
568 }
97fcaf92 569
b5cb0608 570 charset_sup = ippFindAttribute(supported, "charset-supported",
571 IPP_TAG_CHARSET);
572 format_sup = ippFindAttribute(supported, "document-format-supported",
573 IPP_TAG_MIMETYPE);
574
575 if (format_sup)
576 {
577 fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
578 format_sup->num_values);
579 for (i = 0; i < format_sup->num_values; i ++)
580 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
581 format_sup->values[i].string.text);
c8f9565c 582 }
d1c2727f 583
584 report_printer_state(supported);
c8f9565c 585 }
97fcaf92 586 while (ipp_status > IPP_OK_CONFLICT);
c8f9565c 587
997cf8b0 588 /*
589 * See if the printer is accepting jobs and is not stopped; if either
590 * condition is true and we are printing to a class, requeue the job...
591 */
592
593 if (getenv("CLASS") != NULL)
594 {
595 printer_state = ippFindAttribute(supported, "printer-state",
596 IPP_TAG_ENUM);
597 printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs",
598 IPP_TAG_BOOLEAN);
599
600 if (printer_state == NULL ||
897922a9 601 (printer_state->values[0].integer > IPP_PRINTER_PROCESSING && waitprinter) ||
997cf8b0 602 printer_accepting == NULL ||
603 !printer_accepting->values[0].boolean)
604 {
605 /*
606 * If the CLASS environment variable is set, the job was submitted
607 * to a class and not to a specific queue. In this case, we want
608 * to abort immediately so that the job can be requeued on the next
609 * available printer in the class.
610 */
611
612 fprintf(stderr, "INFO: Unable to queue job on %s, queuing on next printer in class...\n",
613 hostname);
614
615 ippDelete(supported);
616 httpClose(http);
617
a684a3b0 618 if (argc == 6 || strcmp(filename, argv[6]))
997cf8b0 619 unlink(filename);
620
621 /*
622 * Sleep 5 seconds to keep the job from requeuing too rapidly...
623 */
624
625 sleep(5);
626
627 return (1);
628 }
629 }
630
c8f9565c 631 /*
97fcaf92 632 * See if the printer supports multiple copies...
c8f9565c 633 */
634
d1c2727f 635 if (copies_sup || argc < 7)
97fcaf92 636 copies = 1;
0c5b0932 637 else
97fcaf92 638 copies = atoi(argv[4]);
0c5b0932 639
25512894 640 /*
641 * Figure out the character set to use...
642 */
643
644 charset = language ? cupsLangEncoding(language) : "us-ascii";
645
646 if (charset_sup)
647 {
648 /*
649 * See if IPP server supports the requested character set...
650 */
651
652 for (i = 0; i < charset_sup->num_values; i ++)
653 if (strcasecmp(charset, charset_sup->values[i].string.text) == 0)
654 break;
655
656 /*
657 * If not, choose us-ascii or utf-8...
658 */
659
660 if (i >= charset_sup->num_values)
661 {
662 /*
663 * See if us-ascii is supported...
664 */
665
666 for (i = 0; i < charset_sup->num_values; i ++)
667 if (strcasecmp("us-ascii", charset_sup->values[i].string.text) == 0)
668 break;
669
670 if (i < charset_sup->num_values)
671 charset = "us-ascii";
672 else
673 charset = "utf-8";
674 }
675 }
676
c8f9565c 677 /*
97fcaf92 678 * Then issue the print-job request...
c8f9565c 679 */
680
d1c2727f 681 reasons = 0;
682
97fcaf92 683 while (copies > 0)
c8f9565c 684 {
685 /*
97fcaf92 686 * Build the IPP request...
c8f9565c 687 */
688
97fcaf92 689 request = ippNew();
0a3ac972 690 request->request.op.version[1] = version;
691 request->request.op.operation_id = IPP_PRINT_JOB;
692 request->request.op.request_id = 1;
c8f9565c 693
97fcaf92 694 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
25512894 695 "attributes-charset", NULL, charset);
8ce7c000 696
97fcaf92 697 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
698 "attributes-natural-language", NULL,
67436e05 699 language != NULL ? language->language : "en");
c8f9565c 700
97fcaf92 701 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
702 NULL, uri);
c8f9565c 703
97fcaf92 704 fprintf(stderr, "DEBUG: printer-uri = \"%s\"\n", uri);
705
753453e4 706 if (argv[2][0])
897922a9 707 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
708 "requesting-user-name", NULL, argv[2]);
97fcaf92 709
710 fprintf(stderr, "DEBUG: requesting-user-name = \"%s\"\n", argv[2]);
711
753453e4 712 if (argv[3][0])
713 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
714 argv[3]);
c8f9565c 715
97fcaf92 716 fprintf(stderr, "DEBUG: job-name = \"%s\"\n", argv[3]);
8ce7c000 717
c8f9565c 718 /*
97fcaf92 719 * Handle options on the command-line...
c8f9565c 720 */
721
97fcaf92 722 options = NULL;
723 num_options = cupsParseOptions(argv[5], 0, &options);
724
e7186d00 725#ifdef __APPLE__
726 if (content_type != NULL && strcasecmp(content_type, "application/pictwps") == 0)
727 {
728 if (format_sup != NULL)
729 {
730 for (i = 0; i < format_sup->num_values; i ++)
731 if (strcasecmp(content_type, format_sup->values[i].string.text) == 0)
732 break;
733 }
734
735 if (format_sup == NULL || i >= format_sup->num_values)
736 {
737 /*
738 * Remote doesn't support "application/pictwps" (i.e. it's not MacOS X)
739 * so convert the document to PostScript...
740 */
741
b8f3410f 742 if (run_pictwps_filter(argv, filename))
e7186d00 743 return (1);
744
b8f3410f 745 filename = pstmpname;
746
e7186d00 747 /*
748 * Change the MIME type to application/postscript...
749 */
750
751 content_type = "application/postscript";
752 }
753 }
754#endif /* __APPLE__ */
755
753453e4 756 if (content_type != NULL && format_sup != NULL)
36aa99bb 757 {
758 for (i = 0; i < format_sup->num_values; i ++)
759 if (strcasecmp(content_type, format_sup->values[i].string.text) == 0)
760 break;
761
762 if (i < format_sup->num_values)
763 num_options = cupsAddOption("document-format", content_type,
764 num_options, &options);
765 }
97fcaf92 766
753453e4 767 if (copies_sup)
768 {
769 /*
770 * Only send options if the destination printer supports the copies
771 * attribute. This is a hack for the HP JetDirect implementation of
772 * IPP, which does not accept extension attributes and incorrectly
773 * reports a client-error-bad-request error instead of the
774 * successful-ok-unsupported-attributes status. In short, at least
775 * some HP implementations of IPP are non-compliant.
776 */
777
778 cupsEncodeOptions(request, num_options, options);
779 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies",
780 atoi(argv[4]));
781 }
782
183914a3 783 cupsFreeOptions(num_options, options);
c8f9565c 784
a4e23897 785 /*
786 * If copies aren't supported, then we are likely dealing with an HP
787 * JetDirect. The HP IPP implementation seems to close the connection
788 * after every request (that is, it does *not* implement HTTP Keep-
789 * Alive, which is REQUIRED by HTTP/1.1...
790 */
791
792 if (!copies_sup)
793 httpReconnect(http);
794
cb555bcf 795 /*
b5cb0608 796 * Do the request...
cb555bcf 797 */
798
b5cb0608 799 if ((response = cupsDoFileRequest(http, request, resource, filename)) == NULL)
800 ipp_status = cupsLastError();
801 else
0a3ac972 802 ipp_status = response->request.status.status_code;
b5cb0608 803
804 if (ipp_status > IPP_OK_CONFLICT)
805 {
753453e4 806 job_id = 0;
807
b5cb0608 808 if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
809 ipp_status == IPP_PRINTER_BUSY)
810 {
811 fputs("INFO: Printer is busy; retrying print job...\n", stderr);
812 sleep(10);
813 }
814 else
815 fprintf(stderr, "ERROR: Print file was not accepted (%s)!\n",
816 ippErrorString(ipp_status));
817 }
818 else if ((job_id_attr = ippFindAttribute(response, "job-id",
819 IPP_TAG_INTEGER)) == NULL)
97fcaf92 820 {
f9aed4f2 821 fputs("NOTICE: Print file accepted - job ID unknown.\n", stderr);
b5cb0608 822 job_id = 0;
823 }
824 else
825 {
826 job_id = job_id_attr->values[0].integer;
f9aed4f2 827 fprintf(stderr, "NOTICE: Print file accepted - job ID %d.\n", job_id);
97fcaf92 828 }
cb555bcf 829
b5cb0608 830 if (response)
831 ippDelete(response);
832
753453e4 833 if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
b5cb0608 834 {
835 fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
836 copies --;
837 }
838 else if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
839 ipp_status != IPP_PRINTER_BUSY)
840 break;
8ce7c000 841
c8f9565c 842 /*
b5cb0608 843 * Wait for the job to complete...
c8f9565c 844 */
845
897922a9 846 if (!job_id || !waitjob)
b5cb0608 847 continue;
848
849 fputs("INFO: Waiting for job to complete...\n", stderr);
850
97fcaf92 851 for (;;)
c8f9565c 852 {
97fcaf92 853 /*
b5cb0608 854 * Build an IPP_GET_JOB_ATTRIBUTES request...
97fcaf92 855 */
856
b5cb0608 857 request = ippNew();
0a3ac972 858 request->request.op.version[1] = version;
859 request->request.op.operation_id = IPP_GET_JOB_ATTRIBUTES;
860 request->request.op.request_id = 1;
97fcaf92 861
b5cb0608 862 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
863 "attributes-charset", NULL, charset);
3f9cb6c6 864
b5cb0608 865 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
866 "attributes-natural-language", NULL,
867 language != NULL ? language->language : "en");
97fcaf92 868
b5cb0608 869 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
870 NULL, uri);
3f9cb6c6 871
b5cb0608 872 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
873 job_id);
c8f9565c 874
ee8b7dd3 875 if (argv[2][0])
897922a9 876 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
877 "requesting-user-name", NULL, argv[2]);
ee8b7dd3 878
8f4595eb 879 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
880 "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
881 NULL, jattrs);
97fcaf92 882
883 /*
b5cb0608 884 * Do the request...
97fcaf92 885 */
886
a4e23897 887 if (!copies_sup)
888 httpReconnect(http);
889
b5cb0608 890 if ((response = cupsDoRequest(http, request, resource)) == NULL)
891 ipp_status = cupsLastError();
892 else
0a3ac972 893 ipp_status = response->request.status.status_code;
97fcaf92 894
b5cb0608 895 if (ipp_status == IPP_NOT_FOUND)
97fcaf92 896 {
b5cb0608 897 /*
d1c2727f 898 * Job has gone away and/or the server has no job history...
b5cb0608 899 */
97fcaf92 900
b5cb0608 901 ippDelete(response);
d1c2727f 902
903 ipp_status = IPP_OK;
b5cb0608 904 break;
97fcaf92 905 }
906
b5cb0608 907 if (ipp_status > IPP_OK_CONFLICT)
6a536282 908 {
b5cb0608 909 if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
910 ipp_status != IPP_PRINTER_BUSY)
97fcaf92 911 {
b5cb0608 912 if (response)
913 ippDelete(response);
914
915 fprintf(stderr, "ERROR: Unable to get job %d attributes (%s)!\n",
916 job_id, ippErrorString(ipp_status));
917 break;
97fcaf92 918 }
6a536282 919 }
8f4595eb 920
921 if (response != NULL)
97fcaf92 922 {
8f4595eb 923 if ((job_sheets = ippFindAttribute(response, "job-media-sheets-completed",
924 IPP_TAG_INTEGER)) != NULL)
925 fprintf(stderr, "PAGE: total %d\n", job_sheets->values[0].integer);
97fcaf92 926
8f4595eb 927 if ((job_state = ippFindAttribute(response, "job-state",
928 IPP_TAG_ENUM)) != NULL)
97fcaf92 929 {
8f4595eb 930 /*
931 * Stop polling if the job is finished or pending-held...
932 */
933
934 if (job_state->values[0].integer > IPP_JOB_PROCESSING ||
935 job_state->values[0].integer == IPP_JOB_HELD)
936 {
937 ippDelete(response);
938 break;
939 }
97fcaf92 940 }
97fcaf92 941 }
942
d1c2727f 943 if (response)
944 ippDelete(response);
945
b5cb0608 946 /*
6bb5a528 947 * Check the printer state and report it if necessary...
b5cb0608 948 */
c8f9565c 949
6bb5a528 950/* if (!copies_sup)
951 httpReconnect(http);*/
d1c2727f 952
6bb5a528 953 check_printer_state(http, language, charset, uri, resource, argv[2],
954 version);
d1c2727f 955
956 /*
957 * Wait 10 seconds before polling again...
958 */
97fcaf92 959
b5cb0608 960 sleep(10);
97fcaf92 961 }
c8f9565c 962 }
963
6bb5a528 964 /*
965 * Check the printer state and report it if necessary...
966 */
967
968/* if (!copies_sup)
969 httpReconnect(http);*/
970
971 check_printer_state(http, language, charset, uri, resource, argv[2], version);
972
c8f9565c 973 /*
974 * Free memory...
975 */
976
977 httpClose(http);
c8f9565c 978
2922de55 979 if (supported)
980 ippDelete(supported);
981
c8f9565c 982 /*
b8f3410f 983 * Remove the temporary file(s) if necessary...
c8f9565c 984 */
985
d8e1f369 986 if (tmpfilename[0])
8f4595eb 987 unlink(tmpfilename);
c8f9565c 988
b8f3410f 989#ifdef __APPLE__
990 if (pstmpname[0])
991 unlink(pstmpname);
992#endif /* __APPLE__ */
993
c8f9565c 994 /*
995 * Return the queue status...
996 */
997
b5cb0608 998 return (ipp_status > IPP_OK_CONFLICT);
999}
1000
1001
6bb5a528 1002/*
1003 * 'check_printer_state()' - Check the printer state...
1004 */
1005
1006void
1007check_printer_state(http_t *http, /* I - HTTP connection */
1008 cups_lang_t *language,
1009 /* I - Language */
1010 const char *charset,
1011 /* I - Charset */
1012 const char *uri, /* I - Printer URI */
1013 const char *resource,
1014 /* I - Resource path */
1015 const char *user, /* I - Username, if any */
1016 int version)/* I - IPP version */
1017{
1018 ipp_t *request, /* IPP request */
1019 *response; /* IPP response */
1020
1021
1022 /*
1023 * Check on the printer state...
1024 */
1025
1026 request = ippNew();
1027 request->request.op.version[1] = version;
1028 request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
1029 request->request.op.request_id = 1;
1030
1031 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
1032 "attributes-charset", NULL, charset);
1033
1034 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
1035 "attributes-natural-language", NULL,
1036 language != NULL ? language->language : "en");
1037
1038 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1039 NULL, uri);
1040
1041 if (user && user[0])
1042 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1043 "requesting-user-name", NULL, user);
1044
1045 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1046 "requested-attributes", NULL, "printer-state-reasons");
1047
1048 /*
1049 * Do the request...
1050 */
1051
1052 if ((response = cupsDoRequest(http, request, resource)) != NULL)
1053 {
1054 report_printer_state(response);
1055 ippDelete(response);
1056 }
1057}
1058
1059
b5cb0608 1060/*
1061 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
1062 */
1063
1064const char * /* O - Password */
1065password_cb(const char *prompt) /* I - Prompt (not used) */
1066{
1067 (void)prompt;
1068
1069 return (password);
c8f9565c 1070}
1071
1072
1073/*
d1c2727f 1074 * 'report_printer_state()' - Report the printer state.
1075 */
1076
d8076497 1077int /* O - Number of reasons shown */
d1c2727f 1078report_printer_state(ipp_t *ipp) /* I - IPP response */
1079{
1080 int i; /* Looping var */
d8076497 1081 int count; /* Count of reasons shown... */
d1c2727f 1082 ipp_attribute_t *reasons; /* printer-state-reasons */
8f4595eb 1083 const char *reason; /* Current reason */
d1c2727f 1084 const char *message; /* Message to show */
d8076497 1085 char unknown[1024]; /* Unknown message string */
8f4595eb 1086 const char *prefix; /* Prefix for STATE: line */
1087 char state[1024]; /* State string */
d1c2727f 1088
1089
1090 if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
1091 IPP_TAG_KEYWORD)) == NULL)
1092 return (0);
1093
8f4595eb 1094 state[0] = '\0';
1095 prefix = "STATE: ";
1096
d8076497 1097 for (i = 0, count = 0; i < reasons->num_values; i ++)
d1c2727f 1098 {
8f4595eb 1099 reason = reasons->values[i].string.text;
1100
1101 strlcat(state, prefix, sizeof(state));
1102 strlcat(state, reason, sizeof(state));
1103
1104 prefix = ",";
d1c2727f 1105 message = NULL;
1106
8f4595eb 1107 if (strncmp(reason, "media-needed", 12) == 0)
d1c2727f 1108 message = "Media tray needs to be filled.";
8f4595eb 1109 else if (strncmp(reason, "media-jam", 9) == 0)
d1c2727f 1110 message = "Media jam!";
8f4595eb 1111 else if (strncmp(reason, "moving-to-paused", 16) == 0 ||
1112 strncmp(reason, "paused", 6) == 0 ||
1113 strncmp(reason, "shutdown", 8) == 0)
d1c2727f 1114 message = "Printer off-line.";
8f4595eb 1115 else if (strncmp(reason, "toner-low", 9) == 0)
d1c2727f 1116 message = "Toner low.";
8f4595eb 1117 else if (strncmp(reason, "toner-empty", 11) == 0)
d1c2727f 1118 message = "Out of toner!";
8f4595eb 1119 else if (strncmp(reason, "cover-open", 10) == 0)
d1c2727f 1120 message = "Cover open.";
8f4595eb 1121 else if (strncmp(reason, "interlock-open", 14) == 0)
d1c2727f 1122 message = "Interlock open.";
8f4595eb 1123 else if (strncmp(reason, "door-open", 9) == 0)
d1c2727f 1124 message = "Door open.";
8f4595eb 1125 else if (strncmp(reason, "input-tray-missing", 18) == 0)
d1c2727f 1126 message = "Media tray missing!";
8f4595eb 1127 else if (strncmp(reason, "media-low", 9) == 0)
d1c2727f 1128 message = "Media tray almost empty.";
8f4595eb 1129 else if (strncmp(reason, "media-empty", 11) == 0)
d1c2727f 1130 message = "Media tray empty!";
8f4595eb 1131 else if (strncmp(reason, "output-tray-missing", 19) == 0)
d1c2727f 1132 message = "Output tray missing!";
8f4595eb 1133 else if (strncmp(reason, "output-area-almost-full", 23) == 0)
d1c2727f 1134 message = "Output bin almost full.";
8f4595eb 1135 else if (strncmp(reason, "output-area-full", 16) == 0)
d1c2727f 1136 message = "Output bin full!";
8f4595eb 1137 else if (strncmp(reason, "marker-supply-low", 17) == 0)
d1c2727f 1138 message = "Ink/toner almost empty.";
8f4595eb 1139 else if (strncmp(reason, "marker-supply-empty", 19) == 0)
d1c2727f 1140 message = "Ink/toner empty!";
8f4595eb 1141 else if (strncmp(reason, "marker-waste-almost-full", 24) == 0)
d1c2727f 1142 message = "Ink/toner waste bin almost full.";
8f4595eb 1143 else if (strncmp(reason, "marker-waste-full", 17) == 0)
d1c2727f 1144 message = "Ink/toner waste bin full!";
8f4595eb 1145 else if (strncmp(reason, "fuser-over-temp", 15) == 0)
d1c2727f 1146 message = "Fuser temperature high!";
8f4595eb 1147 else if (strncmp(reason, "fuser-under-temp", 16) == 0)
d1c2727f 1148 message = "Fuser temperature low!";
8f4595eb 1149 else if (strncmp(reason, "opc-near-eol", 12) == 0)
d1c2727f 1150 message = "OPC almost at end-of-life.";
8f4595eb 1151 else if (strncmp(reason, "opc-life-over", 13) == 0)
d1c2727f 1152 message = "OPC at end-of-life!";
8f4595eb 1153 else if (strncmp(reason, "developer-low", 13) == 0)
d1c2727f 1154 message = "Developer almost empty.";
8f4595eb 1155 else if (strncmp(reason, "developer-empty", 15) == 0)
d1c2727f 1156 message = "Developer empty!";
8f4595eb 1157 else if (strstr(reason, "error") != NULL)
d8076497 1158 {
1159 message = unknown;
1160
1161 snprintf(unknown, sizeof(unknown), "Unknown printer error (%s)!",
8f4595eb 1162 reason);
d8076497 1163 }
d1c2727f 1164
1165 if (message)
1166 {
d8076497 1167 count ++;
d1c2727f 1168 if (strstr(reasons->values[i].string.text, "error"))
1169 fprintf(stderr, "ERROR: %s\n", message);
1170 else if (strstr(reasons->values[i].string.text, "warning"))
1171 fprintf(stderr, "WARNING: %s\n", message);
1172 else
1173 fprintf(stderr, "INFO: %s\n", message);
1174 }
1175 }
1176
8f4595eb 1177 fprintf(stderr, "%s\n", state);
1178
d8076497 1179 return (count);
d1c2727f 1180}
1181
1182
a684a3b0 1183#ifdef __APPLE__
1184/*
1185 * 'run_pictwps_filter()' - Convert PICT files to PostScript when printing
1186 * remotely.
1187 *
1188 * This step is required because the PICT format is not documented and
1189 * subject to change, so developing a filter for other OS's is infeasible.
1190 * Also, fonts required by the PICT file need to be embedded on the
1191 * client side (which has the fonts), so we run the filter to get a
1192 * PostScript file for printing...
1193 */
1194
1195int /* O - Exit status of filter */
b8f3410f 1196run_pictwps_filter(char **argv, /* I - Command-line arguments */
1197 const char *filename) /* I - Filename */
a684a3b0 1198{
1199 struct stat fileinfo; /* Print file information */
1200 const char *ppdfile; /* PPD file for destination printer */
1201 int pid; /* Child process ID */
1202 int fd; /* Temporary file descriptor */
1203 int status; /* Exit status of filter */
1204 const char *printer; /* PRINTER env var */
1205 static char ppdenv[1024]; /* PPD environment variable */
1206
1207
1208 /*
1209 * First get the PPD file for the printer...
1210 */
1211
1212 printer = getenv("PRINTER");
1213 if (!printer)
1214 {
1215 fputs("ERROR: PRINTER environment variable not defined!\n", stderr);
1216 return (-1);
1217 }
1218
1219 if ((ppdfile = cupsGetPPD(printer)) == NULL)
1220 {
1221 fprintf(stderr, "ERROR: Unable to get PPD file for printer \"%s\" - %s.\n",
1222 printer, ippErrorString(cupsLastError()));
1c85e5c0 1223 /*return (-1);*/
1224 }
1225 else
1226 {
1227 snprintf(ppdenv, sizeof(ppdenv), "PPD=%s", ppdfile);
1228 putenv(ppdenv);
a684a3b0 1229 }
a684a3b0 1230
1231 /*
1232 * Then create a temporary file for printing...
1233 */
1234
b8f3410f 1235 if ((fd = cupsTempFd(pstmpname, sizeof(pstmpname))) < 0)
a684a3b0 1236 {
1237 fprintf(stderr, "ERROR: Unable to create temporary file - %s.\n",
1c85e5c0 1238 strerror(errno));
1239 if (ppdfile)
1240 unlink(ppdfile);
a684a3b0 1241 return (-1);
1242 }
1243
1244 /*
1245 * Get the owner of the spool file - it is owned by the user we want to run
1246 * as...
1247 */
1248
b8f3410f 1249 if (argv[6])
1250 stat(argv[6], &fileinfo);
1251 else
1252 {
1253 /*
1254 * Use the OSX defaults, as an up-stream filter created the PICT
1255 * file...
1256 */
1257
1258 fileinfo.st_uid = 1;
1259 fileinfo.st_gid = 80;
1260 }
a684a3b0 1261
1c85e5c0 1262 if (ppdfile)
1263 chown(ppdfile, fileinfo.st_uid, fileinfo.st_gid);
1264
1265 fchown(fd, fileinfo.st_uid, fileinfo.st_gid);
1266
a684a3b0 1267 /*
1268 * Finally, run the filter to convert the file...
1269 */
1270
1271 if ((pid = fork()) == 0)
1272 {
1273 /*
1274 * Child process for pictwpstops... Redirect output of pictwpstops to a
1275 * file...
1276 */
1277
1278 close(1);
1279 dup(fd);
1280 close(fd);
1281
1282 if (!getuid())
1283 {
1284 /*
1285 * Change to an unpriviledged user...
1286 */
1287
1288 setgid(fileinfo.st_gid);
1289 setuid(fileinfo.st_uid);
1290 }
1291
1292 execlp("pictwpstops", printer, argv[1], argv[2], argv[3], argv[4], argv[5],
b8f3410f 1293 filename, NULL);
a684a3b0 1294 perror("ERROR: Unable to exec pictwpstops");
1295 return (errno);
1296 }
1c85e5c0 1297
1298 close(fd);
1299
1300 if (pid < 0)
a684a3b0 1301 {
1302 /*
1303 * Error!
1304 */
1305
1306 perror("ERROR: Unable to fork pictwpstops");
a684a3b0 1307 unlink(filename);
1c85e5c0 1308 if (ppdfile)
1309 unlink(ppdfile);
a684a3b0 1310 return (-1);
1311 }
1312
1313 /*
1314 * Now wait for the filter to complete...
1315 */
1316
1317 if (wait(&status) < 0)
1318 {
1319 perror("ERROR: Unable to wait for pictwpstops");
1320 close(fd);
1321 unlink(filename);
1c85e5c0 1322 if (ppdfile)
1323 unlink(ppdfile);
a684a3b0 1324 return (-1);
1325 }
1326
1c85e5c0 1327 if (ppdfile)
1328 unlink(ppdfile);
1329
a684a3b0 1330 close(fd);
1331
1332 if (status)
1333 {
1334 if (status >= 256)
1335 fprintf(stderr, "ERROR: pictwpstops exited with status %d!\n",
1336 status / 256);
1337 else
1338 fprintf(stderr, "ERROR: pictwpstops exited on signal %d!\n",
1339 status);
1340
1341 unlink(filename);
1342 return (status);
1343 }
1344
1345 /*
1346 * Return with no errors..
1347 */
1348
1349 return (0);
1350}
1351#endif /* __APPLE__ */
1352
d1c2727f 1353
1354/*
8f4595eb 1355 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
1356 */
1357
1358static void
1359sigterm_handler(int sig) /* I - Signal */
1360{
1361 (void)sig; /* remove compiler warnings... */
1362
1363 /*
b8f3410f 1364 * Remove the temporary file(s) if necessary...
8f4595eb 1365 */
1366
1367 if (tmpfilename[0])
1368 unlink(tmpfilename);
1369
b8f3410f 1370#ifdef __APPLE__
1371 if (pstmpname[0])
1372 unlink(pstmpname);
1373#endif /* __APPLE__ */
1374
8f4595eb 1375 exit(1);
1376}
1377
1378
1379/*
c9d3f842 1380 * End of "$Id$".
c8f9565c 1381 */