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