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