]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/ipp.c
Fix another potential 100% CPU bug.
[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 *
22d59a30 6 * Copyright 2007-2009 by Apple Inc.
bb3ff448 7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
c8f9565c 8 *
9 * These coded instructions, statements, and computer programs are the
4e8d321f 10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
c8f9565c 12 * "LICENSE" which should have been included with this file. If this
4e8d321f 13 * file is missing or damaged, see the license at "http://www.cups.org/".
c8f9565c 14 *
dab1a4d8 15 * This file is subject to the Apple OS-Developed Software exception.
16 *
c8f9565c 17 * Contents:
18 *
d1c2727f 19 * main() - Send a file to the printer or server.
1230a8a0 20 * cancel_job() - Cancel a print job.
6bb5a528 21 * check_printer_state() - Check the printer state...
1230a8a0 22 * compress_files() - Compress print files...
d1c2727f 23 * password_cb() - Disable the password prompt for
24 * cupsDoFileRequest().
7c7997c3 25 * report_attr() - Report an IPP attribute value.
d1c2727f 26 * report_printer_state() - Report the printer state.
8f4595eb 27 * run_pictwps_filter() - Convert PICT files to PostScript when printing
28 * remotely.
29 * sigterm_handler() - Handle 'terminate' signals that stop the backend.
c8f9565c 30 */
31
32/*
33 * Include necessary headers.
34 */
35
1a59b1c1 36#include <cups/http-private.h>
eab5b04e 37#include "backend-private.h"
c8f9565c 38#include <sys/types.h>
39#include <sys/stat.h>
1c85e5c0 40#include <sys/wait.h>
c8f9565c 41
8f4595eb 42/*
43 * Globals...
44 */
45
6bb5a528 46static char *password = NULL; /* Password for device URI */
9c85dfbf 47static int password_tries = 0; /* Password tries */
b8f3410f 48#ifdef __APPLE__
49static char pstmpname[1024] = ""; /* Temporary PostScript file name */
50#endif /* __APPLE__ */
6bb5a528 51static char tmpfilename[1024] = ""; /* Temporary spool file name */
072c4872 52static int job_cancelled = 0; /* Job cancelled? */
8f4595eb 53
54
b5cb0608 55/*
56 * Local functions...
57 */
58
072c4872 59static void cancel_job(http_t *http, const char *uri, int id,
60 const char *resource, const char *user, int version);
61static void check_printer_state(http_t *http, const char *uri,
6bb5a528 62 const char *resource, const char *user,
41368129 63 int version, int job_id);
1230a8a0 64#ifdef HAVE_LIBZ
65static void compress_files(int num_files, char **files);
66#endif /* HAVE_LIBZ */
072c4872 67static const char *password_cb(const char *);
7c7997c3 68static void report_attr(ipp_attribute_t *attr);
41368129 69static int report_printer_state(ipp_t *ipp, int job_id);
b5cb0608 70
a684a3b0 71#ifdef __APPLE__
072c4872 72static int run_pictwps_filter(char **argv, const char *filename);
a684a3b0 73#endif /* __APPLE__ */
8f4595eb 74static void sigterm_handler(int sig);
a684a3b0 75
b5cb0608 76
c8f9565c 77/*
78 * 'main()' - Send a file to the printer or server.
79 *
80 * Usage:
81 *
82 * printer-uri job-id user title copies options [file]
83 */
84
74ef1ffb 85int /* O - Exit status */
072c4872 86main(int argc, /* I - Number of command-line args */
74ef1ffb 87 char *argv[]) /* I - Command-line arguments */
c8f9565c 88{
74ef1ffb 89 int i; /* Looping var */
cad2b75f 90 int send_options; /* Send job options? */
74ef1ffb 91 int num_options; /* Number of printer options */
92 cups_option_t *options; /* Printer options */
cda2b561 93 const char *device_uri; /* Device URI */
74ef1ffb 94 char method[255], /* Method in URI */
95 hostname[1024], /* Hostname */
96 username[255], /* Username info */
97 resource[1024], /* Resource info (printer name) */
a0c0bbe7 98 addrname[256], /* Address name */
74ef1ffb 99 *optptr, /* Pointer to URI options */
abd1f85f 100 *name, /* Name of option */
101 *value, /* Value of option */
102 sep; /* Separator character */
eab5b04e 103 int snmp_fd, /* SNMP socket */
104 start_count, /* Page count via SNMP at start */
6bffe4a6 105 page_count, /* Page count via SNMP */
106 have_supplies; /* Printer supports supply levels? */
072c4872 107 int num_files; /* Number of files to print */
108 char **files, /* Files to print */
109 *filename; /* Pointer to single filename */
74ef1ffb 110 int port; /* Port number (not used) */
111 char uri[HTTP_MAX_URI]; /* Updated URI without user/pass */
112 ipp_status_t ipp_status; /* Status of IPP request */
113 http_t *http; /* HTTP connection */
114 ipp_t *request, /* IPP request */
115 *response, /* IPP response */
116 *supported; /* get-printer-attributes response */
ff0295f0 117 time_t start_time; /* Time of first connect */
118 int recoverable; /* Recoverable error shown? */
119 int contimeout; /* Connection timeout */
120 int delay; /* Delay for retries... */
1230a8a0 121 int compression, /* Do compression of the job data? */
122 waitjob, /* Wait for job complete? */
74ef1ffb 123 waitprinter; /* Wait for printer ready? */
124 ipp_attribute_t *job_id_attr; /* job-id attribute */
125 int job_id; /* job-id value */
072c4872 126 ipp_attribute_t *job_sheets; /* job-media-sheets-completed */
127 ipp_attribute_t *job_state; /* job-state */
128 ipp_attribute_t *copies_sup; /* copies-supported */
129 ipp_attribute_t *format_sup; /* document-format-supported */
74ef1ffb 130 ipp_attribute_t *printer_state; /* printer-state attribute */
072c4872 131 ipp_attribute_t *printer_accepting; /* printer-is-accepting-jobs */
37de726f 132 int copies, /* Number of copies for job */
133 copies_remaining; /* Number of copies remaining */
b43a3371 134 const char *content_type, /* CONTENT_TYPE environment variable */
135 *final_content_type; /* FINAL_CONTENT_TYPE environment var */
4ff40357 136#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
74ef1ffb 137 struct sigaction action; /* Actions for POSIX signals */
4ff40357 138#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
74ef1ffb 139 int version; /* IPP version */
8f4595eb 140 static const char * const pattrs[] =
74ef1ffb 141 { /* Printer attributes we want */
c11fea22 142 "com.apple.print.recoverable-message",
8f4595eb 143 "copies-supported",
8f4595eb 144 "document-format-supported",
7c7997c3 145 "marker-colors",
146 "marker-levels",
147 "marker-message",
148 "marker-names",
149 "marker-types",
8f4595eb 150 "printer-is-accepting-jobs",
151 "printer-state",
a6ccc6e8 152 "printer-state-message",
8f4595eb 153 "printer-state-reasons",
154 };
155 static const char * const jattrs[] =
74ef1ffb 156 { /* Job attributes we want */
8f4595eb 157 "job-media-sheets-completed",
158 "job-state"
159 };
c8f9565c 160
4b23f3b3 161
162 /*
163 * Make sure status messages are not buffered...
164 */
165
2922de55 166 setbuf(stderr, NULL);
c8f9565c 167
98904cd6 168 /*
8f4595eb 169 * Ignore SIGPIPE and catch SIGTERM signals...
98904cd6 170 */
171
172#ifdef HAVE_SIGSET
173 sigset(SIGPIPE, SIG_IGN);
8f4595eb 174 sigset(SIGTERM, sigterm_handler);
98904cd6 175#elif defined(HAVE_SIGACTION)
176 memset(&action, 0, sizeof(action));
177 action.sa_handler = SIG_IGN;
178 sigaction(SIGPIPE, &action, NULL);
8f4595eb 179
180 sigemptyset(&action.sa_mask);
181 sigaddset(&action.sa_mask, SIGTERM);
182 action.sa_handler = sigterm_handler;
183 sigaction(SIGTERM, &action, NULL);
98904cd6 184#else
185 signal(SIGPIPE, SIG_IGN);
8f4595eb 186 signal(SIGTERM, sigterm_handler);
98904cd6 187#endif /* HAVE_SIGSET */
188
4b23f3b3 189 /*
190 * Check command-line...
191 */
192
68edc300 193 if (argc == 1)
194 {
183914a3 195 char *s;
196
d4c438d4 197 if ((s = strrchr(argv[0], '/')) != NULL)
198 s ++;
199 else
200 s = argv[0];
201
3037604c 202 printf("network %s \"Unknown\" \"%s (%s)\"\n",
203 s, _cupsLangString(cupsLangDefault(),
204 _("Internet Printing Protocol")), s);
6248387b 205 return (CUPS_BACKEND_OK);
68edc300 206 }
072c4872 207 else if (argc < 6)
c8f9565c 208 {
472af6f3 209 _cupsLangPrintf(stderr,
210 _("Usage: %s job-id user title copies options [file]\n"),
211 argv[0]);
6248387b 212 return (CUPS_BACKEND_STOP);
c8f9565c 213 }
214
a684a3b0 215 /*
072c4872 216 * Get the (final) content type...
a684a3b0 217 */
218
b43a3371 219 if ((content_type = getenv("CONTENT_TYPE")) == NULL)
220 content_type = "application/octet-stream";
a684a3b0 221
b43a3371 222 if ((final_content_type = getenv("FINAL_CONTENT_TYPE")) == NULL)
223 {
224 final_content_type = content_type;
225
226 if (!strncmp(final_content_type, "printer/", 8))
227 final_content_type = "application/vnd.cups-raw";
228 }
12d8a513 229
7abb7137 230 /*
231 * Extract the hostname and printer name from the URI...
232 */
233
cda2b561 234 if ((device_uri = cupsBackendDeviceURI(argv)) == NULL)
235 return (CUPS_BACKEND_FAILED);
236
237 if (httpSeparateURI(HTTP_URI_CODING_ALL, device_uri,
00a1fad8 238 method, sizeof(method), username, sizeof(username),
239 hostname, sizeof(hostname), &port,
240 resource, sizeof(resource)) < HTTP_URI_OK)
7abb7137 241 {
472af6f3 242 _cupsLangPuts(stderr,
243 _("ERROR: Missing device URI on command-line and no "
244 "DEVICE_URI environment variable!\n"));
6248387b 245 return (CUPS_BACKEND_STOP);
7abb7137 246 }
247
6df6deaf 248 if (!port)
249 port = IPP_PORT; /* Default to port 631 */
250
7abb7137 251 if (!strcmp(method, "https"))
252 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
692bbbae 253 else
254 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
7abb7137 255
897922a9 256 /*
257 * See if there are any options...
258 */
259
1230a8a0 260 compression = 0;
22d59a30 261 version = 11;
897922a9 262 waitjob = 1;
263 waitprinter = 1;
ff0295f0 264 contimeout = 7 * 24 * 60 * 60;
897922a9 265
266 if ((optptr = strchr(resource, '?')) != NULL)
267 {
268 /*
269 * Yup, terminate the device name string and move to the first
270 * character of the optptr...
271 */
272
273 *optptr++ = '\0';
274
275 /*
276 * Then parse the optptr...
277 */
278
279 while (*optptr)
280 {
281 /*
282 * Get the name...
283 */
284
abd1f85f 285 name = optptr;
897922a9 286
abd1f85f 287 while (*optptr && *optptr != '=' && *optptr != '+' && *optptr != '&')
288 optptr ++;
289
290 if ((sep = *optptr) != '\0')
291 *optptr++ = '\0';
292
293 if (sep == '=')
897922a9 294 {
295 /*
296 * Get the value...
297 */
298
abd1f85f 299 value = optptr;
897922a9 300
abd1f85f 301 while (*optptr && *optptr != '+' && *optptr != '&')
897922a9 302 optptr ++;
abd1f85f 303
304 if (*optptr)
305 *optptr++ = '\0';
897922a9 306 }
307 else
abd1f85f 308 value = (char *)"";
897922a9 309
310 /*
311 * Process the option...
312 */
313
314 if (!strcasecmp(name, "waitjob"))
315 {
316 /*
317 * Wait for job completion?
318 */
319
320 waitjob = !strcasecmp(value, "on") ||
321 !strcasecmp(value, "yes") ||
322 !strcasecmp(value, "true");
323 }
324 else if (!strcasecmp(name, "waitprinter"))
325 {
326 /*
327 * Wait for printer idle?
328 */
329
330 waitprinter = !strcasecmp(value, "on") ||
331 !strcasecmp(value, "yes") ||
332 !strcasecmp(value, "true");
333 }
334 else if (!strcasecmp(name, "encryption"))
335 {
336 /*
337 * Enable/disable encryption?
338 */
339
340 if (!strcasecmp(value, "always"))
341 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
342 else if (!strcasecmp(value, "required"))
343 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
344 else if (!strcasecmp(value, "never"))
345 cupsSetEncryption(HTTP_ENCRYPT_NEVER);
346 else if (!strcasecmp(value, "ifrequested"))
347 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
348 else
349 {
472af6f3 350 _cupsLangPrintf(stderr,
34b18bd4 351 _("ERROR: Unknown encryption option value \"%s\"!\n"),
352 value);
897922a9 353 }
354 }
a708af2a 355 else if (!strcasecmp(name, "version"))
356 {
357 if (!strcmp(value, "1.0"))
22d59a30 358 version = 10;
a708af2a 359 else if (!strcmp(value, "1.1"))
22d59a30 360 version = 11;
361 else if (!strcmp(value, "2.0"))
362 version = 20;
363 else if (!strcmp(value, "2.1"))
364 version = 21;
a708af2a 365 else
366 {
472af6f3 367 _cupsLangPrintf(stderr,
34b18bd4 368 _("ERROR: Unknown version option value \"%s\"!\n"),
369 value);
a708af2a 370 }
371 }
1230a8a0 372#ifdef HAVE_LIBZ
373 else if (!strcasecmp(name, "compression"))
374 {
375 compression = !strcasecmp(value, "true") ||
376 !strcasecmp(value, "yes") ||
377 !strcasecmp(value, "on") ||
378 !strcasecmp(value, "gzip");
379 }
380#endif /* HAVE_LIBZ */
ff0295f0 381 else if (!strcasecmp(name, "contimeout"))
382 {
383 /*
384 * Set the connection timeout...
385 */
386
387 if (atoi(value) > 0)
388 contimeout = atoi(value);
389 }
897922a9 390 else
391 {
392 /*
393 * Unknown option...
394 */
395
472af6f3 396 _cupsLangPrintf(stderr,
397 _("ERROR: Unknown option \"%s\" with value \"%s\"!\n"),
398 name, value);
897922a9 399 }
400 }
401 }
402
1230a8a0 403 /*
404 * If we have 7 arguments, print the file named on the command-line.
405 * Otherwise, copy stdin to a temporary file and print the temporary
406 * file.
407 */
408
409 if (argc == 6)
410 {
411 /*
412 * Copy stdin to a temporary file...
413 */
414
415 int fd; /* File descriptor */
416 cups_file_t *fp; /* Temporary file */
417 char buffer[8192]; /* Buffer for copying */
418 int bytes; /* Number of bytes read */
80c55d99 419 off_t tbytes; /* Total bytes copied */
1230a8a0 420
421
422 if ((fd = cupsTempFd(tmpfilename, sizeof(tmpfilename))) < 0)
423 {
537a5f1d 424 _cupsLangPrintError(_("ERROR: Unable to create temporary file"));
1230a8a0 425 return (CUPS_BACKEND_FAILED);
426 }
427
428 if ((fp = cupsFileOpenFd(fd, compression ? "w9" : "w")) == NULL)
429 {
537a5f1d 430 _cupsLangPrintError(_("ERROR: Unable to open temporary file"));
1230a8a0 431 close(fd);
432 unlink(tmpfilename);
433 return (CUPS_BACKEND_FAILED);
434 }
435
80c55d99 436 tbytes = 0;
437
1230a8a0 438 while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
439 if (cupsFileWrite(fp, buffer, bytes) < bytes)
440 {
537a5f1d 441 _cupsLangPrintError(_("ERROR: Unable to write to temporary file"));
1230a8a0 442 cupsFileClose(fp);
443 unlink(tmpfilename);
444 return (CUPS_BACKEND_FAILED);
445 }
80c55d99 446 else
447 tbytes += bytes;
1230a8a0 448
449 cupsFileClose(fp);
450
80c55d99 451 /*
452 * Don't try printing files less than 2 bytes...
453 */
454
455 if (tbytes <= 1)
456 {
937922f8 457 _cupsLangPuts(stderr, _("ERROR: Empty print file!\n"));
80c55d99 458 unlink(tmpfilename);
459 return (CUPS_BACKEND_FAILED);
460 }
461
1230a8a0 462 /*
463 * Point to the single file from stdin...
464 */
465
b43a3371 466 filename = tmpfilename;
467 num_files = 1;
468 files = &filename;
cad2b75f 469 send_options = 0;
1230a8a0 470 }
471 else
472 {
473 /*
474 * Point to the files on the command-line...
475 */
476
b43a3371 477 num_files = argc - 6;
478 files = argv + 6;
479 send_options = 1;
cad2b75f 480
1230a8a0 481#ifdef HAVE_LIBZ
482 if (compression)
483 compress_files(num_files, files);
484#endif /* HAVE_LIBZ */
485 }
486
2054e655 487 fprintf(stderr, "DEBUG: %d files to send in job...\n", num_files);
1230a8a0 488
c8f9565c 489 /*
b5cb0608 490 * Set the authentication info, if any...
c8f9565c 491 */
492
b5cb0608 493 cupsSetPasswordCB(password_cb);
494
495 if (username[0])
496 {
48e211f3 497 /*
498 * Use authenticaion information in the device URI...
499 */
500
b5cb0608 501 if ((password = strchr(username, ':')) != NULL)
502 *password++ = '\0';
503
504 cupsSetUser(username);
505 }
48e211f3 506 else if (!getuid())
507 {
508 /*
fa1fc704 509 * Try loading authentication information from the environment.
48e211f3 510 */
511
abd1f85f 512 const char *ptr = getenv("AUTH_USERNAME");
513
514 if (ptr)
fa1fc704 515 cupsSetUser(ptr);
48e211f3 516
fa1fc704 517 password = getenv("AUTH_PASSWORD");
48e211f3 518 }
c8f9565c 519
520 /*
521 * Try connecting to the remote server...
522 */
523
ff0295f0 524 delay = 5;
525 recoverable = 0;
526 start_time = time(NULL);
527
22a980cc 528 fputs("STATE: +connecting-to-device\n", stderr);
529
97fcaf92 530 do
c8f9565c 531 {
22c808e2 532 fprintf(stderr, "DEBUG: Connecting to %s:%d\n",
533 hostname, port);
534 _cupsLangPuts(stderr, _("INFO: Connecting to printer...\n"));
c8f9565c 535
7914a6a1 536 if ((http = httpConnectEncrypt(hostname, port, cupsEncryption())) == NULL)
d21a7597 537 {
bb3ff448 538 if (job_cancelled)
539 break;
540
997cf8b0 541 if (getenv("CLASS") != NULL)
542 {
543 /*
544 * If the CLASS environment variable is set, the job was submitted
545 * to a class and not to a specific queue. In this case, we want
546 * to abort immediately so that the job can be requeued on the next
547 * available printer in the class.
548 */
549
472af6f3 550 _cupsLangPuts(stderr,
551 _("INFO: Unable to contact printer, queuing on next "
552 "printer in class...\n"));
997cf8b0 553
ba1e2736 554 if (tmpfilename[0])
555 unlink(tmpfilename);
997cf8b0 556
557 /*
558 * Sleep 5 seconds to keep the job from requeuing too rapidly...
559 */
560
561 sleep(5);
562
6248387b 563 return (CUPS_BACKEND_FAILED);
997cf8b0 564 }
565
4c2096b8 566 if (errno == ECONNREFUSED || errno == EHOSTDOWN ||
567 errno == EHOSTUNREACH)
97fcaf92 568 {
ff0295f0 569 if (contimeout && (time(NULL) - start_time) > contimeout)
570 {
472af6f3 571 _cupsLangPuts(stderr, _("ERROR: Printer not responding!\n"));
ff0295f0 572 return (CUPS_BACKEND_FAILED);
573 }
574
575 recoverable = 1;
576
472af6f3 577 _cupsLangPrintf(stderr,
578 _("WARNING: recoverable: Network host \'%s\' is busy; "
579 "will retry in %d seconds...\n"),
580 hostname, delay);
ff0295f0 581
582 sleep(delay);
583
584 if (delay < 30)
585 delay += 5;
97fcaf92 586 }
7e3ba0af 587 else if (h_errno)
588 {
472af6f3 589 _cupsLangPrintf(stderr, _("ERROR: Unable to locate printer \'%s\'!\n"),
590 hostname);
ff0295f0 591 return (CUPS_BACKEND_STOP);
7e3ba0af 592 }
97fcaf92 593 else
594 {
ff0295f0 595 recoverable = 1;
596
597 fprintf(stderr, "DEBUG: Connection error: %s\n", strerror(errno));
472af6f3 598 _cupsLangPuts(stderr,
599 _("ERROR: recoverable: Unable to connect to printer; will "
600 "retry in 30 seconds...\n"));
2cc18dd2 601 sleep(30);
97fcaf92 602 }
bb3ff448 603
604 if (job_cancelled)
605 break;
d21a7597 606 }
c8f9565c 607 }
97fcaf92 608 while (http == NULL);
c8f9565c 609
bb3ff448 610 if (job_cancelled)
611 {
ba1e2736 612 if (tmpfilename[0])
613 unlink(tmpfilename);
bb3ff448 614
615 return (CUPS_BACKEND_FAILED);
616 }
617
22a980cc 618 fputs("STATE: -connecting-to-device\n", stderr);
22c808e2 619 _cupsLangPuts(stderr, _("INFO: Connected to printer...\n"));
a4e23897 620
a0c0bbe7 621#ifdef AF_INET6
622 if (http->hostaddr->addr.sa_family == AF_INET6)
ff0295f0 623 fprintf(stderr, "DEBUG: Connected to [%s]:%d (IPv6)...\n",
624 httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
625 ntohs(http->hostaddr->ipv6.sin6_port));
a0c0bbe7 626 else
627#endif /* AF_INET6 */
628 if (http->hostaddr->addr.sa_family == AF_INET)
ff0295f0 629 fprintf(stderr, "DEBUG: Connected to %s:%d (IPv4)...\n",
630 httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
631 ntohs(http->hostaddr->ipv4.sin_port));
a0c0bbe7 632
eab5b04e 633 /*
634 * See if the printer supports SNMP...
635 */
636
b9e7ae13 637 if ((snmp_fd = _cupsSNMPOpen(http->hostaddr->addr.sa_family)) >= 0)
6bffe4a6 638 have_supplies = !backendSNMPSupplies(snmp_fd, http->hostaddr, &start_count,
639 NULL);
c97b7208 640 else
6bffe4a6 641 have_supplies = start_count = 0;
eab5b04e 642
c8f9565c 643 /*
644 * Build a URI for the printer and fill the standard IPP attributes for
8ce7c000 645 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
646 * might contain username:password information...
c8f9565c 647 */
648
3f9cb6c6 649 snprintf(uri, sizeof(uri), "%s://%s:%d%s", method, hostname, port, resource);
c8f9565c 650
651 /*
97fcaf92 652 * First validate the destination and see if the device supports multiple
653 * copies. We have to do this because some IPP servers (e.g. HP JetDirect)
654 * don't support the copies attribute...
c8f9565c 655 */
656
e12df069 657 copies_sup = NULL;
658 format_sup = NULL;
659 supported = NULL;
c8f9565c 660
97fcaf92 661 do
c8f9565c 662 {
56f7a531 663 /*
664 * Check for side-channel requests...
665 */
666
667 backendCheckSideChannel(snmp_fd, http->hostaddr);
668
c8f9565c 669 /*
97fcaf92 670 * Build the IPP request...
c8f9565c 671 */
672
e12df069 673 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
22d59a30 674 request->request.op.version[0] = version / 10;
675 request->request.op.version[1] = version % 10;
c8f9565c 676
97fcaf92 677 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
678 NULL, uri);
c8f9565c 679
8f4595eb 680 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
681 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
682 NULL, pattrs);
683
97fcaf92 684 /*
685 * Do the request...
686 */
c8f9565c 687
2054e655 688 fputs("DEBUG: Getting supported attributes...\n", stderr);
a4e23897 689
b5cb0608 690 if ((supported = cupsDoRequest(http, request, resource)) == NULL)
691 ipp_status = cupsLastError();
692 else
0a3ac972 693 ipp_status = supported->request.status.status_code;
b5cb0608 694
695 if (ipp_status > IPP_OK_CONFLICT)
c8f9565c 696 {
b5cb0608 697 if (ipp_status == IPP_PRINTER_BUSY ||
698 ipp_status == IPP_SERVICE_UNAVAILABLE)
c8f9565c 699 {
ff0295f0 700 if (contimeout && (time(NULL) - start_time) > contimeout)
701 {
472af6f3 702 _cupsLangPuts(stderr, _("ERROR: Printer not responding!\n"));
ff0295f0 703 return (CUPS_BACKEND_FAILED);
704 }
705
706 recoverable = 1;
707
472af6f3 708 _cupsLangPrintf(stderr,
709 _("WARNING: recoverable: Network host \'%s\' is busy; "
710 "will retry in %d seconds...\n"),
711 hostname, delay);
ff0295f0 712
41368129 713 report_printer_state(supported, 0);
ff0295f0 714
715 sleep(delay);
716
717 if (delay < 30)
718 delay += 5;
97fcaf92 719 }
b5cb0608 720 else if ((ipp_status == IPP_BAD_REQUEST ||
22d59a30 721 ipp_status == IPP_VERSION_NOT_SUPPORTED) && version > 10)
c8f9565c 722 {
b5cb0608 723 /*
724 * Switch to IPP/1.0...
725 */
97fcaf92 726
22d59a30 727 _cupsLangPrintf(stderr,
728 _("INFO: Printer does not support IPP/%d.%d, trying "
729 "IPP/1.0...\n"), version / 10, version % 10);
730 version = 10;
a4e23897 731 httpReconnect(http);
c8f9565c 732 }
67474245 733 else if (ipp_status == IPP_NOT_FOUND)
734 {
472af6f3 735 _cupsLangPuts(stderr, _("ERROR: Destination printer does not exist!\n"));
67474245 736
737 if (supported)
738 ippDelete(supported);
739
6248387b 740 return (CUPS_BACKEND_STOP);
67474245 741 }
97fcaf92 742 else
67474245 743 {
472af6f3 744 _cupsLangPrintf(stderr,
745 _("ERROR: Unable to get printer status (%s)!\n"),
746 cupsLastErrorString());
67474245 747 sleep(10);
748 }
d1c2727f 749
750 if (supported)
751 ippDelete(supported);
752
753 continue;
97fcaf92 754 }
b5cb0608 755 else if ((copies_sup = ippFindAttribute(supported, "copies-supported",
756 IPP_TAG_RANGE)) != NULL)
97fcaf92 757 {
b5cb0608 758 /*
759 * Has the "copies-supported" attribute - does it have an upper
760 * bound > 1?
761 */
97fcaf92 762
b5cb0608 763 if (copies_sup->values[0].range.upper <= 1)
764 copies_sup = NULL; /* No */
765 }
97fcaf92 766
e12df069 767 format_sup = ippFindAttribute(supported, "document-format-supported",
768 IPP_TAG_MIMETYPE);
b5cb0608 769
770 if (format_sup)
771 {
772 fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
773 format_sup->num_values);
774 for (i = 0; i < format_sup->num_values; i ++)
775 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
776 format_sup->values[i].string.text);
c8f9565c 777 }
d1c2727f 778
41368129 779 report_printer_state(supported, 0);
c8f9565c 780 }
97fcaf92 781 while (ipp_status > IPP_OK_CONFLICT);
c8f9565c 782
997cf8b0 783 /*
784 * See if the printer is accepting jobs and is not stopped; if either
785 * condition is true and we are printing to a class, requeue the job...
786 */
787
788 if (getenv("CLASS") != NULL)
789 {
790 printer_state = ippFindAttribute(supported, "printer-state",
791 IPP_TAG_ENUM);
792 printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs",
793 IPP_TAG_BOOLEAN);
794
795 if (printer_state == NULL ||
072c4872 796 (printer_state->values[0].integer > IPP_PRINTER_PROCESSING &&
797 waitprinter) ||
997cf8b0 798 printer_accepting == NULL ||
799 !printer_accepting->values[0].boolean)
800 {
801 /*
802 * If the CLASS environment variable is set, the job was submitted
803 * to a class and not to a specific queue. In this case, we want
804 * to abort immediately so that the job can be requeued on the next
805 * available printer in the class.
806 */
807
472af6f3 808 _cupsLangPuts(stderr,
809 _("INFO: Unable to contact printer, queuing on next "
810 "printer in class...\n"));
997cf8b0 811
812 ippDelete(supported);
813 httpClose(http);
814
ba1e2736 815 if (tmpfilename[0])
816 unlink(tmpfilename);
997cf8b0 817
818 /*
819 * Sleep 5 seconds to keep the job from requeuing too rapidly...
820 */
821
822 sleep(5);
823
6248387b 824 return (CUPS_BACKEND_FAILED);
997cf8b0 825 }
826 }
827
ff0295f0 828 if (recoverable)
829 {
830 /*
831 * If we've shown a recoverable error make sure the printer proxies
832 * have a chance to see the recovered message. Not pretty but
833 * necessary for now...
834 */
835
836 fputs("INFO: recovered: \n", stderr);
837 sleep(5);
838 }
839
c8f9565c 840 /*
97fcaf92 841 * See if the printer supports multiple copies...
c8f9565c 842 */
843
37de726f 844 copies = atoi(argv[4]);
845
d1c2727f 846 if (copies_sup || argc < 7)
7d52601e 847 {
37de726f 848 copies_remaining = 1;
7d52601e 849
850 if (argc < 7)
851 copies = 1;
852 }
0c5b0932 853 else
37de726f 854 copies_remaining = copies;
0c5b0932 855
c8f9565c 856 /*
97fcaf92 857 * Then issue the print-job request...
c8f9565c 858 */
859
072c4872 860 job_id = 0;
d1c2727f 861
37de726f 862 while (copies_remaining > 0)
c8f9565c 863 {
56f7a531 864 /*
865 * Check for side-channel requests...
866 */
867
868 backendCheckSideChannel(snmp_fd, http->hostaddr);
869
c8f9565c 870 /*
97fcaf92 871 * Build the IPP request...
c8f9565c 872 */
873
072c4872 874 if (job_cancelled)
875 break;
876
877 if (num_files > 1)
878 request = ippNewRequest(IPP_CREATE_JOB);
879 else
880 request = ippNewRequest(IPP_PRINT_JOB);
881
22d59a30 882 request->request.op.version[0] = version / 10;
883 request->request.op.version[1] = version % 10;
c8f9565c 884
97fcaf92 885 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
886 NULL, uri);
c8f9565c 887
97fcaf92 888 fprintf(stderr, "DEBUG: printer-uri = \"%s\"\n", uri);
889
753453e4 890 if (argv[2][0])
897922a9 891 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
892 "requesting-user-name", NULL, argv[2]);
97fcaf92 893
894 fprintf(stderr, "DEBUG: requesting-user-name = \"%s\"\n", argv[2]);
895
07bfc4e9 896 /*
897 * Only add a "job-name" attribute if the remote server supports
898 * copy generation - some IPP implementations like HP's don't seem
899 * to like UTF-8 job names (STR #1837)...
900 */
901
0a7645fc 902 if (argv[3][0] && copies_sup)
753453e4 903 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
904 argv[3]);
c8f9565c 905
97fcaf92 906 fprintf(stderr, "DEBUG: job-name = \"%s\"\n", argv[3]);
8ce7c000 907
1230a8a0 908#ifdef HAVE_LIBZ
909 if (compression)
910 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
911 "compression", NULL, "gzip");
912#endif /* HAVE_LIBZ */
913
c8f9565c 914 /*
97fcaf92 915 * Handle options on the command-line...
c8f9565c 916 */
917
97fcaf92 918 options = NULL;
919 num_options = cupsParseOptions(argv[5], 0, &options);
920
e7186d00 921#ifdef __APPLE__
7b072344 922 if (!strcasecmp(final_content_type, "application/pictwps") &&
923 num_files == 1)
e7186d00 924 {
925 if (format_sup != NULL)
926 {
927 for (i = 0; i < format_sup->num_values; i ++)
7b072344 928 if (!strcasecmp(final_content_type, format_sup->values[i].string.text))
e7186d00 929 break;
930 }
931
932 if (format_sup == NULL || i >= format_sup->num_values)
933 {
934 /*
935 * Remote doesn't support "application/pictwps" (i.e. it's not MacOS X)
936 * so convert the document to PostScript...
937 */
938
ba1e2736 939 if (run_pictwps_filter(argv, files[0]))
940 {
941 if (pstmpname[0])
942 unlink(pstmpname);
943
944 if (tmpfilename[0])
945 unlink(tmpfilename);
946
6248387b 947 return (CUPS_BACKEND_FAILED);
ba1e2736 948 }
e7186d00 949
ba1e2736 950 files[0] = pstmpname;
b8f3410f 951
e7186d00 952 /*
8e1e3983 953 * Change the MIME type to application/postscript and change the
954 * number of copies to 1...
e7186d00 955 */
956
b43a3371 957 final_content_type = "application/postscript";
958 copies = 1;
959 copies_remaining = 1;
960 send_options = 0;
e7186d00 961 }
962 }
963#endif /* __APPLE__ */
964
b43a3371 965 if (format_sup != NULL)
36aa99bb 966 {
967 for (i = 0; i < format_sup->num_values; i ++)
b43a3371 968 if (!strcasecmp(final_content_type, format_sup->values[i].string.text))
36aa99bb 969 break;
970
971 if (i < format_sup->num_values)
fd7f37f5 972 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
b43a3371 973 "document-format", NULL, final_content_type);
36aa99bb 974 }
97fcaf92 975
22d59a30 976 if (copies_sup && version > 10 && send_options)
753453e4 977 {
978 /*
979 * Only send options if the destination printer supports the copies
31cab86b 980 * attribute and IPP/1.1. This is a hack for the HP and Lexmark
981 * implementations of IPP, which do not accept extension attributes
982 * and incorrectly report a client-error-bad-request error instead of
983 * the successful-ok-unsupported-attributes status. In short, at least
984 * some HP and Lexmark implementations of IPP are non-compliant.
753453e4 985 */
986
987 cupsEncodeOptions(request, num_options, options);
37de726f 988
753453e4 989 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies",
37de726f 990 copies);
753453e4 991 }
992
183914a3 993 cupsFreeOptions(num_options, options);
c8f9565c 994
a4e23897 995 /*
996 * If copies aren't supported, then we are likely dealing with an HP
997 * JetDirect. The HP IPP implementation seems to close the connection
31cab86b 998 * after every request - that is, it does *not* implement HTTP Keep-
a4e23897 999 * Alive, which is REQUIRED by HTTP/1.1...
1000 */
1001
1002 if (!copies_sup)
1003 httpReconnect(http);
1004
cb555bcf 1005 /*
b5cb0608 1006 * Do the request...
cb555bcf 1007 */
1008
072c4872 1009 if (num_files > 1)
1010 response = cupsDoRequest(http, request, resource);
b5cb0608 1011 else
072c4872 1012 response = cupsDoFileRequest(http, request, resource, files[0]);
1013
1014 ipp_status = cupsLastError();
b5cb0608 1015
1016 if (ipp_status > IPP_OK_CONFLICT)
1017 {
753453e4 1018 job_id = 0;
1019
072c4872 1020 if (job_cancelled)
1021 break;
1022
b5cb0608 1023 if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1024 ipp_status == IPP_PRINTER_BUSY)
1025 {
472af6f3 1026 _cupsLangPuts(stderr,
1027 _("INFO: Printer busy; will retry in 10 seconds...\n"));
b5cb0608 1028 sleep(10);
1029 }
609d870f 1030 else if ((ipp_status == IPP_BAD_REQUEST ||
22d59a30 1031 ipp_status == IPP_VERSION_NOT_SUPPORTED) && version > 10)
609d870f 1032 {
1033 /*
1034 * Switch to IPP/1.0...
1035 */
1036
22d59a30 1037 _cupsLangPrintf(stderr,
1038 _("INFO: Printer does not support IPP/%d.%d, trying "
1039 "IPP/1.0...\n"), version / 10, version % 10);
1040 version = 10;
609d870f 1041 httpReconnect(http);
1042 }
b5cb0608 1043 else
472af6f3 1044 _cupsLangPrintf(stderr, _("ERROR: Print file was not accepted (%s)!\n"),
1045 cupsLastErrorString());
b5cb0608 1046 }
1047 else if ((job_id_attr = ippFindAttribute(response, "job-id",
1048 IPP_TAG_INTEGER)) == NULL)
97fcaf92 1049 {
472af6f3 1050 _cupsLangPuts(stderr,
1051 _("NOTICE: Print file accepted - job ID unknown.\n"));
b5cb0608 1052 job_id = 0;
1053 }
1054 else
1055 {
1056 job_id = job_id_attr->values[0].integer;
472af6f3 1057 _cupsLangPrintf(stderr, _("NOTICE: Print file accepted - job ID %d.\n"),
1058 job_id);
97fcaf92 1059 }
cb555bcf 1060
072c4872 1061 ippDelete(response);
1062
1063 if (job_cancelled)
1064 break;
1065
1066 if (job_id && num_files > 1)
1067 {
1068 for (i = 0; i < num_files; i ++)
1069 {
56f7a531 1070 /*
1071 * Check for side-channel requests...
1072 */
1073
1074 backendCheckSideChannel(snmp_fd, http->hostaddr);
1075
1076 /*
1077 * Send the next file in the job...
1078 */
1079
072c4872 1080 request = ippNewRequest(IPP_SEND_DOCUMENT);
22d59a30 1081 request->request.op.version[0] = version / 10;
1082 request->request.op.version[1] = version % 10;
072c4872 1083
1084 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1085 NULL, uri);
1086
1087 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1088 job_id);
1089
1090 if (argv[2][0])
1091 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1092 "requesting-user-name", NULL, argv[2]);
1093
1094 if ((i + 1) == num_files)
1095 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
1096
1097 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
1098 "document-format", NULL, content_type);
1099
1100 ippDelete(cupsDoFileRequest(http, request, resource, files[i]));
1101
1102 if (cupsLastError() > IPP_OK_CONFLICT)
1103 {
1104 ipp_status = cupsLastError();
1105
472af6f3 1106 _cupsLangPrintf(stderr,
1107 _("ERROR: Unable to add file %d to job: %s\n"),
1108 job_id, cupsLastErrorString());
072c4872 1109 break;
1110 }
1111 }
1112 }
b5cb0608 1113
753453e4 1114 if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
b5cb0608 1115 {
1116 fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
37de726f 1117 copies_remaining --;
b5cb0608 1118 }
ab827512 1119 else if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1120 ipp_status == IPP_PRINTER_BUSY)
a04d2365 1121 continue;
ab827512 1122 else
37de726f 1123 copies_remaining --;
8ce7c000 1124
c8f9565c 1125 /*
b5cb0608 1126 * Wait for the job to complete...
c8f9565c 1127 */
1128
897922a9 1129 if (!job_id || !waitjob)
b5cb0608 1130 continue;
1131
472af6f3 1132 _cupsLangPuts(stderr, _("INFO: Waiting for job to complete...\n"));
b5cb0608 1133
2ce8588b 1134 for (delay = 1; !job_cancelled;)
c8f9565c 1135 {
56f7a531 1136 /*
1137 * Check for side-channel requests...
1138 */
1139
1140 backendCheckSideChannel(snmp_fd, http->hostaddr);
1141
97fcaf92 1142 /*
b5cb0608 1143 * Build an IPP_GET_JOB_ATTRIBUTES request...
97fcaf92 1144 */
1145
e12df069 1146 request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
22d59a30 1147 request->request.op.version[0] = version / 10;
1148 request->request.op.version[1] = version % 10;
97fcaf92 1149
b5cb0608 1150 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1151 NULL, uri);
3f9cb6c6 1152
b5cb0608 1153 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1154 job_id);
c8f9565c 1155
ee8b7dd3 1156 if (argv[2][0])
897922a9 1157 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1158 "requesting-user-name", NULL, argv[2]);
ee8b7dd3 1159
8f4595eb 1160 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1161 "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
1162 NULL, jattrs);
97fcaf92 1163
1164 /*
b5cb0608 1165 * Do the request...
97fcaf92 1166 */
1167
a4e23897 1168 if (!copies_sup)
1169 httpReconnect(http);
1170
072c4872 1171 response = cupsDoRequest(http, request, resource);
1172 ipp_status = cupsLastError();
97fcaf92 1173
b5cb0608 1174 if (ipp_status == IPP_NOT_FOUND)
97fcaf92 1175 {
b5cb0608 1176 /*
d1c2727f 1177 * Job has gone away and/or the server has no job history...
b5cb0608 1178 */
97fcaf92 1179
b5cb0608 1180 ippDelete(response);
d1c2727f 1181
1182 ipp_status = IPP_OK;
b5cb0608 1183 break;
97fcaf92 1184 }
1185
b5cb0608 1186 if (ipp_status > IPP_OK_CONFLICT)
6a536282 1187 {
b5cb0608 1188 if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
1189 ipp_status != IPP_PRINTER_BUSY)
97fcaf92 1190 {
072c4872 1191 ippDelete(response);
b5cb0608 1192
472af6f3 1193 _cupsLangPrintf(stderr,
1194 _("ERROR: Unable to get job %d attributes (%s)!\n"),
1195 job_id, cupsLastErrorString());
b5cb0608 1196 break;
97fcaf92 1197 }
6a536282 1198 }
8f4595eb 1199
072c4872 1200 if (response)
97fcaf92 1201 {
8f4595eb 1202 if ((job_state = ippFindAttribute(response, "job-state",
1203 IPP_TAG_ENUM)) != NULL)
97fcaf92 1204 {
8f4595eb 1205 /*
1206 * Stop polling if the job is finished or pending-held...
1207 */
1208
7951315f 1209 if (job_state->values[0].integer > IPP_JOB_STOPPED)
8f4595eb 1210 {
e12df069 1211 if ((job_sheets = ippFindAttribute(response,
1212 "job-media-sheets-completed",
1213 IPP_TAG_INTEGER)) != NULL)
072c4872 1214 fprintf(stderr, "PAGE: total %d\n",
1215 job_sheets->values[0].integer);
ab827512 1216
8f4595eb 1217 ippDelete(response);
1218 break;
1219 }
97fcaf92 1220 }
97fcaf92 1221 }
1222
072c4872 1223 ippDelete(response);
d1c2727f 1224
b5cb0608 1225 /*
6bb5a528 1226 * Check the printer state and report it if necessary...
b5cb0608 1227 */
c8f9565c 1228
41368129 1229 check_printer_state(http, uri, resource, argv[2], version, job_id);
d1c2727f 1230
1231 /*
2ce8588b 1232 * Wait 1-10 seconds before polling again...
d1c2727f 1233 */
97fcaf92 1234
2ce8588b 1235 sleep(delay);
1236
1237 delay ++;
1238 if (delay > 10)
1239 delay = 1;
97fcaf92 1240 }
c8f9565c 1241 }
1242
6bb5a528 1243 /*
072c4872 1244 * Cancel the job as needed...
6bb5a528 1245 */
1246
072c4872 1247 if (job_cancelled && job_id)
1248 cancel_job(http, uri, job_id, resource, argv[2], version);
1249
1250 /*
1251 * Check the printer state and report it if necessary...
1252 */
6bb5a528 1253
41368129 1254 check_printer_state(http, uri, resource, argv[2], version, job_id);
6bb5a528 1255
eab5b04e 1256 /*
1257 * Collect the final page count as needed...
1258 */
1259
6bffe4a6 1260 if (have_supplies &&
eab5b04e 1261 !backendSNMPSupplies(snmp_fd, http->hostaddr, &page_count, NULL) &&
1262 page_count > start_count)
1263 fprintf(stderr, "PAGE: total %d\n", page_count - start_count);
1264
7fc9423b 1265 /*
1266 * Update auth-info-required as needed...
1267 */
1268
1269 if (ipp_status == IPP_NOT_AUTHORIZED)
1270 {
1271 if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
1272 "Negotiate", 9))
1273 fputs("ATTR: auth-info-required=negotiate\n", stderr);
1274 else
1275 fputs("ATTR: auth-info-required=username,password\n", stderr);
1276 }
1277
c8f9565c 1278 /*
1279 * Free memory...
1280 */
1281
1282 httpClose(http);
c8f9565c 1283
072c4872 1284 ippDelete(supported);
2922de55 1285
c8f9565c 1286 /*
b8f3410f 1287 * Remove the temporary file(s) if necessary...
c8f9565c 1288 */
1289
d8e1f369 1290 if (tmpfilename[0])
8f4595eb 1291 unlink(tmpfilename);
c8f9565c 1292
1230a8a0 1293#ifdef HAVE_LIBZ
1294 if (compression)
1295 {
1296 for (i = 0; i < num_files; i ++)
1297 unlink(files[i]);
1298 }
1299#endif /* HAVE_LIBZ */
1300
b8f3410f 1301#ifdef __APPLE__
1302 if (pstmpname[0])
1303 unlink(pstmpname);
1304#endif /* __APPLE__ */
1305
c8f9565c 1306 /*
1307 * Return the queue status...
1308 */
1309
3fe655a3 1310 if (ipp_status == IPP_NOT_AUTHORIZED)
1311 return (CUPS_BACKEND_AUTH_REQUIRED);
1312 else if (ipp_status > IPP_OK_CONFLICT)
1313 return (CUPS_BACKEND_FAILED);
1314 else
1315 return (CUPS_BACKEND_OK);
b5cb0608 1316}
1317
1318
072c4872 1319/*
1320 * 'cancel_job()' - Cancel a print job.
1321 */
1322
1323static void
1324cancel_job(http_t *http, /* I - HTTP connection */
1325 const char *uri, /* I - printer-uri */
1326 int id, /* I - job-id */
1327 const char *resource, /* I - Resource path */
1328 const char *user, /* I - requesting-user-name */
1329 int version) /* I - IPP version */
1330{
1331 ipp_t *request; /* Cancel-Job request */
1332
1333
472af6f3 1334 _cupsLangPuts(stderr, _("INFO: Canceling print job...\n"));
072c4872 1335
1336 request = ippNewRequest(IPP_CANCEL_JOB);
22d59a30 1337 request->request.op.version[0] = version / 10;
1338 request->request.op.version[1] = version % 10;
072c4872 1339
1340 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1341 NULL, uri);
1342 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
1343
1344 if (user && user[0])
1345 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1346 "requesting-user-name", NULL, user);
1347
1348 /*
1349 * Do the request...
1350 */
1351
1352 ippDelete(cupsDoRequest(http, request, resource));
1353
1354 if (cupsLastError() > IPP_OK_CONFLICT)
472af6f3 1355 _cupsLangPrintf(stderr, _("ERROR: Unable to cancel job %d: %s\n"), id,
1356 cupsLastErrorString());
072c4872 1357}
1358
1359
6bb5a528 1360/*
1361 * 'check_printer_state()' - Check the printer state...
1362 */
1363
072c4872 1364static void
e12df069 1365check_printer_state(
1366 http_t *http, /* I - HTTP connection */
1367 const char *uri, /* I - Printer URI */
1368 const char *resource, /* I - Resource path */
1369 const char *user, /* I - Username, if any */
41368129 1370 int version, /* I - IPP version */
1371 int job_id) /* I - Current job ID */
6bb5a528 1372{
1373 ipp_t *request, /* IPP request */
1374 *response; /* IPP response */
a6ccc6e8 1375 static const char * const attrs[] = /* Attributes we want */
1376 {
c11fea22 1377 "com.apple.print.recoverable-message",
7c7997c3 1378 "marker-colors",
1379 "marker-levels",
1380 "marker-message",
1381 "marker-names",
1382 "marker-types",
a6ccc6e8 1383 "printer-state-message",
1384 "printer-state-reasons"
1385 };
6bb5a528 1386
1387
1388 /*
1389 * Check on the printer state...
1390 */
1391
e12df069 1392 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
22d59a30 1393 request->request.op.version[0] = version / 10;
1394 request->request.op.version[1] = version % 10;
6bb5a528 1395
1396 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1397 NULL, uri);
1398
1399 if (user && user[0])
1400 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1401 "requesting-user-name", NULL, user);
1402
a6ccc6e8 1403 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1404 "requested-attributes",
1405 (int)(sizeof(attrs) / sizeof(attrs[0])), NULL, attrs);
6bb5a528 1406
1407 /*
1408 * Do the request...
1409 */
1410
1411 if ((response = cupsDoRequest(http, request, resource)) != NULL)
1412 {
41368129 1413 report_printer_state(response, job_id);
6bb5a528 1414 ippDelete(response);
1415 }
1416}
1417
1418
1230a8a0 1419#ifdef HAVE_LIBZ
1420/*
1421 * 'compress_files()' - Compress print files...
1422 */
1423
1424static void
1425compress_files(int num_files, /* I - Number of files */
1426 char **files) /* I - Files */
1427{
1428 int i, /* Looping var */
1429 fd; /* Temporary file descriptor */
1430 ssize_t bytes; /* Bytes read/written */
1431 size_t total; /* Total bytes read */
1432 cups_file_t *in, /* Input file */
1433 *out; /* Output file */
1434 struct stat outinfo; /* Output file information */
1435 char filename[1024], /* Temporary filename */
1fb9f3e8 1436 buffer[32768]; /* Copy buffer */
1230a8a0 1437
1438
1439 fprintf(stderr, "DEBUG: Compressing %d job files...\n", num_files);
1440 for (i = 0; i < num_files; i ++)
1441 {
1442 if ((fd = cupsTempFd(filename, sizeof(filename))) < 0)
1443 {
472af6f3 1444 _cupsLangPrintf(stderr,
1445 _("ERROR: Unable to create temporary compressed print "
1446 "file: %s\n"), strerror(errno));
1230a8a0 1447 exit(CUPS_BACKEND_FAILED);
1448 }
1449
1450 if ((out = cupsFileOpenFd(fd, "w9")) == NULL)
1451 {
472af6f3 1452 _cupsLangPrintf(stderr,
1453 _("ERROR: Unable to open temporary compressed print "
1454 "file: %s\n"), strerror(errno));
1230a8a0 1455 exit(CUPS_BACKEND_FAILED);
1456 }
1457
1458 if ((in = cupsFileOpen(files[i], "r")) == NULL)
1459 {
472af6f3 1460 _cupsLangPrintf(stderr,
1461 _("ERROR: Unable to open print file \"%s\": %s\n"),
1462 files[i], strerror(errno));
1230a8a0 1463 cupsFileClose(out);
1464 exit(CUPS_BACKEND_FAILED);
1465 }
1466
1467 total = 0;
1468 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
1469 if (cupsFileWrite(out, buffer, bytes) < bytes)
1470 {
472af6f3 1471 _cupsLangPrintf(stderr,
1472 _("ERROR: Unable to write %d bytes to \"%s\": %s\n"),
1473 (int)bytes, filename, strerror(errno));
1230a8a0 1474 cupsFileClose(in);
1475 cupsFileClose(out);
1476 exit(CUPS_BACKEND_FAILED);
1477 }
1478 else
1479 total += bytes;
1480
1481 cupsFileClose(out);
1482 cupsFileClose(in);
1483
1484 files[i] = strdup(filename);
1485
1486 if (!stat(filename, &outinfo))
ff0295f0 1487 fprintf(stderr,
1488 "DEBUG: File %d compressed to %.1f%% of original size, "
1489 CUPS_LLFMT " bytes...\n",
1490 i + 1, 100.0 * outinfo.st_size / total,
1491 CUPS_LLCAST outinfo.st_size);
1230a8a0 1492 }
1493}
1494#endif /* HAVE_LIBZ */
1495
1496
b5cb0608 1497/*
1498 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
1499 */
1500
072c4872 1501static const char * /* O - Password */
e12df069 1502password_cb(const char *prompt) /* I - Prompt (not used) */
b5cb0608 1503{
1504 (void)prompt;
1505
acbc1533 1506 if (password && *password && password_tries < 3)
9c85dfbf 1507 {
1508 password_tries ++;
1509
6248387b 1510 return (password);
9c85dfbf 1511 }
6248387b 1512 else
1513 {
1514 /*
1515 * If there is no password set in the device URI, return the
1516 * "authentication required" exit code...
1517 */
1518
1519 if (tmpfilename[0])
1520 unlink(tmpfilename);
1521
1522#ifdef __APPLE__
1523 if (pstmpname[0])
1524 unlink(pstmpname);
1525#endif /* __APPLE__ */
1526
0f4c64f6 1527 fputs("ATTR: auth-info-required=username,password\n", stderr);
1528
6248387b 1529 exit(CUPS_BACKEND_AUTH_REQUIRED);
be5262d8 1530
1531 return (NULL); /* Eliminate compiler warning */
6248387b 1532 }
c8f9565c 1533}
1534
1535
7c7997c3 1536/*
1537 * 'report_attr()' - Report an IPP attribute value.
1538 */
1539
1540static void
1541report_attr(ipp_attribute_t *attr) /* I - Attribute */
1542{
1543 int i; /* Looping var */
1544 char value[1024], /* Value string */
1545 *valptr, /* Pointer into value string */
1546 *attrptr; /* Pointer into attribute value */
1547
1548
1549 /*
1550 * Convert the attribute values into quoted strings...
1551 */
1552
1553 for (i = 0, valptr = value;
1554 i < attr->num_values && valptr < (value + sizeof(value) - 10);
1555 i ++)
1556 {
1557 if (i > 0)
1558 *valptr++ = ',';
1559
1560 switch (attr->value_tag)
1561 {
1562 case IPP_TAG_INTEGER :
1563 case IPP_TAG_ENUM :
1564 snprintf(valptr, sizeof(value) - (valptr - value), "%d",
1565 attr->values[i].integer);
1566 valptr += strlen(valptr);
1567 break;
1568
1569 case IPP_TAG_TEXT :
1570 case IPP_TAG_NAME :
1571 case IPP_TAG_KEYWORD :
1572 *valptr++ = '\"';
1573 for (attrptr = attr->values[i].string.text;
1574 *attrptr && valptr < (value + sizeof(value) - 10);
1575 attrptr ++)
1576 {
1577 if (*attrptr == '\\' || *attrptr == '\"')
1578 *valptr++ = '\\';
1579
1580 *valptr++ = *attrptr;
1581 }
1582 *valptr++ = '\"';
1583 break;
1584
1585 default :
1586 /*
1587 * Unsupported value type...
1588 */
1589
1590 return;
1591 }
1592 }
1593
1594 *valptr = '\0';
1595
1596 /*
1597 * Tell the scheduler about the new values...
1598 */
1599
1600 fprintf(stderr, "ATTR: %s=%s\n", attr->name, value);
1601}
1602
1603
c8f9565c 1604/*
d1c2727f 1605 * 'report_printer_state()' - Report the printer state.
1606 */
1607
072c4872 1608static int /* O - Number of reasons shown */
41368129 1609report_printer_state(ipp_t *ipp, /* I - IPP response */
1610 int job_id) /* I - Current job ID */
d1c2727f 1611{
1612 int i; /* Looping var */
d8076497 1613 int count; /* Count of reasons shown... */
c11fea22 1614 ipp_attribute_t *caprm, /* com.apple.print.recoverable-message */
1615 *psm, /* printer-state-message */
7c7997c3 1616 *reasons, /* printer-state-reasons */
1617 *marker; /* marker-* attributes */
8f4595eb 1618 const char *reason; /* Current reason */
d1c2727f 1619 const char *message; /* Message to show */
d8076497 1620 char unknown[1024]; /* Unknown message string */
8f4595eb 1621 const char *prefix; /* Prefix for STATE: line */
1622 char state[1024]; /* State string */
472af6f3 1623 cups_lang_t *language; /* Current localization */
c11fea22 1624 int saw_caprw; /* Saw com.apple.print.recoverable-warning state */
d1c2727f 1625
1626
a6ccc6e8 1627 if ((psm = ippFindAttribute(ipp, "printer-state-message",
1628 IPP_TAG_TEXT)) != NULL)
1629 fprintf(stderr, "INFO: %s\n", psm->values[0].string.text);
1630
d1c2727f 1631 if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
1632 IPP_TAG_KEYWORD)) == NULL)
1633 return (0);
1634
c11fea22 1635 saw_caprw = 0;
1636 state[0] = '\0';
1637 prefix = "STATE: ";
1638 language = cupsLangDefault();
8f4595eb 1639
d8076497 1640 for (i = 0, count = 0; i < reasons->num_values; i ++)
d1c2727f 1641 {
8f4595eb 1642 reason = reasons->values[i].string.text;
1643
6ca25eb7 1644 if (strcmp(reason, "paused"))
41368129 1645 {
1646 strlcat(state, prefix, sizeof(state));
1647 strlcat(state, reason, sizeof(state));
1648
1649 prefix = ",";
1650 }
8f4595eb 1651
d950cd16 1652 message = "";
d1c2727f 1653
5baa731f 1654 if (!strncmp(reason, "media-needed", 12))
34b18bd4 1655 message = _("Media tray needs to be filled.");
5baa731f 1656 else if (!strncmp(reason, "media-jam", 9))
34b18bd4 1657 message = _("Media jam!");
5baa731f 1658 else if (!strncmp(reason, "moving-to-paused", 16) ||
696b7be9 1659 !strncmp(reason, "offline", 7) ||
5baa731f 1660 !strncmp(reason, "paused", 6) ||
1661 !strncmp(reason, "shutdown", 8))
c7c5b066 1662 message = _("Printer offline.");
5baa731f 1663 else if (!strncmp(reason, "toner-low", 9))
34b18bd4 1664 message = _("Toner low.");
5baa731f 1665 else if (!strncmp(reason, "toner-empty", 11))
34b18bd4 1666 message = _("Out of toner!");
5baa731f 1667 else if (!strncmp(reason, "cover-open", 10))
34b18bd4 1668 message = _("Cover open.");
5baa731f 1669 else if (!strncmp(reason, "interlock-open", 14))
34b18bd4 1670 message = _("Interlock open.");
5baa731f 1671 else if (!strncmp(reason, "door-open", 9))
34b18bd4 1672 message = _("Door open.");
5baa731f 1673 else if (!strncmp(reason, "input-tray-missing", 18))
34b18bd4 1674 message = _("Media tray missing!");
5baa731f 1675 else if (!strncmp(reason, "media-low", 9))
34b18bd4 1676 message = _("Media tray almost empty.");
5baa731f 1677 else if (!strncmp(reason, "media-empty", 11))
34b18bd4 1678 message = _("Media tray empty!");
5baa731f 1679 else if (!strncmp(reason, "output-tray-missing", 19))
34b18bd4 1680 message = _("Output tray missing!");
5baa731f 1681 else if (!strncmp(reason, "output-area-almost-full", 23))
34b18bd4 1682 message = _("Output bin almost full.");
5baa731f 1683 else if (!strncmp(reason, "output-area-full", 16))
34b18bd4 1684 message = _("Output bin full!");
5baa731f 1685 else if (!strncmp(reason, "marker-supply-low", 17))
34b18bd4 1686 message = _("Ink/toner almost empty.");
5baa731f 1687 else if (!strncmp(reason, "marker-supply-empty", 19))
34b18bd4 1688 message = _("Ink/toner empty!");
5baa731f 1689 else if (!strncmp(reason, "marker-waste-almost-full", 24))
34b18bd4 1690 message = _("Ink/toner waste bin almost full.");
5baa731f 1691 else if (!strncmp(reason, "marker-waste-full", 17))
34b18bd4 1692 message = _("Ink/toner waste bin full!");
5baa731f 1693 else if (!strncmp(reason, "fuser-over-temp", 15))
34b18bd4 1694 message = _("Fuser temperature high!");
5baa731f 1695 else if (!strncmp(reason, "fuser-under-temp", 16))
34b18bd4 1696 message = _("Fuser temperature low!");
5baa731f 1697 else if (!strncmp(reason, "opc-near-eol", 12))
34b18bd4 1698 message = _("OPC almost at end-of-life.");
5baa731f 1699 else if (!strncmp(reason, "opc-life-over", 13))
34b18bd4 1700 message = _("OPC at end-of-life!");
5baa731f 1701 else if (!strncmp(reason, "developer-low", 13))
34b18bd4 1702 message = _("Developer almost empty.");
5baa731f 1703 else if (!strncmp(reason, "developer-empty", 15))
34b18bd4 1704 message = _("Developer empty!");
c11fea22 1705 else if (!strcmp(reason, "com.apple.print.recoverable-warning"))
1706 saw_caprw = 1;
8f4595eb 1707 else if (strstr(reason, "error") != NULL)
d8076497 1708 {
1709 message = unknown;
1710
34b18bd4 1711 snprintf(unknown, sizeof(unknown), _("Unknown printer error (%s)!"),
8f4595eb 1712 reason);
d8076497 1713 }
d1c2727f 1714
933466f0 1715 if (message[0])
d1c2727f 1716 {
d8076497 1717 count ++;
d1c2727f 1718 if (strstr(reasons->values[i].string.text, "error"))
472af6f3 1719 fprintf(stderr, "ERROR: %s\n", _cupsLangString(language, message));
d1c2727f 1720 else if (strstr(reasons->values[i].string.text, "warning"))
472af6f3 1721 fprintf(stderr, "WARNING: %s\n", _cupsLangString(language, message));
d1c2727f 1722 else
472af6f3 1723 fprintf(stderr, "INFO: %s\n", _cupsLangString(language, message));
d1c2727f 1724 }
1725 }
1726
8f4595eb 1727 fprintf(stderr, "%s\n", state);
1728
c11fea22 1729 /*
1730 * Relay com.apple.print.recoverable-message...
1731 */
1732
1733 if ((caprm = ippFindAttribute(ipp, "com.apple.print.recoverable-message",
1734 IPP_TAG_TEXT)) != NULL)
1735 fprintf(stderr, "WARNING: %s: %s\n",
1736 saw_caprw ? "recoverable" : "recovered",
1737 caprm->values[0].string.text);
1738
7c7997c3 1739 /*
1740 * Relay the current marker-* attribute values...
1741 */
1742
1743 if ((marker = ippFindAttribute(ipp, "marker-colors", IPP_TAG_NAME)) != NULL)
1744 report_attr(marker);
1745 if ((marker = ippFindAttribute(ipp, "marker-levels",
1746 IPP_TAG_INTEGER)) != NULL)
1747 report_attr(marker);
1748 if ((marker = ippFindAttribute(ipp, "marker-message", IPP_TAG_TEXT)) != NULL)
1749 report_attr(marker);
1750 if ((marker = ippFindAttribute(ipp, "marker-names", IPP_TAG_NAME)) != NULL)
1751 report_attr(marker);
1752 if ((marker = ippFindAttribute(ipp, "marker-types", IPP_TAG_KEYWORD)) != NULL)
1753 report_attr(marker);
1754
d8076497 1755 return (count);
d1c2727f 1756}
1757
1758
a684a3b0 1759#ifdef __APPLE__
1760/*
1761 * 'run_pictwps_filter()' - Convert PICT files to PostScript when printing
1762 * remotely.
1763 *
1764 * This step is required because the PICT format is not documented and
1765 * subject to change, so developing a filter for other OS's is infeasible.
1766 * Also, fonts required by the PICT file need to be embedded on the
1767 * client side (which has the fonts), so we run the filter to get a
1768 * PostScript file for printing...
1769 */
1770
072c4872 1771static int /* O - Exit status of filter */
e12df069 1772run_pictwps_filter(char **argv, /* I - Command-line arguments */
1773 const char *filename)/* I - Filename */
a684a3b0 1774{
e12df069 1775 struct stat fileinfo; /* Print file information */
1776 const char *ppdfile; /* PPD file for destination printer */
1777 int pid; /* Child process ID */
1778 int fd; /* Temporary file descriptor */
1779 int status; /* Exit status of filter */
1780 const char *printer; /* PRINTER env var */
1781 static char ppdenv[1024]; /* PPD environment variable */
a684a3b0 1782
1783
1784 /*
1785 * First get the PPD file for the printer...
1786 */
1787
1788 printer = getenv("PRINTER");
1789 if (!printer)
1790 {
472af6f3 1791 _cupsLangPuts(stderr,
1792 _("ERROR: PRINTER environment variable not defined!\n"));
a684a3b0 1793 return (-1);
1794 }
1795
1796 if ((ppdfile = cupsGetPPD(printer)) == NULL)
1797 {
472af6f3 1798 _cupsLangPrintf(stderr,
1799 _("ERROR: Unable to get PPD file for printer \"%s\" - "
1800 "%s.\n"), printer, cupsLastErrorString());
1c85e5c0 1801 }
1802 else
1803 {
1804 snprintf(ppdenv, sizeof(ppdenv), "PPD=%s", ppdfile);
1805 putenv(ppdenv);
a684a3b0 1806 }
a684a3b0 1807
1808 /*
1809 * Then create a temporary file for printing...
1810 */
1811
b8f3410f 1812 if ((fd = cupsTempFd(pstmpname, sizeof(pstmpname))) < 0)
a684a3b0 1813 {
c41dc3e3 1814 _cupsLangPrintError(_("ERROR: Unable to create temporary file"));
1c85e5c0 1815 if (ppdfile)
1816 unlink(ppdfile);
a684a3b0 1817 return (-1);
1818 }
1819
1820 /*
1821 * Get the owner of the spool file - it is owned by the user we want to run
1822 * as...
1823 */
1824
b8f3410f 1825 if (argv[6])
1826 stat(argv[6], &fileinfo);
1827 else
1828 {
1829 /*
1830 * Use the OSX defaults, as an up-stream filter created the PICT
1831 * file...
1832 */
1833
1834 fileinfo.st_uid = 1;
1835 fileinfo.st_gid = 80;
1836 }
a684a3b0 1837
1c85e5c0 1838 if (ppdfile)
1839 chown(ppdfile, fileinfo.st_uid, fileinfo.st_gid);
1840
1841 fchown(fd, fileinfo.st_uid, fileinfo.st_gid);
1842
a684a3b0 1843 /*
1844 * Finally, run the filter to convert the file...
1845 */
1846
1847 if ((pid = fork()) == 0)
1848 {
1849 /*
1850 * Child process for pictwpstops... Redirect output of pictwpstops to a
1851 * file...
1852 */
1853
1854 close(1);
1855 dup(fd);
1856 close(fd);
1857
1858 if (!getuid())
1859 {
1860 /*
1861 * Change to an unpriviledged user...
1862 */
1863
1864 setgid(fileinfo.st_gid);
1865 setuid(fileinfo.st_uid);
1866 }
1867
1868 execlp("pictwpstops", printer, argv[1], argv[2], argv[3], argv[4], argv[5],
b8f3410f 1869 filename, NULL);
472af6f3 1870 _cupsLangPrintf(stderr, _("ERROR: Unable to exec pictwpstops: %s\n"),
1871 strerror(errno));
a684a3b0 1872 return (errno);
1873 }
1c85e5c0 1874
1875 close(fd);
1876
1877 if (pid < 0)
a684a3b0 1878 {
1879 /*
1880 * Error!
1881 */
1882
472af6f3 1883 _cupsLangPrintf(stderr, _("ERROR: Unable to fork pictwpstops: %s\n"),
1884 strerror(errno));
1c85e5c0 1885 if (ppdfile)
1886 unlink(ppdfile);
a684a3b0 1887 return (-1);
1888 }
1889
1890 /*
1891 * Now wait for the filter to complete...
1892 */
1893
1894 if (wait(&status) < 0)
1895 {
472af6f3 1896 _cupsLangPrintf(stderr, _("ERROR: Unable to wait for pictwpstops: %s\n"),
1897 strerror(errno));
a684a3b0 1898 close(fd);
1c85e5c0 1899 if (ppdfile)
1900 unlink(ppdfile);
a684a3b0 1901 return (-1);
1902 }
1903
1c85e5c0 1904 if (ppdfile)
1905 unlink(ppdfile);
1906
a684a3b0 1907 close(fd);
1908
1909 if (status)
1910 {
1911 if (status >= 256)
472af6f3 1912 _cupsLangPrintf(stderr, _("ERROR: pictwpstops exited with status %d!\n"),
1913 status / 256);
a684a3b0 1914 else
472af6f3 1915 _cupsLangPrintf(stderr, _("ERROR: pictwpstops exited on signal %d!\n"),
1916 status);
a684a3b0 1917
a684a3b0 1918 return (status);
1919 }
1920
1921 /*
1922 * Return with no errors..
1923 */
1924
1925 return (0);
1926}
1927#endif /* __APPLE__ */
1928
d1c2727f 1929
1930/*
8f4595eb 1931 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
1932 */
1933
1934static void
1935sigterm_handler(int sig) /* I - Signal */
1936{
1937 (void)sig; /* remove compiler warnings... */
1938
072c4872 1939 if (!job_cancelled)
1940 {
1941 /*
1942 * Flag that the job should be cancelled...
1943 */
1944
1945 job_cancelled = 1;
1946 return;
1947 }
1948
8f4595eb 1949 /*
072c4872 1950 * The scheduler already tried to cancel us once, now just terminate
1951 * after removing our temp files!
8f4595eb 1952 */
1953
1954 if (tmpfilename[0])
1955 unlink(tmpfilename);
1956
b8f3410f 1957#ifdef __APPLE__
1958 if (pstmpname[0])
1959 unlink(pstmpname);
1960#endif /* __APPLE__ */
1961
8f4595eb 1962 exit(1);
1963}
1964
1965
1966/*
c9d3f842 1967 * End of "$Id$".
c8f9565c 1968 */