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