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