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