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