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