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