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