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