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