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