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