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