2 * "$Id: ipp.c 7948 2008-09-17 00:04:12Z mike $"
4 * IPP backend for CUPS.
6 * Copyright 2007-2011 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 * monitor_printer() - Monitor the printer state...
24 * new_request() - Create a new print creation or validation request.
25 * password_cb() - Disable the password prompt for
26 * cupsDoFileRequest().
27 * report_attr() - Report an IPP attribute value.
28 * report_printer_state() - Report the printer state.
29 * sigterm_handler() - Handle 'terminate' signals that stop the backend.
33 * Include necessary headers.
36 #include "backend-private.h"
37 #include <sys/types.h>
46 typedef struct _cups_monitor_s
/**** Monitoring data ****/
48 const char *uri
, /* Printer URI */
49 *hostname
, /* Hostname */
51 *resource
; /* Resource path */
52 int port
, /* Port number */
53 version
, /* IPP version */
54 job_id
; /* Job ID for submitted job */
55 http_encryption_t encryption
; /* Use encryption? */
56 ipp_jstate_t job_state
; /* Current job state */
57 ipp_pstate_t printer_state
; /* Current printer state */
65 static const char *auth_info_required
= "none";
66 /* New auth-info-required value */
67 static const char * const jattrs
[] = /* Job attributes we want */
69 "job-media-sheets-completed",
73 static int job_canceled
= 0; /* Job cancelled? */
74 static char *password
= NULL
; /* Password for device URI */
75 static int password_tries
= 0; /* Password tries */
76 static const char * const pattrs
[] = /* Printer attributes we want */
80 "document-format-supported",
88 "media-col-supported",
89 "multiple-document-handling-supported",
90 "operations-supported",
92 "printer-alert-description",
93 "printer-is-accepting-jobs",
95 "printer-state-message",
96 "printer-state-reasons",
98 static const char * const remote_job_states
[] =
99 { /* Remote job state keywords */
100 "cups-remote-pending",
101 "cups-remote-pending-held",
102 "cups-remote-processing",
103 "cups-remote-stopped",
104 "cups-remote-canceled",
105 "cups-remote-aborted",
106 "cups-remote-completed"
108 static char tmpfilename
[1024] = ""; /* Temporary spool file name */
115 static void cancel_job(http_t
*http
, const char *uri
, int id
,
116 const char *resource
, const char *user
,
118 static ipp_pstate_t
check_printer_state(http_t
*http
, const char *uri
,
119 const char *resource
,
120 const char *user
, int version
,
123 static void compress_files(int num_files
, char **files
);
124 #endif /* HAVE_LIBZ */
125 static void *monitor_printer(_cups_monitor_t
*monitor
);
126 static ipp_t
*new_request(ipp_op_t op
, int version
, const char *uri
,
127 const char *user
, const char *title
,
128 int num_options
, cups_option_t
*options
,
129 const char *compression
, int copies
,
130 const char *format
, _ppd_cache_t
*pc
,
131 ipp_attribute_t
*media_col_sup
,
132 ipp_attribute_t
*doc_handling_sup
);
133 static const char *password_cb(const char *);
134 static void report_attr(ipp_attribute_t
*attr
);
135 static int report_printer_state(ipp_t
*ipp
, int job_id
);
136 static void sigterm_handler(int sig
);
140 * 'main()' - Send a file to the printer or server.
144 * printer-uri job-id user title copies options [file]
147 int /* O - Exit status */
148 main(int argc
, /* I - Number of command-line args */
149 char *argv
[]) /* I - Command-line arguments */
151 int i
; /* Looping var */
152 int send_options
; /* Send job options? */
153 int num_options
; /* Number of printer options */
154 cups_option_t
*options
; /* Printer options */
155 const char *device_uri
; /* Device URI */
156 char scheme
[255], /* Scheme in URI */
157 hostname
[1024], /* Hostname */
158 username
[255], /* Username info */
159 resource
[1024], /* Resource info (printer name) */
160 addrname
[256], /* Address name */
161 *optptr
, /* Pointer to URI options */
162 *name
, /* Name of option */
163 *value
, /* Value of option */
164 sep
; /* Separator character */
165 http_addrlist_t
*addrlist
; /* Address of printer */
166 int snmp_fd
, /* SNMP socket */
167 start_count
, /* Page count via SNMP at start */
168 page_count
, /* Page count via SNMP */
169 have_supplies
; /* Printer supports supply levels? */
170 int num_files
; /* Number of files to print */
171 char **files
, /* Files to print */
172 *compatfile
= NULL
; /* Compatibility filename */
173 off_t compatsize
= 0; /* Size of compatibility file */
174 int port
; /* Port number (not used) */
175 char portname
[255]; /* Port name */
176 char uri
[HTTP_MAX_URI
]; /* Updated URI without user/pass */
177 http_status_t http_status
; /* Status of HTTP request */
178 ipp_status_t ipp_status
; /* Status of IPP request */
179 http_t
*http
; /* HTTP connection */
180 ipp_t
*request
, /* IPP request */
181 *response
, /* IPP response */
182 *supported
; /* get-printer-attributes response */
183 time_t start_time
; /* Time of first connect */
184 int contimeout
; /* Connection timeout */
185 int delay
, /* Delay for retries */
186 prev_delay
; /* Previous delay */
187 const char *compression
; /* Compression mode */
188 int waitjob
, /* Wait for job complete? */
189 waitprinter
; /* Wait for printer ready? */
190 _cups_monitor_t monitor
; /* Monitoring data */
191 ipp_attribute_t
*job_id_attr
; /* job-id attribute */
192 int job_id
; /* job-id value */
193 ipp_attribute_t
*job_sheets
; /* job-media-sheets-completed */
194 ipp_attribute_t
*job_state
; /* job-state */
195 ipp_attribute_t
*copies_sup
; /* copies-supported */
196 ipp_attribute_t
*cups_version
; /* cups-version */
197 ipp_attribute_t
*format_sup
; /* document-format-supported */
198 ipp_attribute_t
*media_col_sup
; /* media-col-supported */
199 ipp_attribute_t
*operations_sup
; /* operations-supported */
200 ipp_attribute_t
*doc_handling_sup
; /* multiple-document-handling-supported */
201 ipp_attribute_t
*printer_state
; /* printer-state attribute */
202 ipp_attribute_t
*printer_accepting
; /* printer-is-accepting-jobs */
203 int validate_job
; /* Does printer support Validate-Job? */
204 int copies
, /* Number of copies for job */
205 copies_remaining
; /* Number of copies remaining */
206 const char *content_type
, /* CONTENT_TYPE environment variable */
207 *final_content_type
, /* FINAL_CONTENT_TYPE environment var */
208 *document_format
; /* document-format value */
209 int fd
; /* File descriptor */
210 off_t bytes
; /* Bytes copied */
211 char buffer
[16384]; /* Copy buffer */
212 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
213 struct sigaction action
; /* Actions for POSIX signals */
214 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
215 int version
; /* IPP version */
216 ppd_file_t
*ppd
; /* PPD file */
217 _ppd_cache_t
*pc
; /* PPD cache and mapping data */
221 * Make sure status messages are not buffered...
224 setbuf(stderr
, NULL
);
227 * Ignore SIGPIPE and catch SIGTERM signals...
231 sigset(SIGPIPE
, SIG_IGN
);
232 sigset(SIGTERM
, sigterm_handler
);
233 #elif defined(HAVE_SIGACTION)
234 memset(&action
, 0, sizeof(action
));
235 action
.sa_handler
= SIG_IGN
;
236 sigaction(SIGPIPE
, &action
, NULL
);
238 sigemptyset(&action
.sa_mask
);
239 sigaddset(&action
.sa_mask
, SIGTERM
);
240 action
.sa_handler
= sigterm_handler
;
241 sigaction(SIGTERM
, &action
, NULL
);
243 signal(SIGPIPE
, SIG_IGN
);
244 signal(SIGTERM
, sigterm_handler
);
245 #endif /* HAVE_SIGSET */
248 * Check command-line...
255 if ((s
= strrchr(argv
[0], '/')) != NULL
)
260 printf("network %s \"Unknown\" \"%s (%s)\"\n",
261 s
, _cupsLangString(cupsLangDefault(),
262 _("Internet Printing Protocol")), s
);
263 return (CUPS_BACKEND_OK
);
267 _cupsLangPrintf(stderr
,
268 _("Usage: %s job-id user title copies options [file]"),
270 return (CUPS_BACKEND_STOP
);
274 * Get the (final) content type...
277 if ((content_type
= getenv("CONTENT_TYPE")) == NULL
)
278 content_type
= "application/octet-stream";
280 if ((final_content_type
= getenv("FINAL_CONTENT_TYPE")) == NULL
)
282 final_content_type
= content_type
;
284 if (!strncmp(final_content_type
, "printer/", 8))
285 final_content_type
= "application/vnd.cups-raw";
289 * Extract the hostname and printer name from the URI...
292 while ((device_uri
= cupsBackendDeviceURI(argv
)) == NULL
)
294 _cupsLangPrintFilter(stderr
, "INFO", _("Unable to locate printer."));
297 if (getenv("CLASS") != NULL
)
298 return (CUPS_BACKEND_FAILED
);
301 httpSeparateURI(HTTP_URI_CODING_ALL
, device_uri
, scheme
, sizeof(scheme
),
302 username
, sizeof(username
), hostname
, sizeof(hostname
), &port
,
303 resource
, sizeof(resource
));
306 port
= IPP_PORT
; /* Default to port 631 */
308 if (!strcmp(scheme
, "https"))
309 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS
);
311 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED
);
314 * See if there are any options...
321 contimeout
= 7 * 24 * 60 * 60;
323 if ((optptr
= strchr(resource
, '?')) != NULL
)
326 * Yup, terminate the device name string and move to the first
327 * character of the optptr...
333 * Then parse the optptr...
344 while (*optptr
&& *optptr
!= '=' && *optptr
!= '+' && *optptr
!= '&')
347 if ((sep
= *optptr
) != '\0')
358 while (*optptr
&& *optptr
!= '+' && *optptr
!= '&')
368 * Process the option...
371 if (!strcasecmp(name
, "waitjob"))
374 * Wait for job completion?
377 waitjob
= !strcasecmp(value
, "on") ||
378 !strcasecmp(value
, "yes") ||
379 !strcasecmp(value
, "true");
381 else if (!strcasecmp(name
, "waitprinter"))
384 * Wait for printer idle?
387 waitprinter
= !strcasecmp(value
, "on") ||
388 !strcasecmp(value
, "yes") ||
389 !strcasecmp(value
, "true");
391 else if (!strcasecmp(name
, "encryption"))
394 * Enable/disable encryption?
397 if (!strcasecmp(value
, "always"))
398 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS
);
399 else if (!strcasecmp(value
, "required"))
400 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED
);
401 else if (!strcasecmp(value
, "never"))
402 cupsSetEncryption(HTTP_ENCRYPT_NEVER
);
403 else if (!strcasecmp(value
, "ifrequested"))
404 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED
);
407 _cupsLangPrintFilter(stderr
, "ERROR",
408 _("Unknown encryption option value: \"%s\"."),
412 else if (!strcasecmp(name
, "version"))
414 if (!strcmp(value
, "1.0"))
416 else if (!strcmp(value
, "1.1"))
418 else if (!strcmp(value
, "2.0"))
420 else if (!strcmp(value
, "2.1"))
422 else if (!strcmp(value
, "2.2"))
426 _cupsLangPrintFilter(stderr
, "ERROR",
427 _("Unknown version option value: \"%s\"."),
432 else if (!strcasecmp(name
, "compression"))
434 if (!strcasecmp(value
, "true") || !strcasecmp(value
, "yes") ||
435 !strcasecmp(value
, "on") || !strcasecmp(value
, "gzip"))
436 compression
= "gzip";
438 #endif /* HAVE_LIBZ */
439 else if (!strcasecmp(name
, "contimeout"))
442 * Set the connection timeout...
446 contimeout
= atoi(value
);
454 _cupsLangPrintFilter(stderr
, "ERROR",
455 _("Unknown option \"%s\" with value \"%s\"."),
462 * If we have 7 arguments, print the file named on the command-line.
463 * Otherwise, copy stdin to a temporary file and print the temporary
470 send_options
= !strcasecmp(final_content_type
, "application/pdf") ||
471 !strcasecmp(final_content_type
, "application/vnd.cups-pdf") ||
472 !strncasecmp(final_content_type
, "image/", 6);
474 fputs("DEBUG: Sending stdin for job...\n", stderr
);
479 * Point to the files on the command-line...
482 num_files
= argc
- 6;
488 compress_files(num_files
, files
);
489 #endif /* HAVE_LIBZ */
491 fprintf(stderr
, "DEBUG: %d files to send in job...\n", num_files
);
495 * Set the authentication info, if any...
498 cupsSetPasswordCB(password_cb
);
503 * Use authenticaion information in the device URI...
506 if ((password
= strchr(username
, ':')) != NULL
)
509 cupsSetUser(username
);
514 * Try loading authentication information from the environment.
517 const char *ptr
= getenv("AUTH_USERNAME");
522 password
= getenv("AUTH_PASSWORD");
527 * For Kerberos, become the printing user (if we can) to get the credentials
531 if (!getuid() && (value
= getenv("AUTH_UID")) != NULL
)
532 seteuid(atoi(value
));
533 #endif /* HAVE_GSSAPI */
536 * Try finding the remote server...
539 start_time
= time(NULL
);
541 sprintf(portname
, "%d", port
);
543 fputs("STATE: +connecting-to-device\n", stderr
);
544 fprintf(stderr
, "DEBUG: Looking up \"%s\"...\n", hostname
);
546 while ((addrlist
= httpAddrGetList(hostname
, AF_UNSPEC
, portname
)) == NULL
)
548 _cupsLangPrintFilter(stderr
, "INFO",
549 _("Unable to locate printer \"%s\"."), hostname
);
552 if (getenv("CLASS") != NULL
)
554 fputs("STATE: -connecting-to-device\n", stderr
);
555 return (CUPS_BACKEND_STOP
);
559 http
= _httpCreate(hostname
, port
, addrlist
, cupsEncryption(), AF_UNSPEC
);
562 * See if the printer supports SNMP...
565 if ((snmp_fd
= _cupsSNMPOpen(addrlist
->addr
.addr
.sa_family
)) >= 0)
567 have_supplies
= !backendSNMPSupplies(snmp_fd
, &(addrlist
->addr
),
571 have_supplies
= start_count
= 0;
574 * Wait for data from the filter...
578 if (!backendWaitLoop(snmp_fd
, &(addrlist
->addr
), 0, backendNetworkSideCB
))
579 return (CUPS_BACKEND_OK
);
582 * Try connecting to the remote server...
585 delay
= _cupsNextDelay(0, &prev_delay
);
589 fprintf(stderr
, "DEBUG: Connecting to %s:%d\n", hostname
, port
);
590 _cupsLangPrintFilter(stderr
, "INFO", _("Connecting to printer."));
592 if (httpReconnect(http
))
594 int error
= errno
; /* Connection error */
596 if (http
->status
== HTTP_PKI_ERROR
)
597 fputs("STATE: +cups-certificate-error\n", stderr
);
602 if (getenv("CLASS") != NULL
)
605 * If the CLASS environment variable is set, the job was submitted
606 * to a class and not to a specific queue. In this case, we want
607 * to abort immediately so that the job can be requeued on the next
608 * available printer in the class.
611 _cupsLangPrintFilter(stderr
, "INFO",
612 _("Unable to contact printer, queuing on next "
613 "printer in class."));
616 * Sleep 5 seconds to keep the job from requeuing too rapidly...
621 fputs("STATE: -connecting-to-device\n", stderr
);
623 return (CUPS_BACKEND_FAILED
);
626 fprintf(stderr
, "DEBUG: Connection error: %s\n", strerror(errno
));
628 if (errno
== ECONNREFUSED
|| errno
== EHOSTDOWN
||
629 errno
== EHOSTUNREACH
)
631 if (contimeout
&& (time(NULL
) - start_time
) > contimeout
)
633 _cupsLangPrintFilter(stderr
, "ERROR",
634 _("The printer is not responding."));
635 fputs("STATE: -connecting-to-device\n", stderr
);
636 return (CUPS_BACKEND_FAILED
);
642 _cupsLangPrintFilter(stderr
, "WARNING",
643 _("The printer may not exist or "
644 "is unavailable at this time."));
648 _cupsLangPrintFilter(stderr
, "WARNING",
649 _("The printer is unreachable at this "
655 _cupsLangPrintFilter(stderr
, "WARNING",
656 _("The printer is busy."));
662 delay
= _cupsNextDelay(delay
, &prev_delay
);
666 _cupsLangPrintFilter(stderr
, "ERROR",
667 _("The printer is not responding."));
675 fputs("STATE: -cups-certificate-error\n", stderr
);
677 while (http
->fd
< 0);
679 if (job_canceled
|| !http
)
680 return (CUPS_BACKEND_FAILED
);
682 fputs("STATE: -connecting-to-device\n", stderr
);
683 _cupsLangPrintFilter(stderr
, "INFO", _("Connected to printer."));
685 fprintf(stderr
, "DEBUG: Connected to %s:%d...\n",
686 httpAddrString(http
->hostaddr
, addrname
, sizeof(addrname
)),
687 _httpAddrPort(http
->hostaddr
));
690 * Build a URI for the printer and fill the standard IPP attributes for
691 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
692 * might contain username:password information...
695 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), scheme
, NULL
, hostname
,
699 * First validate the destination and see if the device supports multiple
706 media_col_sup
= NULL
;
708 operations_sup
= NULL
;
709 doc_handling_sup
= NULL
;
715 * Check for side-channel requests...
718 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
721 * Build the IPP request...
724 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
725 request
->request
.op
.version
[0] = version
/ 10;
726 request
->request
.op
.version
[1] = version
% 10;
728 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
731 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
732 "requested-attributes", sizeof(pattrs
) / sizeof(pattrs
[0]),
739 fputs("DEBUG: Getting supported attributes...\n", stderr
);
741 if (http
->version
< HTTP_1_1
)
743 fprintf(stderr
, "DEBUG: Printer responded with HTTP version %d.%d.\n",
744 http
->version
/ 100, http
->version
% 100);
746 _cupsLangPrintFilter(stderr
, "ERROR",
747 _("This printer does not conform to the IPP "
748 "standard. Please contact the manufacturer of "
749 "your printer for assistance."));
752 supported
= cupsDoRequest(http
, request
, resource
);
753 ipp_status
= cupsLastError();
755 fprintf(stderr
, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
756 ippErrorString(ipp_status
), cupsLastErrorString());
758 if (ipp_status
> IPP_OK_CONFLICT
)
760 fprintf(stderr
, "DEBUG: Get-Printer-Attributes returned %s.\n",
761 ippErrorString(ipp_status
));
763 if (ipp_status
== IPP_PRINTER_BUSY
||
764 ipp_status
== IPP_SERVICE_UNAVAILABLE
)
766 if (contimeout
&& (time(NULL
) - start_time
) > contimeout
)
768 _cupsLangPrintFilter(stderr
, "ERROR",
769 _("The printer is not responding."));
770 return (CUPS_BACKEND_FAILED
);
773 _cupsLangPrintFilter(stderr
, "INFO", _("The printer is busy."));
775 report_printer_state(supported
, 0);
779 delay
= _cupsNextDelay(delay
, &prev_delay
);
781 else if ((ipp_status
== IPP_BAD_REQUEST
||
782 ipp_status
== IPP_VERSION_NOT_SUPPORTED
) && version
> 10)
785 * Switch to IPP/1.1 or IPP/1.0...
790 _cupsLangPrintFilter(stderr
, "INFO",
791 _("Printer does not support IPP/%d.%d, trying "
792 "IPP/%s."), version
/ 10, version
% 10, "1.1");
797 _cupsLangPrintFilter(stderr
, "INFO",
798 _("Printer does not support IPP/%d.%d, trying "
799 "IPP/%s."), version
/ 10, version
% 10, "1.0");
805 else if (ipp_status
== IPP_NOT_FOUND
)
807 _cupsLangPrintFilter(stderr
, "ERROR",
808 _("The printer URI is incorrect or no longer "
811 ippDelete(supported
);
813 return (CUPS_BACKEND_STOP
);
815 else if (ipp_status
== IPP_NOT_AUTHORIZED
|| ipp_status
== IPP_FORBIDDEN
)
817 if (!strncmp(httpGetField(http
, HTTP_FIELD_WWW_AUTHENTICATE
),
819 auth_info_required
= "negotiate";
821 fprintf(stderr
, "ATTR: auth-info-required=%s\n", auth_info_required
);
822 return (CUPS_BACKEND_AUTH_REQUIRED
);
826 _cupsLangPrintFilter(stderr
, "ERROR",
827 _("Unable to get printer status."));
831 ippDelete(supported
);
837 * Check printer-state-reasons for the "spool-area-full" keyword...
840 if ((printer_state
= ippFindAttribute(supported
, "printer-state-reasons",
841 IPP_TAG_KEYWORD
)) != NULL
)
843 for (i
= 0; i
< printer_state
->num_values
; i
++)
844 if (!strcmp(printer_state
->values
[0].string
.text
, "spool-area-full") ||
845 !strncmp(printer_state
->values
[0].string
.text
, "spool-area-full-",
849 if (i
< printer_state
->num_values
)
851 _cupsLangPrintFilter(stderr
, "INFO", _("The printer is busy."));
853 report_printer_state(supported
, 0);
857 delay
= _cupsNextDelay(delay
, &prev_delay
);
859 ippDelete(supported
);
865 _cupsLangPrintFilter(stderr
, "ERROR",
866 _("This printer does not conform to the IPP "
867 "standard. Please contact the manufacturer of "
868 "your printer for assistance."));
871 * Check for supported attributes...
874 if ((copies_sup
= ippFindAttribute(supported
, "copies-supported",
875 IPP_TAG_RANGE
)) != NULL
)
878 * Has the "copies-supported" attribute - does it have an upper
882 fprintf(stderr
, "DEBUG: copies-supported=%d-%d\n",
883 copies_sup
->values
[0].range
.lower
,
884 copies_sup
->values
[0].range
.upper
);
886 if (copies_sup
->values
[0].range
.upper
<= 1)
887 copies_sup
= NULL
; /* No */
890 cups_version
= ippFindAttribute(supported
, "cups-version", IPP_TAG_TEXT
);
892 if ((format_sup
= ippFindAttribute(supported
, "document-format-supported",
893 IPP_TAG_MIMETYPE
)) != NULL
)
895 fprintf(stderr
, "DEBUG: document-format-supported (%d values)\n",
896 format_sup
->num_values
);
897 for (i
= 0; i
< format_sup
->num_values
; i
++)
898 fprintf(stderr
, "DEBUG: [%d] = \"%s\"\n", i
,
899 format_sup
->values
[i
].string
.text
);
902 if ((media_col_sup
= ippFindAttribute(supported
, "media-col-supported",
903 IPP_TAG_KEYWORD
)) != NULL
)
905 fprintf(stderr
, "DEBUG: media-col-supported (%d values)\n",
906 media_col_sup
->num_values
);
907 for (i
= 0; i
< media_col_sup
->num_values
; i
++)
908 fprintf(stderr
, "DEBUG: [%d] = \"%s\"\n", i
,
909 media_col_sup
->values
[i
].string
.text
);
912 if ((operations_sup
= ippFindAttribute(supported
, "operations-supported",
913 IPP_TAG_ENUM
)) != NULL
)
915 for (i
= 0; i
< operations_sup
->num_values
; i
++)
916 if (operations_sup
->values
[i
].integer
== IPP_VALIDATE_JOB
)
924 _cupsLangPrintFilter(stderr
, "WARNING",
925 _("This printer does not conform to the IPP "
926 "standard and may not work."));
927 fputs("DEBUG: operations-supported does not list Validate-Job.\n",
933 _cupsLangPrintFilter(stderr
, "WARNING",
934 _("This printer does not conform to the IPP "
935 "standard and may not work."));
936 fputs("DEBUG: operations-supported not returned in "
937 "Get-Printer-Attributes request.\n", stderr
);
940 doc_handling_sup
= ippFindAttribute(supported
,
941 "multiple-document-handling-supported",
944 report_printer_state(supported
, 0);
946 while (ipp_status
> IPP_OK_CONFLICT
);
949 * See if the printer is accepting jobs and is not stopped; if either
950 * condition is true and we are printing to a class, requeue the job...
953 if (getenv("CLASS") != NULL
)
955 printer_state
= ippFindAttribute(supported
, "printer-state",
957 printer_accepting
= ippFindAttribute(supported
, "printer-is-accepting-jobs",
960 if (printer_state
== NULL
||
961 (printer_state
->values
[0].integer
> IPP_PRINTER_PROCESSING
&&
963 printer_accepting
== NULL
||
964 !printer_accepting
->values
[0].boolean
)
967 * If the CLASS environment variable is set, the job was submitted
968 * to a class and not to a specific queue. In this case, we want
969 * to abort immediately so that the job can be requeued on the next
970 * available printer in the class.
973 _cupsLangPrintFilter(stderr
, "INFO",
974 _("Unable to contact printer, queuing on next "
975 "printer in class."));
977 ippDelete(supported
);
981 * Sleep 5 seconds to keep the job from requeuing too rapidly...
986 return (CUPS_BACKEND_FAILED
);
991 * See if the printer supports multiple copies...
994 copies
= atoi(argv
[4]);
996 if (copies_sup
|| argc
< 7)
998 copies_remaining
= 1;
1000 if (argc
< 7 && !send_options
)
1004 copies_remaining
= copies
;
1007 * Prepare remaining printing options...
1015 num_options
= cupsParseOptions(argv
[5], 0, &options
);
1017 if (!cups_version
&& media_col_sup
)
1020 * Load the PPD file and generate PWG attribute mapping information...
1023 ppd
= ppdOpenFile(getenv("PPD"));
1024 pc
= _ppdCacheCreateWithPPD(ppd
);
1032 document_format
= NULL
;
1034 if (format_sup
!= NULL
)
1036 for (i
= 0; i
< format_sup
->num_values
; i
++)
1037 if (!strcasecmp(final_content_type
, format_sup
->values
[i
].string
.text
))
1039 document_format
= final_content_type
;
1043 if (!document_format
)
1045 for (i
= 0; i
< format_sup
->num_values
; i
++)
1046 if (!strcasecmp("application/octet-stream",
1047 format_sup
->values
[i
].string
.text
))
1049 document_format
= "application/octet-stream";
1056 * If the printer does not support HTTP/1.1 (which IPP requires), copy stdin
1057 * to a temporary file so that we can do a HTTP/1.0 submission...
1059 * (I hate compatibility hacks!)
1062 if (http
->version
< HTTP_1_1
&& num_files
== 0)
1064 if ((fd
= cupsTempFd(tmpfilename
, sizeof(tmpfilename
))) < 0)
1066 perror("DEBUG: Unable to create temporary file");
1067 return (CUPS_BACKEND_FAILED
);
1070 _cupsLangPrintFilter(stderr
, "INFO", _("Copying print data."));
1072 compatsize
= backendRunLoop(-1, fd
, snmp_fd
, &(addrlist
->addr
), 0, 0,
1073 backendNetworkSideCB
);
1077 compatfile
= tmpfilename
;
1078 files
= &compatfile
;
1081 else if (http
->version
< HTTP_1_1
&& num_files
== 1)
1083 struct stat fileinfo
; /* File information */
1085 if (!stat(files
[0], &fileinfo
))
1086 compatsize
= fileinfo
.st_size
;
1090 * Start monitoring the printer in the background...
1094 monitor
.hostname
= hostname
;
1095 monitor
.user
= argv
[2];
1096 monitor
.resource
= resource
;
1097 monitor
.port
= port
;
1098 monitor
.version
= version
;
1100 monitor
.encryption
= cupsEncryption();
1101 monitor
.job_state
= IPP_JOB_PENDING
;
1102 monitor
.printer_state
= IPP_PRINTER_IDLE
;
1104 _cupsThreadCreate((_cups_thread_func_t
)monitor_printer
, &monitor
);
1107 * Validate access to the printer...
1110 while (!job_canceled
&& validate_job
)
1112 request
= new_request(IPP_VALIDATE_JOB
, version
, uri
, argv
[2], argv
[3],
1113 num_options
, options
, compression
,
1114 copies_sup
? copies
: 1, document_format
, pc
,
1115 media_col_sup
, doc_handling_sup
);
1117 ippDelete(cupsDoRequest(http
, request
, resource
));
1119 ipp_status
= cupsLastError();
1121 fprintf(stderr
, "DEBUG: Validate-Job: %s (%s)\n",
1122 ippErrorString(ipp_status
), cupsLastErrorString());
1127 if (ipp_status
== IPP_SERVICE_UNAVAILABLE
|| ipp_status
== IPP_PRINTER_BUSY
)
1129 _cupsLangPrintFilter(stderr
, "INFO", _("The printer is busy."));
1132 else if (ipp_status
== IPP_NOT_AUTHORIZED
|| ipp_status
== IPP_FORBIDDEN
)
1135 * Update auth-info-required as needed...
1138 fprintf(stderr
, "DEBUG: WWW-Authenticate=\"%s\"\n",
1139 httpGetField(http
, HTTP_FIELD_WWW_AUTHENTICATE
));
1142 * Normal authentication goes through the password callback, which sets
1143 * auth_info_required to "username,password". Kerberos goes directly
1144 * through GSSAPI, so look for Negotiate in the WWW-Authenticate header
1145 * here and set auth_info_required as needed...
1148 if (!strncmp(httpGetField(http
, HTTP_FIELD_WWW_AUTHENTICATE
),
1150 auth_info_required
= "negotiate";
1154 else if (ipp_status
== IPP_OPERATION_NOT_SUPPORTED
)
1156 _cupsLangPrintFilter(stderr
, "WARNING",
1157 _("This printer does not conform to the IPP "
1158 "standard and may not work."));
1161 else if (ipp_status
< IPP_REDIRECTION_OTHER_SITE
)
1166 * Then issue the print-job request...
1171 while (!job_canceled
&& copies_remaining
> 0)
1174 * Check for side-channel requests...
1177 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
1180 * Build the IPP job creation request...
1186 request
= new_request(num_files
> 1 ? IPP_CREATE_JOB
: IPP_PRINT_JOB
,
1187 version
, uri
, argv
[2], argv
[3], num_options
, options
,
1188 compression
, copies_sup
? copies
: 1, document_format
,
1189 pc
, media_col_sup
, doc_handling_sup
);
1196 response
= cupsDoRequest(http
, request
, resource
);
1199 size_t length
= 0; /* Length of request */
1203 fputs("DEBUG: Sending file using HTTP/1.0 Content-Length...\n", stderr
);
1204 length
= ippLength(request
) + (size_t)compatsize
;
1207 fputs("DEBUG: Sending file using HTTP/1.1 chunking...\n", stderr
);
1209 http_status
= cupsSendRequest(http
, request
, resource
, length
);
1210 if (http_status
== HTTP_CONTINUE
&& request
->state
== IPP_DATA
)
1213 fd
= open(files
[0], O_RDONLY
);
1217 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
1219 fprintf(stderr
, "DEBUG: Read %d bytes...\n", (int)bytes
);
1221 if (cupsWriteRequestData(http
, buffer
, bytes
) != HTTP_CONTINUE
)
1226 * Check for side-channel requests...
1229 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
1237 response
= cupsGetResponse(http
, resource
);
1241 ipp_status
= cupsLastError();
1243 fprintf(stderr
, "DEBUG: %s: %s (%s)\n",
1244 num_files
> 1 ? "Create-Job" : "Print-Job",
1245 ippErrorString(ipp_status
), cupsLastErrorString());
1247 if (ipp_status
> IPP_OK_CONFLICT
)
1254 if (ipp_status
== IPP_SERVICE_UNAVAILABLE
||
1255 ipp_status
== IPP_PRINTER_BUSY
)
1257 _cupsLangPrintFilter(stderr
, "INFO", _("The printer is busy."));
1263 * We can't re-submit when we have no files to print, so exit
1264 * immediately with the right status code...
1273 * Update auth-info-required as needed...
1276 _cupsLangPrintFilter(stderr
, "ERROR",
1277 _("Print file was not accepted."));
1279 if (ipp_status
== IPP_NOT_AUTHORIZED
|| ipp_status
== IPP_FORBIDDEN
)
1281 fprintf(stderr
, "DEBUG: WWW-Authenticate=\"%s\"\n",
1282 httpGetField(http
, HTTP_FIELD_WWW_AUTHENTICATE
));
1285 * Normal authentication goes through the password callback, which sets
1286 * auth_info_required to "username,password". Kerberos goes directly
1287 * through GSSAPI, so look for Negotiate in the WWW-Authenticate header
1288 * here and set auth_info_required as needed...
1291 if (!strncmp(httpGetField(http
, HTTP_FIELD_WWW_AUTHENTICATE
),
1293 auth_info_required
= "negotiate";
1301 * We can't re-submit when we have no files to print, so exit
1302 * immediately with the right status code...
1309 else if ((job_id_attr
= ippFindAttribute(response
, "job-id",
1310 IPP_TAG_INTEGER
)) == NULL
)
1312 _cupsLangPrintFilter(stderr
, "INFO",
1313 _("Print file accepted - job ID unknown."));
1318 monitor
.job_id
= job_id
= job_id_attr
->values
[0].integer
;
1319 _cupsLangPrintFilter(stderr
, "INFO",
1320 _("Print file accepted - job ID %d."), job_id
);
1323 ippDelete(response
);
1328 if (job_id
&& num_files
> 1)
1330 for (i
= 0; i
< num_files
; i
++)
1333 * Check for side-channel requests...
1336 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
1339 * Send the next file in the job...
1342 request
= ippNewRequest(IPP_SEND_DOCUMENT
);
1343 request
->request
.op
.version
[0] = version
/ 10;
1344 request
->request
.op
.version
[1] = version
% 10;
1346 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1349 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id",
1353 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1354 "requesting-user-name", NULL
, argv
[2]);
1356 if ((i
+ 1) == num_files
)
1357 ippAddBoolean(request
, IPP_TAG_OPERATION
, "last-document", 1);
1359 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
,
1360 "document-format", NULL
, content_type
);
1362 fprintf(stderr
, "DEBUG: Sending file %d using chunking...\n", i
+ 1);
1363 http_status
= cupsSendRequest(http
, request
, resource
, 0);
1364 if (http_status
== HTTP_CONTINUE
&& request
->state
== IPP_DATA
&&
1365 (fd
= open(files
[i
], O_RDONLY
)) >= 0)
1367 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
1369 if (cupsWriteRequestData(http
, buffer
, bytes
) != HTTP_CONTINUE
)
1374 * Check for side-channel requests...
1377 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
1384 ippDelete(cupsGetResponse(http
, resource
));
1387 fprintf(stderr
, "DEBUG: Send-Document: %s (%s)\n",
1388 ippErrorString(cupsLastError()), cupsLastErrorString());
1390 if (cupsLastError() > IPP_OK_CONFLICT
)
1392 ipp_status
= cupsLastError();
1394 _cupsLangPrintFilter(stderr
, "ERROR",
1395 _("Unable to add document to print job."));
1401 if (ipp_status
<= IPP_OK_CONFLICT
&& argc
> 6)
1403 fprintf(stderr
, "PAGE: 1 %d\n", copies_sup
? atoi(argv
[4]) : 1);
1404 copies_remaining
--;
1406 else if (ipp_status
== IPP_SERVICE_UNAVAILABLE
||
1407 ipp_status
== IPP_PRINTER_BUSY
)
1410 copies_remaining
--;
1413 * Wait for the job to complete...
1416 if (!job_id
|| !waitjob
)
1419 _cupsLangPrintFilter(stderr
, "INFO", _("Waiting for job to complete."));
1421 for (delay
= _cupsNextDelay(0, &prev_delay
); !job_canceled
;)
1424 * Check for side-channel requests...
1427 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
1430 * Build an IPP_GET_JOB_ATTRIBUTES request...
1433 request
= ippNewRequest(IPP_GET_JOB_ATTRIBUTES
);
1434 request
->request
.op
.version
[0] = version
/ 10;
1435 request
->request
.op
.version
[1] = version
% 10;
1437 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1440 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id",
1444 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1445 "requesting-user-name", NULL
, argv
[2]);
1447 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1448 "requested-attributes", sizeof(jattrs
) / sizeof(jattrs
[0]),
1455 httpReconnect(http
);
1456 response
= cupsDoRequest(http
, request
, resource
);
1457 ipp_status
= cupsLastError();
1459 if (ipp_status
== IPP_NOT_FOUND
)
1462 * Job has gone away and/or the server has no job history...
1465 ippDelete(response
);
1467 ipp_status
= IPP_OK
;
1471 fprintf(stderr
, "DEBUG: Get-Job-Attributes: %s (%s)\n",
1472 ippErrorString(ipp_status
), cupsLastErrorString());
1474 if (ipp_status
> IPP_OK_CONFLICT
)
1476 if (ipp_status
!= IPP_SERVICE_UNAVAILABLE
&&
1477 ipp_status
!= IPP_PRINTER_BUSY
)
1479 ippDelete(response
);
1481 _cupsLangPrintFilter(stderr
, "ERROR",
1482 _("Unable to get print job status."));
1489 if ((job_state
= ippFindAttribute(response
, "job-state",
1490 IPP_TAG_ENUM
)) != NULL
)
1493 * Reflect the remote job state in the local queue...
1496 fputs("STATE: -cups-remote-pending,"
1497 "cups-remote-pending-held,"
1498 "cups-remote-processing,"
1499 "cups-remote-stopped,"
1500 "cups-remote-canceled,"
1501 "cups-remote-aborted,"
1502 "cups-remote-completed\n", stderr
);
1503 if (job_state
->values
[0].integer
>= IPP_JOB_PENDING
&&
1504 job_state
->values
[0].integer
<= IPP_JOB_COMPLETED
)
1505 fprintf(stderr
, "STATE: +%s\n",
1506 remote_job_states
[job_state
->values
[0].integer
-
1510 * Stop polling if the job is finished or pending-held...
1513 if (job_state
->values
[0].integer
> IPP_JOB_STOPPED
)
1515 if ((job_sheets
= ippFindAttribute(response
,
1516 "job-media-sheets-completed",
1517 IPP_TAG_INTEGER
)) != NULL
)
1518 fprintf(stderr
, "PAGE: total %d\n",
1519 job_sheets
->values
[0].integer
);
1521 ippDelete(response
);
1528 * If the printer does not return a job-state attribute, it does not
1529 * conform to the IPP specification - break out immediately and fail
1533 fputs("DEBUG: No job-state available from printer - stopping queue.\n",
1535 ipp_status
= IPP_INTERNAL_ERROR
;
1540 ippDelete(response
);
1543 * Wait before polling again...
1548 delay
= _cupsNextDelay(delay
, &prev_delay
);
1553 * Cancel the job as needed...
1556 if (job_canceled
&& job_id
)
1557 cancel_job(http
, uri
, job_id
, resource
, argv
[2], version
);
1560 * Check the printer state and report it if necessary...
1563 check_printer_state(http
, uri
, resource
, argv
[2], version
, job_id
);
1566 * Collect the final page count as needed...
1569 if (have_supplies
&&
1570 !backendSNMPSupplies(snmp_fd
, http
->hostaddr
, &page_count
, NULL
) &&
1571 page_count
> start_count
)
1572 fprintf(stderr
, "PAGE: total %d\n", page_count
- start_count
);
1576 * See if we used Kerberos at all...
1580 auth_info_required
= "negotiate";
1581 #endif /* HAVE_GSSAPI */
1589 cupsFreeOptions(num_options
, options
);
1590 _ppdCacheDestroy(pc
);
1594 ippDelete(supported
);
1597 * Remove the temporary file(s) if necessary...
1601 unlink(tmpfilename
);
1606 for (i
= 0; i
< num_files
; i
++)
1609 #endif /* HAVE_LIBZ */
1612 * Return the queue status...
1615 if (ipp_status
== IPP_NOT_AUTHORIZED
|| ipp_status
== IPP_FORBIDDEN
||
1616 ipp_status
== IPP_AUTHENTICATION_CANCELED
||
1617 ipp_status
<= IPP_OK_CONFLICT
)
1618 fprintf(stderr
, "ATTR: auth-info-required=%s\n", auth_info_required
);
1620 if (ipp_status
== IPP_NOT_AUTHORIZED
|| ipp_status
== IPP_FORBIDDEN
||
1621 ipp_status
== IPP_AUTHENTICATION_CANCELED
)
1622 return (CUPS_BACKEND_AUTH_REQUIRED
);
1623 else if (ipp_status
== IPP_INTERNAL_ERROR
)
1624 return (CUPS_BACKEND_STOP
);
1625 else if (ipp_status
== IPP_DOCUMENT_FORMAT
||
1626 ipp_status
== IPP_CONFLICT
)
1627 return (CUPS_BACKEND_FAILED
);
1628 else if (ipp_status
> IPP_OK_CONFLICT
)
1629 return (CUPS_BACKEND_RETRY_CURRENT
);
1632 _cupsLangPrintFilter(stderr
, "INFO", _("Ready to print."));
1633 return (CUPS_BACKEND_OK
);
1639 * 'cancel_job()' - Cancel a print job.
1643 cancel_job(http_t
*http
, /* I - HTTP connection */
1644 const char *uri
, /* I - printer-uri */
1645 int id
, /* I - job-id */
1646 const char *resource
, /* I - Resource path */
1647 const char *user
, /* I - requesting-user-name */
1648 int version
) /* I - IPP version */
1650 ipp_t
*request
; /* Cancel-Job request */
1653 _cupsLangPrintFilter(stderr
, "INFO", _("Canceling print job."));
1655 request
= ippNewRequest(IPP_CANCEL_JOB
);
1656 request
->request
.op
.version
[0] = version
/ 10;
1657 request
->request
.op
.version
[1] = version
% 10;
1659 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1661 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id", id
);
1663 if (user
&& user
[0])
1664 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1665 "requesting-user-name", NULL
, user
);
1671 ippDelete(cupsDoRequest(http
, request
, resource
));
1673 if (cupsLastError() > IPP_OK_CONFLICT
)
1674 _cupsLangPrintFilter(stderr
, "ERROR", _("Unable to cancel print job."));
1679 * 'check_printer_state()' - Check the printer state.
1682 static ipp_pstate_t
/* O - Current printer-state */
1683 check_printer_state(
1684 http_t
*http
, /* I - HTTP connection */
1685 const char *uri
, /* I - Printer URI */
1686 const char *resource
, /* I - Resource path */
1687 const char *user
, /* I - Username, if any */
1688 int version
, /* I - IPP version */
1691 ipp_t
*request
, /* IPP request */
1692 *response
; /* IPP response */
1693 ipp_attribute_t
*attr
; /* Attribute in response */
1694 ipp_pstate_t printer_state
= IPP_PRINTER_STOPPED
;
1695 /* Current printer-state */
1699 * Send a Get-Printer-Attributes request and log the results...
1702 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
1703 request
->request
.op
.version
[0] = version
/ 10;
1704 request
->request
.op
.version
[1] = version
% 10;
1706 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1709 if (user
&& user
[0])
1710 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1711 "requesting-user-name", NULL
, user
);
1713 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1714 "requested-attributes",
1715 (int)(sizeof(pattrs
) / sizeof(pattrs
[0])), NULL
, pattrs
);
1717 if ((response
= cupsDoRequest(http
, request
, resource
)) != NULL
)
1719 report_printer_state(response
, job_id
);
1721 if ((attr
= ippFindAttribute(response
, "printer-state",
1722 IPP_TAG_ENUM
)) != NULL
)
1723 printer_state
= (ipp_pstate_t
)attr
->values
[0].integer
;
1725 ippDelete(response
);
1728 fprintf(stderr
, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
1729 ippErrorString(cupsLastError()), cupsLastErrorString());
1732 * Return the printer-state value...
1735 return (printer_state
);
1741 * 'compress_files()' - Compress print files...
1745 compress_files(int num_files
, /* I - Number of files */
1746 char **files
) /* I - Files */
1748 int i
, /* Looping var */
1749 fd
; /* Temporary file descriptor */
1750 ssize_t bytes
; /* Bytes read/written */
1751 size_t total
; /* Total bytes read */
1752 cups_file_t
*in
, /* Input file */
1753 *out
; /* Output file */
1754 struct stat outinfo
; /* Output file information */
1755 char filename
[1024], /* Temporary filename */
1756 buffer
[32768]; /* Copy buffer */
1759 fprintf(stderr
, "DEBUG: Compressing %d job files...\n", num_files
);
1760 for (i
= 0; i
< num_files
; i
++)
1762 if ((fd
= cupsTempFd(filename
, sizeof(filename
))) < 0)
1764 _cupsLangPrintError("ERROR", _("Unable to create compressed print file"));
1765 exit(CUPS_BACKEND_FAILED
);
1768 if ((out
= cupsFileOpenFd(fd
, "w9")) == NULL
)
1770 _cupsLangPrintError("ERROR", _("Unable to open compressed print file"));
1771 exit(CUPS_BACKEND_FAILED
);
1774 if ((in
= cupsFileOpen(files
[i
], "r")) == NULL
)
1776 _cupsLangPrintError("ERROR", _("Unable to open print file"));
1778 exit(CUPS_BACKEND_FAILED
);
1782 while ((bytes
= cupsFileRead(in
, buffer
, sizeof(buffer
))) > 0)
1783 if (cupsFileWrite(out
, buffer
, bytes
) < bytes
)
1785 _cupsLangPrintError("ERROR",
1786 _("Unable to generate compressed print file"));
1789 exit(CUPS_BACKEND_FAILED
);
1797 files
[i
] = strdup(filename
);
1799 if (!stat(filename
, &outinfo
))
1801 "DEBUG: File %d compressed to %.1f%% of original size, "
1802 CUPS_LLFMT
" bytes...\n",
1803 i
+ 1, 100.0 * outinfo
.st_size
/ total
,
1804 CUPS_LLCAST outinfo
.st_size
);
1807 #endif /* HAVE_LIBZ */
1811 * 'monitor_printer()' - Monitor the printer state...
1814 static void * /* O - Thread exit code */
1816 _cups_monitor_t
*monitor
) /* I - Monitoring data */
1818 http_t
*http
; /* Connection to printer */
1819 ipp_t
*request
, /* IPP request */
1820 *response
; /* IPP response */
1821 ipp_attribute_t
*attr
; /* Attribute in response */
1822 int delay
, /* Current delay */
1823 prev_delay
; /* Previous delay */
1827 * Make a copy of the printer connection...
1830 http
= _httpCreate(monitor
->hostname
, monitor
->port
, NULL
, monitor
->encryption
,
1832 cupsSetPasswordCB(password_cb
);
1835 * Loop until the job is canceled, aborted, or completed.
1838 delay
= _cupsNextDelay(0, &prev_delay
);
1840 while (monitor
->job_state
< IPP_JOB_CANCELED
&& !job_canceled
)
1843 * Reconnect to the printer...
1846 if (!httpReconnect(http
))
1849 * Connected, so check on the printer state...
1852 monitor
->printer_state
= check_printer_state(http
, monitor
->uri
,
1858 if (monitor
->job_id
> 0)
1861 * Check the status of the job itself...
1864 request
= ippNewRequest(IPP_GET_JOB_ATTRIBUTES
);
1865 request
->request
.op
.version
[0] = monitor
->version
/ 10;
1866 request
->request
.op
.version
[1] = monitor
->version
% 10;
1868 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1869 NULL
, monitor
->uri
);
1870 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id",
1873 if (monitor
->user
&& monitor
->user
[0])
1874 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1875 "requesting-user-name", NULL
, monitor
->user
);
1877 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1878 "requested-attributes",
1879 (int)(sizeof(jattrs
) / sizeof(jattrs
[0])), NULL
, jattrs
);
1885 response
= cupsDoRequest(http
, request
, monitor
->resource
);
1887 fprintf(stderr
, "DEBUG: Get-Job-Attributes: %s (%s)\n",
1888 ippErrorString(cupsLastError()), cupsLastErrorString());
1890 if ((attr
= ippFindAttribute(response
, "job-state",
1891 IPP_TAG_ENUM
)) != NULL
)
1892 monitor
->job_state
= (ipp_jstate_t
)attr
->values
[0].integer
;
1894 monitor
->job_state
= IPP_JOB_COMPLETED
;
1896 ippDelete(response
);
1900 * Disconnect from the printer - we'll reconnect on the next poll...
1903 _httpDisconnect(http
);
1907 * Sleep for N seconds...
1912 delay
= _cupsNextDelay(delay
, &prev_delay
);
1916 * Cleanup and return...
1926 * 'new_request()' - Create a new print creation or validation request.
1929 static ipp_t
* /* O - Request data */
1931 ipp_op_t op
, /* I - IPP operation code */
1932 int version
, /* I - IPP version number */
1933 const char *uri
, /* I - printer-uri value */
1934 const char *user
, /* I - requesting-user-name value */
1935 const char *title
, /* I - job-name value */
1936 int num_options
, /* I - Number of options to send */
1937 cups_option_t
*options
, /* I - Options to send */
1938 const char *compression
, /* I - compression value or NULL */
1939 int copies
, /* I - copies value or 0 */
1940 const char *format
, /* I - documet-format value or NULL */
1941 _ppd_cache_t
*pc
, /* I - PPD cache and mapping data */
1942 ipp_attribute_t
*media_col_sup
, /* I - media-col-supported values */
1943 ipp_attribute_t
*doc_handling_sup
) /* I - multiple-document-handling-supported values */
1945 int i
; /* Looping var */
1946 ipp_t
*request
; /* Request data */
1947 const char *keyword
; /* PWG keyword */
1948 _pwg_size_t
*size
; /* PWG media size */
1949 ipp_t
*media_col
, /* media-col value */
1950 *media_size
; /* media-size value */
1951 const char *media_source
, /* media-source value */
1952 *media_type
, /* media-type value */
1953 *collate_str
; /* multiple-document-handling value */
1957 * Create the IPP request...
1960 request
= ippNewRequest(op
);
1961 request
->request
.op
.version
[0] = version
/ 10;
1962 request
->request
.op
.version
[1] = version
% 10;
1964 fprintf(stderr
, "DEBUG: %s IPP/%d.%d\n",
1965 ippOpString(request
->request
.op
.operation_id
),
1966 request
->request
.op
.version
[0],
1967 request
->request
.op
.version
[1]);
1970 * Add standard attributes...
1973 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1975 fprintf(stderr
, "DEBUG: printer-uri=\"%s\"\n", uri
);
1979 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1980 "requesting-user-name", NULL
, user
);
1981 fprintf(stderr
, "DEBUG: requesting-user-name=\"%s\"\n", user
);
1984 if (title
&& *title
)
1986 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "job-name", NULL
,
1988 fprintf(stderr
, "DEBUG: job-name=\"%s\"\n", title
);
1993 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
,
1994 "document-format", NULL
, format
);
1995 fprintf(stderr
, "DEBUG: document-format=\"%s\"\n", format
);
2001 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
2002 "compression", NULL
, compression
);
2003 fprintf(stderr
, "DEBUG: compression=\"%s\"\n", compression
);
2005 #endif /* HAVE_LIBZ */
2008 * Handle options on the command-line...
2011 if (num_options
> 0)
2016 * Send standard IPP attributes...
2019 if ((keyword
= cupsGetOption("PageSize", num_options
, options
)) == NULL
)
2020 keyword
= cupsGetOption("media", num_options
, options
);
2022 if ((size
= _ppdCacheGetSize(pc
, keyword
)) != NULL
)
2025 * Add a media-col value...
2028 media_size
= ippNew();
2029 ippAddInteger(media_size
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
2030 "x-dimension", size
->width
);
2031 ippAddInteger(media_size
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
2032 "y-dimension", size
->length
);
2034 media_col
= ippNew();
2035 ippAddCollection(media_col
, IPP_TAG_ZERO
, "media-size", media_size
);
2037 media_source
= _ppdCacheGetSource(pc
, cupsGetOption("InputSlot",
2040 media_type
= _ppdCacheGetType(pc
, cupsGetOption("MediaType",
2044 for (i
= 0; i
< media_col_sup
->num_values
; i
++)
2046 if (!strcmp(media_col_sup
->values
[i
].string
.text
,
2047 "media-left-margin"))
2048 ippAddInteger(media_col
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
2049 "media-left-margin", size
->left
);
2050 else if (!strcmp(media_col_sup
->values
[i
].string
.text
,
2051 "media-bottom-margin"))
2052 ippAddInteger(media_col
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
2053 "media-bottom-margin", size
->left
);
2054 else if (!strcmp(media_col_sup
->values
[i
].string
.text
,
2055 "media-right-margin"))
2056 ippAddInteger(media_col
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
2057 "media-right-margin", size
->left
);
2058 else if (!strcmp(media_col_sup
->values
[i
].string
.text
,
2059 "media-top-margin"))
2060 ippAddInteger(media_col
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
2061 "media-top-margin", size
->left
);
2062 else if (!strcmp(media_col_sup
->values
[i
].string
.text
,
2063 "media-source") && media_source
)
2064 ippAddString(media_col
, IPP_TAG_ZERO
, IPP_TAG_KEYWORD
,
2065 "media-source", NULL
, media_source
);
2066 else if (!strcmp(media_col_sup
->values
[i
].string
.text
,
2067 "media-type") && media_type
)
2068 ippAddString(media_col
, IPP_TAG_ZERO
, IPP_TAG_KEYWORD
,
2069 "media-type", NULL
, media_type
);
2072 ippAddCollection(request
, IPP_TAG_JOB
, "media-col", media_col
);
2075 if ((keyword
= cupsGetOption("output-bin", num_options
,
2077 keyword
= _ppdCacheGetBin(pc
, cupsGetOption("OutputBin", num_options
,
2081 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "output-bin",
2084 if ((keyword
= cupsGetOption("output-mode", num_options
,
2086 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "output-mode",
2088 else if ((keyword
= cupsGetOption("ColorModel", num_options
,
2091 if (!strcasecmp(keyword
, "Gray"))
2092 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "output-mode",
2093 NULL
, "monochrome");
2095 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "output-mode",
2099 if ((keyword
= cupsGetOption("print-quality", num_options
,
2101 ippAddInteger(request
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "print-quality",
2103 else if ((keyword
= cupsGetOption("cupsPrintQuality", num_options
,
2106 if (!strcasecmp(keyword
, "draft"))
2107 ippAddInteger(request
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "print-quality",
2109 else if (!strcasecmp(keyword
, "normal"))
2110 ippAddInteger(request
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "print-quality",
2111 IPP_QUALITY_NORMAL
);
2112 else if (!strcasecmp(keyword
, "high"))
2113 ippAddInteger(request
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "print-quality",
2117 if ((keyword
= cupsGetOption("sides", num_options
, options
)) != NULL
)
2118 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "sides",
2120 else if (pc
->sides_option
&&
2121 (keyword
= cupsGetOption(pc
->sides_option
, num_options
,
2124 if (!strcasecmp(keyword
, pc
->sides_1sided
))
2125 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "sides",
2127 else if (!strcasecmp(keyword
, pc
->sides_2sided_long
))
2128 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "sides",
2129 NULL
, "two-sided-long-edge");
2130 if (!strcasecmp(keyword
, pc
->sides_2sided_short
))
2131 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "sides",
2132 NULL
, "two-sided-short-edge");
2135 if (doc_handling_sup
&&
2136 (keyword
= cupsGetOption("collate", num_options
, options
)) != NULL
)
2138 if (!strcasecmp(keyword
, "true"))
2139 collate_str
= "separate-documents-collated-copies";
2141 collate_str
= "separate-documents-uncollated-copies";
2143 for (i
= 0; i
< doc_handling_sup
->num_values
; i
++)
2144 if (!strcmp(doc_handling_sup
->values
[i
].string
.text
, collate_str
))
2146 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
2147 "multiple-document-handling", NULL
, collate_str
);
2155 * When talking to another CUPS server, send all options...
2158 cupsEncodeOptions(request
, num_options
, options
);
2162 ippAddInteger(request
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "copies", copies
);
2170 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
2173 static const char * /* O - Password */
2174 password_cb(const char *prompt
) /* I - Prompt (not used) */
2179 * Remember that we need to authenticate...
2182 auth_info_required
= "username,password";
2184 if (password
&& *password
&& password_tries
< 3)
2193 * Give up after 3 tries or if we don't have a password to begin with...
2202 * 'report_attr()' - Report an IPP attribute value.
2206 report_attr(ipp_attribute_t
*attr
) /* I - Attribute */
2208 int i
; /* Looping var */
2209 char value
[1024], /* Value string */
2210 *valptr
, /* Pointer into value string */
2211 *attrptr
; /* Pointer into attribute value */
2215 * Convert the attribute values into quoted strings...
2218 for (i
= 0, valptr
= value
;
2219 i
< attr
->num_values
&& valptr
< (value
+ sizeof(value
) - 10);
2225 switch (attr
->value_tag
)
2227 case IPP_TAG_INTEGER
:
2229 snprintf(valptr
, sizeof(value
) - (valptr
- value
), "%d",
2230 attr
->values
[i
].integer
);
2231 valptr
+= strlen(valptr
);
2236 case IPP_TAG_KEYWORD
:
2238 for (attrptr
= attr
->values
[i
].string
.text
;
2239 *attrptr
&& valptr
< (value
+ sizeof(value
) - 10);
2242 if (*attrptr
== '\\' || *attrptr
== '\"')
2245 *valptr
++ = *attrptr
;
2252 * Unsupported value type...
2262 * Tell the scheduler about the new values...
2265 fprintf(stderr
, "ATTR: %s=%s\n", attr
->name
, value
);
2270 * 'report_printer_state()' - Report the printer state.
2273 static int /* O - Number of reasons shown */
2274 report_printer_state(ipp_t
*ipp
, /* I - IPP response */
2275 int job_id
) /* I - Current job ID */
2277 int i
; /* Looping var */
2278 int count
; /* Count of reasons shown... */
2279 ipp_attribute_t
*pa
, /* printer-alert */
2280 *pam
, /* printer-alert-message */
2281 *psm
, /* printer-state-message */
2282 *reasons
, /* printer-state-reasons */
2283 *marker
; /* marker-* attributes */
2284 const char *reason
; /* Current reason */
2285 const char *prefix
; /* Prefix for STATE: line */
2286 char value
[1024], /* State/message string */
2287 *valptr
; /* Pointer into string */
2288 static int ipp_supplies
= -1;
2289 /* Report supply levels? */
2293 * Report alerts and messages...
2296 if ((pa
= ippFindAttribute(ipp
, "printer-alert", IPP_TAG_TEXT
)) != NULL
)
2299 if ((pam
= ippFindAttribute(ipp
, "printer-alert-message",
2300 IPP_TAG_TEXT
)) != NULL
)
2303 if ((psm
= ippFindAttribute(ipp
, "printer-state-message",
2304 IPP_TAG_TEXT
)) != NULL
)
2306 char *ptr
; /* Pointer into message */
2309 strlcpy(value
, "INFO: ", sizeof(value
));
2310 for (ptr
= psm
->values
[0].string
.text
, valptr
= value
+ 6;
2311 *ptr
&& valptr
< (value
+ sizeof(value
) - 6);
2314 if (*ptr
< ' ' && *ptr
> 0 && *ptr
!= '\t')
2317 * Substitute "<XX>" for the control character; sprintf is safe because
2318 * we always leave 6 chars free at the end...
2321 sprintf(valptr
, "<%02X>", *ptr
);
2331 fputs(value
, stderr
);
2335 * Now report printer-state-reasons, filtering out some of the reasons we never
2339 if ((reasons
= ippFindAttribute(ipp
, "printer-state-reasons",
2340 IPP_TAG_KEYWORD
)) == NULL
)
2346 for (i
= 0, count
= 0, valptr
= value
; i
< reasons
->num_values
; i
++)
2348 reason
= reasons
->values
[i
].string
.text
;
2350 if (strcmp(reason
, "paused") &&
2351 strcmp(reason
, "com.apple.print.recoverable-warning"))
2353 strlcpy(valptr
, prefix
, sizeof(value
) - (valptr
- value
) - 1);
2354 valptr
+= strlen(valptr
);
2355 strlcpy(valptr
, reason
, sizeof(value
) - (valptr
- value
) - 1);
2356 valptr
+= strlen(valptr
);
2366 fputs(value
, stderr
);
2370 * Relay the current marker-* attribute values...
2373 if (ipp_supplies
< 0)
2375 ppd_file_t
*ppd
; /* PPD file */
2376 ppd_attr_t
*ppdattr
; /* Attribute in PPD file */
2378 if ((ppd
= ppdOpenFile(getenv("PPD"))) != NULL
&&
2379 (ppdattr
= ppdFindAttr(ppd
, "cupsIPPSupplies", NULL
)) != NULL
&&
2380 ppdattr
->value
&& strcasecmp(ppdattr
->value
, "true"))
2388 if (ipp_supplies
> 0)
2390 if ((marker
= ippFindAttribute(ipp
, "marker-colors", IPP_TAG_NAME
)) != NULL
)
2391 report_attr(marker
);
2392 if ((marker
= ippFindAttribute(ipp
, "marker-high-levels",
2393 IPP_TAG_INTEGER
)) != NULL
)
2394 report_attr(marker
);
2395 if ((marker
= ippFindAttribute(ipp
, "marker-levels",
2396 IPP_TAG_INTEGER
)) != NULL
)
2397 report_attr(marker
);
2398 if ((marker
= ippFindAttribute(ipp
, "marker-low-levels",
2399 IPP_TAG_INTEGER
)) != NULL
)
2400 report_attr(marker
);
2401 if ((marker
= ippFindAttribute(ipp
, "marker-message",
2402 IPP_TAG_TEXT
)) != NULL
)
2403 report_attr(marker
);
2404 if ((marker
= ippFindAttribute(ipp
, "marker-names", IPP_TAG_NAME
)) != NULL
)
2405 report_attr(marker
);
2406 if ((marker
= ippFindAttribute(ipp
, "marker-types",
2407 IPP_TAG_KEYWORD
)) != NULL
)
2408 report_attr(marker
);
2416 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
2420 sigterm_handler(int sig
) /* I - Signal */
2422 (void)sig
; /* remove compiler warnings... */
2427 * Flag that the job should be cancelled...
2435 * The scheduler already tried to cancel us once, now just terminate
2436 * after removing our temp files!
2440 unlink(tmpfilename
);
2447 * End of "$Id: ipp.c 7948 2008-09-17 00:04:12Z mike $".