]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/ipp.c
Import CUPS 1.4svn r7023 into easysw/current.
[thirdparty/cups.git] / backend / ipp.c
CommitLineData
ef416fc2 1/*
2e4ff8af 2 * "$Id: ipp.c 7018 2007-10-10 22:14:03Z 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
2e4ff8af 1058 for (delay = 1; !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 /*
2e4ff8af 1149 * Wait 1-10 seconds before polling again...
ef416fc2 1150 */
1151
2e4ff8af
MS
1152 sleep(delay);
1153
1154 delay ++;
1155 if (delay > 10)
1156 delay = 1;
ef416fc2 1157 }
1158 }
1159
1160 /*
bd7854cb 1161 * Cancel the job as needed...
ef416fc2 1162 */
1163
bd7854cb 1164 if (job_cancelled && job_id)
1165 cancel_job(http, uri, job_id, resource, argv[2], version);
1166
1167 /*
1168 * Check the printer state and report it if necessary...
1169 */
ef416fc2 1170
db1f069b 1171 check_printer_state(http, uri, resource, argv[2], version, job_id);
ef416fc2 1172
1173 /*
1174 * Free memory...
1175 */
1176
1177 httpClose(http);
1178
bd7854cb 1179 ippDelete(supported);
ef416fc2 1180
1181 /*
1182 * Remove the temporary file(s) if necessary...
1183 */
1184
1185 if (tmpfilename[0])
1186 unlink(tmpfilename);
1187
b423cd4c 1188#ifdef HAVE_LIBZ
1189 if (compression)
1190 {
1191 for (i = 0; i < num_files; i ++)
1192 unlink(files[i]);
1193 }
1194#endif /* HAVE_LIBZ */
1195
ef416fc2 1196#ifdef __APPLE__
1197 if (pstmpname[0])
1198 unlink(pstmpname);
1199#endif /* __APPLE__ */
1200
1201 /*
1202 * Return the queue status...
1203 */
1204
7ff4fea9
MS
1205 if (ipp_status == IPP_NOT_AUTHORIZED)
1206 {
1207 /*
1208 * Authorization failures here mean that we need Kerberos. Username +
1209 * password authentication is handled in the password_cb function.
1210 */
1211
1212 fputs("ATTR: auth-info-required=negotiate\n", stderr);
1213 return (CUPS_BACKEND_AUTH_REQUIRED);
1214 }
1215 else if (ipp_status > IPP_OK_CONFLICT)
1216 return (CUPS_BACKEND_FAILED);
1217 else
1218 return (CUPS_BACKEND_OK);
ef416fc2 1219}
1220
1221
bd7854cb 1222/*
1223 * 'cancel_job()' - Cancel a print job.
1224 */
1225
1226static void
1227cancel_job(http_t *http, /* I - HTTP connection */
1228 const char *uri, /* I - printer-uri */
1229 int id, /* I - job-id */
1230 const char *resource, /* I - Resource path */
1231 const char *user, /* I - requesting-user-name */
1232 int version) /* I - IPP version */
1233{
1234 ipp_t *request; /* Cancel-Job request */
1235
1236
db1f069b 1237 _cupsLangPuts(stderr, _("INFO: Canceling print job...\n"));
bd7854cb 1238
1239 request = ippNewRequest(IPP_CANCEL_JOB);
1240 request->request.op.version[1] = version;
1241
1242 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1243 NULL, uri);
1244 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
1245
1246 if (user && user[0])
1247 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1248 "requesting-user-name", NULL, user);
1249
1250 /*
1251 * Do the request...
1252 */
1253
1254 ippDelete(cupsDoRequest(http, request, resource));
1255
1256 if (cupsLastError() > IPP_OK_CONFLICT)
db1f069b
MS
1257 _cupsLangPrintf(stderr, _("ERROR: Unable to cancel job %d: %s\n"), id,
1258 cupsLastErrorString());
bd7854cb 1259}
1260
1261
ef416fc2 1262/*
1263 * 'check_printer_state()' - Check the printer state...
1264 */
1265
bd7854cb 1266static void
fa73b229 1267check_printer_state(
1268 http_t *http, /* I - HTTP connection */
1269 const char *uri, /* I - Printer URI */
1270 const char *resource, /* I - Resource path */
1271 const char *user, /* I - Username, if any */
db1f069b
MS
1272 int version, /* I - IPP version */
1273 int job_id) /* I - Current job ID */
ef416fc2 1274{
1275 ipp_t *request, /* IPP request */
1276 *response; /* IPP response */
323c5de1 1277 static const char * const attrs[] = /* Attributes we want */
1278 {
1279 "printer-state-message",
1280 "printer-state-reasons"
1281 };
ef416fc2 1282
1283
1284 /*
1285 * Check on the printer state...
1286 */
1287
fa73b229 1288 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
1289 request->request.op.version[1] = version;
ef416fc2 1290
1291 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1292 NULL, uri);
1293
1294 if (user && user[0])
1295 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1296 "requesting-user-name", NULL, user);
1297
323c5de1 1298 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1299 "requested-attributes",
1300 (int)(sizeof(attrs) / sizeof(attrs[0])), NULL, attrs);
ef416fc2 1301
1302 /*
1303 * Do the request...
1304 */
1305
1306 if ((response = cupsDoRequest(http, request, resource)) != NULL)
1307 {
db1f069b 1308 report_printer_state(response, job_id);
ef416fc2 1309 ippDelete(response);
1310 }
1311}
1312
1313
b423cd4c 1314#ifdef HAVE_LIBZ
1315/*
1316 * 'compress_files()' - Compress print files...
1317 */
1318
1319static void
1320compress_files(int num_files, /* I - Number of files */
1321 char **files) /* I - Files */
1322{
1323 int i, /* Looping var */
1324 fd; /* Temporary file descriptor */
1325 ssize_t bytes; /* Bytes read/written */
1326 size_t total; /* Total bytes read */
1327 cups_file_t *in, /* Input file */
1328 *out; /* Output file */
1329 struct stat outinfo; /* Output file information */
1330 char filename[1024], /* Temporary filename */
a74454a7 1331 buffer[32768]; /* Copy buffer */
b423cd4c 1332
1333
1334 fprintf(stderr, "DEBUG: Compressing %d job files...\n", num_files);
1335 for (i = 0; i < num_files; i ++)
1336 {
1337 if ((fd = cupsTempFd(filename, sizeof(filename))) < 0)
1338 {
db1f069b
MS
1339 _cupsLangPrintf(stderr,
1340 _("ERROR: Unable to create temporary compressed print "
1341 "file: %s\n"), strerror(errno));
b423cd4c 1342 exit(CUPS_BACKEND_FAILED);
1343 }
1344
1345 if ((out = cupsFileOpenFd(fd, "w9")) == NULL)
1346 {
db1f069b
MS
1347 _cupsLangPrintf(stderr,
1348 _("ERROR: Unable to open temporary compressed print "
1349 "file: %s\n"), strerror(errno));
b423cd4c 1350 exit(CUPS_BACKEND_FAILED);
1351 }
1352
1353 if ((in = cupsFileOpen(files[i], "r")) == NULL)
1354 {
db1f069b
MS
1355 _cupsLangPrintf(stderr,
1356 _("ERROR: Unable to open print file \"%s\": %s\n"),
1357 files[i], strerror(errno));
b423cd4c 1358 cupsFileClose(out);
1359 exit(CUPS_BACKEND_FAILED);
1360 }
1361
1362 total = 0;
1363 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
1364 if (cupsFileWrite(out, buffer, bytes) < bytes)
1365 {
db1f069b
MS
1366 _cupsLangPrintf(stderr,
1367 _("ERROR: Unable to write %d bytes to \"%s\": %s\n"),
1368 (int)bytes, filename, strerror(errno));
b423cd4c 1369 cupsFileClose(in);
1370 cupsFileClose(out);
1371 exit(CUPS_BACKEND_FAILED);
1372 }
1373 else
1374 total += bytes;
1375
1376 cupsFileClose(out);
1377 cupsFileClose(in);
1378
1379 files[i] = strdup(filename);
1380
1381 if (!stat(filename, &outinfo))
1382 fprintf(stderr,
1383 "DEBUG: File %d compressed to %.1f%% of original size, "
1384 CUPS_LLFMT " bytes...\n",
e1d6a774 1385 i + 1, 100.0 * outinfo.st_size / total,
1386 CUPS_LLCAST outinfo.st_size);
b423cd4c 1387 }
1388}
1389#endif /* HAVE_LIBZ */
1390
1391
ef416fc2 1392/*
1393 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
1394 */
1395
bd7854cb 1396static const char * /* O - Password */
fa73b229 1397password_cb(const char *prompt) /* I - Prompt (not used) */
ef416fc2 1398{
1399 (void)prompt;
1400
76cd9e37
MS
1401 if (password && password_tries < 3)
1402 {
1403 password_tries ++;
1404
ef416fc2 1405 return (password);
76cd9e37 1406 }
ef416fc2 1407 else
1408 {
1409 /*
1410 * If there is no password set in the device URI, return the
1411 * "authentication required" exit code...
1412 */
1413
1414 if (tmpfilename[0])
1415 unlink(tmpfilename);
1416
1417#ifdef __APPLE__
1418 if (pstmpname[0])
1419 unlink(pstmpname);
1420#endif /* __APPLE__ */
1421
f7deaa1a 1422 fputs("ATTR: auth-info-required=username,password\n", stderr);
1423
ef416fc2 1424 exit(CUPS_BACKEND_AUTH_REQUIRED);
d09495fa 1425
1426 return (NULL); /* Eliminate compiler warning */
ef416fc2 1427 }
1428}
1429
1430
1431/*
1432 * 'report_printer_state()' - Report the printer state.
1433 */
1434
bd7854cb 1435static int /* O - Number of reasons shown */
db1f069b
MS
1436report_printer_state(ipp_t *ipp, /* I - IPP response */
1437 int job_id) /* I - Current job ID */
ef416fc2 1438{
1439 int i; /* Looping var */
1440 int count; /* Count of reasons shown... */
323c5de1 1441 ipp_attribute_t *psm, /* pritner-state-message */
1442 *reasons; /* printer-state-reasons */
ef416fc2 1443 const char *reason; /* Current reason */
1444 const char *message; /* Message to show */
1445 char unknown[1024]; /* Unknown message string */
1446 const char *prefix; /* Prefix for STATE: line */
1447 char state[1024]; /* State string */
db1f069b 1448 cups_lang_t *language; /* Current localization */
ef416fc2 1449
1450
323c5de1 1451 if ((psm = ippFindAttribute(ipp, "printer-state-message",
1452 IPP_TAG_TEXT)) != NULL)
1453 fprintf(stderr, "INFO: %s\n", psm->values[0].string.text);
1454
ef416fc2 1455 if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
1456 IPP_TAG_KEYWORD)) == NULL)
1457 return (0);
1458
1459 state[0] = '\0';
1460 prefix = "STATE: ";
db1f069b 1461 language = cupsLangDefault();
ef416fc2 1462
1463 for (i = 0, count = 0; i < reasons->num_values; i ++)
1464 {
1465 reason = reasons->values[i].string.text;
1466
db1f069b
MS
1467 if (job_id == 0 || strcmp(reason, "paused"))
1468 {
1469 strlcat(state, prefix, sizeof(state));
1470 strlcat(state, reason, sizeof(state));
1471
1472 prefix = ",";
1473 }
ef416fc2 1474
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
cc0d019f 1535 if (message[0])
ef416fc2 1536 {
1537 count ++;
1538 if (strstr(reasons->values[i].string.text, "error"))
db1f069b 1539 fprintf(stderr, "ERROR: %s\n", _cupsLangString(language, message));
ef416fc2 1540 else if (strstr(reasons->values[i].string.text, "warning"))
db1f069b 1541 fprintf(stderr, "WARNING: %s\n", _cupsLangString(language, message));
ef416fc2 1542 else
db1f069b 1543 fprintf(stderr, "INFO: %s\n", _cupsLangString(language, message));
ef416fc2 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 {
db1f069b
MS
1585 _cupsLangPuts(stderr,
1586 _("ERROR: PRINTER environment variable not defined!\n"));
ef416fc2 1587 return (-1);
1588 }
1589
1590 if ((ppdfile = cupsGetPPD(printer)) == NULL)
1591 {
db1f069b
MS
1592 _cupsLangPrintf(stderr,
1593 _("ERROR: Unable to get PPD file for printer \"%s\" - "
1594 "%s.\n"), printer, cupsLastErrorString());
ef416fc2 1595 }
1596 else
1597 {
1598 snprintf(ppdenv, sizeof(ppdenv), "PPD=%s", ppdfile);
1599 putenv(ppdenv);
1600 }
1601
1602 /*
1603 * Then create a temporary file for printing...
1604 */
1605
1606 if ((fd = cupsTempFd(pstmpname, sizeof(pstmpname))) < 0)
1607 {
db1f069b
MS
1608 _cupsLangPrintf(stderr, _("ERROR: Unable to create temporary file - %s.\n"),
1609 strerror(errno));
ef416fc2 1610 if (ppdfile)
1611 unlink(ppdfile);
1612 return (-1);
1613 }
1614
1615 /*
1616 * Get the owner of the spool file - it is owned by the user we want to run
1617 * as...
1618 */
1619
1620 if (argv[6])
1621 stat(argv[6], &fileinfo);
1622 else
1623 {
1624 /*
1625 * Use the OSX defaults, as an up-stream filter created the PICT
1626 * file...
1627 */
1628
1629 fileinfo.st_uid = 1;
1630 fileinfo.st_gid = 80;
1631 }
1632
1633 if (ppdfile)
1634 chown(ppdfile, fileinfo.st_uid, fileinfo.st_gid);
1635
1636 fchown(fd, fileinfo.st_uid, fileinfo.st_gid);
1637
1638 /*
1639 * Finally, run the filter to convert the file...
1640 */
1641
1642 if ((pid = fork()) == 0)
1643 {
1644 /*
1645 * Child process for pictwpstops... Redirect output of pictwpstops to a
1646 * file...
1647 */
1648
1649 close(1);
1650 dup(fd);
1651 close(fd);
1652
1653 if (!getuid())
1654 {
1655 /*
1656 * Change to an unpriviledged user...
1657 */
1658
1659 setgid(fileinfo.st_gid);
1660 setuid(fileinfo.st_uid);
1661 }
1662
1663 execlp("pictwpstops", printer, argv[1], argv[2], argv[3], argv[4], argv[5],
1664 filename, NULL);
db1f069b
MS
1665 _cupsLangPrintf(stderr, _("ERROR: Unable to exec pictwpstops: %s\n"),
1666 strerror(errno));
ef416fc2 1667 return (errno);
1668 }
1669
1670 close(fd);
1671
1672 if (pid < 0)
1673 {
1674 /*
1675 * Error!
1676 */
1677
db1f069b
MS
1678 _cupsLangPrintf(stderr, _("ERROR: Unable to fork pictwpstops: %s\n"),
1679 strerror(errno));
ef416fc2 1680 unlink(filename);
1681 if (ppdfile)
1682 unlink(ppdfile);
1683 return (-1);
1684 }
1685
1686 /*
1687 * Now wait for the filter to complete...
1688 */
1689
1690 if (wait(&status) < 0)
1691 {
db1f069b
MS
1692 _cupsLangPrintf(stderr, _("ERROR: Unable to wait for pictwpstops: %s\n"),
1693 strerror(errno));
ef416fc2 1694 close(fd);
1695 unlink(filename);
1696 if (ppdfile)
1697 unlink(ppdfile);
1698 return (-1);
1699 }
1700
1701 if (ppdfile)
1702 unlink(ppdfile);
1703
1704 close(fd);
1705
1706 if (status)
1707 {
1708 if (status >= 256)
db1f069b
MS
1709 _cupsLangPrintf(stderr, _("ERROR: pictwpstops exited with status %d!\n"),
1710 status / 256);
ef416fc2 1711 else
db1f069b
MS
1712 _cupsLangPrintf(stderr, _("ERROR: pictwpstops exited on signal %d!\n"),
1713 status);
ef416fc2 1714
1715 unlink(filename);
1716 return (status);
1717 }
1718
1719 /*
1720 * Return with no errors..
1721 */
1722
1723 return (0);
1724}
1725#endif /* __APPLE__ */
1726
1727
1728/*
1729 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
1730 */
1731
1732static void
1733sigterm_handler(int sig) /* I - Signal */
1734{
1735 (void)sig; /* remove compiler warnings... */
1736
bd7854cb 1737 if (!job_cancelled)
1738 {
1739 /*
1740 * Flag that the job should be cancelled...
1741 */
1742
1743 job_cancelled = 1;
1744 return;
1745 }
1746
ef416fc2 1747 /*
bd7854cb 1748 * The scheduler already tried to cancel us once, now just terminate
1749 * after removing our temp files!
ef416fc2 1750 */
1751
1752 if (tmpfilename[0])
1753 unlink(tmpfilename);
1754
1755#ifdef __APPLE__
1756 if (pstmpname[0])
1757 unlink(pstmpname);
1758#endif /* __APPLE__ */
1759
1760 exit(1);
1761}
1762
1763
1764/*
2e4ff8af 1765 * End of "$Id: ipp.c 7018 2007-10-10 22:14:03Z mike $".
ef416fc2 1766 */