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 "operations-supported",
91 "printer-alert-description",
92 "printer-is-accepting-jobs",
94 "printer-state-message",
95 "printer-state-reasons",
97 static char tmpfilename
[1024] = ""; /* Temporary spool file name */
104 static void cancel_job(http_t
*http
, const char *uri
, int id
,
105 const char *resource
, const char *user
,
107 static ipp_pstate_t
check_printer_state(http_t
*http
, const char *uri
,
108 const char *resource
,
109 const char *user
, int version
,
112 static void compress_files(int num_files
, char **files
);
113 #endif /* HAVE_LIBZ */
114 static void *monitor_printer(_cups_monitor_t
*monitor
);
115 static ipp_t
*new_request(ipp_op_t op
, int version
, const char *uri
,
116 const char *user
, const char *title
,
117 int num_options
, cups_option_t
*options
,
118 const char *compression
, int copies
,
119 const char *format
, _ppd_cache_t
*pc
,
120 ipp_attribute_t
*media_col_sup
);
121 static const char *password_cb(const char *);
122 static void report_attr(ipp_attribute_t
*attr
);
123 static int report_printer_state(ipp_t
*ipp
, int job_id
);
124 static void sigterm_handler(int sig
);
128 * 'main()' - Send a file to the printer or server.
132 * printer-uri job-id user title copies options [file]
135 int /* O - Exit status */
136 main(int argc
, /* I - Number of command-line args */
137 char *argv
[]) /* I - Command-line arguments */
139 int i
; /* Looping var */
140 int send_options
; /* Send job options? */
141 int num_options
; /* Number of printer options */
142 cups_option_t
*options
; /* Printer options */
143 const char *device_uri
; /* Device URI */
144 char scheme
[255], /* Scheme in URI */
145 hostname
[1024], /* Hostname */
146 username
[255], /* Username info */
147 resource
[1024], /* Resource info (printer name) */
148 addrname
[256], /* Address name */
149 *optptr
, /* Pointer to URI options */
150 *name
, /* Name of option */
151 *value
, /* Value of option */
152 sep
; /* Separator character */
153 http_addrlist_t
*addrlist
; /* Address of printer */
154 int snmp_fd
, /* SNMP socket */
155 start_count
, /* Page count via SNMP at start */
156 page_count
, /* Page count via SNMP */
157 have_supplies
; /* Printer supports supply levels? */
158 int num_files
; /* Number of files to print */
159 char **files
, /* Files to print */
160 *compatfile
= NULL
; /* Compatibility filename */
161 off_t compatsize
= 0; /* Size of compatibility file */
162 int port
; /* Port number (not used) */
163 char portname
[255]; /* Port name */
164 char uri
[HTTP_MAX_URI
]; /* Updated URI without user/pass */
165 http_status_t http_status
; /* Status of HTTP request */
166 ipp_status_t ipp_status
; /* Status of IPP request */
167 http_t
*http
; /* HTTP connection */
168 ipp_t
*request
, /* IPP request */
169 *response
, /* IPP response */
170 *supported
; /* get-printer-attributes response */
171 time_t start_time
; /* Time of first connect */
172 int contimeout
; /* Connection timeout */
173 int delay
, /* Delay for retries */
174 prev_delay
; /* Previous delay */
175 const char *compression
; /* Compression mode */
176 int waitjob
, /* Wait for job complete? */
177 waitprinter
; /* Wait for printer ready? */
178 _cups_monitor_t monitor
; /* Monitoring data */
179 ipp_attribute_t
*job_id_attr
; /* job-id attribute */
180 int job_id
; /* job-id value */
181 ipp_attribute_t
*job_sheets
; /* job-media-sheets-completed */
182 ipp_attribute_t
*job_state
; /* job-state */
183 ipp_attribute_t
*copies_sup
; /* copies-supported */
184 ipp_attribute_t
*cups_version
; /* cups-version */
185 ipp_attribute_t
*format_sup
; /* document-format-supported */
186 ipp_attribute_t
*media_col_sup
; /* media-col-supported */
187 ipp_attribute_t
*operations_sup
; /* operations-supported */
188 ipp_attribute_t
*printer_state
; /* printer-state attribute */
189 ipp_attribute_t
*printer_accepting
; /* printer-is-accepting-jobs */
190 int validate_job
; /* Does printer support Validate-Job? */
191 int copies
, /* Number of copies for job */
192 copies_remaining
; /* Number of copies remaining */
193 const char *content_type
, /* CONTENT_TYPE environment variable */
194 *final_content_type
, /* FINAL_CONTENT_TYPE environment var */
195 *document_format
; /* document-format value */
196 int fd
; /* File descriptor */
197 off_t bytes
; /* Bytes copied */
198 char buffer
[16384]; /* Copy buffer */
199 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
200 struct sigaction action
; /* Actions for POSIX signals */
201 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
202 int version
; /* IPP version */
203 ppd_file_t
*ppd
; /* PPD file */
204 _ppd_cache_t
*pc
; /* PPD cache and mapping data */
208 * Make sure status messages are not buffered...
211 setbuf(stderr
, NULL
);
214 * Ignore SIGPIPE and catch SIGTERM signals...
218 sigset(SIGPIPE
, SIG_IGN
);
219 sigset(SIGTERM
, sigterm_handler
);
220 #elif defined(HAVE_SIGACTION)
221 memset(&action
, 0, sizeof(action
));
222 action
.sa_handler
= SIG_IGN
;
223 sigaction(SIGPIPE
, &action
, NULL
);
225 sigemptyset(&action
.sa_mask
);
226 sigaddset(&action
.sa_mask
, SIGTERM
);
227 action
.sa_handler
= sigterm_handler
;
228 sigaction(SIGTERM
, &action
, NULL
);
230 signal(SIGPIPE
, SIG_IGN
);
231 signal(SIGTERM
, sigterm_handler
);
232 #endif /* HAVE_SIGSET */
235 * Check command-line...
242 if ((s
= strrchr(argv
[0], '/')) != NULL
)
247 printf("network %s \"Unknown\" \"%s (%s)\"\n",
248 s
, _cupsLangString(cupsLangDefault(),
249 _("Internet Printing Protocol")), s
);
250 return (CUPS_BACKEND_OK
);
254 _cupsLangPrintf(stderr
,
255 _("Usage: %s job-id user title copies options [file]"),
257 return (CUPS_BACKEND_STOP
);
261 * Get the (final) content type...
264 if ((content_type
= getenv("CONTENT_TYPE")) == NULL
)
265 content_type
= "application/octet-stream";
267 if ((final_content_type
= getenv("FINAL_CONTENT_TYPE")) == NULL
)
269 final_content_type
= content_type
;
271 if (!strncmp(final_content_type
, "printer/", 8))
272 final_content_type
= "application/vnd.cups-raw";
276 * Extract the hostname and printer name from the URI...
279 while ((device_uri
= cupsBackendDeviceURI(argv
)) == NULL
)
281 _cupsLangPrintFilter(stderr
, "INFO", _("Unable to locate printer."));
284 if (getenv("CLASS") != NULL
)
285 return (CUPS_BACKEND_FAILED
);
288 httpSeparateURI(HTTP_URI_CODING_ALL
, device_uri
, scheme
, sizeof(scheme
),
289 username
, sizeof(username
), hostname
, sizeof(hostname
), &port
,
290 resource
, sizeof(resource
));
293 port
= IPP_PORT
; /* Default to port 631 */
295 if (!strcmp(scheme
, "https"))
296 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS
);
298 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED
);
301 * See if there are any options...
308 contimeout
= 7 * 24 * 60 * 60;
310 if ((optptr
= strchr(resource
, '?')) != NULL
)
313 * Yup, terminate the device name string and move to the first
314 * character of the optptr...
320 * Then parse the optptr...
331 while (*optptr
&& *optptr
!= '=' && *optptr
!= '+' && *optptr
!= '&')
334 if ((sep
= *optptr
) != '\0')
345 while (*optptr
&& *optptr
!= '+' && *optptr
!= '&')
355 * Process the option...
358 if (!strcasecmp(name
, "waitjob"))
361 * Wait for job completion?
364 waitjob
= !strcasecmp(value
, "on") ||
365 !strcasecmp(value
, "yes") ||
366 !strcasecmp(value
, "true");
368 else if (!strcasecmp(name
, "waitprinter"))
371 * Wait for printer idle?
374 waitprinter
= !strcasecmp(value
, "on") ||
375 !strcasecmp(value
, "yes") ||
376 !strcasecmp(value
, "true");
378 else if (!strcasecmp(name
, "encryption"))
381 * Enable/disable encryption?
384 if (!strcasecmp(value
, "always"))
385 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS
);
386 else if (!strcasecmp(value
, "required"))
387 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED
);
388 else if (!strcasecmp(value
, "never"))
389 cupsSetEncryption(HTTP_ENCRYPT_NEVER
);
390 else if (!strcasecmp(value
, "ifrequested"))
391 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED
);
394 _cupsLangPrintFilter(stderr
, "ERROR",
395 _("Unknown encryption option value: \"%s\"."),
399 else if (!strcasecmp(name
, "version"))
401 if (!strcmp(value
, "1.0"))
403 else if (!strcmp(value
, "1.1"))
405 else if (!strcmp(value
, "2.0"))
407 else if (!strcmp(value
, "2.1"))
409 else if (!strcmp(value
, "2.2"))
413 _cupsLangPrintFilter(stderr
, "ERROR",
414 _("Unknown version option value: \"%s\"."),
419 else if (!strcasecmp(name
, "compression"))
421 if (!strcasecmp(value
, "true") || !strcasecmp(value
, "yes") ||
422 !strcasecmp(value
, "on") || !strcasecmp(value
, "gzip"))
423 compression
= "gzip";
425 #endif /* HAVE_LIBZ */
426 else if (!strcasecmp(name
, "contimeout"))
429 * Set the connection timeout...
433 contimeout
= atoi(value
);
441 _cupsLangPrintFilter(stderr
, "ERROR",
442 _("Unknown option \"%s\" with value \"%s\"."),
449 * If we have 7 arguments, print the file named on the command-line.
450 * Otherwise, copy stdin to a temporary file and print the temporary
457 send_options
= !strcasecmp(final_content_type
, "application/pdf") ||
458 !strcasecmp(final_content_type
, "application/vnd.cups-pdf") ||
459 !strncasecmp(final_content_type
, "image/", 6);
461 fputs("DEBUG: Sending stdin for job...\n", stderr
);
466 * Point to the files on the command-line...
469 num_files
= argc
- 6;
475 compress_files(num_files
, files
);
476 #endif /* HAVE_LIBZ */
478 fprintf(stderr
, "DEBUG: %d files to send in job...\n", num_files
);
482 * Set the authentication info, if any...
485 cupsSetPasswordCB(password_cb
);
490 * Use authenticaion information in the device URI...
493 if ((password
= strchr(username
, ':')) != NULL
)
496 cupsSetUser(username
);
501 * Try loading authentication information from the environment.
504 const char *ptr
= getenv("AUTH_USERNAME");
509 password
= getenv("AUTH_PASSWORD");
513 * Try finding the remote server...
516 start_time
= time(NULL
);
518 sprintf(portname
, "%d", port
);
520 fputs("STATE: +connecting-to-device\n", stderr
);
521 fprintf(stderr
, "DEBUG: Looking up \"%s\"...\n", hostname
);
523 while ((addrlist
= httpAddrGetList(hostname
, AF_UNSPEC
, portname
)) == NULL
)
525 _cupsLangPrintFilter(stderr
, "INFO",
526 _("Unable to locate printer \"%s\"."), hostname
);
529 if (getenv("CLASS") != NULL
)
531 fputs("STATE: -connecting-to-device\n", stderr
);
532 return (CUPS_BACKEND_STOP
);
536 http
= _httpCreate(hostname
, port
, addrlist
, cupsEncryption(), AF_UNSPEC
);
539 * See if the printer supports SNMP...
542 if ((snmp_fd
= _cupsSNMPOpen(addrlist
->addr
.addr
.sa_family
)) >= 0)
544 have_supplies
= !backendSNMPSupplies(snmp_fd
, &(addrlist
->addr
),
548 have_supplies
= start_count
= 0;
551 * Wait for data from the filter...
555 if (!backendWaitLoop(snmp_fd
, &(addrlist
->addr
), 0, backendNetworkSideCB
))
556 return (CUPS_BACKEND_OK
);
559 * Try connecting to the remote server...
562 delay
= _cupsNextDelay(0, &prev_delay
);
566 fprintf(stderr
, "DEBUG: Connecting to %s:%d\n", hostname
, port
);
567 _cupsLangPrintFilter(stderr
, "INFO", _("Connecting to printer."));
569 if (httpReconnect(http
))
571 int error
= errno
; /* Connection error */
573 if (http
->status
== HTTP_PKI_ERROR
)
574 fputs("STATE: +cups-certificate-error\n", stderr
);
579 if (getenv("CLASS") != NULL
)
582 * If the CLASS environment variable is set, the job was submitted
583 * to a class and not to a specific queue. In this case, we want
584 * to abort immediately so that the job can be requeued on the next
585 * available printer in the class.
588 _cupsLangPrintFilter(stderr
, "INFO",
589 _("Unable to contact printer, queuing on next "
590 "printer in class."));
593 * Sleep 5 seconds to keep the job from requeuing too rapidly...
598 fputs("STATE: -connecting-to-device\n", stderr
);
600 return (CUPS_BACKEND_FAILED
);
603 fprintf(stderr
, "DEBUG: Connection error: %s\n", strerror(errno
));
605 if (errno
== ECONNREFUSED
|| errno
== EHOSTDOWN
||
606 errno
== EHOSTUNREACH
)
608 if (contimeout
&& (time(NULL
) - start_time
) > contimeout
)
610 _cupsLangPrintFilter(stderr
, "ERROR",
611 _("The printer is not responding."));
612 fputs("STATE: -connecting-to-device\n", stderr
);
613 return (CUPS_BACKEND_FAILED
);
619 _cupsLangPrintFilter(stderr
, "WARNING",
620 _("Network printer \"%s\" may not exist or "
621 "is unavailable at this time."),
626 _cupsLangPrintFilter(stderr
, "WARNING",
627 _("Network printer \"%s\" is unreachable at "
628 "this time."), hostname
);
633 _cupsLangPrintFilter(stderr
, "WARNING",
634 _("Network printer \"%s\" is busy."),
641 delay
= _cupsNextDelay(delay
, &prev_delay
);
645 _cupsLangPrintFilter(stderr
, "ERROR",
646 _("Network printer \"%s\" is not responding."),
655 fputs("STATE: -cups-certificate-error\n", stderr
);
657 while (http
->fd
< 0);
659 if (job_canceled
|| !http
)
660 return (CUPS_BACKEND_FAILED
);
662 fputs("STATE: -connecting-to-device\n", stderr
);
663 _cupsLangPrintFilter(stderr
, "INFO", _("Connected to printer."));
666 if (http
->hostaddr
->addr
.sa_family
== AF_INET6
)
667 fprintf(stderr
, "DEBUG: Connected to [%s]:%d (IPv6)...\n",
668 httpAddrString(http
->hostaddr
, addrname
, sizeof(addrname
)),
669 ntohs(http
->hostaddr
->ipv6
.sin6_port
));
671 #endif /* AF_INET6 */
672 if (http
->hostaddr
->addr
.sa_family
== AF_INET
)
673 fprintf(stderr
, "DEBUG: Connected to %s:%d (IPv4)...\n",
674 httpAddrString(http
->hostaddr
, addrname
, sizeof(addrname
)),
675 ntohs(http
->hostaddr
->ipv4
.sin_port
));
678 * Build a URI for the printer and fill the standard IPP attributes for
679 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
680 * might contain username:password information...
683 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), scheme
, NULL
, hostname
,
687 * First validate the destination and see if the device supports multiple
694 media_col_sup
= NULL
;
696 operations_sup
= NULL
;
702 * Check for side-channel requests...
705 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
708 * Build the IPP request...
711 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
712 request
->request
.op
.version
[0] = version
/ 10;
713 request
->request
.op
.version
[1] = version
% 10;
715 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
718 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
719 "requested-attributes", sizeof(pattrs
) / sizeof(pattrs
[0]),
726 fputs("DEBUG: Getting supported attributes...\n", stderr
);
728 if (http
->version
< HTTP_1_1
)
730 fprintf(stderr
, "DEBUG: Printer responded with HTTP version %d.%d.\n",
731 http
->version
/ 100, http
->version
% 100);
733 _cupsLangPrintFilter(stderr
, "ERROR",
734 _("This printer does not conform to the IPP "
735 "standard. Please contact the manufacturer of "
736 "your printer for assistance."));
739 supported
= cupsDoRequest(http
, request
, resource
);
740 ipp_status
= cupsLastError();
742 if (ipp_status
> IPP_OK_CONFLICT
)
744 fprintf(stderr
, "DEBUG: Get-Printer-Attributes returned %s.\n",
745 ippErrorString(ipp_status
));
747 if (ipp_status
== IPP_PRINTER_BUSY
||
748 ipp_status
== IPP_SERVICE_UNAVAILABLE
)
750 if (contimeout
&& (time(NULL
) - start_time
) > contimeout
)
752 _cupsLangPrintFilter(stderr
, "ERROR",
753 _("The printer is not responding."));
754 return (CUPS_BACKEND_FAILED
);
757 _cupsLangPrintFilter(stderr
, "WARNING",
758 _("Network host \"%s\" is busy; will retry in %d "
759 "seconds."), hostname
, delay
);
761 report_printer_state(supported
, 0);
765 delay
= _cupsNextDelay(delay
, &prev_delay
);
767 else if ((ipp_status
== IPP_BAD_REQUEST
||
768 ipp_status
== IPP_VERSION_NOT_SUPPORTED
) && version
> 10)
771 * Switch to IPP/1.1 or IPP/1.0...
776 _cupsLangPrintFilter(stderr
, "INFO",
777 _("Printer does not support IPP/%d.%d, trying "
778 "IPP/%s."), version
/ 10, version
% 10, "1.1");
783 _cupsLangPrintFilter(stderr
, "INFO",
784 _("Printer does not support IPP/%d.%d, trying "
785 "IPP/%s."), version
/ 10, version
% 10, "1.0");
791 else if (ipp_status
== IPP_NOT_FOUND
)
793 _cupsLangPrintFilter(stderr
, "ERROR",
794 _("The printer URI is incorrect or no longer "
798 ippDelete(supported
);
800 return (CUPS_BACKEND_STOP
);
802 else if (ipp_status
== IPP_NOT_AUTHORIZED
|| ipp_status
== IPP_FORBIDDEN
)
804 if (!strncmp(httpGetField(http
, HTTP_FIELD_WWW_AUTHENTICATE
),
806 auth_info_required
= "negotiate";
808 fprintf(stderr
, "ATTR: auth-info-required=%s\n", auth_info_required
);
809 return (CUPS_BACKEND_AUTH_REQUIRED
);
813 _cupsLangPrintFilter(stderr
, "ERROR",
814 _("Unable to get printer status: %s"),
815 cupsLastErrorString());
820 ippDelete(supported
);
826 * Check for supported attributes...
829 if ((copies_sup
= ippFindAttribute(supported
, "copies-supported",
830 IPP_TAG_RANGE
)) != NULL
)
833 * Has the "copies-supported" attribute - does it have an upper
837 fprintf(stderr
, "DEBUG: copies-supported=%d-%d\n",
838 copies_sup
->values
[0].range
.lower
,
839 copies_sup
->values
[0].range
.upper
);
841 if (copies_sup
->values
[0].range
.upper
<= 1)
842 copies_sup
= NULL
; /* No */
845 cups_version
= ippFindAttribute(supported
, "cups-version", IPP_TAG_TEXT
);
847 if ((format_sup
= ippFindAttribute(supported
, "document-format-supported",
848 IPP_TAG_MIMETYPE
)) != NULL
)
850 fprintf(stderr
, "DEBUG: document-format-supported (%d values)\n",
851 format_sup
->num_values
);
852 for (i
= 0; i
< format_sup
->num_values
; i
++)
853 fprintf(stderr
, "DEBUG: [%d] = \"%s\"\n", i
,
854 format_sup
->values
[i
].string
.text
);
857 if ((media_col_sup
= ippFindAttribute(supported
, "media-col-supported",
858 IPP_TAG_KEYWORD
)) != NULL
)
860 fprintf(stderr
, "DEBUG: media-col-supported (%d values)\n",
861 media_col_sup
->num_values
);
862 for (i
= 0; i
< media_col_sup
->num_values
; i
++)
863 fprintf(stderr
, "DEBUG: [%d] = \"%s\"\n", i
,
864 media_col_sup
->values
[i
].string
.text
);
867 if ((operations_sup
= ippFindAttribute(supported
, "operations-supported",
868 IPP_TAG_ENUM
)) != NULL
)
870 for (i
= 0; i
< operations_sup
->num_values
; i
++)
871 if (operations_sup
->values
[i
].integer
== IPP_VALIDATE_JOB
)
879 _cupsLangPrintFilter(stderr
, "WARNING",
880 _("This printer does not conform to the IPP "
881 "standard and may not work."));
882 fputs("DEBUG: operations-supported does not list Validate-Job.\n",
888 _cupsLangPrintFilter(stderr
, "WARNING",
889 _("This printer does not conform to the IPP "
890 "standard and may not work."));
891 fputs("DEBUG: operations-supported not returned in "
892 "Get-Printer-Attributes request.\n", stderr
);
895 report_printer_state(supported
, 0);
897 while (ipp_status
> IPP_OK_CONFLICT
);
900 * See if the printer is accepting jobs and is not stopped; if either
901 * condition is true and we are printing to a class, requeue the job...
904 if (getenv("CLASS") != NULL
)
906 printer_state
= ippFindAttribute(supported
, "printer-state",
908 printer_accepting
= ippFindAttribute(supported
, "printer-is-accepting-jobs",
911 if (printer_state
== NULL
||
912 (printer_state
->values
[0].integer
> IPP_PRINTER_PROCESSING
&&
914 printer_accepting
== NULL
||
915 !printer_accepting
->values
[0].boolean
)
918 * If the CLASS environment variable is set, the job was submitted
919 * to a class and not to a specific queue. In this case, we want
920 * to abort immediately so that the job can be requeued on the next
921 * available printer in the class.
924 _cupsLangPrintFilter(stderr
, "INFO",
925 _("Unable to contact printer, queuing on next "
926 "printer in class."));
928 ippDelete(supported
);
932 * Sleep 5 seconds to keep the job from requeuing too rapidly...
937 return (CUPS_BACKEND_FAILED
);
942 * See if the printer supports multiple copies...
945 copies
= atoi(argv
[4]);
947 if (copies_sup
|| argc
< 7)
949 copies_remaining
= 1;
951 if (argc
< 7 && !send_options
)
955 copies_remaining
= copies
;
958 * Prepare remaining printing options...
966 num_options
= cupsParseOptions(argv
[5], 0, &options
);
968 if (!cups_version
&& media_col_sup
)
971 * Load the PPD file and generate PWG attribute mapping information...
974 ppd
= ppdOpenFile(getenv("PPD"));
975 pc
= _ppdCacheCreateWithPPD(ppd
);
983 document_format
= NULL
;
985 if (format_sup
!= NULL
)
987 for (i
= 0; i
< format_sup
->num_values
; i
++)
988 if (!strcasecmp(final_content_type
, format_sup
->values
[i
].string
.text
))
990 document_format
= final_content_type
;
994 if (!document_format
)
996 for (i
= 0; i
< format_sup
->num_values
; i
++)
997 if (!strcasecmp("application/octet-stream",
998 format_sup
->values
[i
].string
.text
))
1000 document_format
= "application/octet-stream";
1007 * If the printer does not support HTTP/1.1 (which IPP requires), copy stdin
1008 * to a temporary file so that we can do a HTTP/1.0 submission...
1010 * (I hate compatibility hacks!)
1013 if (http
->version
< HTTP_1_1
&& num_files
== 0)
1015 if ((fd
= cupsTempFd(tmpfilename
, sizeof(tmpfilename
))) < 0)
1017 perror("DEBUG: Unable to create temporary file");
1018 return (CUPS_BACKEND_FAILED
);
1021 _cupsLangPrintFilter(stderr
, "INFO", _("Copying print data."));
1023 compatsize
= backendRunLoop(-1, fd
, snmp_fd
, &(addrlist
->addr
), 0, 0,
1024 backendNetworkSideCB
);
1028 compatfile
= tmpfilename
;
1029 files
= &compatfile
;
1032 else if (http
->version
< HTTP_1_1
&& num_files
== 1)
1034 struct stat fileinfo
; /* File information */
1036 if (!stat(files
[0], &fileinfo
))
1037 compatsize
= fileinfo
.st_size
;
1041 * Start monitoring the printer in the background...
1045 monitor
.hostname
= hostname
;
1046 monitor
.user
= argv
[2];
1047 monitor
.resource
= resource
;
1048 monitor
.port
= port
;
1049 monitor
.version
= version
;
1051 monitor
.encryption
= cupsEncryption();
1052 monitor
.job_state
= IPP_JOB_PENDING
;
1053 monitor
.printer_state
= IPP_PRINTER_IDLE
;
1055 _cupsThreadCreate((_cups_thread_func_t
)monitor_printer
, &monitor
);
1058 * Validate access to the printer...
1061 while (!job_canceled
)
1063 request
= new_request(IPP_VALIDATE_JOB
, version
, uri
, argv
[2], argv
[3],
1064 num_options
, options
, compression
,
1065 copies_sup
? copies
: 1, document_format
, pc
,
1068 ippDelete(cupsDoRequest(http
, request
, resource
));
1070 ipp_status
= cupsLastError();
1072 if (ipp_status
> IPP_OK_CONFLICT
&&
1073 ipp_status
!= IPP_OPERATION_NOT_SUPPORTED
)
1078 if (ipp_status
== IPP_SERVICE_UNAVAILABLE
||
1079 ipp_status
== IPP_PRINTER_BUSY
)
1081 _cupsLangPrintFilter(stderr
, "INFO",
1082 _("Printer busy; will retry in 10 seconds."));
1088 * Update auth-info-required as needed...
1091 _cupsLangPrintFilter(stderr
, "ERROR", "%s", cupsLastErrorString());
1093 if (ipp_status
== IPP_NOT_AUTHORIZED
|| ipp_status
== IPP_FORBIDDEN
)
1095 fprintf(stderr
, "DEBUG: WWW-Authenticate=\"%s\"\n",
1096 httpGetField(http
, HTTP_FIELD_WWW_AUTHENTICATE
));
1099 * Normal authentication goes through the password callback, which sets
1100 * auth_info_required to "username,password". Kerberos goes directly
1101 * through GSSAPI, so look for Negotiate in the WWW-Authenticate header
1102 * here and set auth_info_required as needed...
1105 if (!strncmp(httpGetField(http
, HTTP_FIELD_WWW_AUTHENTICATE
),
1107 auth_info_required
= "negotiate";
1118 * Then issue the print-job request...
1123 while (!job_canceled
&& copies_remaining
> 0)
1126 * Check for side-channel requests...
1129 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
1132 * Build the IPP job creation request...
1138 request
= new_request(num_files
> 1 ? IPP_CREATE_JOB
: IPP_PRINT_JOB
,
1139 version
, uri
, argv
[2], argv
[3], num_options
, options
,
1140 compression
, copies_sup
? copies
: 1, document_format
,
1148 response
= cupsDoRequest(http
, request
, resource
);
1151 size_t length
= 0; /* Length of request */
1155 fputs("DEBUG: Sending file using HTTP/1.0 Content-Length...\n", stderr
);
1156 length
= ippLength(request
) + (size_t)compatsize
;
1159 fputs("DEBUG: Sending file using HTTP/1.1 chunking...\n", stderr
);
1161 http_status
= cupsSendRequest(http
, request
, resource
, length
);
1162 if (http_status
== HTTP_CONTINUE
&& request
->state
== IPP_DATA
)
1165 fd
= open(files
[0], O_RDONLY
);
1169 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
1171 fprintf(stderr
, "DEBUG: Read %d bytes...\n", (int)bytes
);
1173 if (cupsWriteRequestData(http
, buffer
, bytes
) != HTTP_CONTINUE
)
1178 * Check for side-channel requests...
1181 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
1189 response
= cupsGetResponse(http
, resource
);
1193 ipp_status
= cupsLastError();
1195 if (ipp_status
> IPP_OK_CONFLICT
)
1202 if (ipp_status
== IPP_SERVICE_UNAVAILABLE
||
1203 ipp_status
== IPP_PRINTER_BUSY
)
1205 _cupsLangPrintFilter(stderr
, "INFO",
1206 _("Printer busy; will retry in 10 seconds."));
1212 * Update auth-info-required as needed...
1215 _cupsLangPrintFilter(stderr
, "ERROR",
1216 _("Print file was not accepted: %s"),
1217 cupsLastErrorString());
1219 if (ipp_status
== IPP_NOT_AUTHORIZED
|| ipp_status
== IPP_FORBIDDEN
)
1221 fprintf(stderr
, "DEBUG: WWW-Authenticate=\"%s\"\n",
1222 httpGetField(http
, HTTP_FIELD_WWW_AUTHENTICATE
));
1225 * Normal authentication goes through the password callback, which sets
1226 * auth_info_required to "username,password". Kerberos goes directly
1227 * through GSSAPI, so look for Negotiate in the WWW-Authenticate header
1228 * here and set auth_info_required as needed...
1231 if (!strncmp(httpGetField(http
, HTTP_FIELD_WWW_AUTHENTICATE
),
1233 auth_info_required
= "negotiate";
1237 else if ((job_id_attr
= ippFindAttribute(response
, "job-id",
1238 IPP_TAG_INTEGER
)) == NULL
)
1240 _cupsLangPrintFilter(stderr
, "INFO",
1241 _("Print file accepted - job ID unknown."));
1246 monitor
.job_id
= job_id
= job_id_attr
->values
[0].integer
;
1247 _cupsLangPrintFilter(stderr
, "INFO",
1248 _("Print file accepted - job ID %d."), job_id
);
1251 ippDelete(response
);
1256 if (job_id
&& num_files
> 1)
1258 for (i
= 0; i
< num_files
; i
++)
1261 * Check for side-channel requests...
1264 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
1267 * Send the next file in the job...
1270 request
= ippNewRequest(IPP_SEND_DOCUMENT
);
1271 request
->request
.op
.version
[0] = version
/ 10;
1272 request
->request
.op
.version
[1] = version
% 10;
1274 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1277 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id",
1281 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1282 "requesting-user-name", NULL
, argv
[2]);
1284 if ((i
+ 1) == num_files
)
1285 ippAddBoolean(request
, IPP_TAG_OPERATION
, "last-document", 1);
1287 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
,
1288 "document-format", NULL
, content_type
);
1290 fprintf(stderr
, "DEBUG: Sending file %d using chunking...\n", i
+ 1);
1291 http_status
= cupsSendRequest(http
, request
, resource
, 0);
1292 if (http_status
== HTTP_CONTINUE
&& request
->state
== IPP_DATA
&&
1293 (fd
= open(files
[i
], O_RDONLY
)) >= 0)
1295 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
1297 if (cupsWriteRequestData(http
, buffer
, bytes
) != HTTP_CONTINUE
)
1302 * Check for side-channel requests...
1305 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
1312 ippDelete(cupsGetResponse(http
, resource
));
1315 if (cupsLastError() > IPP_OK_CONFLICT
)
1317 ipp_status
= cupsLastError();
1319 _cupsLangPrintFilter(stderr
, "ERROR",
1320 _("Unable to add file to job: %s"),
1321 cupsLastErrorString());
1327 if (ipp_status
<= IPP_OK_CONFLICT
&& argc
> 6)
1329 fprintf(stderr
, "PAGE: 1 %d\n", copies_sup
? atoi(argv
[4]) : 1);
1330 copies_remaining
--;
1332 else if (ipp_status
== IPP_SERVICE_UNAVAILABLE
||
1333 ipp_status
== IPP_PRINTER_BUSY
)
1336 copies_remaining
--;
1339 * Wait for the job to complete...
1342 if (!job_id
|| !waitjob
)
1345 _cupsLangPrintFilter(stderr
, "INFO", _("Waiting for job to complete."));
1347 for (delay
= _cupsNextDelay(0, &prev_delay
); !job_canceled
;)
1350 * Check for side-channel requests...
1353 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
1356 * Build an IPP_GET_JOB_ATTRIBUTES request...
1359 request
= ippNewRequest(IPP_GET_JOB_ATTRIBUTES
);
1360 request
->request
.op
.version
[0] = version
/ 10;
1361 request
->request
.op
.version
[1] = version
% 10;
1363 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1366 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id",
1370 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1371 "requesting-user-name", NULL
, argv
[2]);
1373 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1374 "requested-attributes", sizeof(jattrs
) / sizeof(jattrs
[0]),
1381 httpReconnect(http
);
1382 response
= cupsDoRequest(http
, request
, resource
);
1383 ipp_status
= cupsLastError();
1385 if (ipp_status
== IPP_NOT_FOUND
)
1388 * Job has gone away and/or the server has no job history...
1391 ippDelete(response
);
1393 ipp_status
= IPP_OK
;
1397 if (ipp_status
> IPP_OK_CONFLICT
)
1399 if (ipp_status
!= IPP_SERVICE_UNAVAILABLE
&&
1400 ipp_status
!= IPP_PRINTER_BUSY
)
1402 ippDelete(response
);
1404 _cupsLangPrintFilter(stderr
, "ERROR",
1405 _("Unable to get job attributes: %s"),
1406 cupsLastErrorString());
1413 if ((job_state
= ippFindAttribute(response
, "job-state",
1414 IPP_TAG_ENUM
)) != NULL
)
1417 * Stop polling if the job is finished or pending-held...
1420 if (job_state
->values
[0].integer
> IPP_JOB_STOPPED
)
1422 if ((job_sheets
= ippFindAttribute(response
,
1423 "job-media-sheets-completed",
1424 IPP_TAG_INTEGER
)) != NULL
)
1425 fprintf(stderr
, "PAGE: total %d\n",
1426 job_sheets
->values
[0].integer
);
1428 ippDelete(response
);
1435 * If the printer does not return a job-state attribute, it does not
1436 * conform to the IPP specification - break out immediately and fail
1440 fputs("DEBUG: No job-state available from printer - stopping queue.\n",
1442 ipp_status
= IPP_INTERNAL_ERROR
;
1447 ippDelete(response
);
1450 * Wait before polling again...
1455 delay
= _cupsNextDelay(delay
, &prev_delay
);
1460 * Cancel the job as needed...
1463 if (job_canceled
&& job_id
)
1464 cancel_job(http
, uri
, job_id
, resource
, argv
[2], version
);
1467 * Check the printer state and report it if necessary...
1470 check_printer_state(http
, uri
, resource
, argv
[2], version
, job_id
);
1473 * Collect the final page count as needed...
1476 if (have_supplies
&&
1477 !backendSNMPSupplies(snmp_fd
, http
->hostaddr
, &page_count
, NULL
) &&
1478 page_count
> start_count
)
1479 fprintf(stderr
, "PAGE: total %d\n", page_count
- start_count
);
1483 * See if we used Kerberos at all...
1487 auth_info_required
= "negotiate";
1488 #endif /* HAVE_GSSAPI */
1496 cupsFreeOptions(num_options
, options
);
1497 _ppdCacheDestroy(pc
);
1501 ippDelete(supported
);
1504 * Remove the temporary file(s) if necessary...
1508 unlink(tmpfilename
);
1513 for (i
= 0; i
< num_files
; i
++)
1516 #endif /* HAVE_LIBZ */
1519 * Return the queue status...
1522 if (ipp_status
== IPP_NOT_AUTHORIZED
|| ipp_status
== IPP_FORBIDDEN
||
1523 ipp_status
== IPP_AUTHENTICATION_CANCELED
||
1524 ipp_status
<= IPP_OK_CONFLICT
)
1525 fprintf(stderr
, "ATTR: auth-info-required=%s\n", auth_info_required
);
1527 if (ipp_status
== IPP_NOT_AUTHORIZED
|| ipp_status
== IPP_FORBIDDEN
||
1528 ipp_status
== IPP_AUTHENTICATION_CANCELED
)
1529 return (CUPS_BACKEND_AUTH_REQUIRED
);
1530 else if (ipp_status
== IPP_INTERNAL_ERROR
)
1531 return (CUPS_BACKEND_STOP
);
1532 else if (ipp_status
> IPP_OK_CONFLICT
)
1533 return (CUPS_BACKEND_FAILED
);
1536 _cupsLangPrintFilter(stderr
, "INFO", _("Ready to print."));
1537 return (CUPS_BACKEND_OK
);
1543 * 'cancel_job()' - Cancel a print job.
1547 cancel_job(http_t
*http
, /* I - HTTP connection */
1548 const char *uri
, /* I - printer-uri */
1549 int id
, /* I - job-id */
1550 const char *resource
, /* I - Resource path */
1551 const char *user
, /* I - requesting-user-name */
1552 int version
) /* I - IPP version */
1554 ipp_t
*request
; /* Cancel-Job request */
1557 _cupsLangPrintFilter(stderr
, "INFO", _("Canceling print job."));
1559 request
= ippNewRequest(IPP_CANCEL_JOB
);
1560 request
->request
.op
.version
[0] = version
/ 10;
1561 request
->request
.op
.version
[1] = version
% 10;
1563 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1565 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id", id
);
1567 if (user
&& user
[0])
1568 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1569 "requesting-user-name", NULL
, user
);
1575 ippDelete(cupsDoRequest(http
, request
, resource
));
1577 if (cupsLastError() > IPP_OK_CONFLICT
)
1578 _cupsLangPrintFilter(stderr
, "ERROR", _("Unable to cancel job: %s"),
1579 cupsLastErrorString());
1584 * 'check_printer_state()' - Check the printer state.
1587 static ipp_pstate_t
/* O - Current printer-state */
1588 check_printer_state(
1589 http_t
*http
, /* I - HTTP connection */
1590 const char *uri
, /* I - Printer URI */
1591 const char *resource
, /* I - Resource path */
1592 const char *user
, /* I - Username, if any */
1593 int version
, /* I - IPP version */
1596 ipp_t
*request
, /* IPP request */
1597 *response
; /* IPP response */
1598 ipp_attribute_t
*attr
; /* Attribute in response */
1599 ipp_pstate_t printer_state
= IPP_PRINTER_STOPPED
;
1600 /* Current printer-state */
1604 * Send a Get-Printer-Attributes request and log the results...
1607 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
1608 request
->request
.op
.version
[0] = version
/ 10;
1609 request
->request
.op
.version
[1] = version
% 10;
1611 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1614 if (user
&& user
[0])
1615 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1616 "requesting-user-name", NULL
, user
);
1618 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1619 "requested-attributes",
1620 (int)(sizeof(pattrs
) / sizeof(pattrs
[0])), NULL
, pattrs
);
1622 if ((response
= cupsDoRequest(http
, request
, resource
)) != NULL
)
1624 report_printer_state(response
, job_id
);
1626 if ((attr
= ippFindAttribute(response
, "printer-state",
1627 IPP_TAG_ENUM
)) != NULL
)
1628 printer_state
= (ipp_pstate_t
)attr
->values
[0].integer
;
1630 ippDelete(response
);
1634 * Return the printer-state value...
1637 return (printer_state
);
1643 * 'compress_files()' - Compress print files...
1647 compress_files(int num_files
, /* I - Number of files */
1648 char **files
) /* I - Files */
1650 int i
, /* Looping var */
1651 fd
; /* Temporary file descriptor */
1652 ssize_t bytes
; /* Bytes read/written */
1653 size_t total
; /* Total bytes read */
1654 cups_file_t
*in
, /* Input file */
1655 *out
; /* Output file */
1656 struct stat outinfo
; /* Output file information */
1657 char filename
[1024], /* Temporary filename */
1658 buffer
[32768]; /* Copy buffer */
1661 fprintf(stderr
, "DEBUG: Compressing %d job files...\n", num_files
);
1662 for (i
= 0; i
< num_files
; i
++)
1664 if ((fd
= cupsTempFd(filename
, sizeof(filename
))) < 0)
1666 _cupsLangPrintError("ERROR", _("Unable to create compressed print file"));
1667 exit(CUPS_BACKEND_FAILED
);
1670 if ((out
= cupsFileOpenFd(fd
, "w9")) == NULL
)
1672 _cupsLangPrintError("ERROR", _("Unable to open compressed print file"));
1673 exit(CUPS_BACKEND_FAILED
);
1676 if ((in
= cupsFileOpen(files
[i
], "r")) == NULL
)
1678 _cupsLangPrintError("ERROR", _("Unable to open print file"));
1680 exit(CUPS_BACKEND_FAILED
);
1684 while ((bytes
= cupsFileRead(in
, buffer
, sizeof(buffer
))) > 0)
1685 if (cupsFileWrite(out
, buffer
, bytes
) < bytes
)
1687 _cupsLangPrintError("ERROR",
1688 _("Unable to generate compressed print file"));
1691 exit(CUPS_BACKEND_FAILED
);
1699 files
[i
] = strdup(filename
);
1701 if (!stat(filename
, &outinfo
))
1703 "DEBUG: File %d compressed to %.1f%% of original size, "
1704 CUPS_LLFMT
" bytes...\n",
1705 i
+ 1, 100.0 * outinfo
.st_size
/ total
,
1706 CUPS_LLCAST outinfo
.st_size
);
1709 #endif /* HAVE_LIBZ */
1713 * 'monitor_printer()' - Monitor the printer state...
1716 static void * /* O - Thread exit code */
1718 _cups_monitor_t
*monitor
) /* I - Monitoring data */
1720 http_t
*http
; /* Connection to printer */
1721 ipp_t
*request
, /* IPP request */
1722 *response
; /* IPP response */
1723 ipp_attribute_t
*attr
; /* Attribute in response */
1724 int delay
, /* Current delay */
1725 prev_delay
; /* Previous delay */
1729 * Make a copy of the printer connection...
1732 http
= _httpCreate(monitor
->hostname
, monitor
->port
, NULL
, monitor
->encryption
,
1734 cupsSetPasswordCB(password_cb
);
1737 * Loop until the job is canceled, aborted, or completed.
1740 delay
= _cupsNextDelay(0, &prev_delay
);
1742 while (monitor
->job_state
< IPP_JOB_CANCELED
&& !job_canceled
)
1745 * Reconnect to the printer...
1748 if (!httpReconnect(http
))
1751 * Connected, so check on the printer state...
1754 monitor
->printer_state
= check_printer_state(http
, monitor
->uri
,
1760 if (monitor
->job_id
> 0)
1763 * Check the status of the job itself...
1766 request
= ippNewRequest(IPP_GET_JOB_ATTRIBUTES
);
1767 request
->request
.op
.version
[0] = monitor
->version
/ 10;
1768 request
->request
.op
.version
[1] = monitor
->version
% 10;
1770 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1771 NULL
, monitor
->uri
);
1772 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id",
1775 if (monitor
->user
&& monitor
->user
[0])
1776 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1777 "requesting-user-name", NULL
, monitor
->user
);
1779 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1780 "requested-attributes",
1781 (int)(sizeof(jattrs
) / sizeof(jattrs
[0])), NULL
, jattrs
);
1787 response
= cupsDoRequest(http
, request
, monitor
->resource
);
1789 if ((attr
= ippFindAttribute(response
, "job-state",
1790 IPP_TAG_ENUM
)) != NULL
)
1791 monitor
->job_state
= (ipp_jstate_t
)attr
->values
[0].integer
;
1793 monitor
->job_state
= IPP_JOB_COMPLETED
;
1795 ippDelete(response
);
1799 * Disconnect from the printer - we'll reconnect on the next poll...
1802 _httpDisconnect(http
);
1806 * Sleep for N seconds...
1811 delay
= _cupsNextDelay(delay
, &prev_delay
);
1815 * Cleanup and return...
1825 * 'new_request()' - Create a new print creation or validation request.
1828 static ipp_t
* /* O - Request data */
1830 ipp_op_t op
, /* I - IPP operation code */
1831 int version
, /* I - IPP version number */
1832 const char *uri
, /* I - printer-uri value */
1833 const char *user
, /* I - requesting-user-name value */
1834 const char *title
, /* I - job-name value */
1835 int num_options
, /* I - Number of options to send */
1836 cups_option_t
*options
, /* I - Options to send */
1837 const char *compression
, /* I - compression value or NULL */
1838 int copies
, /* I - copies value or 0 */
1839 const char *format
, /* I - documet-format value or NULL */
1840 _ppd_cache_t
*pc
, /* I - PPD cache and mapping data */
1841 ipp_attribute_t
*media_col_sup
) /* I - media-col-supported values */
1843 int i
; /* Looping var */
1844 ipp_t
*request
; /* Request data */
1845 const char *keyword
; /* PWG keyword */
1846 _pwg_size_t
*size
; /* PWG media size */
1847 ipp_t
*media_col
, /* media-col value */
1848 *media_size
; /* media-size value */
1849 const char *media_source
, /* media-source value */
1850 *media_type
; /* media-type value */
1854 * Create the IPP request...
1857 request
= ippNewRequest(op
);
1858 request
->request
.op
.version
[0] = version
/ 10;
1859 request
->request
.op
.version
[1] = version
% 10;
1861 fprintf(stderr
, "DEBUG: %s IPP/%d.%d\n",
1862 ippOpString(request
->request
.op
.operation_id
),
1863 request
->request
.op
.version
[0],
1864 request
->request
.op
.version
[1]);
1867 * Add standard attributes...
1870 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1872 fprintf(stderr
, "DEBUG: printer-uri=\"%s\"\n", uri
);
1876 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1877 "requesting-user-name", NULL
, user
);
1878 fprintf(stderr
, "DEBUG: requesting-user-name=\"%s\"\n", user
);
1881 if (title
&& *title
)
1883 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "job-name", NULL
,
1885 fprintf(stderr
, "DEBUG: job-name=\"%s\"\n", title
);
1890 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
,
1891 "document-format", NULL
, format
);
1892 fprintf(stderr
, "DEBUG: document-format=\"%s\"\n", format
);
1898 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1899 "compression", NULL
, compression
);
1900 fprintf(stderr
, "DEBUG: compression=\"%s\"\n", compression
);
1902 #endif /* HAVE_LIBZ */
1905 * Handle options on the command-line...
1908 if (num_options
> 0)
1913 * Send standard IPP attributes...
1916 if ((keyword
= cupsGetOption("PageSize", num_options
, options
)) == NULL
)
1917 keyword
= cupsGetOption("media", num_options
, options
);
1919 if ((size
= _ppdCacheGetSize(pc
, keyword
)) != NULL
)
1922 * Add a media-col value...
1925 media_size
= ippNew();
1926 ippAddInteger(media_size
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
1927 "x-dimension", size
->width
);
1928 ippAddInteger(media_size
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
1929 "y-dimension", size
->length
);
1931 media_col
= ippNew();
1932 ippAddCollection(media_col
, IPP_TAG_ZERO
, "media-size", media_size
);
1934 media_source
= _ppdCacheGetSource(pc
, cupsGetOption("InputSlot",
1937 media_type
= _ppdCacheGetType(pc
, cupsGetOption("MediaType",
1941 for (i
= 0; i
< media_col_sup
->num_values
; i
++)
1943 if (!strcmp(media_col_sup
->values
[i
].string
.text
,
1944 "media-left-margin"))
1945 ippAddInteger(media_col
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
1946 "media-left-margin", size
->left
);
1947 else if (!strcmp(media_col_sup
->values
[i
].string
.text
,
1948 "media-bottom-margin"))
1949 ippAddInteger(media_col
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
1950 "media-bottom-margin", size
->left
);
1951 else if (!strcmp(media_col_sup
->values
[i
].string
.text
,
1952 "media-right-margin"))
1953 ippAddInteger(media_col
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
1954 "media-right-margin", size
->left
);
1955 else if (!strcmp(media_col_sup
->values
[i
].string
.text
,
1956 "media-top-margin"))
1957 ippAddInteger(media_col
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
1958 "media-top-margin", size
->left
);
1959 else if (!strcmp(media_col_sup
->values
[i
].string
.text
,
1960 "media-source") && media_source
)
1961 ippAddString(media_col
, IPP_TAG_ZERO
, IPP_TAG_KEYWORD
,
1962 "media-source", NULL
, media_source
);
1963 else if (!strcmp(media_col_sup
->values
[i
].string
.text
,
1964 "media-type") && media_type
)
1965 ippAddString(media_col
, IPP_TAG_ZERO
, IPP_TAG_KEYWORD
,
1966 "media-type", NULL
, media_type
);
1969 ippAddCollection(request
, IPP_TAG_JOB
, "media-col", media_col
);
1972 if ((keyword
= cupsGetOption("output-bin", num_options
,
1974 keyword
= _ppdCacheGetBin(pc
, cupsGetOption("OutputBin", num_options
,
1978 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "output-bin",
1981 if ((keyword
= cupsGetOption("output-mode", num_options
,
1983 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "output-mode",
1985 else if ((keyword
= cupsGetOption("ColorModel", num_options
,
1988 if (!strcasecmp(keyword
, "Gray"))
1989 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "output-mode",
1990 NULL
, "monochrome");
1992 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "output-mode",
1996 if ((keyword
= cupsGetOption("print-quality", num_options
,
1998 ippAddInteger(request
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "print-quality",
2000 else if ((keyword
= cupsGetOption("cupsPrintQuality", num_options
,
2003 if (!strcasecmp(keyword
, "draft"))
2004 ippAddInteger(request
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "print-quality",
2006 else if (!strcasecmp(keyword
, "normal"))
2007 ippAddInteger(request
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "print-quality",
2008 IPP_QUALITY_NORMAL
);
2009 else if (!strcasecmp(keyword
, "high"))
2010 ippAddInteger(request
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "print-quality",
2014 if ((keyword
= cupsGetOption("sides", num_options
, options
)) != NULL
)
2015 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "sides",
2017 else if (pc
->sides_option
&&
2018 (keyword
= cupsGetOption(pc
->sides_option
, num_options
,
2021 if (!strcasecmp(keyword
, pc
->sides_1sided
))
2022 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "sides",
2024 else if (!strcasecmp(keyword
, pc
->sides_2sided_long
))
2025 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "sides",
2026 NULL
, "two-sided-long-edge");
2027 if (!strcasecmp(keyword
, pc
->sides_2sided_short
))
2028 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "sides",
2029 NULL
, "two-sided-short-edge");
2035 * When talking to another CUPS server, send all options...
2038 cupsEncodeOptions(request
, num_options
, options
);
2042 ippAddInteger(request
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "copies", copies
);
2050 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
2053 static const char * /* O - Password */
2054 password_cb(const char *prompt
) /* I - Prompt (not used) */
2059 * Remember that we need to authenticate...
2062 auth_info_required
= "username,password";
2064 if (password
&& *password
&& password_tries
< 3)
2073 * Give up after 3 tries or if we don't have a password to begin with...
2082 * 'report_attr()' - Report an IPP attribute value.
2086 report_attr(ipp_attribute_t
*attr
) /* I - Attribute */
2088 int i
; /* Looping var */
2089 char value
[1024], /* Value string */
2090 *valptr
, /* Pointer into value string */
2091 *attrptr
; /* Pointer into attribute value */
2095 * Convert the attribute values into quoted strings...
2098 for (i
= 0, valptr
= value
;
2099 i
< attr
->num_values
&& valptr
< (value
+ sizeof(value
) - 10);
2105 switch (attr
->value_tag
)
2107 case IPP_TAG_INTEGER
:
2109 snprintf(valptr
, sizeof(value
) - (valptr
- value
), "%d",
2110 attr
->values
[i
].integer
);
2111 valptr
+= strlen(valptr
);
2116 case IPP_TAG_KEYWORD
:
2118 for (attrptr
= attr
->values
[i
].string
.text
;
2119 *attrptr
&& valptr
< (value
+ sizeof(value
) - 10);
2122 if (*attrptr
== '\\' || *attrptr
== '\"')
2125 *valptr
++ = *attrptr
;
2132 * Unsupported value type...
2142 * Tell the scheduler about the new values...
2145 fprintf(stderr
, "ATTR: %s=%s\n", attr
->name
, value
);
2150 * 'report_printer_state()' - Report the printer state.
2153 static int /* O - Number of reasons shown */
2154 report_printer_state(ipp_t
*ipp
, /* I - IPP response */
2155 int job_id
) /* I - Current job ID */
2157 int i
; /* Looping var */
2158 int count
; /* Count of reasons shown... */
2159 ipp_attribute_t
*pa
, /* printer-alert */
2160 *pam
, /* printer-alert-message */
2161 *psm
, /* printer-state-message */
2162 *reasons
, /* printer-state-reasons */
2163 *marker
; /* marker-* attributes */
2164 const char *reason
; /* Current reason */
2165 const char *prefix
; /* Prefix for STATE: line */
2166 char value
[1024], /* State/message string */
2167 *valptr
; /* Pointer into string */
2168 static int ipp_supplies
= -1;
2169 /* Report supply levels? */
2173 * Report alerts and messages...
2176 if ((pa
= ippFindAttribute(ipp
, "printer-alert", IPP_TAG_TEXT
)) != NULL
)
2179 if ((pam
= ippFindAttribute(ipp
, "printer-alert-message",
2180 IPP_TAG_TEXT
)) != NULL
)
2183 if ((psm
= ippFindAttribute(ipp
, "printer-state-message",
2184 IPP_TAG_TEXT
)) != NULL
)
2186 char *ptr
; /* Pointer into message */
2189 strlcpy(value
, "INFO: ", sizeof(value
));
2190 for (ptr
= psm
->values
[0].string
.text
, valptr
= value
+ 6;
2191 *ptr
&& valptr
< (value
+ sizeof(value
) - 6);
2194 if (*ptr
< ' ' && *ptr
> 0 && *ptr
!= '\t')
2197 * Substitute "<XX>" for the control character; sprintf is safe because
2198 * we always leave 6 chars free at the end...
2201 sprintf(valptr
, "<%02X>", *ptr
);
2211 fputs(value
, stderr
);
2215 * Now report printer-state-reasons, filtering out some of the reasons we never
2219 if ((reasons
= ippFindAttribute(ipp
, "printer-state-reasons",
2220 IPP_TAG_KEYWORD
)) == NULL
)
2226 for (i
= 0, count
= 0, valptr
= value
; i
< reasons
->num_values
; i
++)
2228 reason
= reasons
->values
[i
].string
.text
;
2230 if (strcmp(reason
, "paused") &&
2231 strcmp(reason
, "com.apple.print.recoverable-warning"))
2233 strlcpy(valptr
, prefix
, sizeof(value
) - (valptr
- value
) - 1);
2234 valptr
+= strlen(valptr
);
2235 strlcpy(valptr
, reason
, sizeof(value
) - (valptr
- value
) - 1);
2236 valptr
+= strlen(valptr
);
2246 fputs(value
, stderr
);
2250 * Relay the current marker-* attribute values...
2253 if (ipp_supplies
< 0)
2255 ppd_file_t
*ppd
; /* PPD file */
2256 ppd_attr_t
*ppdattr
; /* Attribute in PPD file */
2258 if ((ppd
= ppdOpenFile(getenv("PPD"))) != NULL
&&
2259 (ppdattr
= ppdFindAttr(ppd
, "cupsIPPSupplies", NULL
)) != NULL
&&
2260 ppdattr
->value
&& strcasecmp(ppdattr
->value
, "true"))
2268 if (ipp_supplies
> 0)
2270 if ((marker
= ippFindAttribute(ipp
, "marker-colors", IPP_TAG_NAME
)) != NULL
)
2271 report_attr(marker
);
2272 if ((marker
= ippFindAttribute(ipp
, "marker-high-levels",
2273 IPP_TAG_INTEGER
)) != NULL
)
2274 report_attr(marker
);
2275 if ((marker
= ippFindAttribute(ipp
, "marker-levels",
2276 IPP_TAG_INTEGER
)) != NULL
)
2277 report_attr(marker
);
2278 if ((marker
= ippFindAttribute(ipp
, "marker-low-levels",
2279 IPP_TAG_INTEGER
)) != NULL
)
2280 report_attr(marker
);
2281 if ((marker
= ippFindAttribute(ipp
, "marker-message",
2282 IPP_TAG_TEXT
)) != NULL
)
2283 report_attr(marker
);
2284 if ((marker
= ippFindAttribute(ipp
, "marker-names", IPP_TAG_NAME
)) != NULL
)
2285 report_attr(marker
);
2286 if ((marker
= ippFindAttribute(ipp
, "marker-types",
2287 IPP_TAG_KEYWORD
)) != NULL
)
2288 report_attr(marker
);
2296 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
2300 sigterm_handler(int sig
) /* I - Signal */
2302 (void)sig
; /* remove compiler warnings... */
2307 * Flag that the job should be cancelled...
2315 * The scheduler already tried to cancel us once, now just terminate
2316 * after removing our temp files!
2320 unlink(tmpfilename
);
2327 * End of "$Id: ipp.c 7948 2008-09-17 00:04:12Z mike $".