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