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