2 * "$Id: ipp.c 8950 2010-01-14 22:40:19Z mike $"
4 * IPP backend for the Common UNIX Printing System (CUPS).
6 * Copyright 2007-2010 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
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/".
15 * This file is subject to the Apple OS-Developed Software exception.
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
29 * sigterm_handler() - Handle 'terminate' signals that stop the backend.
33 * Include necessary headers.
36 #include <cups/http-private.h>
37 #include "backend-private.h"
38 #include <sys/types.h>
46 static char *password = NULL; /* Password for device URI */
47 static int password_tries = 0; /* Password tries */
48 static const char *auth_info_required = "none";
49 /* New auth-info-required value */
51 static char pstmpname[1024] = ""; /* Temporary PostScript file name */
52 #endif /* __APPLE__ */
53 static char tmpfilename[1024] = ""; /* Temporary spool file name */
54 static int job_cancelled = 0; /* Job cancelled? */
61 static void cancel_job(http_t *http, const char *uri, int id,
62 const char *resource, const char *user, int version);
63 static void check_printer_state(http_t *http, const char *uri,
64 const char *resource, const char *user,
65 int version, int job_id);
67 static void compress_files(int num_files, char **files);
68 #endif /* HAVE_LIBZ */
69 static const char *password_cb(const char *);
70 static void report_attr(ipp_attribute_t *attr);
71 static int report_printer_state(ipp_t *ipp, int job_id);
74 static int run_pictwps_filter(char **argv, const char *filename);
75 #endif /* __APPLE__ */
76 static void sigterm_handler(int sig);
80 * 'main()' - Send a file to the printer or server.
84 * printer-uri job-id user title copies options [file]
87 int /* O - Exit status */
88 main(int argc, /* I - Number of command-line args */
89 char *argv[]) /* I - Command-line arguments */
91 int i; /* Looping var */
92 int send_options; /* Send job options? */
93 int num_options; /* Number of printer options */
94 cups_option_t *options; /* Printer options */
95 const char *device_uri; /* Device URI */
96 char scheme[255], /* Scheme in URI */
97 hostname[1024], /* Hostname */
98 username[255], /* Username info */
99 resource[1024], /* Resource info (printer name) */
100 addrname[256], /* Address name */
101 *optptr, /* Pointer to URI options */
102 *name, /* Name of option */
103 *value, /* Value of option */
104 sep; /* Separator character */
105 int snmp_fd, /* SNMP socket */
106 start_count, /* Page count via SNMP at start */
107 page_count, /* Page count via SNMP */
108 have_supplies; /* Printer supports supply levels? */
109 int num_files; /* Number of files to print */
110 char **files, /* Files to print */
111 *filename; /* Pointer to single filename */
112 int port; /* Port number (not used) */
113 char uri[HTTP_MAX_URI]; /* Updated URI without user/pass */
114 ipp_status_t ipp_status; /* Status of IPP request */
115 http_t *http; /* HTTP connection */
116 ipp_t *request, /* IPP request */
117 *response, /* IPP response */
118 *supported; /* get-printer-attributes response */
119 time_t start_time; /* Time of first connect */
120 int recoverable; /* Recoverable error shown? */
121 int contimeout; /* Connection timeout */
122 int delay; /* Delay for retries... */
123 int compression, /* Do compression of the job data? */
124 waitjob, /* Wait for job complete? */
125 waitprinter; /* Wait for printer ready? */
126 ipp_attribute_t *job_id_attr; /* job-id attribute */
127 int job_id; /* job-id value */
128 ipp_attribute_t *job_sheets; /* job-media-sheets-completed */
129 ipp_attribute_t *job_state; /* job-state */
130 ipp_attribute_t *copies_sup; /* copies-supported */
131 ipp_attribute_t *format_sup; /* document-format-supported */
132 ipp_attribute_t *printer_state; /* printer-state attribute */
133 ipp_attribute_t *printer_accepting; /* printer-is-accepting-jobs */
134 int copies, /* Number of copies for job */
135 copies_remaining; /* Number of copies remaining */
136 const char *content_type, /* CONTENT_TYPE environment variable */
137 *final_content_type; /* FINAL_CONTENT_TYPE environment var */
138 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
139 struct sigaction action; /* Actions for POSIX signals */
140 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
141 int version; /* IPP version */
142 static const char * const pattrs[] =
143 { /* Printer attributes we want */
144 "com.apple.print.recoverable-message",
146 "document-format-supported",
148 "marker-high-levels",
154 "printer-is-accepting-jobs",
156 "printer-state-message",
157 "printer-state-reasons",
159 static const char * const jattrs[] =
160 { /* Job attributes we want */
161 "job-media-sheets-completed",
167 * Make sure status messages are not buffered...
170 setbuf(stderr, NULL);
173 * Ignore SIGPIPE and catch SIGTERM signals...
177 sigset(SIGPIPE, SIG_IGN);
178 sigset(SIGTERM, sigterm_handler);
179 #elif defined(HAVE_SIGACTION)
180 memset(&action, 0, sizeof(action));
181 action.sa_handler = SIG_IGN;
182 sigaction(SIGPIPE, &action, NULL);
184 sigemptyset(&action.sa_mask);
185 sigaddset(&action.sa_mask, SIGTERM);
186 action.sa_handler = sigterm_handler;
187 sigaction(SIGTERM, &action, NULL);
189 signal(SIGPIPE, SIG_IGN);
190 signal(SIGTERM, sigterm_handler);
191 #endif /* HAVE_SIGSET */
194 * Check command-line...
201 if ((s = strrchr(argv[0], '/')) != NULL)
206 printf("network %s \"Unknown\" \"%s (%s)\"\n",
207 s, _cupsLangString(cupsLangDefault(),
208 _("Internet Printing Protocol")), s);
209 return (CUPS_BACKEND_OK);
213 _cupsLangPrintf(stderr,
214 _("Usage: %s job-id user title copies options [file]\n"),
216 return (CUPS_BACKEND_STOP);
220 * Get the (final) content type...
223 if ((content_type = getenv("CONTENT_TYPE")) == NULL)
224 content_type = "application/octet-stream";
226 if ((final_content_type = getenv("FINAL_CONTENT_TYPE")) == NULL)
228 final_content_type = content_type;
230 if (!strncmp(final_content_type, "printer/", 8))
231 final_content_type = "application/vnd.cups-raw";
235 * Extract the hostname and printer name from the URI...
238 if ((device_uri = cupsBackendDeviceURI(argv)) == NULL)
239 return (CUPS_BACKEND_FAILED);
241 httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme),
242 username, sizeof(username), hostname, sizeof(hostname), &port,
243 resource, sizeof(resource));
246 port = IPP_PORT; /* Default to port 631 */
248 if (!strcmp(scheme, "https"))
249 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
251 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
254 * See if there are any options...
261 contimeout = 7 * 24 * 60 * 60;
263 if ((optptr = strchr(resource, '?')) != NULL)
266 * Yup, terminate the device name string and move to the first
267 * character of the optptr...
273 * Then parse the optptr...
284 while (*optptr && *optptr != '=' && *optptr != '+' && *optptr != '&')
287 if ((sep = *optptr) != '\0')
298 while (*optptr && *optptr != '+' && *optptr != '&')
308 * Process the option...
311 if (!strcasecmp(name, "waitjob"))
314 * Wait for job completion?
317 waitjob = !strcasecmp(value, "on") ||
318 !strcasecmp(value, "yes") ||
319 !strcasecmp(value, "true");
321 else if (!strcasecmp(name, "waitprinter"))
324 * Wait for printer idle?
327 waitprinter = !strcasecmp(value, "on") ||
328 !strcasecmp(value, "yes") ||
329 !strcasecmp(value, "true");
331 else if (!strcasecmp(name, "encryption"))
334 * Enable/disable encryption?
337 if (!strcasecmp(value, "always"))
338 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
339 else if (!strcasecmp(value, "required"))
340 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
341 else if (!strcasecmp(value, "never"))
342 cupsSetEncryption(HTTP_ENCRYPT_NEVER);
343 else if (!strcasecmp(value, "ifrequested"))
344 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
347 _cupsLangPrintf(stderr,
348 _("ERROR: Unknown encryption option value \"%s\"!\n"),
352 else if (!strcasecmp(name, "version"))
354 if (!strcmp(value, "1.0"))
356 else if (!strcmp(value, "1.1"))
358 else if (!strcmp(value, "2.0"))
360 else if (!strcmp(value, "2.1"))
364 _cupsLangPrintf(stderr,
365 _("ERROR: Unknown version option value \"%s\"!\n"),
370 else if (!strcasecmp(name, "compression"))
372 compression = !strcasecmp(value, "true") ||
373 !strcasecmp(value, "yes") ||
374 !strcasecmp(value, "on") ||
375 !strcasecmp(value, "gzip");
377 #endif /* HAVE_LIBZ */
378 else if (!strcasecmp(name, "contimeout"))
381 * Set the connection timeout...
385 contimeout = atoi(value);
393 _cupsLangPrintf(stderr,
394 _("ERROR: Unknown option \"%s\" with value \"%s\"!\n"),
401 * If we have 7 arguments, print the file named on the command-line.
402 * Otherwise, copy stdin to a temporary file and print the temporary
409 * Copy stdin to a temporary file...
412 int fd; /* File descriptor */
413 http_addrlist_t *addrlist; /* Address list */
414 off_t tbytes; /* Total bytes copied */
417 fputs("STATE: +connecting-to-device\n", stderr);
418 fprintf(stderr, "DEBUG: Looking up \"%s\"...\n", hostname);
420 if ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, "1")) == NULL)
422 _cupsLangPrintf(stderr, _("ERROR: Unable to locate printer \'%s\'!\n"),
424 return (CUPS_BACKEND_STOP);
427 snmp_fd = _cupsSNMPOpen(addrlist->addr.addr.sa_family);
429 if ((fd = cupsTempFd(tmpfilename, sizeof(tmpfilename))) < 0)
431 _cupsLangPrintError("ERROR", _("Unable to create temporary file"));
432 return (CUPS_BACKEND_FAILED);
435 _cupsLangPuts(stderr, _("INFO: Copying print data...\n"));
437 tbytes = backendRunLoop(-1, fd, snmp_fd, &(addrlist->addr), 0, 0,
438 backendNetworkSideCB);
441 _cupsSNMPClose(snmp_fd);
443 httpAddrFreeList(addrlist);
448 * Don't try printing files less than 2 bytes...
453 _cupsLangPuts(stderr, _("ERROR: Empty print file!\n"));
455 return (CUPS_BACKEND_FAILED);
459 * Point to the single file from stdin...
462 filename = tmpfilename;
470 * Point to the files on the command-line...
473 num_files = argc - 6;
479 compress_files(num_files, files);
480 #endif /* HAVE_LIBZ */
483 fprintf(stderr, "DEBUG: %d files to send in job...\n", num_files);
486 * Set the authentication info, if any...
489 cupsSetPasswordCB(password_cb);
494 * Use authenticaion information in the device URI...
497 if ((password = strchr(username, ':')) != NULL)
500 cupsSetUser(username);
505 * Try loading authentication information from the environment.
508 const char *ptr = getenv("AUTH_USERNAME");
513 password = getenv("AUTH_PASSWORD");
517 * Try connecting to the remote server...
522 start_time = time(NULL);
524 fputs("STATE: +connecting-to-device\n", stderr);
528 fprintf(stderr, "DEBUG: Connecting to %s:%d\n", hostname, port);
529 _cupsLangPuts(stderr, _("INFO: Connecting to printer...\n"));
531 if ((http = httpConnectEncrypt(hostname, port, cupsEncryption())) == NULL)
536 if (getenv("CLASS") != NULL)
539 * If the CLASS environment variable is set, the job was submitted
540 * to a class and not to a specific queue. In this case, we want
541 * to abort immediately so that the job can be requeued on the next
542 * available printer in the class.
545 _cupsLangPuts(stderr,
546 _("INFO: Unable to contact printer, queuing on next "
547 "printer in class...\n"));
553 * Sleep 5 seconds to keep the job from requeuing too rapidly...
558 return (CUPS_BACKEND_FAILED);
561 if (errno == ECONNREFUSED || errno == EHOSTDOWN ||
562 errno == EHOSTUNREACH)
564 if (contimeout && (time(NULL) - start_time) > contimeout)
566 _cupsLangPuts(stderr, _("ERROR: Printer not responding!\n"));
567 return (CUPS_BACKEND_FAILED);
572 _cupsLangPrintf(stderr,
573 _("WARNING: recoverable: Network host \'%s\' is busy; "
574 "will retry in %d seconds...\n"),
584 _cupsLangPrintf(stderr, _("ERROR: Unable to locate printer \'%s\'!\n"),
586 return (CUPS_BACKEND_STOP);
592 fprintf(stderr, "DEBUG: Connection error: %s\n", strerror(errno));
593 _cupsLangPuts(stderr,
594 _("ERROR: recoverable: Unable to connect to printer; will "
595 "retry in 30 seconds...\n"));
603 while (http == NULL);
605 if (job_cancelled || !http)
610 return (CUPS_BACKEND_FAILED);
613 fputs("STATE: -connecting-to-device\n", stderr);
614 _cupsLangPuts(stderr, _("INFO: Connected to printer...\n"));
617 if (http->hostaddr->addr.sa_family == AF_INET6)
618 fprintf(stderr, "DEBUG: Connected to [%s]:%d (IPv6)...\n",
619 httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
620 ntohs(http->hostaddr->ipv6.sin6_port));
622 #endif /* AF_INET6 */
623 if (http->hostaddr->addr.sa_family == AF_INET)
624 fprintf(stderr, "DEBUG: Connected to %s:%d (IPv4)...\n",
625 httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
626 ntohs(http->hostaddr->ipv4.sin_port));
629 * See if the printer supports SNMP...
632 if ((snmp_fd = _cupsSNMPOpen(http->hostaddr->addr.sa_family)) >= 0)
633 have_supplies = !backendSNMPSupplies(snmp_fd, http->hostaddr, &start_count,
636 have_supplies = start_count = 0;
639 * Build a URI for the printer and fill the standard IPP attributes for
640 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
641 * might contain username:password information...
644 httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), scheme, NULL, hostname,
648 * First validate the destination and see if the device supports multiple
649 * copies. We have to do this because some IPP servers (e.g. HP JetDirect)
650 * don't support the copies attribute...
660 * Check for side-channel requests...
663 backendCheckSideChannel(snmp_fd, http->hostaddr);
666 * Build the IPP request...
669 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
670 request->request.op.version[0] = version / 10;
671 request->request.op.version[1] = version % 10;
673 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
676 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
677 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
684 fputs("DEBUG: Getting supported attributes...\n", stderr);
686 if (http->version < HTTP_1_1)
689 if ((supported = cupsDoRequest(http, request, resource)) == NULL)
690 ipp_status = cupsLastError();
692 ipp_status = supported->request.status.status_code;
694 if (ipp_status > IPP_OK_CONFLICT)
696 if (ipp_status == IPP_PRINTER_BUSY ||
697 ipp_status == IPP_SERVICE_UNAVAILABLE)
699 if (contimeout && (time(NULL) - start_time) > contimeout)
701 _cupsLangPuts(stderr, _("ERROR: Printer not responding!\n"));
702 return (CUPS_BACKEND_FAILED);
707 _cupsLangPrintf(stderr,
708 _("WARNING: recoverable: Network host \'%s\' is busy; "
709 "will retry in %d seconds...\n"),
712 report_printer_state(supported, 0);
719 else if ((ipp_status == IPP_BAD_REQUEST ||
720 ipp_status == IPP_VERSION_NOT_SUPPORTED) && version > 10)
723 * Switch to IPP/1.0...
726 _cupsLangPrintf(stderr,
727 _("INFO: Printer does not support IPP/%d.%d, trying "
728 "IPP/1.0...\n"), version / 10, version % 10);
732 else if (ipp_status == IPP_NOT_FOUND)
734 _cupsLangPuts(stderr, _("ERROR: Destination printer does not exist!\n"));
737 ippDelete(supported);
739 return (CUPS_BACKEND_STOP);
741 else if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
743 if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
745 auth_info_required = "negotiate";
747 fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
748 return (CUPS_BACKEND_AUTH_REQUIRED);
752 _cupsLangPrintf(stderr,
753 _("ERROR: Unable to get printer status (%s)!\n"),
754 cupsLastErrorString());
759 ippDelete(supported);
763 else if ((copies_sup = ippFindAttribute(supported, "copies-supported",
764 IPP_TAG_RANGE)) != NULL)
767 * Has the "copies-supported" attribute - does it have an upper
771 if (copies_sup->values[0].range.upper <= 1)
772 copies_sup = NULL; /* No */
775 format_sup = ippFindAttribute(supported, "document-format-supported",
780 fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
781 format_sup->num_values);
782 for (i = 0; i < format_sup->num_values; i ++)
783 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
784 format_sup->values[i].string.text);
787 report_printer_state(supported, 0);
789 while (ipp_status > IPP_OK_CONFLICT);
792 * See if the printer is accepting jobs and is not stopped; if either
793 * condition is true and we are printing to a class, requeue the job...
796 if (getenv("CLASS") != NULL)
798 printer_state = ippFindAttribute(supported, "printer-state",
800 printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs",
803 if (printer_state == NULL ||
804 (printer_state->values[0].integer > IPP_PRINTER_PROCESSING &&
806 printer_accepting == NULL ||
807 !printer_accepting->values[0].boolean)
810 * If the CLASS environment variable is set, the job was submitted
811 * to a class and not to a specific queue. In this case, we want
812 * to abort immediately so that the job can be requeued on the next
813 * available printer in the class.
816 _cupsLangPuts(stderr,
817 _("INFO: Unable to contact printer, queuing on next "
818 "printer in class...\n"));
820 ippDelete(supported);
827 * Sleep 5 seconds to keep the job from requeuing too rapidly...
832 return (CUPS_BACKEND_FAILED);
839 * If we've shown a recoverable error make sure the printer proxies
840 * have a chance to see the recovered message. Not pretty but
841 * necessary for now...
844 fputs("INFO: recovered: \n", stderr);
849 * See if the printer supports multiple copies...
852 copies = atoi(argv[4]);
854 if (copies_sup || argc < 7)
856 copies_remaining = 1;
862 copies_remaining = copies;
865 * Then issue the print-job request...
870 while (copies_remaining > 0)
873 * Check for side-channel requests...
876 backendCheckSideChannel(snmp_fd, http->hostaddr);
879 * Build the IPP request...
886 request = ippNewRequest(IPP_CREATE_JOB);
888 request = ippNewRequest(IPP_PRINT_JOB);
890 request->request.op.version[0] = version / 10;
891 request->request.op.version[1] = version % 10;
893 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
896 fprintf(stderr, "DEBUG: printer-uri = \"%s\"\n", uri);
899 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
900 "requesting-user-name", NULL, argv[2]);
902 fprintf(stderr, "DEBUG: requesting-user-name = \"%s\"\n", argv[2]);
905 * Only add a "job-name" attribute if the remote server supports
906 * copy generation - some IPP implementations like HP's don't seem
907 * to like UTF-8 job names (STR #1837)...
910 if (argv[3][0] && copies_sup)
911 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
914 fprintf(stderr, "DEBUG: job-name = \"%s\"\n", argv[3]);
918 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
919 "compression", NULL, "gzip");
920 #endif /* HAVE_LIBZ */
923 * Handle options on the command-line...
927 num_options = cupsParseOptions(argv[5], 0, &options);
930 if (!strcasecmp(final_content_type, "application/pictwps") &&
933 if (format_sup != NULL)
935 for (i = 0; i < format_sup->num_values; i ++)
936 if (!strcasecmp(final_content_type, format_sup->values[i].string.text))
940 if (format_sup == NULL || i >= format_sup->num_values)
943 * Remote doesn't support "application/pictwps" (i.e. it's not MacOS X)
944 * so convert the document to PostScript...
947 if (run_pictwps_filter(argv, files[0]))
955 return (CUPS_BACKEND_FAILED);
958 files[0] = pstmpname;
961 * Change the MIME type to application/postscript and change the
962 * number of copies to 1...
965 final_content_type = "application/postscript";
967 copies_remaining = 1;
971 #endif /* __APPLE__ */
973 if (format_sup != NULL)
975 for (i = 0; i < format_sup->num_values; i ++)
976 if (!strcasecmp(final_content_type, format_sup->values[i].string.text))
979 if (i < format_sup->num_values)
980 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
981 "document-format", NULL, final_content_type);
984 if (copies_sup && version > 10 && send_options)
987 * Only send options if the destination printer supports the copies
988 * attribute and IPP/1.1. This is a hack for the HP and Lexmark
989 * implementations of IPP, which do not accept extension attributes
990 * and incorrectly report a client-error-bad-request error instead of
991 * the successful-ok-unsupported-attributes status. In short, at least
992 * some HP and Lexmark implementations of IPP are non-compliant.
995 cupsEncodeOptions(request, num_options, options);
997 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies",
1001 cupsFreeOptions(num_options, options);
1004 * If copies aren't supported, then we are likely dealing with an HP
1005 * JetDirect. The HP IPP implementation seems to close the connection
1006 * after every request - that is, it does *not* implement HTTP Keep-
1007 * Alive, which is REQUIRED by HTTP/1.1...
1011 httpReconnect(http);
1017 if (http->version < HTTP_1_1)
1018 httpReconnect(http);
1021 response = cupsDoRequest(http, request, resource);
1023 response = cupsDoFileRequest(http, request, resource, files[0]);
1025 ipp_status = cupsLastError();
1027 if (ipp_status > IPP_OK_CONFLICT)
1034 if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1035 ipp_status == IPP_PRINTER_BUSY)
1037 _cupsLangPuts(stderr,
1038 _("INFO: Printer busy; will retry in 10 seconds...\n"));
1041 else if ((ipp_status == IPP_BAD_REQUEST ||
1042 ipp_status == IPP_VERSION_NOT_SUPPORTED) && version > 10)
1045 * Switch to IPP/1.0...
1048 _cupsLangPrintf(stderr,
1049 _("INFO: Printer does not support IPP/%d.%d, trying "
1050 "IPP/1.0...\n"), version / 10, version % 10);
1052 httpReconnect(http);
1057 * Update auth-info-required as needed...
1060 _cupsLangPrintf(stderr, _("ERROR: Print file was not accepted (%s)!\n"),
1061 cupsLastErrorString());
1063 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
1065 fprintf(stderr, "DEBUG: WWW-Authenticate=\"%s\"\n",
1066 httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE));
1069 * Normal authentication goes through the password callback, which sets
1070 * auth_info_required to "username,password". Kerberos goes directly
1071 * through GSSAPI, so look for Negotiate in the WWW-Authenticate header
1072 * here and set auth_info_required as needed...
1075 if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
1077 auth_info_required = "negotiate";
1081 else if ((job_id_attr = ippFindAttribute(response, "job-id",
1082 IPP_TAG_INTEGER)) == NULL)
1084 _cupsLangPuts(stderr,
1085 _("NOTICE: Print file accepted - job ID unknown.\n"));
1090 job_id = job_id_attr->values[0].integer;
1091 _cupsLangPrintf(stderr, _("NOTICE: Print file accepted - job ID %d.\n"),
1095 ippDelete(response);
1100 if (job_id && num_files > 1)
1102 for (i = 0; i < num_files; i ++)
1105 * Check for side-channel requests...
1108 backendCheckSideChannel(snmp_fd, http->hostaddr);
1111 * Send the next file in the job...
1114 request = ippNewRequest(IPP_SEND_DOCUMENT);
1115 request->request.op.version[0] = version / 10;
1116 request->request.op.version[1] = version % 10;
1118 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1121 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1125 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1126 "requesting-user-name", NULL, argv[2]);
1128 if ((i + 1) == num_files)
1129 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
1131 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
1132 "document-format", NULL, content_type);
1134 if (http->version < HTTP_1_1)
1135 httpReconnect(http);
1137 ippDelete(cupsDoFileRequest(http, request, resource, files[i]));
1139 if (cupsLastError() > IPP_OK_CONFLICT)
1141 ipp_status = cupsLastError();
1143 _cupsLangPrintf(stderr,
1144 _("ERROR: Unable to add file %d to job: %s\n"),
1145 job_id, cupsLastErrorString());
1151 if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
1153 fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
1154 copies_remaining --;
1156 else if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1157 ipp_status == IPP_PRINTER_BUSY)
1160 copies_remaining --;
1163 * Wait for the job to complete...
1166 if (!job_id || !waitjob)
1169 _cupsLangPuts(stderr, _("INFO: Waiting for job to complete...\n"));
1171 for (delay = 1; !job_cancelled;)
1174 * Check for side-channel requests...
1177 backendCheckSideChannel(snmp_fd, http->hostaddr);
1180 * Build an IPP_GET_JOB_ATTRIBUTES request...
1183 request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
1184 request->request.op.version[0] = version / 10;
1185 request->request.op.version[1] = version % 10;
1187 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1190 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1194 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1195 "requesting-user-name", NULL, argv[2]);
1197 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1198 "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
1205 if (!copies_sup || http->version < HTTP_1_1)
1206 httpReconnect(http);
1208 response = cupsDoRequest(http, request, resource);
1209 ipp_status = cupsLastError();
1211 if (ipp_status == IPP_NOT_FOUND)
1214 * Job has gone away and/or the server has no job history...
1217 ippDelete(response);
1219 ipp_status = IPP_OK;
1223 if (ipp_status > IPP_OK_CONFLICT)
1225 if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
1226 ipp_status != IPP_PRINTER_BUSY)
1228 ippDelete(response);
1230 _cupsLangPrintf(stderr,
1231 _("ERROR: Unable to get job %d attributes (%s)!\n"),
1232 job_id, cupsLastErrorString());
1239 if ((job_state = ippFindAttribute(response, "job-state",
1240 IPP_TAG_ENUM)) != NULL)
1243 * Stop polling if the job is finished or pending-held...
1246 if (job_state->values[0].integer > IPP_JOB_STOPPED)
1248 if ((job_sheets = ippFindAttribute(response,
1249 "job-media-sheets-completed",
1250 IPP_TAG_INTEGER)) != NULL)
1251 fprintf(stderr, "PAGE: total %d\n",
1252 job_sheets->values[0].integer);
1254 ippDelete(response);
1261 * If the printer does not return a job-state attribute, it does not
1262 * conform to the IPP specification - break out immediately and fail
1266 fputs("DEBUG: No job-state available from printer - stopping queue.\n",
1268 ipp_status = IPP_INTERNAL_ERROR;
1273 ippDelete(response);
1276 * Check the printer state and report it if necessary...
1279 check_printer_state(http, uri, resource, argv[2], version, job_id);
1282 * Wait 1-10 seconds before polling again...
1294 * Cancel the job as needed...
1297 if (job_cancelled && job_id)
1298 cancel_job(http, uri, job_id, resource, argv[2], version);
1301 * Check the printer state and report it if necessary...
1304 check_printer_state(http, uri, resource, argv[2], version, job_id);
1307 * Collect the final page count as needed...
1310 if (have_supplies &&
1311 !backendSNMPSupplies(snmp_fd, http->hostaddr, &page_count, NULL) &&
1312 page_count > start_count)
1313 fprintf(stderr, "PAGE: total %d\n", page_count - start_count);
1317 * See if we used Kerberos at all...
1321 auth_info_required = "negotiate";
1322 #endif /* HAVE_GSSAPI */
1330 ippDelete(supported);
1333 * Remove the temporary file(s) if necessary...
1337 unlink(tmpfilename);
1342 for (i = 0; i < num_files; i ++)
1345 #endif /* HAVE_LIBZ */
1350 #endif /* __APPLE__ */
1353 * Return the queue status...
1356 fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
1358 if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
1359 return (CUPS_BACKEND_AUTH_REQUIRED);
1360 else if (ipp_status == IPP_INTERNAL_ERROR)
1361 return (CUPS_BACKEND_STOP);
1362 else if (ipp_status > IPP_OK_CONFLICT)
1363 return (CUPS_BACKEND_FAILED);
1366 _cupsLangPuts(stderr, _("INFO: Ready to print.\n"));
1367 return (CUPS_BACKEND_OK);
1373 * 'cancel_job()' - Cancel a print job.
1377 cancel_job(http_t *http, /* I - HTTP connection */
1378 const char *uri, /* I - printer-uri */
1379 int id, /* I - job-id */
1380 const char *resource, /* I - Resource path */
1381 const char *user, /* I - requesting-user-name */
1382 int version) /* I - IPP version */
1384 ipp_t *request; /* Cancel-Job request */
1387 _cupsLangPuts(stderr, _("INFO: Canceling print job...\n"));
1389 request = ippNewRequest(IPP_CANCEL_JOB);
1390 request->request.op.version[0] = version / 10;
1391 request->request.op.version[1] = version % 10;
1393 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1395 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
1397 if (user && user[0])
1398 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1399 "requesting-user-name", NULL, user);
1405 if (http->version < HTTP_1_1)
1406 httpReconnect(http);
1408 ippDelete(cupsDoRequest(http, request, resource));
1410 if (cupsLastError() > IPP_OK_CONFLICT)
1411 _cupsLangPrintf(stderr, _("ERROR: Unable to cancel job %d: %s\n"), id,
1412 cupsLastErrorString());
1417 * 'check_printer_state()' - Check the printer state...
1421 check_printer_state(
1422 http_t *http, /* I - HTTP connection */
1423 const char *uri, /* I - Printer URI */
1424 const char *resource, /* I - Resource path */
1425 const char *user, /* I - Username, if any */
1426 int version, /* I - IPP version */
1427 int job_id) /* I - Current job ID */
1429 ipp_t *request, /* IPP request */
1430 *response; /* IPP response */
1431 static const char * const attrs[] = /* Attributes we want */
1433 "com.apple.print.recoverable-message",
1439 "printer-state-message",
1440 "printer-state-reasons"
1445 * Check on the printer state...
1448 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
1449 request->request.op.version[0] = version / 10;
1450 request->request.op.version[1] = version % 10;
1452 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1455 if (user && user[0])
1456 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1457 "requesting-user-name", NULL, user);
1459 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1460 "requested-attributes",
1461 (int)(sizeof(attrs) / sizeof(attrs[0])), NULL, attrs);
1467 if (http->version < HTTP_1_1)
1468 httpReconnect(http);
1470 if ((response = cupsDoRequest(http, request, resource)) != NULL)
1472 report_printer_state(response, job_id);
1473 ippDelete(response);
1480 * 'compress_files()' - Compress print files...
1484 compress_files(int num_files, /* I - Number of files */
1485 char **files) /* I - Files */
1487 int i, /* Looping var */
1488 fd; /* Temporary file descriptor */
1489 ssize_t bytes; /* Bytes read/written */
1490 size_t total; /* Total bytes read */
1491 cups_file_t *in, /* Input file */
1492 *out; /* Output file */
1493 struct stat outinfo; /* Output file information */
1494 char filename[1024], /* Temporary filename */
1495 buffer[32768]; /* Copy buffer */
1498 fprintf(stderr, "DEBUG: Compressing %d job files...\n", num_files);
1499 for (i = 0; i < num_files; i ++)
1501 if ((fd = cupsTempFd(filename, sizeof(filename))) < 0)
1503 _cupsLangPrintf(stderr,
1504 _("ERROR: Unable to create temporary compressed print "
1505 "file: %s\n"), strerror(errno));
1506 exit(CUPS_BACKEND_FAILED);
1509 if ((out = cupsFileOpenFd(fd, "w9")) == NULL)
1511 _cupsLangPrintf(stderr,
1512 _("ERROR: Unable to open temporary compressed print "
1513 "file: %s\n"), strerror(errno));
1514 exit(CUPS_BACKEND_FAILED);
1517 if ((in = cupsFileOpen(files[i], "r")) == NULL)
1519 _cupsLangPrintf(stderr,
1520 _("ERROR: Unable to open print file \"%s\": %s\n"),
1521 files[i], strerror(errno));
1523 exit(CUPS_BACKEND_FAILED);
1527 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
1528 if (cupsFileWrite(out, buffer, bytes) < bytes)
1530 _cupsLangPrintf(stderr,
1531 _("ERROR: Unable to write %d bytes to \"%s\": %s\n"),
1532 (int)bytes, filename, strerror(errno));
1535 exit(CUPS_BACKEND_FAILED);
1543 files[i] = strdup(filename);
1545 if (!stat(filename, &outinfo))
1547 "DEBUG: File %d compressed to %.1f%% of original size, "
1548 CUPS_LLFMT " bytes...\n",
1549 i + 1, 100.0 * outinfo.st_size / total,
1550 CUPS_LLCAST outinfo.st_size);
1553 #endif /* HAVE_LIBZ */
1557 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
1560 static const char * /* O - Password */
1561 password_cb(const char *prompt) /* I - Prompt (not used) */
1566 * Remember that we need to authenticate...
1569 auth_info_required = "username,password";
1571 if (password && *password && password_tries < 3)
1580 * Give up after 3 tries or if we don't have a password to begin with...
1589 * 'report_attr()' - Report an IPP attribute value.
1593 report_attr(ipp_attribute_t *attr) /* I - Attribute */
1595 int i; /* Looping var */
1596 char value[1024], /* Value string */
1597 *valptr, /* Pointer into value string */
1598 *attrptr; /* Pointer into attribute value */
1602 * Convert the attribute values into quoted strings...
1605 for (i = 0, valptr = value;
1606 i < attr->num_values && valptr < (value + sizeof(value) - 10);
1612 switch (attr->value_tag)
1614 case IPP_TAG_INTEGER :
1616 snprintf(valptr, sizeof(value) - (valptr - value), "%d",
1617 attr->values[i].integer);
1618 valptr += strlen(valptr);
1623 case IPP_TAG_KEYWORD :
1625 for (attrptr = attr->values[i].string.text;
1626 *attrptr && valptr < (value + sizeof(value) - 10);
1629 if (*attrptr == '\\' || *attrptr == '\"')
1632 *valptr++ = *attrptr;
1639 * Unsupported value type...
1649 * Tell the scheduler about the new values...
1652 fprintf(stderr, "ATTR: %s=%s\n", attr->name, value);
1657 * 'report_printer_state()' - Report the printer state.
1660 static int /* O - Number of reasons shown */
1661 report_printer_state(ipp_t *ipp, /* I - IPP response */
1662 int job_id) /* I - Current job ID */
1664 int i; /* Looping var */
1665 int count; /* Count of reasons shown... */
1666 ipp_attribute_t *caprm, /* com.apple.print.recoverable-message */
1667 *psm, /* printer-state-message */
1668 *reasons, /* printer-state-reasons */
1669 *marker; /* marker-* attributes */
1670 const char *reason; /* Current reason */
1671 const char *prefix; /* Prefix for STATE: line */
1672 char state[1024]; /* State string */
1673 int saw_caprw; /* Saw com.apple.print.recoverable-warning state */
1676 if ((psm = ippFindAttribute(ipp, "printer-state-message",
1677 IPP_TAG_TEXT)) != NULL)
1678 fprintf(stderr, "INFO: %s\n", psm->values[0].string.text);
1680 if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
1681 IPP_TAG_KEYWORD)) == NULL)
1688 for (i = 0, count = 0; i < reasons->num_values; i ++)
1690 reason = reasons->values[i].string.text;
1692 if (!strcmp(reason, "com.apple.print.recoverable-warning"))
1694 else if (strcmp(reason, "paused"))
1696 strlcat(state, prefix, sizeof(state));
1697 strlcat(state, reason, sizeof(state));
1704 fprintf(stderr, "%s\n", state);
1707 * Relay com.apple.print.recoverable-message...
1710 if ((caprm = ippFindAttribute(ipp, "com.apple.print.recoverable-message",
1711 IPP_TAG_TEXT)) != NULL)
1712 fprintf(stderr, "WARNING: %s: %s\n",
1713 saw_caprw ? "recoverable" : "recovered",
1714 caprm->values[0].string.text);
1717 * Relay the current marker-* attribute values...
1720 if ((marker = ippFindAttribute(ipp, "marker-colors", IPP_TAG_NAME)) != NULL)
1721 report_attr(marker);
1722 if ((marker = ippFindAttribute(ipp, "marker-high-levels",
1723 IPP_TAG_INTEGER)) != NULL)
1724 report_attr(marker);
1725 if ((marker = ippFindAttribute(ipp, "marker-levels",
1726 IPP_TAG_INTEGER)) != NULL)
1727 report_attr(marker);
1728 if ((marker = ippFindAttribute(ipp, "marker-low-levels",
1729 IPP_TAG_INTEGER)) != NULL)
1730 report_attr(marker);
1731 if ((marker = ippFindAttribute(ipp, "marker-message", IPP_TAG_TEXT)) != NULL)
1732 report_attr(marker);
1733 if ((marker = ippFindAttribute(ipp, "marker-names", IPP_TAG_NAME)) != NULL)
1734 report_attr(marker);
1735 if ((marker = ippFindAttribute(ipp, "marker-types", IPP_TAG_KEYWORD)) != NULL)
1736 report_attr(marker);
1744 * 'run_pictwps_filter()' - Convert PICT files to PostScript when printing
1747 * This step is required because the PICT format is not documented and
1748 * subject to change, so developing a filter for other OS's is infeasible.
1749 * Also, fonts required by the PICT file need to be embedded on the
1750 * client side (which has the fonts), so we run the filter to get a
1751 * PostScript file for printing...
1754 static int /* O - Exit status of filter */
1755 run_pictwps_filter(char **argv, /* I - Command-line arguments */
1756 const char *filename)/* I - Filename */
1758 struct stat fileinfo; /* Print file information */
1759 const char *ppdfile; /* PPD file for destination printer */
1760 int pid; /* Child process ID */
1761 int fd; /* Temporary file descriptor */
1762 int status; /* Exit status of filter */
1763 const char *printer; /* PRINTER env var */
1764 static char ppdenv[1024]; /* PPD environment variable */
1768 * First get the PPD file for the printer...
1771 printer = getenv("PRINTER");
1774 _cupsLangPuts(stderr,
1775 _("ERROR: PRINTER environment variable not defined!\n"));
1779 if ((ppdfile = cupsGetPPD(printer)) == NULL)
1781 _cupsLangPrintf(stderr,
1782 _("ERROR: Unable to get PPD file for printer \"%s\" - "
1783 "%s.\n"), printer, cupsLastErrorString());
1787 snprintf(ppdenv, sizeof(ppdenv), "PPD=%s", ppdfile);
1792 * Then create a temporary file for printing...
1795 if ((fd = cupsTempFd(pstmpname, sizeof(pstmpname))) < 0)
1797 _cupsLangPrintError("ERROR", _("Unable to create temporary file"));
1804 * Get the owner of the spool file - it is owned by the user we want to run
1809 stat(argv[6], &fileinfo);
1813 * Use the OSX defaults, as an up-stream filter created the PICT
1817 fileinfo.st_uid = 1;
1818 fileinfo.st_gid = 80;
1822 chown(ppdfile, fileinfo.st_uid, fileinfo.st_gid);
1824 fchown(fd, fileinfo.st_uid, fileinfo.st_gid);
1827 * Finally, run the filter to convert the file...
1830 if ((pid = fork()) == 0)
1833 * Child process for pictwpstops... Redirect output of pictwpstops to a
1843 * Change to an unpriviledged user...
1846 if (setgid(fileinfo.st_gid))
1849 if (setuid(fileinfo.st_uid))
1853 execlp("pictwpstops", printer, argv[1], argv[2], argv[3], argv[4], argv[5],
1855 _cupsLangPrintf(stderr, _("ERROR: Unable to exec pictwpstops: %s\n"),
1868 _cupsLangPrintf(stderr, _("ERROR: Unable to fork pictwpstops: %s\n"),
1876 * Now wait for the filter to complete...
1879 if (wait(&status) < 0)
1881 _cupsLangPrintf(stderr, _("ERROR: Unable to wait for pictwpstops: %s\n"),
1897 _cupsLangPrintf(stderr, _("ERROR: pictwpstops exited with status %d!\n"),
1900 _cupsLangPrintf(stderr, _("ERROR: pictwpstops exited on signal %d!\n"),
1907 * Return with no errors..
1912 #endif /* __APPLE__ */
1916 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
1920 sigterm_handler(int sig) /* I - Signal */
1922 (void)sig; /* remove compiler warnings... */
1927 * Flag that the job should be cancelled...
1935 * The scheduler already tried to cancel us once, now just terminate
1936 * after removing our temp files!
1940 unlink(tmpfilename);
1945 #endif /* __APPLE__ */
1952 * End of "$Id: ipp.c 8950 2010-01-14 22:40:19Z mike $".