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