]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/ipp.c
Hide test main from tools.
[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)
eab5b04e 603 if (backendSNMPSupplies(snmp_fd, http->hostaddr, &start_count, NULL))
604 {
605 /*
606 * No, close it...
607 */
608
b9e7ae13 609 _cupsSNMPClose(snmp_fd);
eab5b04e 610 snmp_fd = -1;
611 }
612
c8f9565c 613 /*
614 * Build a URI for the printer and fill the standard IPP attributes for
8ce7c000 615 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
616 * might contain username:password information...
c8f9565c 617 */
618
3f9cb6c6 619 snprintf(uri, sizeof(uri), "%s://%s:%d%s", method, hostname, port, resource);
c8f9565c 620
621 /*
97fcaf92 622 * First validate the destination and see if the device supports multiple
623 * copies. We have to do this because some IPP servers (e.g. HP JetDirect)
624 * don't support the copies attribute...
c8f9565c 625 */
626
e12df069 627 copies_sup = NULL;
628 format_sup = NULL;
629 supported = NULL;
c8f9565c 630
97fcaf92 631 do
c8f9565c 632 {
56f7a531 633 /*
634 * Check for side-channel requests...
635 */
636
637 backendCheckSideChannel(snmp_fd, http->hostaddr);
638
c8f9565c 639 /*
97fcaf92 640 * Build the IPP request...
c8f9565c 641 */
642
e12df069 643 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
644 request->request.op.version[1] = version;
c8f9565c 645
97fcaf92 646 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
647 NULL, uri);
c8f9565c 648
8f4595eb 649 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
650 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
651 NULL, pattrs);
652
97fcaf92 653 /*
654 * Do the request...
655 */
c8f9565c 656
2054e655 657 fputs("DEBUG: Getting supported attributes...\n", stderr);
a4e23897 658
b5cb0608 659 if ((supported = cupsDoRequest(http, request, resource)) == NULL)
660 ipp_status = cupsLastError();
661 else
0a3ac972 662 ipp_status = supported->request.status.status_code;
b5cb0608 663
664 if (ipp_status > IPP_OK_CONFLICT)
c8f9565c 665 {
b5cb0608 666 if (ipp_status == IPP_PRINTER_BUSY ||
667 ipp_status == IPP_SERVICE_UNAVAILABLE)
c8f9565c 668 {
ff0295f0 669 if (contimeout && (time(NULL) - start_time) > contimeout)
670 {
472af6f3 671 _cupsLangPuts(stderr, _("ERROR: Printer not responding!\n"));
ff0295f0 672 return (CUPS_BACKEND_FAILED);
673 }
674
675 recoverable = 1;
676
472af6f3 677 _cupsLangPrintf(stderr,
678 _("WARNING: recoverable: Network host \'%s\' is busy; "
679 "will retry in %d seconds...\n"),
680 hostname, delay);
ff0295f0 681
41368129 682 report_printer_state(supported, 0);
ff0295f0 683
684 sleep(delay);
685
686 if (delay < 30)
687 delay += 5;
97fcaf92 688 }
b5cb0608 689 else if ((ipp_status == IPP_BAD_REQUEST ||
690 ipp_status == IPP_VERSION_NOT_SUPPORTED) && version == 1)
c8f9565c 691 {
b5cb0608 692 /*
693 * Switch to IPP/1.0...
694 */
97fcaf92 695
472af6f3 696 _cupsLangPuts(stderr,
697 _("INFO: Printer does not support IPP/1.1, trying "
698 "IPP/1.0...\n"));
b5cb0608 699 version = 0;
a4e23897 700 httpReconnect(http);
c8f9565c 701 }
67474245 702 else if (ipp_status == IPP_NOT_FOUND)
703 {
472af6f3 704 _cupsLangPuts(stderr, _("ERROR: Destination printer does not exist!\n"));
67474245 705
706 if (supported)
707 ippDelete(supported);
708
6248387b 709 return (CUPS_BACKEND_STOP);
67474245 710 }
97fcaf92 711 else
67474245 712 {
472af6f3 713 _cupsLangPrintf(stderr,
714 _("ERROR: Unable to get printer status (%s)!\n"),
715 cupsLastErrorString());
67474245 716 sleep(10);
717 }
d1c2727f 718
719 if (supported)
720 ippDelete(supported);
721
722 continue;
97fcaf92 723 }
b5cb0608 724 else if ((copies_sup = ippFindAttribute(supported, "copies-supported",
725 IPP_TAG_RANGE)) != NULL)
97fcaf92 726 {
b5cb0608 727 /*
728 * Has the "copies-supported" attribute - does it have an upper
729 * bound > 1?
730 */
97fcaf92 731
b5cb0608 732 if (copies_sup->values[0].range.upper <= 1)
733 copies_sup = NULL; /* No */
734 }
97fcaf92 735
e12df069 736 format_sup = ippFindAttribute(supported, "document-format-supported",
737 IPP_TAG_MIMETYPE);
b5cb0608 738
739 if (format_sup)
740 {
741 fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
742 format_sup->num_values);
743 for (i = 0; i < format_sup->num_values; i ++)
744 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
745 format_sup->values[i].string.text);
c8f9565c 746 }
d1c2727f 747
41368129 748 report_printer_state(supported, 0);
c8f9565c 749 }
97fcaf92 750 while (ipp_status > IPP_OK_CONFLICT);
c8f9565c 751
997cf8b0 752 /*
753 * See if the printer is accepting jobs and is not stopped; if either
754 * condition is true and we are printing to a class, requeue the job...
755 */
756
757 if (getenv("CLASS") != NULL)
758 {
759 printer_state = ippFindAttribute(supported, "printer-state",
760 IPP_TAG_ENUM);
761 printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs",
762 IPP_TAG_BOOLEAN);
763
764 if (printer_state == NULL ||
072c4872 765 (printer_state->values[0].integer > IPP_PRINTER_PROCESSING &&
766 waitprinter) ||
997cf8b0 767 printer_accepting == NULL ||
768 !printer_accepting->values[0].boolean)
769 {
770 /*
771 * If the CLASS environment variable is set, the job was submitted
772 * to a class and not to a specific queue. In this case, we want
773 * to abort immediately so that the job can be requeued on the next
774 * available printer in the class.
775 */
776
472af6f3 777 _cupsLangPuts(stderr,
778 _("INFO: Unable to contact printer, queuing on next "
779 "printer in class...\n"));
997cf8b0 780
781 ippDelete(supported);
782 httpClose(http);
783
ba1e2736 784 if (tmpfilename[0])
785 unlink(tmpfilename);
997cf8b0 786
787 /*
788 * Sleep 5 seconds to keep the job from requeuing too rapidly...
789 */
790
791 sleep(5);
792
6248387b 793 return (CUPS_BACKEND_FAILED);
997cf8b0 794 }
795 }
796
ff0295f0 797 if (recoverable)
798 {
799 /*
800 * If we've shown a recoverable error make sure the printer proxies
801 * have a chance to see the recovered message. Not pretty but
802 * necessary for now...
803 */
804
805 fputs("INFO: recovered: \n", stderr);
806 sleep(5);
807 }
808
c8f9565c 809 /*
97fcaf92 810 * See if the printer supports multiple copies...
c8f9565c 811 */
812
37de726f 813 copies = atoi(argv[4]);
814
d1c2727f 815 if (copies_sup || argc < 7)
7d52601e 816 {
37de726f 817 copies_remaining = 1;
7d52601e 818
819 if (argc < 7)
820 copies = 1;
821 }
0c5b0932 822 else
37de726f 823 copies_remaining = copies;
0c5b0932 824
c8f9565c 825 /*
97fcaf92 826 * Then issue the print-job request...
c8f9565c 827 */
828
072c4872 829 job_id = 0;
d1c2727f 830
37de726f 831 while (copies_remaining > 0)
c8f9565c 832 {
56f7a531 833 /*
834 * Check for side-channel requests...
835 */
836
837 backendCheckSideChannel(snmp_fd, http->hostaddr);
838
c8f9565c 839 /*
97fcaf92 840 * Build the IPP request...
c8f9565c 841 */
842
072c4872 843 if (job_cancelled)
844 break;
845
846 if (num_files > 1)
847 request = ippNewRequest(IPP_CREATE_JOB);
848 else
849 request = ippNewRequest(IPP_PRINT_JOB);
850
e12df069 851 request->request.op.version[1] = version;
c8f9565c 852
97fcaf92 853 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
854 NULL, uri);
c8f9565c 855
97fcaf92 856 fprintf(stderr, "DEBUG: printer-uri = \"%s\"\n", uri);
857
753453e4 858 if (argv[2][0])
897922a9 859 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
860 "requesting-user-name", NULL, argv[2]);
97fcaf92 861
862 fprintf(stderr, "DEBUG: requesting-user-name = \"%s\"\n", argv[2]);
863
07bfc4e9 864 /*
865 * Only add a "job-name" attribute if the remote server supports
866 * copy generation - some IPP implementations like HP's don't seem
867 * to like UTF-8 job names (STR #1837)...
868 */
869
0a7645fc 870 if (argv[3][0] && copies_sup)
753453e4 871 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
872 argv[3]);
c8f9565c 873
97fcaf92 874 fprintf(stderr, "DEBUG: job-name = \"%s\"\n", argv[3]);
8ce7c000 875
1230a8a0 876#ifdef HAVE_LIBZ
877 if (compression)
878 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
879 "compression", NULL, "gzip");
880#endif /* HAVE_LIBZ */
881
c8f9565c 882 /*
97fcaf92 883 * Handle options on the command-line...
c8f9565c 884 */
885
97fcaf92 886 options = NULL;
887 num_options = cupsParseOptions(argv[5], 0, &options);
888
e7186d00 889#ifdef __APPLE__
7b072344 890 if (!strcasecmp(final_content_type, "application/pictwps") &&
891 num_files == 1)
e7186d00 892 {
893 if (format_sup != NULL)
894 {
895 for (i = 0; i < format_sup->num_values; i ++)
7b072344 896 if (!strcasecmp(final_content_type, format_sup->values[i].string.text))
e7186d00 897 break;
898 }
899
900 if (format_sup == NULL || i >= format_sup->num_values)
901 {
902 /*
903 * Remote doesn't support "application/pictwps" (i.e. it's not MacOS X)
904 * so convert the document to PostScript...
905 */
906
ba1e2736 907 if (run_pictwps_filter(argv, files[0]))
908 {
909 if (pstmpname[0])
910 unlink(pstmpname);
911
912 if (tmpfilename[0])
913 unlink(tmpfilename);
914
6248387b 915 return (CUPS_BACKEND_FAILED);
ba1e2736 916 }
e7186d00 917
ba1e2736 918 files[0] = pstmpname;
b8f3410f 919
e7186d00 920 /*
8e1e3983 921 * Change the MIME type to application/postscript and change the
922 * number of copies to 1...
e7186d00 923 */
924
b43a3371 925 final_content_type = "application/postscript";
926 copies = 1;
927 copies_remaining = 1;
928 send_options = 0;
e7186d00 929 }
930 }
931#endif /* __APPLE__ */
932
b43a3371 933 if (format_sup != NULL)
36aa99bb 934 {
935 for (i = 0; i < format_sup->num_values; i ++)
b43a3371 936 if (!strcasecmp(final_content_type, format_sup->values[i].string.text))
36aa99bb 937 break;
938
939 if (i < format_sup->num_values)
fd7f37f5 940 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
b43a3371 941 "document-format", NULL, final_content_type);
36aa99bb 942 }
97fcaf92 943
cad2b75f 944 if (copies_sup && version > 0 && send_options)
753453e4 945 {
946 /*
947 * Only send options if the destination printer supports the copies
31cab86b 948 * attribute and IPP/1.1. This is a hack for the HP and Lexmark
949 * implementations of IPP, which do not accept extension attributes
950 * and incorrectly report a client-error-bad-request error instead of
951 * the successful-ok-unsupported-attributes status. In short, at least
952 * some HP and Lexmark implementations of IPP are non-compliant.
753453e4 953 */
954
955 cupsEncodeOptions(request, num_options, options);
37de726f 956
753453e4 957 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies",
37de726f 958 copies);
753453e4 959 }
960
183914a3 961 cupsFreeOptions(num_options, options);
c8f9565c 962
a4e23897 963 /*
964 * If copies aren't supported, then we are likely dealing with an HP
965 * JetDirect. The HP IPP implementation seems to close the connection
31cab86b 966 * after every request - that is, it does *not* implement HTTP Keep-
a4e23897 967 * Alive, which is REQUIRED by HTTP/1.1...
968 */
969
970 if (!copies_sup)
971 httpReconnect(http);
972
cb555bcf 973 /*
b5cb0608 974 * Do the request...
cb555bcf 975 */
976
072c4872 977 if (num_files > 1)
978 response = cupsDoRequest(http, request, resource);
b5cb0608 979 else
072c4872 980 response = cupsDoFileRequest(http, request, resource, files[0]);
981
982 ipp_status = cupsLastError();
b5cb0608 983
984 if (ipp_status > IPP_OK_CONFLICT)
985 {
753453e4 986 job_id = 0;
987
072c4872 988 if (job_cancelled)
989 break;
990
b5cb0608 991 if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
992 ipp_status == IPP_PRINTER_BUSY)
993 {
472af6f3 994 _cupsLangPuts(stderr,
995 _("INFO: Printer busy; will retry in 10 seconds...\n"));
b5cb0608 996 sleep(10);
997 }
609d870f 998 else if ((ipp_status == IPP_BAD_REQUEST ||
999 ipp_status == IPP_VERSION_NOT_SUPPORTED) && version == 1)
1000 {
1001 /*
1002 * Switch to IPP/1.0...
1003 */
1004
472af6f3 1005 _cupsLangPuts(stderr,
1006 _("INFO: Printer does not support IPP/1.1, trying "
1007 "IPP/1.0...\n"));
609d870f 1008 version = 0;
1009 httpReconnect(http);
1010 }
b5cb0608 1011 else
472af6f3 1012 _cupsLangPrintf(stderr, _("ERROR: Print file was not accepted (%s)!\n"),
1013 cupsLastErrorString());
b5cb0608 1014 }
1015 else if ((job_id_attr = ippFindAttribute(response, "job-id",
1016 IPP_TAG_INTEGER)) == NULL)
97fcaf92 1017 {
472af6f3 1018 _cupsLangPuts(stderr,
1019 _("NOTICE: Print file accepted - job ID unknown.\n"));
b5cb0608 1020 job_id = 0;
1021 }
1022 else
1023 {
1024 job_id = job_id_attr->values[0].integer;
472af6f3 1025 _cupsLangPrintf(stderr, _("NOTICE: Print file accepted - job ID %d.\n"),
1026 job_id);
97fcaf92 1027 }
cb555bcf 1028
072c4872 1029 ippDelete(response);
1030
1031 if (job_cancelled)
1032 break;
1033
1034 if (job_id && num_files > 1)
1035 {
1036 for (i = 0; i < num_files; i ++)
1037 {
56f7a531 1038 /*
1039 * Check for side-channel requests...
1040 */
1041
1042 backendCheckSideChannel(snmp_fd, http->hostaddr);
1043
1044 /*
1045 * Send the next file in the job...
1046 */
1047
072c4872 1048 request = ippNewRequest(IPP_SEND_DOCUMENT);
1049
1050 request->request.op.version[1] = version;
1051
1052 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1053 NULL, uri);
1054
1055 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1056 job_id);
1057
1058 if (argv[2][0])
1059 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1060 "requesting-user-name", NULL, argv[2]);
1061
1062 if ((i + 1) == num_files)
1063 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
1064
1065 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
1066 "document-format", NULL, content_type);
1067
1068 ippDelete(cupsDoFileRequest(http, request, resource, files[i]));
1069
1070 if (cupsLastError() > IPP_OK_CONFLICT)
1071 {
1072 ipp_status = cupsLastError();
1073
472af6f3 1074 _cupsLangPrintf(stderr,
1075 _("ERROR: Unable to add file %d to job: %s\n"),
1076 job_id, cupsLastErrorString());
072c4872 1077 break;
1078 }
1079 }
1080 }
b5cb0608 1081
753453e4 1082 if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
b5cb0608 1083 {
1084 fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
37de726f 1085 copies_remaining --;
b5cb0608 1086 }
ab827512 1087 else if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1088 ipp_status == IPP_PRINTER_BUSY)
b5cb0608 1089 break;
ab827512 1090 else
37de726f 1091 copies_remaining --;
8ce7c000 1092
c8f9565c 1093 /*
b5cb0608 1094 * Wait for the job to complete...
c8f9565c 1095 */
1096
897922a9 1097 if (!job_id || !waitjob)
b5cb0608 1098 continue;
1099
472af6f3 1100 _cupsLangPuts(stderr, _("INFO: Waiting for job to complete...\n"));
b5cb0608 1101
2ce8588b 1102 for (delay = 1; !job_cancelled;)
c8f9565c 1103 {
56f7a531 1104 /*
1105 * Check for side-channel requests...
1106 */
1107
1108 backendCheckSideChannel(snmp_fd, http->hostaddr);
1109
97fcaf92 1110 /*
b5cb0608 1111 * Build an IPP_GET_JOB_ATTRIBUTES request...
97fcaf92 1112 */
1113
e12df069 1114 request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
1115 request->request.op.version[1] = version;
97fcaf92 1116
b5cb0608 1117 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1118 NULL, uri);
3f9cb6c6 1119
b5cb0608 1120 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1121 job_id);
c8f9565c 1122
ee8b7dd3 1123 if (argv[2][0])
897922a9 1124 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1125 "requesting-user-name", NULL, argv[2]);
ee8b7dd3 1126
8f4595eb 1127 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1128 "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
1129 NULL, jattrs);
97fcaf92 1130
1131 /*
b5cb0608 1132 * Do the request...
97fcaf92 1133 */
1134
a4e23897 1135 if (!copies_sup)
1136 httpReconnect(http);
1137
072c4872 1138 response = cupsDoRequest(http, request, resource);
1139 ipp_status = cupsLastError();
97fcaf92 1140
b5cb0608 1141 if (ipp_status == IPP_NOT_FOUND)
97fcaf92 1142 {
b5cb0608 1143 /*
d1c2727f 1144 * Job has gone away and/or the server has no job history...
b5cb0608 1145 */
97fcaf92 1146
b5cb0608 1147 ippDelete(response);
d1c2727f 1148
1149 ipp_status = IPP_OK;
b5cb0608 1150 break;
97fcaf92 1151 }
1152
b5cb0608 1153 if (ipp_status > IPP_OK_CONFLICT)
6a536282 1154 {
b5cb0608 1155 if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
1156 ipp_status != IPP_PRINTER_BUSY)
97fcaf92 1157 {
072c4872 1158 ippDelete(response);
b5cb0608 1159
472af6f3 1160 _cupsLangPrintf(stderr,
1161 _("ERROR: Unable to get job %d attributes (%s)!\n"),
1162 job_id, cupsLastErrorString());
b5cb0608 1163 break;
97fcaf92 1164 }
6a536282 1165 }
8f4595eb 1166
072c4872 1167 if (response)
97fcaf92 1168 {
8f4595eb 1169 if ((job_state = ippFindAttribute(response, "job-state",
1170 IPP_TAG_ENUM)) != NULL)
97fcaf92 1171 {
8f4595eb 1172 /*
1173 * Stop polling if the job is finished or pending-held...
1174 */
1175
7951315f 1176 if (job_state->values[0].integer > IPP_JOB_STOPPED)
8f4595eb 1177 {
e12df069 1178 if ((job_sheets = ippFindAttribute(response,
1179 "job-media-sheets-completed",
1180 IPP_TAG_INTEGER)) != NULL)
072c4872 1181 fprintf(stderr, "PAGE: total %d\n",
1182 job_sheets->values[0].integer);
ab827512 1183
8f4595eb 1184 ippDelete(response);
1185 break;
1186 }
97fcaf92 1187 }
97fcaf92 1188 }
1189
072c4872 1190 ippDelete(response);
d1c2727f 1191
b5cb0608 1192 /*
6bb5a528 1193 * Check the printer state and report it if necessary...
b5cb0608 1194 */
c8f9565c 1195
41368129 1196 check_printer_state(http, uri, resource, argv[2], version, job_id);
d1c2727f 1197
1198 /*
2ce8588b 1199 * Wait 1-10 seconds before polling again...
d1c2727f 1200 */
97fcaf92 1201
2ce8588b 1202 sleep(delay);
1203
1204 delay ++;
1205 if (delay > 10)
1206 delay = 1;
97fcaf92 1207 }
c8f9565c 1208 }
1209
6bb5a528 1210 /*
072c4872 1211 * Cancel the job as needed...
6bb5a528 1212 */
1213
072c4872 1214 if (job_cancelled && job_id)
1215 cancel_job(http, uri, job_id, resource, argv[2], version);
1216
1217 /*
1218 * Check the printer state and report it if necessary...
1219 */
6bb5a528 1220
41368129 1221 check_printer_state(http, uri, resource, argv[2], version, job_id);
6bb5a528 1222
eab5b04e 1223 /*
1224 * Collect the final page count as needed...
1225 */
1226
1227 if (snmp_fd >= 0 &&
1228 !backendSNMPSupplies(snmp_fd, http->hostaddr, &page_count, NULL) &&
1229 page_count > start_count)
1230 fprintf(stderr, "PAGE: total %d\n", page_count - start_count);
1231
c8f9565c 1232 /*
1233 * Free memory...
1234 */
1235
1236 httpClose(http);
c8f9565c 1237
072c4872 1238 ippDelete(supported);
2922de55 1239
c8f9565c 1240 /*
b8f3410f 1241 * Remove the temporary file(s) if necessary...
c8f9565c 1242 */
1243
d8e1f369 1244 if (tmpfilename[0])
8f4595eb 1245 unlink(tmpfilename);
c8f9565c 1246
1230a8a0 1247#ifdef HAVE_LIBZ
1248 if (compression)
1249 {
1250 for (i = 0; i < num_files; i ++)
1251 unlink(files[i]);
1252 }
1253#endif /* HAVE_LIBZ */
1254
b8f3410f 1255#ifdef __APPLE__
1256 if (pstmpname[0])
1257 unlink(pstmpname);
1258#endif /* __APPLE__ */
1259
c8f9565c 1260 /*
1261 * Return the queue status...
1262 */
1263
3fe655a3 1264 if (ipp_status == IPP_NOT_AUTHORIZED)
475752ad 1265 {
1266 /*
1267 * Authorization failures here mean that we need Kerberos. Username +
1268 * password authentication is handled in the password_cb function.
1269 */
1270
1271 fputs("ATTR: auth-info-required=negotiate\n", stderr);
3fe655a3 1272 return (CUPS_BACKEND_AUTH_REQUIRED);
475752ad 1273 }
3fe655a3 1274 else if (ipp_status > IPP_OK_CONFLICT)
1275 return (CUPS_BACKEND_FAILED);
1276 else
1277 return (CUPS_BACKEND_OK);
b5cb0608 1278}
1279
1280
072c4872 1281/*
1282 * 'cancel_job()' - Cancel a print job.
1283 */
1284
1285static void
1286cancel_job(http_t *http, /* I - HTTP connection */
1287 const char *uri, /* I - printer-uri */
1288 int id, /* I - job-id */
1289 const char *resource, /* I - Resource path */
1290 const char *user, /* I - requesting-user-name */
1291 int version) /* I - IPP version */
1292{
1293 ipp_t *request; /* Cancel-Job request */
1294
1295
472af6f3 1296 _cupsLangPuts(stderr, _("INFO: Canceling print job...\n"));
072c4872 1297
1298 request = ippNewRequest(IPP_CANCEL_JOB);
1299 request->request.op.version[1] = version;
1300
1301 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1302 NULL, uri);
1303 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
1304
1305 if (user && user[0])
1306 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1307 "requesting-user-name", NULL, user);
1308
1309 /*
1310 * Do the request...
1311 */
1312
1313 ippDelete(cupsDoRequest(http, request, resource));
1314
1315 if (cupsLastError() > IPP_OK_CONFLICT)
472af6f3 1316 _cupsLangPrintf(stderr, _("ERROR: Unable to cancel job %d: %s\n"), id,
1317 cupsLastErrorString());
072c4872 1318}
1319
1320
6bb5a528 1321/*
1322 * 'check_printer_state()' - Check the printer state...
1323 */
1324
072c4872 1325static void
e12df069 1326check_printer_state(
1327 http_t *http, /* I - HTTP connection */
1328 const char *uri, /* I - Printer URI */
1329 const char *resource, /* I - Resource path */
1330 const char *user, /* I - Username, if any */
41368129 1331 int version, /* I - IPP version */
1332 int job_id) /* I - Current job ID */
6bb5a528 1333{
1334 ipp_t *request, /* IPP request */
1335 *response; /* IPP response */
a6ccc6e8 1336 static const char * const attrs[] = /* Attributes we want */
1337 {
1338 "printer-state-message",
1339 "printer-state-reasons"
1340 };
6bb5a528 1341
1342
1343 /*
1344 * Check on the printer state...
1345 */
1346
e12df069 1347 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
1348 request->request.op.version[1] = version;
6bb5a528 1349
1350 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1351 NULL, uri);
1352
1353 if (user && user[0])
1354 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1355 "requesting-user-name", NULL, user);
1356
a6ccc6e8 1357 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1358 "requested-attributes",
1359 (int)(sizeof(attrs) / sizeof(attrs[0])), NULL, attrs);
6bb5a528 1360
1361 /*
1362 * Do the request...
1363 */
1364
1365 if ((response = cupsDoRequest(http, request, resource)) != NULL)
1366 {
41368129 1367 report_printer_state(response, job_id);
6bb5a528 1368 ippDelete(response);
1369 }
1370}
1371
1372
1230a8a0 1373#ifdef HAVE_LIBZ
1374/*
1375 * 'compress_files()' - Compress print files...
1376 */
1377
1378static void
1379compress_files(int num_files, /* I - Number of files */
1380 char **files) /* I - Files */
1381{
1382 int i, /* Looping var */
1383 fd; /* Temporary file descriptor */
1384 ssize_t bytes; /* Bytes read/written */
1385 size_t total; /* Total bytes read */
1386 cups_file_t *in, /* Input file */
1387 *out; /* Output file */
1388 struct stat outinfo; /* Output file information */
1389 char filename[1024], /* Temporary filename */
1fb9f3e8 1390 buffer[32768]; /* Copy buffer */
1230a8a0 1391
1392
1393 fprintf(stderr, "DEBUG: Compressing %d job files...\n", num_files);
1394 for (i = 0; i < num_files; i ++)
1395 {
1396 if ((fd = cupsTempFd(filename, sizeof(filename))) < 0)
1397 {
472af6f3 1398 _cupsLangPrintf(stderr,
1399 _("ERROR: Unable to create temporary compressed print "
1400 "file: %s\n"), strerror(errno));
1230a8a0 1401 exit(CUPS_BACKEND_FAILED);
1402 }
1403
1404 if ((out = cupsFileOpenFd(fd, "w9")) == NULL)
1405 {
472af6f3 1406 _cupsLangPrintf(stderr,
1407 _("ERROR: Unable to open temporary compressed print "
1408 "file: %s\n"), strerror(errno));
1230a8a0 1409 exit(CUPS_BACKEND_FAILED);
1410 }
1411
1412 if ((in = cupsFileOpen(files[i], "r")) == NULL)
1413 {
472af6f3 1414 _cupsLangPrintf(stderr,
1415 _("ERROR: Unable to open print file \"%s\": %s\n"),
1416 files[i], strerror(errno));
1230a8a0 1417 cupsFileClose(out);
1418 exit(CUPS_BACKEND_FAILED);
1419 }
1420
1421 total = 0;
1422 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
1423 if (cupsFileWrite(out, buffer, bytes) < bytes)
1424 {
472af6f3 1425 _cupsLangPrintf(stderr,
1426 _("ERROR: Unable to write %d bytes to \"%s\": %s\n"),
1427 (int)bytes, filename, strerror(errno));
1230a8a0 1428 cupsFileClose(in);
1429 cupsFileClose(out);
1430 exit(CUPS_BACKEND_FAILED);
1431 }
1432 else
1433 total += bytes;
1434
1435 cupsFileClose(out);
1436 cupsFileClose(in);
1437
1438 files[i] = strdup(filename);
1439
1440 if (!stat(filename, &outinfo))
ff0295f0 1441 fprintf(stderr,
1442 "DEBUG: File %d compressed to %.1f%% of original size, "
1443 CUPS_LLFMT " bytes...\n",
1444 i + 1, 100.0 * outinfo.st_size / total,
1445 CUPS_LLCAST outinfo.st_size);
1230a8a0 1446 }
1447}
1448#endif /* HAVE_LIBZ */
1449
1450
b5cb0608 1451/*
1452 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
1453 */
1454
072c4872 1455static const char * /* O - Password */
e12df069 1456password_cb(const char *prompt) /* I - Prompt (not used) */
b5cb0608 1457{
1458 (void)prompt;
1459
acbc1533 1460 if (password && *password && password_tries < 3)
9c85dfbf 1461 {
1462 password_tries ++;
1463
6248387b 1464 return (password);
9c85dfbf 1465 }
6248387b 1466 else
1467 {
1468 /*
1469 * If there is no password set in the device URI, return the
1470 * "authentication required" exit code...
1471 */
1472
1473 if (tmpfilename[0])
1474 unlink(tmpfilename);
1475
1476#ifdef __APPLE__
1477 if (pstmpname[0])
1478 unlink(pstmpname);
1479#endif /* __APPLE__ */
1480
0f4c64f6 1481 fputs("ATTR: auth-info-required=username,password\n", stderr);
1482
6248387b 1483 exit(CUPS_BACKEND_AUTH_REQUIRED);
be5262d8 1484
1485 return (NULL); /* Eliminate compiler warning */
6248387b 1486 }
c8f9565c 1487}
1488
1489
1490/*
d1c2727f 1491 * 'report_printer_state()' - Report the printer state.
1492 */
1493
072c4872 1494static int /* O - Number of reasons shown */
41368129 1495report_printer_state(ipp_t *ipp, /* I - IPP response */
1496 int job_id) /* I - Current job ID */
d1c2727f 1497{
1498 int i; /* Looping var */
d8076497 1499 int count; /* Count of reasons shown... */
a6ccc6e8 1500 ipp_attribute_t *psm, /* pritner-state-message */
1501 *reasons; /* printer-state-reasons */
8f4595eb 1502 const char *reason; /* Current reason */
d1c2727f 1503 const char *message; /* Message to show */
d8076497 1504 char unknown[1024]; /* Unknown message string */
8f4595eb 1505 const char *prefix; /* Prefix for STATE: line */
1506 char state[1024]; /* State string */
472af6f3 1507 cups_lang_t *language; /* Current localization */
d1c2727f 1508
1509
a6ccc6e8 1510 if ((psm = ippFindAttribute(ipp, "printer-state-message",
1511 IPP_TAG_TEXT)) != NULL)
1512 fprintf(stderr, "INFO: %s\n", psm->values[0].string.text);
1513
d1c2727f 1514 if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
1515 IPP_TAG_KEYWORD)) == NULL)
1516 return (0);
1517
8f4595eb 1518 state[0] = '\0';
1519 prefix = "STATE: ";
472af6f3 1520 language = cupsLangDefault();
8f4595eb 1521
d8076497 1522 for (i = 0, count = 0; i < reasons->num_values; i ++)
d1c2727f 1523 {
8f4595eb 1524 reason = reasons->values[i].string.text;
1525
41368129 1526 if (job_id == 0 || strcmp(reason, "paused"))
1527 {
1528 strlcat(state, prefix, sizeof(state));
1529 strlcat(state, reason, sizeof(state));
1530
1531 prefix = ",";
1532 }
8f4595eb 1533
d950cd16 1534 message = "";
d1c2727f 1535
5baa731f 1536 if (!strncmp(reason, "media-needed", 12))
34b18bd4 1537 message = _("Media tray needs to be filled.");
5baa731f 1538 else if (!strncmp(reason, "media-jam", 9))
34b18bd4 1539 message = _("Media jam!");
5baa731f 1540 else if (!strncmp(reason, "moving-to-paused", 16) ||
1541 !strncmp(reason, "paused", 6) ||
1542 !strncmp(reason, "shutdown", 8))
c7c5b066 1543 message = _("Printer offline.");
5baa731f 1544 else if (!strncmp(reason, "toner-low", 9))
34b18bd4 1545 message = _("Toner low.");
5baa731f 1546 else if (!strncmp(reason, "toner-empty", 11))
34b18bd4 1547 message = _("Out of toner!");
5baa731f 1548 else if (!strncmp(reason, "cover-open", 10))
34b18bd4 1549 message = _("Cover open.");
5baa731f 1550 else if (!strncmp(reason, "interlock-open", 14))
34b18bd4 1551 message = _("Interlock open.");
5baa731f 1552 else if (!strncmp(reason, "door-open", 9))
34b18bd4 1553 message = _("Door open.");
5baa731f 1554 else if (!strncmp(reason, "input-tray-missing", 18))
34b18bd4 1555 message = _("Media tray missing!");
5baa731f 1556 else if (!strncmp(reason, "media-low", 9))
34b18bd4 1557 message = _("Media tray almost empty.");
5baa731f 1558 else if (!strncmp(reason, "media-empty", 11))
34b18bd4 1559 message = _("Media tray empty!");
5baa731f 1560 else if (!strncmp(reason, "output-tray-missing", 19))
34b18bd4 1561 message = _("Output tray missing!");
5baa731f 1562 else if (!strncmp(reason, "output-area-almost-full", 23))
34b18bd4 1563 message = _("Output bin almost full.");
5baa731f 1564 else if (!strncmp(reason, "output-area-full", 16))
34b18bd4 1565 message = _("Output bin full!");
5baa731f 1566 else if (!strncmp(reason, "marker-supply-low", 17))
34b18bd4 1567 message = _("Ink/toner almost empty.");
5baa731f 1568 else if (!strncmp(reason, "marker-supply-empty", 19))
34b18bd4 1569 message = _("Ink/toner empty!");
5baa731f 1570 else if (!strncmp(reason, "marker-waste-almost-full", 24))
34b18bd4 1571 message = _("Ink/toner waste bin almost full.");
5baa731f 1572 else if (!strncmp(reason, "marker-waste-full", 17))
34b18bd4 1573 message = _("Ink/toner waste bin full!");
5baa731f 1574 else if (!strncmp(reason, "fuser-over-temp", 15))
34b18bd4 1575 message = _("Fuser temperature high!");
5baa731f 1576 else if (!strncmp(reason, "fuser-under-temp", 16))
34b18bd4 1577 message = _("Fuser temperature low!");
5baa731f 1578 else if (!strncmp(reason, "opc-near-eol", 12))
34b18bd4 1579 message = _("OPC almost at end-of-life.");
5baa731f 1580 else if (!strncmp(reason, "opc-life-over", 13))
34b18bd4 1581 message = _("OPC at end-of-life!");
5baa731f 1582 else if (!strncmp(reason, "developer-low", 13))
34b18bd4 1583 message = _("Developer almost empty.");
5baa731f 1584 else if (!strncmp(reason, "developer-empty", 15))
34b18bd4 1585 message = _("Developer empty!");
8f4595eb 1586 else if (strstr(reason, "error") != NULL)
d8076497 1587 {
1588 message = unknown;
1589
34b18bd4 1590 snprintf(unknown, sizeof(unknown), _("Unknown printer error (%s)!"),
8f4595eb 1591 reason);
d8076497 1592 }
d1c2727f 1593
933466f0 1594 if (message[0])
d1c2727f 1595 {
d8076497 1596 count ++;
d1c2727f 1597 if (strstr(reasons->values[i].string.text, "error"))
472af6f3 1598 fprintf(stderr, "ERROR: %s\n", _cupsLangString(language, message));
d1c2727f 1599 else if (strstr(reasons->values[i].string.text, "warning"))
472af6f3 1600 fprintf(stderr, "WARNING: %s\n", _cupsLangString(language, message));
d1c2727f 1601 else
472af6f3 1602 fprintf(stderr, "INFO: %s\n", _cupsLangString(language, message));
d1c2727f 1603 }
1604 }
1605
8f4595eb 1606 fprintf(stderr, "%s\n", state);
1607
d8076497 1608 return (count);
d1c2727f 1609}
1610
1611
a684a3b0 1612#ifdef __APPLE__
1613/*
1614 * 'run_pictwps_filter()' - Convert PICT files to PostScript when printing
1615 * remotely.
1616 *
1617 * This step is required because the PICT format is not documented and
1618 * subject to change, so developing a filter for other OS's is infeasible.
1619 * Also, fonts required by the PICT file need to be embedded on the
1620 * client side (which has the fonts), so we run the filter to get a
1621 * PostScript file for printing...
1622 */
1623
072c4872 1624static int /* O - Exit status of filter */
e12df069 1625run_pictwps_filter(char **argv, /* I - Command-line arguments */
1626 const char *filename)/* I - Filename */
a684a3b0 1627{
e12df069 1628 struct stat fileinfo; /* Print file information */
1629 const char *ppdfile; /* PPD file for destination printer */
1630 int pid; /* Child process ID */
1631 int fd; /* Temporary file descriptor */
1632 int status; /* Exit status of filter */
1633 const char *printer; /* PRINTER env var */
1634 static char ppdenv[1024]; /* PPD environment variable */
a684a3b0 1635
1636
1637 /*
1638 * First get the PPD file for the printer...
1639 */
1640
1641 printer = getenv("PRINTER");
1642 if (!printer)
1643 {
472af6f3 1644 _cupsLangPuts(stderr,
1645 _("ERROR: PRINTER environment variable not defined!\n"));
a684a3b0 1646 return (-1);
1647 }
1648
1649 if ((ppdfile = cupsGetPPD(printer)) == NULL)
1650 {
472af6f3 1651 _cupsLangPrintf(stderr,
1652 _("ERROR: Unable to get PPD file for printer \"%s\" - "
1653 "%s.\n"), printer, cupsLastErrorString());
1c85e5c0 1654 }
1655 else
1656 {
1657 snprintf(ppdenv, sizeof(ppdenv), "PPD=%s", ppdfile);
1658 putenv(ppdenv);
a684a3b0 1659 }
a684a3b0 1660
1661 /*
1662 * Then create a temporary file for printing...
1663 */
1664
b8f3410f 1665 if ((fd = cupsTempFd(pstmpname, sizeof(pstmpname))) < 0)
a684a3b0 1666 {
472af6f3 1667 _cupsLangPrintf(stderr, _("ERROR: Unable to create temporary file - %s.\n"),
1668 strerror(errno));
1c85e5c0 1669 if (ppdfile)
1670 unlink(ppdfile);
a684a3b0 1671 return (-1);
1672 }
1673
1674 /*
1675 * Get the owner of the spool file - it is owned by the user we want to run
1676 * as...
1677 */
1678
b8f3410f 1679 if (argv[6])
1680 stat(argv[6], &fileinfo);
1681 else
1682 {
1683 /*
1684 * Use the OSX defaults, as an up-stream filter created the PICT
1685 * file...
1686 */
1687
1688 fileinfo.st_uid = 1;
1689 fileinfo.st_gid = 80;
1690 }
a684a3b0 1691
1c85e5c0 1692 if (ppdfile)
1693 chown(ppdfile, fileinfo.st_uid, fileinfo.st_gid);
1694
1695 fchown(fd, fileinfo.st_uid, fileinfo.st_gid);
1696
a684a3b0 1697 /*
1698 * Finally, run the filter to convert the file...
1699 */
1700
1701 if ((pid = fork()) == 0)
1702 {
1703 /*
1704 * Child process for pictwpstops... Redirect output of pictwpstops to a
1705 * file...
1706 */
1707
1708 close(1);
1709 dup(fd);
1710 close(fd);
1711
1712 if (!getuid())
1713 {
1714 /*
1715 * Change to an unpriviledged user...
1716 */
1717
1718 setgid(fileinfo.st_gid);
1719 setuid(fileinfo.st_uid);
1720 }
1721
1722 execlp("pictwpstops", printer, argv[1], argv[2], argv[3], argv[4], argv[5],
b8f3410f 1723 filename, NULL);
472af6f3 1724 _cupsLangPrintf(stderr, _("ERROR: Unable to exec pictwpstops: %s\n"),
1725 strerror(errno));
a684a3b0 1726 return (errno);
1727 }
1c85e5c0 1728
1729 close(fd);
1730
1731 if (pid < 0)
a684a3b0 1732 {
1733 /*
1734 * Error!
1735 */
1736
472af6f3 1737 _cupsLangPrintf(stderr, _("ERROR: Unable to fork pictwpstops: %s\n"),
1738 strerror(errno));
1c85e5c0 1739 if (ppdfile)
1740 unlink(ppdfile);
a684a3b0 1741 return (-1);
1742 }
1743
1744 /*
1745 * Now wait for the filter to complete...
1746 */
1747
1748 if (wait(&status) < 0)
1749 {
472af6f3 1750 _cupsLangPrintf(stderr, _("ERROR: Unable to wait for pictwpstops: %s\n"),
1751 strerror(errno));
a684a3b0 1752 close(fd);
1c85e5c0 1753 if (ppdfile)
1754 unlink(ppdfile);
a684a3b0 1755 return (-1);
1756 }
1757
1c85e5c0 1758 if (ppdfile)
1759 unlink(ppdfile);
1760
a684a3b0 1761 close(fd);
1762
1763 if (status)
1764 {
1765 if (status >= 256)
472af6f3 1766 _cupsLangPrintf(stderr, _("ERROR: pictwpstops exited with status %d!\n"),
1767 status / 256);
a684a3b0 1768 else
472af6f3 1769 _cupsLangPrintf(stderr, _("ERROR: pictwpstops exited on signal %d!\n"),
1770 status);
a684a3b0 1771
a684a3b0 1772 return (status);
1773 }
1774
1775 /*
1776 * Return with no errors..
1777 */
1778
1779 return (0);
1780}
1781#endif /* __APPLE__ */
1782
d1c2727f 1783
1784/*
8f4595eb 1785 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
1786 */
1787
1788static void
1789sigterm_handler(int sig) /* I - Signal */
1790{
1791 (void)sig; /* remove compiler warnings... */
1792
072c4872 1793 if (!job_cancelled)
1794 {
1795 /*
1796 * Flag that the job should be cancelled...
1797 */
1798
1799 job_cancelled = 1;
1800 return;
1801 }
1802
8f4595eb 1803 /*
072c4872 1804 * The scheduler already tried to cancel us once, now just terminate
1805 * after removing our temp files!
8f4595eb 1806 */
1807
1808 if (tmpfilename[0])
1809 unlink(tmpfilename);
1810
b8f3410f 1811#ifdef __APPLE__
1812 if (pstmpname[0])
1813 unlink(pstmpname);
1814#endif /* __APPLE__ */
1815
8f4595eb 1816 exit(1);
1817}
1818
1819
1820/*
c9d3f842 1821 * End of "$Id$".
c8f9565c 1822 */