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