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