2 * "$Id: ipp.c 7948 2008-09-17 00:04:12Z mike $"
4 * IPP backend for CUPS.
6 * Copyright 2007-2010 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * "LICENSE" which should have been included with this file. If this
13 * file is missing or damaged, see the license at "http://www.cups.org/".
15 * This file is subject to the Apple OS-Developed Software exception.
19 * main() - Send a file to the printer or server.
20 * cancel_job() - Cancel a print job.
21 * check_printer_state() - Check the printer state...
22 * compress_files() - Compress print files...
23 * password_cb() - Disable the password prompt for
24 * cupsDoFileRequest().
25 * report_attr() - Report an IPP attribute value.
26 * report_printer_state() - Report the printer state.
27 * sigterm_handler() - Handle 'terminate' signals that stop the backend.
31 * Include necessary headers.
34 #include "backend-private.h"
35 #include <sys/types.h>
43 static char *password
= NULL
; /* Password for device URI */
44 static int password_tries
= 0; /* Password tries */
45 static const char *auth_info_required
= "none";
46 /* New auth-info-required value */
47 static int job_canceled
= 0; /* Job cancelled? */
54 static void cancel_job(http_t
*http
, const char *uri
, int id
,
55 const char *resource
, const char *user
, int version
);
56 static void check_printer_state(http_t
*http
, const char *uri
,
57 const char *resource
, const char *user
,
58 int version
, int job_id
);
60 static void compress_files(int num_files
, char **files
);
61 #endif /* HAVE_LIBZ */
62 static const char *password_cb(const char *);
63 static void report_attr(ipp_attribute_t
*attr
);
64 static int report_printer_state(ipp_t
*ipp
, int job_id
);
65 static void sigterm_handler(int sig
);
69 * 'main()' - Send a file to the printer or server.
73 * printer-uri job-id user title copies options [file]
76 int /* O - Exit status */
77 main(int argc
, /* I - Number of command-line args */
78 char *argv
[]) /* I - Command-line arguments */
80 int i
; /* Looping var */
81 int send_options
; /* Send job options? */
82 int num_options
; /* Number of printer options */
83 cups_option_t
*options
; /* Printer options */
84 const char *device_uri
; /* Device URI */
85 char scheme
[255], /* Scheme in URI */
86 hostname
[1024], /* Hostname */
87 username
[255], /* Username info */
88 resource
[1024], /* Resource info (printer name) */
89 addrname
[256], /* Address name */
90 *optptr
, /* Pointer to URI options */
91 *name
, /* Name of option */
92 *value
, /* Value of option */
93 sep
; /* Separator character */
94 int snmp_fd
, /* SNMP socket */
95 start_count
, /* Page count via SNMP at start */
96 page_count
, /* Page count via SNMP */
97 have_supplies
; /* Printer supports supply levels? */
98 int num_files
; /* Number of files to print */
99 char **files
; /* Files to print */
100 int port
; /* Port number (not used) */
101 char uri
[HTTP_MAX_URI
]; /* Updated URI without user/pass */
102 http_status_t http_status
; /* Status of HTTP request */
103 ipp_status_t ipp_status
; /* Status of IPP request */
104 http_t
*http
; /* HTTP connection */
105 ipp_t
*request
, /* IPP request */
106 *response
, /* IPP response */
107 *supported
; /* get-printer-attributes response */
108 time_t start_time
; /* Time of first connect */
109 int contimeout
; /* Connection timeout */
110 int delay
; /* Delay for retries... */
111 int compression
, /* Do compression of the job data? */
112 waitjob
, /* Wait for job complete? */
113 waitprinter
; /* Wait for printer ready? */
114 ipp_attribute_t
*job_id_attr
; /* job-id attribute */
115 int job_id
; /* job-id value */
116 ipp_attribute_t
*job_sheets
; /* job-media-sheets-completed */
117 ipp_attribute_t
*job_state
; /* job-state */
118 ipp_attribute_t
*copies_sup
; /* copies-supported */
119 ipp_attribute_t
*cups_version
; /* cups-version */
120 ipp_attribute_t
*format_sup
; /* document-format-supported */
121 ipp_attribute_t
*media_col_sup
; /* media-col-supported */
122 ipp_attribute_t
*printer_state
; /* printer-state attribute */
123 ipp_attribute_t
*printer_accepting
; /* printer-is-accepting-jobs */
124 int copies
, /* Number of copies for job */
125 copies_remaining
; /* Number of copies remaining */
126 const char *content_type
, /* CONTENT_TYPE environment variable */
127 *final_content_type
; /* FINAL_CONTENT_TYPE environment var */
128 int fd
; /* File descriptor */
129 off_t bytes
; /* Bytes copied */
130 char buffer
[16384]; /* Copy buffer */
131 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
132 struct sigaction action
; /* Actions for POSIX signals */
133 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
134 int version
; /* IPP version */
135 static const char * const pattrs
[] =
136 { /* Printer attributes we want */
138 "document-format-supported",
140 "marker-high-levels",
146 "media-col-supported",
147 "printer-is-accepting-jobs",
149 "printer-state-message",
150 "printer-state-reasons",
152 static const char * const jattrs
[] =
153 { /* Job attributes we want */
154 "job-media-sheets-completed",
160 * Make sure status messages are not buffered...
163 setbuf(stderr
, NULL
);
166 * Ignore SIGPIPE and catch SIGTERM signals...
170 sigset(SIGPIPE
, SIG_IGN
);
171 sigset(SIGTERM
, sigterm_handler
);
172 #elif defined(HAVE_SIGACTION)
173 memset(&action
, 0, sizeof(action
));
174 action
.sa_handler
= SIG_IGN
;
175 sigaction(SIGPIPE
, &action
, NULL
);
177 sigemptyset(&action
.sa_mask
);
178 sigaddset(&action
.sa_mask
, SIGTERM
);
179 action
.sa_handler
= sigterm_handler
;
180 sigaction(SIGTERM
, &action
, NULL
);
182 signal(SIGPIPE
, SIG_IGN
);
183 signal(SIGTERM
, sigterm_handler
);
184 #endif /* HAVE_SIGSET */
187 * Check command-line...
194 if ((s
= strrchr(argv
[0], '/')) != NULL
)
199 printf("network %s \"Unknown\" \"%s (%s)\"\n",
200 s
, _cupsLangString(cupsLangDefault(),
201 _("Internet Printing Protocol")), s
);
202 return (CUPS_BACKEND_OK
);
206 _cupsLangPrintf(stderr
,
207 _("Usage: %s job-id user title copies options [file]\n"),
209 return (CUPS_BACKEND_STOP
);
213 * Get the (final) content type...
216 if ((content_type
= getenv("CONTENT_TYPE")) == NULL
)
217 content_type
= "application/octet-stream";
219 if ((final_content_type
= getenv("FINAL_CONTENT_TYPE")) == NULL
)
221 final_content_type
= content_type
;
223 if (!strncmp(final_content_type
, "printer/", 8))
224 final_content_type
= "application/vnd.cups-raw";
228 * Extract the hostname and printer name from the URI...
231 if ((device_uri
= cupsBackendDeviceURI(argv
)) == NULL
)
232 return (CUPS_BACKEND_FAILED
);
234 httpSeparateURI(HTTP_URI_CODING_ALL
, device_uri
, scheme
, sizeof(scheme
),
235 username
, sizeof(username
), hostname
, sizeof(hostname
), &port
,
236 resource
, sizeof(resource
));
239 port
= IPP_PORT
; /* Default to port 631 */
241 if (!strcmp(scheme
, "https"))
242 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS
);
244 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED
);
247 * See if there are any options...
254 contimeout
= 7 * 24 * 60 * 60;
256 if ((optptr
= strchr(resource
, '?')) != NULL
)
259 * Yup, terminate the device name string and move to the first
260 * character of the optptr...
266 * Then parse the optptr...
277 while (*optptr
&& *optptr
!= '=' && *optptr
!= '+' && *optptr
!= '&')
280 if ((sep
= *optptr
) != '\0')
291 while (*optptr
&& *optptr
!= '+' && *optptr
!= '&')
301 * Process the option...
304 if (!strcasecmp(name
, "waitjob"))
307 * Wait for job completion?
310 waitjob
= !strcasecmp(value
, "on") ||
311 !strcasecmp(value
, "yes") ||
312 !strcasecmp(value
, "true");
314 else if (!strcasecmp(name
, "waitprinter"))
317 * Wait for printer idle?
320 waitprinter
= !strcasecmp(value
, "on") ||
321 !strcasecmp(value
, "yes") ||
322 !strcasecmp(value
, "true");
324 else if (!strcasecmp(name
, "encryption"))
327 * Enable/disable encryption?
330 if (!strcasecmp(value
, "always"))
331 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS
);
332 else if (!strcasecmp(value
, "required"))
333 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED
);
334 else if (!strcasecmp(value
, "never"))
335 cupsSetEncryption(HTTP_ENCRYPT_NEVER
);
336 else if (!strcasecmp(value
, "ifrequested"))
337 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED
);
340 _cupsLangPrintf(stderr
,
341 _("ERROR: Unknown encryption option value \"%s\"\n"),
345 else if (!strcasecmp(name
, "version"))
347 if (!strcmp(value
, "1.0"))
349 else if (!strcmp(value
, "1.1"))
351 else if (!strcmp(value
, "2.0"))
353 else if (!strcmp(value
, "2.1"))
357 _cupsLangPrintf(stderr
,
358 _("ERROR: Unknown version option value \"%s\"\n"),
363 else if (!strcasecmp(name
, "compression"))
365 compression
= !strcasecmp(value
, "true") ||
366 !strcasecmp(value
, "yes") ||
367 !strcasecmp(value
, "on") ||
368 !strcasecmp(value
, "gzip");
370 #endif /* HAVE_LIBZ */
371 else if (!strcasecmp(name
, "contimeout"))
374 * Set the connection timeout...
378 contimeout
= atoi(value
);
386 _cupsLangPrintf(stderr
,
387 _("ERROR: Unknown option \"%s\" with value \"%s\"\n"),
394 * If we have 7 arguments, print the file named on the command-line.
395 * Otherwise, copy stdin to a temporary file and print the temporary
402 send_options
= !strcasecmp(final_content_type
, "application/pdf") ||
403 !strcasecmp(final_content_type
, "application/vnd.cups-pdf") ||
404 !strcasecmp(final_content_type
, "image/jpeg") ||
405 !strcasecmp(final_content_type
, "image/png") ||
406 !strcasecmp(final_content_type
, "image/urf");
408 fputs("DEBUG: Sending stdin for job...\n", stderr
);
413 * Point to the files on the command-line...
416 num_files
= argc
- 6;
422 compress_files(num_files
, files
);
423 #endif /* HAVE_LIBZ */
425 fprintf(stderr
, "DEBUG: %d files to send in job...\n", num_files
);
429 * Set the authentication info, if any...
432 cupsSetPasswordCB(password_cb
);
437 * Use authenticaion information in the device URI...
440 if ((password
= strchr(username
, ':')) != NULL
)
443 cupsSetUser(username
);
448 * Try loading authentication information from the environment.
451 const char *ptr
= getenv("AUTH_USERNAME");
456 password
= getenv("AUTH_PASSWORD");
460 * Try connecting to the remote server...
464 start_time
= time(NULL
);
466 fputs("STATE: +connecting-to-device\n", stderr
);
470 fprintf(stderr
, "DEBUG: Connecting to %s:%d\n", hostname
, port
);
471 _cupsLangPuts(stderr
, _("INFO: Connecting to printer...\n"));
473 if ((http
= httpConnectEncrypt(hostname
, port
, cupsEncryption())) == NULL
)
475 int error
= errno
; /* Connection error */
480 if (getenv("CLASS") != NULL
)
483 * If the CLASS environment variable is set, the job was submitted
484 * to a class and not to a specific queue. In this case, we want
485 * to abort immediately so that the job can be requeued on the next
486 * available printer in the class.
489 _cupsLangPuts(stderr
,
490 _("INFO: Unable to contact printer, queuing on next "
491 "printer in class...\n"));
494 * Sleep 5 seconds to keep the job from requeuing too rapidly...
499 return (CUPS_BACKEND_FAILED
);
502 fprintf(stderr
, "DEBUG: Connection error: %s\n", strerror(errno
));
504 if (errno
== ECONNREFUSED
|| errno
== EHOSTDOWN
||
505 errno
== EHOSTUNREACH
)
507 if (contimeout
&& (time(NULL
) - start_time
) > contimeout
)
509 _cupsLangPuts(stderr
, _("ERROR: The printer is not responding.\n"));
510 return (CUPS_BACKEND_FAILED
);
516 _cupsLangPrintf(stderr
,
517 _("WARNING: Network printer \'%s\' may not exist "
518 "or is unavailable at this time.\n"),
523 _cupsLangPrintf(stderr
,
524 _("WARNING: Network printer \'%s\' is "
525 "unreachable at this time.\n"),
531 _cupsLangPrintf(stderr
,
532 _("WARNING: Network printer \'%s\' is busy.\n"),
544 _cupsLangPrintf(stderr
,
545 _("ERROR: Unable to locate network printer \'%s\'.\n"),
547 return (CUPS_BACKEND_STOP
);
551 _cupsLangPrintf(stderr
, _("ERROR: Network printer \'%s\' is not "
552 "responding.\n"), hostname
);
560 while (http
== NULL
);
562 if (job_canceled
|| !http
)
563 return (CUPS_BACKEND_FAILED
);
565 fputs("STATE: -connecting-to-device\n", stderr
);
566 _cupsLangPuts(stderr
, _("INFO: Connected to printer...\n"));
569 if (http
->hostaddr
->addr
.sa_family
== AF_INET6
)
570 fprintf(stderr
, "DEBUG: Connected to [%s]:%d (IPv6)...\n",
571 httpAddrString(http
->hostaddr
, addrname
, sizeof(addrname
)),
572 ntohs(http
->hostaddr
->ipv6
.sin6_port
));
574 #endif /* AF_INET6 */
575 if (http
->hostaddr
->addr
.sa_family
== AF_INET
)
576 fprintf(stderr
, "DEBUG: Connected to %s:%d (IPv4)...\n",
577 httpAddrString(http
->hostaddr
, addrname
, sizeof(addrname
)),
578 ntohs(http
->hostaddr
->ipv4
.sin_port
));
581 * See if the printer supports SNMP...
584 if ((snmp_fd
= _cupsSNMPOpen(http
->hostaddr
->addr
.sa_family
)) >= 0)
585 have_supplies
= !backendSNMPSupplies(snmp_fd
, http
->hostaddr
, &start_count
,
588 have_supplies
= start_count
= 0;
591 * Build a URI for the printer and fill the standard IPP attributes for
592 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
593 * might contain username:password information...
596 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), scheme
, NULL
, hostname
,
600 * First validate the destination and see if the device supports multiple
607 media_col_sup
= NULL
;
613 * Check for side-channel requests...
616 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
619 * Build the IPP request...
622 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
623 request
->request
.op
.version
[0] = version
/ 10;
624 request
->request
.op
.version
[1] = version
% 10;
626 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
629 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
630 "requested-attributes", sizeof(pattrs
) / sizeof(pattrs
[0]),
637 fputs("DEBUG: Getting supported attributes...\n", stderr
);
639 if (http
->version
< HTTP_1_1
)
641 fprintf(stderr
, "DEBUG: Printer responded with HTTP version %d.%d.\n",
642 http
->version
/ 100, http
->version
% 100);
644 _cupsLangPuts(stderr
,
645 _("ERROR: Unable to print: the printer does not conform to "
646 "the IPP standard.\n"));
647 exit(CUPS_BACKEND_STOP
);
650 if ((supported
= cupsDoRequest(http
, request
, resource
)) == NULL
)
651 ipp_status
= cupsLastError();
653 ipp_status
= supported
->request
.status
.status_code
;
655 if (ipp_status
> IPP_OK_CONFLICT
)
657 if (ipp_status
== IPP_PRINTER_BUSY
||
658 ipp_status
== IPP_SERVICE_UNAVAILABLE
)
660 if (contimeout
&& (time(NULL
) - start_time
) > contimeout
)
662 _cupsLangPuts(stderr
, _("ERROR: The printer is not responding.\n"));
663 return (CUPS_BACKEND_FAILED
);
666 _cupsLangPrintf(stderr
,
667 _("WARNING: Network host \'%s\' is busy; will retry in "
668 "%d seconds...\n"), hostname
, delay
);
670 report_printer_state(supported
, 0);
677 else if ((ipp_status
== IPP_BAD_REQUEST
||
678 ipp_status
== IPP_VERSION_NOT_SUPPORTED
) && version
> 10)
681 * Switch to IPP/1.1 or IPP/1.0...
686 _cupsLangPrintf(stderr
,
687 _("INFO: Printer does not support IPP/%d.%d, trying "
688 "IPP/%s...\n"), version
/ 10, version
% 10, "1.1");
693 _cupsLangPrintf(stderr
,
694 _("INFO: Printer does not support IPP/%d.%d, trying "
695 "IPP/%s...\n"), version
/ 10, version
% 10, "1.0");
701 else if (ipp_status
== IPP_NOT_FOUND
)
703 _cupsLangPuts(stderr
, _("ERROR: The printer URI is incorrect or no longer exists.\n"));
706 ippDelete(supported
);
708 return (CUPS_BACKEND_STOP
);
710 else if (ipp_status
== IPP_NOT_AUTHORIZED
|| ipp_status
== IPP_FORBIDDEN
)
712 if (!strncmp(httpGetField(http
, HTTP_FIELD_WWW_AUTHENTICATE
),
714 auth_info_required
= "negotiate";
716 fprintf(stderr
, "ATTR: auth-info-required=%s\n", auth_info_required
);
717 return (CUPS_BACKEND_AUTH_REQUIRED
);
721 _cupsLangPrintf(stderr
,
722 _("ERROR: Unable to get printer status (%s)\n"),
723 cupsLastErrorString());
728 ippDelete(supported
);
734 * Check for supported attributes...
737 if ((copies_sup
= ippFindAttribute(supported
, "copies-supported",
738 IPP_TAG_RANGE
)) != NULL
)
741 * Has the "copies-supported" attribute - does it have an upper
745 fprintf(stderr
, "DEBUG: copies-supported=%d-%d\n",
746 copies_sup
->values
[0].range
.lower
,
747 copies_sup
->values
[0].range
.upper
);
749 if (copies_sup
->values
[0].range
.upper
<= 1)
750 copies_sup
= NULL
; /* No */
753 cups_version
= ippFindAttribute(supported
, "cups-version", IPP_TAG_TEXT
);
755 if ((format_sup
= ippFindAttribute(supported
, "document-format-supported",
756 IPP_TAG_MIMETYPE
)) != NULL
)
758 fprintf(stderr
, "DEBUG: document-format-supported (%d values)\n",
759 format_sup
->num_values
);
760 for (i
= 0; i
< format_sup
->num_values
; i
++)
761 fprintf(stderr
, "DEBUG: [%d] = \"%s\"\n", i
,
762 format_sup
->values
[i
].string
.text
);
765 if ((media_col_sup
= ippFindAttribute(supported
, "media-col-supported",
766 IPP_TAG_KEYWORD
)) != NULL
)
768 fprintf(stderr
, "DEBUG: media-col-supported (%d values)\n",
769 media_col_sup
->num_values
);
770 for (i
= 0; i
< media_col_sup
->num_values
; i
++)
771 fprintf(stderr
, "DEBUG: [%d] = \"%s\"\n", i
,
772 media_col_sup
->values
[i
].string
.text
);
775 report_printer_state(supported
, 0);
777 while (ipp_status
> IPP_OK_CONFLICT
);
780 * See if the printer is accepting jobs and is not stopped; if either
781 * condition is true and we are printing to a class, requeue the job...
784 if (getenv("CLASS") != NULL
)
786 printer_state
= ippFindAttribute(supported
, "printer-state",
788 printer_accepting
= ippFindAttribute(supported
, "printer-is-accepting-jobs",
791 if (printer_state
== NULL
||
792 (printer_state
->values
[0].integer
> IPP_PRINTER_PROCESSING
&&
794 printer_accepting
== NULL
||
795 !printer_accepting
->values
[0].boolean
)
798 * If the CLASS environment variable is set, the job was submitted
799 * to a class and not to a specific queue. In this case, we want
800 * to abort immediately so that the job can be requeued on the next
801 * available printer in the class.
804 _cupsLangPuts(stderr
,
805 _("INFO: Unable to contact printer, queuing on next "
806 "printer in class...\n"));
808 ippDelete(supported
);
812 * Sleep 5 seconds to keep the job from requeuing too rapidly...
817 return (CUPS_BACKEND_FAILED
);
822 * See if the printer supports multiple copies...
825 copies
= atoi(argv
[4]);
827 if (copies_sup
|| argc
< 7)
829 copies_remaining
= 1;
835 copies_remaining
= copies
;
838 * Then issue the print-job request...
843 while (copies_remaining
> 0)
846 * Check for side-channel requests...
849 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
852 * Build the IPP request...
859 request
= ippNewRequest(IPP_CREATE_JOB
);
861 request
= ippNewRequest(IPP_PRINT_JOB
);
863 request
->request
.op
.version
[0] = version
/ 10;
864 request
->request
.op
.version
[1] = version
% 10;
866 fprintf(stderr
, "DEBUG: %s IPP/%d.%d\n",
867 ippOpString(request
->request
.op
.operation_id
),
868 request
->request
.op
.version
[0],
869 request
->request
.op
.version
[1]);
871 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
873 fprintf(stderr
, "DEBUG: printer-uri=\"%s\"\n", uri
);
877 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
878 "requesting-user-name", NULL
, argv
[2]);
879 fprintf(stderr
, "DEBUG: requesting-user-name=\"%s\"\n", argv
[2]);
884 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "job-name", NULL
,
886 fprintf(stderr
, "DEBUG: job-name=\"%s\"\n", argv
[3]);
891 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
892 "compression", NULL
, "gzip");
893 #endif /* HAVE_LIBZ */
896 * Handle options on the command-line...
900 num_options
= cupsParseOptions(argv
[5], 0, &options
);
902 if (format_sup
!= NULL
)
904 for (i
= 0; i
< format_sup
->num_values
; i
++)
905 if (!strcasecmp(final_content_type
, format_sup
->values
[i
].string
.text
))
908 if (i
< format_sup
->num_values
)
909 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
,
910 "document-format", NULL
, final_content_type
);
915 if (cups_version
|| !media_col_sup
)
918 * When talking to another CUPS server, send all options...
921 cupsEncodeOptions(request
, num_options
, options
);
926 * Otherwise send standard IPP attributes...
929 char cachefile
[1024];/* Printer PWG cache file */
930 const char *cups_cachedir
; /* Location of cache file */
931 _pwg_t
*pwg
; /* PWG mapping data */
932 const char *keyword
; /* PWG keyword */
933 _pwg_size_t
*size
; /* PWG media size */
936 if ((cups_cachedir
= getenv("CUPS_CACHEDIR")) == NULL
)
937 cups_cachedir
= CUPS_CACHEDIR
;
939 snprintf(cachefile
, sizeof(cachefile
), "%s/%s.pwg", cups_cachedir
,
942 if ((keyword
= cupsGetOption("PageSize", num_options
, options
)) == NULL
)
943 keyword
= cupsGetOption("media", num_options
, options
);
945 pwg
= _pwgCreateWithFile(cachefile
);
946 size
= _pwgGetSize(pwg
, keyword
);
948 if (media_col_sup
&& size
)
950 ipp_t
*media_col
, /* media-col value */
951 *media_size
; /* media-size value */
952 const char *media_source
, /* media-source value */
953 *media_type
; /* media-type value */
955 media_size
= ippNew();
956 ippAddInteger(media_size
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
957 "x-dimension", size
->width
);
958 ippAddInteger(media_size
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
959 "y-dimension", size
->length
);
961 media_col
= ippNew();
962 ippAddCollection(media_col
, IPP_TAG_ZERO
, "media-size", media_size
);
964 media_source
= _pwgGetSource(pwg
, cupsGetOption("InputSlot",
967 media_type
= _pwgGetType(pwg
, cupsGetOption("MediaType",
968 num_options
, options
));
970 for (i
= 0; i
< media_col_sup
->num_values
; i
++)
972 if (!strcmp(media_col_sup
->values
[i
].string
.text
,
973 "media-left-margin"))
974 ippAddInteger(media_col
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
975 "media-left-margin", size
->left
);
976 else if (!strcmp(media_col_sup
->values
[i
].string
.text
,
977 "media-bottom-margin"))
978 ippAddInteger(media_col
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
979 "media-bottom-margin", size
->left
);
980 else if (!strcmp(media_col_sup
->values
[i
].string
.text
,
981 "media-right-margin"))
982 ippAddInteger(media_col
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
983 "media-right-margin", size
->left
);
984 else if (!strcmp(media_col_sup
->values
[i
].string
.text
,
986 ippAddInteger(media_col
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
987 "media-top-margin", size
->left
);
988 else if (!strcmp(media_col_sup
->values
[i
].string
.text
,
989 "media-source") && media_source
)
990 ippAddString(media_col
, IPP_TAG_ZERO
, IPP_TAG_KEYWORD
,
991 "media-source", NULL
, media_source
);
992 else if (!strcmp(media_col_sup
->values
[i
].string
.text
,
993 "media-type") && media_type
)
994 ippAddString(media_col
, IPP_TAG_ZERO
, IPP_TAG_KEYWORD
,
995 "media-type", NULL
, media_type
);
998 ippAddCollection(request
, IPP_TAG_JOB
, "media-col", media_col
);
1001 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "media",
1002 NULL
, size
->map
.pwg
);
1005 _pwg_media_t
*found
; /* PWG media */
1008 if ((found
= _pwgMediaForPPD(keyword
)) == NULL
)
1009 keyword
= found
->pwg
;
1010 else if ((found
= _pwgMediaForLegacy(keyword
)) == NULL
)
1011 keyword
= found
->pwg
;
1013 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "media",
1017 if ((keyword
= cupsGetOption("output-bin", num_options
,
1019 keyword
= _pwgGetBin(pwg
, cupsGetOption("OutputBin", num_options
,
1023 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "output-bin",
1026 if ((keyword
= cupsGetOption("output-mode", num_options
,
1028 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "output-mode",
1031 if ((keyword
= cupsGetOption("sides", num_options
, options
)) != NULL
)
1032 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "sides",
1038 if (copies_sup
&& copies
> 1)
1039 ippAddInteger(request
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "copies", copies
);
1042 cupsFreeOptions(num_options
, options
);
1049 response
= cupsDoRequest(http
, request
, resource
);
1052 fputs("DEBUG: Sending file using chunking...\n", stderr
);
1053 http_status
= cupsSendRequest(http
, request
, resource
, 0);
1054 if (http_status
== HTTP_CONTINUE
&& request
->state
== IPP_DATA
)
1057 fd
= open(files
[0], O_RDONLY
);
1061 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
1063 fprintf(stderr
, "DEBUG: Read %d bytes...\n", (int)bytes
);
1065 if (cupsWriteRequestData(http
, buffer
, bytes
) != HTTP_CONTINUE
)
1070 * Check for side-channel requests...
1073 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
1081 response
= cupsGetResponse(http
, resource
);
1085 ipp_status
= cupsLastError();
1087 if (ipp_status
> IPP_OK_CONFLICT
)
1094 if (ipp_status
== IPP_SERVICE_UNAVAILABLE
||
1095 ipp_status
== IPP_PRINTER_BUSY
)
1097 _cupsLangPuts(stderr
,
1098 _("INFO: Printer busy; will retry in 10 seconds...\n"));
1104 * Update auth-info-required as needed...
1107 _cupsLangPrintf(stderr
, _("ERROR: Print file was not accepted (%s)\n"),
1108 cupsLastErrorString());
1110 if (ipp_status
== IPP_NOT_AUTHORIZED
|| ipp_status
== IPP_FORBIDDEN
)
1112 fprintf(stderr
, "DEBUG: WWW-Authenticate=\"%s\"\n",
1113 httpGetField(http
, HTTP_FIELD_WWW_AUTHENTICATE
));
1116 * Normal authentication goes through the password callback, which sets
1117 * auth_info_required to "username,password". Kerberos goes directly
1118 * through GSSAPI, so look for Negotiate in the WWW-Authenticate header
1119 * here and set auth_info_required as needed...
1122 if (!strncmp(httpGetField(http
, HTTP_FIELD_WWW_AUTHENTICATE
),
1124 auth_info_required
= "negotiate";
1128 else if ((job_id_attr
= ippFindAttribute(response
, "job-id",
1129 IPP_TAG_INTEGER
)) == NULL
)
1131 _cupsLangPuts(stderr
,
1132 _("NOTICE: Print file accepted - job ID unknown.\n"));
1137 job_id
= job_id_attr
->values
[0].integer
;
1138 _cupsLangPrintf(stderr
, _("NOTICE: Print file accepted - job ID %d.\n"),
1142 ippDelete(response
);
1147 if (job_id
&& num_files
> 1)
1149 for (i
= 0; i
< num_files
; i
++)
1152 * Check for side-channel requests...
1155 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
1158 * Send the next file in the job...
1161 request
= ippNewRequest(IPP_SEND_DOCUMENT
);
1162 request
->request
.op
.version
[0] = version
/ 10;
1163 request
->request
.op
.version
[1] = version
% 10;
1165 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1168 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id",
1172 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1173 "requesting-user-name", NULL
, argv
[2]);
1175 if ((i
+ 1) == num_files
)
1176 ippAddBoolean(request
, IPP_TAG_OPERATION
, "last-document", 1);
1178 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
,
1179 "document-format", NULL
, content_type
);
1181 fprintf(stderr
, "DEBUG: Sending file %d using chunking...\n", i
+ 1);
1182 http_status
= cupsSendRequest(http
, request
, resource
, 0);
1183 if (http_status
== HTTP_CONTINUE
&& request
->state
== IPP_DATA
&&
1184 (fd
= open(files
[i
], O_RDONLY
)) >= 0)
1186 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
1188 if (cupsWriteRequestData(http
, buffer
, bytes
) != HTTP_CONTINUE
)
1193 * Check for side-channel requests...
1196 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
1203 ippDelete(cupsGetResponse(http
, resource
));
1206 if (cupsLastError() > IPP_OK_CONFLICT
)
1208 ipp_status
= cupsLastError();
1210 _cupsLangPrintf(stderr
,
1211 _("ERROR: Unable to add file %d to job: %s\n"),
1212 job_id
, cupsLastErrorString());
1218 if (ipp_status
<= IPP_OK_CONFLICT
&& argc
> 6)
1220 fprintf(stderr
, "PAGE: 1 %d\n", copies_sup
? atoi(argv
[4]) : 1);
1221 copies_remaining
--;
1223 else if (ipp_status
== IPP_SERVICE_UNAVAILABLE
||
1224 ipp_status
== IPP_PRINTER_BUSY
)
1227 copies_remaining
--;
1230 * Wait for the job to complete...
1233 if (!job_id
|| !waitjob
)
1236 _cupsLangPuts(stderr
, _("INFO: Waiting for job to complete...\n"));
1238 for (delay
= 1; !job_canceled
;)
1241 * Check for side-channel requests...
1244 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
1247 * Build an IPP_GET_JOB_ATTRIBUTES request...
1250 request
= ippNewRequest(IPP_GET_JOB_ATTRIBUTES
);
1251 request
->request
.op
.version
[0] = version
/ 10;
1252 request
->request
.op
.version
[1] = version
% 10;
1254 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1257 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id",
1261 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1262 "requesting-user-name", NULL
, argv
[2]);
1264 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1265 "requested-attributes", sizeof(jattrs
) / sizeof(jattrs
[0]),
1272 response
= cupsDoRequest(http
, request
, resource
);
1273 ipp_status
= cupsLastError();
1275 if (ipp_status
== IPP_NOT_FOUND
)
1278 * Job has gone away and/or the server has no job history...
1281 ippDelete(response
);
1283 ipp_status
= IPP_OK
;
1287 if (ipp_status
> IPP_OK_CONFLICT
)
1289 if (ipp_status
!= IPP_SERVICE_UNAVAILABLE
&&
1290 ipp_status
!= IPP_PRINTER_BUSY
)
1292 ippDelete(response
);
1294 _cupsLangPrintf(stderr
,
1295 _("ERROR: Unable to get job %d attributes (%s)\n"),
1296 job_id
, cupsLastErrorString());
1303 if ((job_state
= ippFindAttribute(response
, "job-state",
1304 IPP_TAG_ENUM
)) != NULL
)
1307 * Stop polling if the job is finished or pending-held...
1310 if (job_state
->values
[0].integer
> IPP_JOB_STOPPED
)
1312 if ((job_sheets
= ippFindAttribute(response
,
1313 "job-media-sheets-completed",
1314 IPP_TAG_INTEGER
)) != NULL
)
1315 fprintf(stderr
, "PAGE: total %d\n",
1316 job_sheets
->values
[0].integer
);
1318 ippDelete(response
);
1325 * If the printer does not return a job-state attribute, it does not
1326 * conform to the IPP specification - break out immediately and fail
1330 fputs("DEBUG: No job-state available from printer - stopping queue.\n",
1332 ipp_status
= IPP_INTERNAL_ERROR
;
1337 ippDelete(response
);
1340 * Check the printer state and report it if necessary...
1343 check_printer_state(http
, uri
, resource
, argv
[2], version
, job_id
);
1346 * Wait 1-10 seconds before polling again...
1358 * Cancel the job as needed...
1361 if (job_canceled
&& job_id
)
1362 cancel_job(http
, uri
, job_id
, resource
, argv
[2], version
);
1365 * Check the printer state and report it if necessary...
1368 check_printer_state(http
, uri
, resource
, argv
[2], version
, job_id
);
1371 * Collect the final page count as needed...
1374 if (have_supplies
&&
1375 !backendSNMPSupplies(snmp_fd
, http
->hostaddr
, &page_count
, NULL
) &&
1376 page_count
> start_count
)
1377 fprintf(stderr
, "PAGE: total %d\n", page_count
- start_count
);
1381 * See if we used Kerberos at all...
1385 auth_info_required
= "negotiate";
1386 #endif /* HAVE_GSSAPI */
1394 ippDelete(supported
);
1397 * Remove the temporary file(s) if necessary...
1403 for (i
= 0; i
< num_files
; i
++)
1406 #endif /* HAVE_LIBZ */
1409 * Return the queue status...
1412 fprintf(stderr
, "ATTR: auth-info-required=%s\n", auth_info_required
);
1414 if (ipp_status
== IPP_NOT_AUTHORIZED
|| ipp_status
== IPP_FORBIDDEN
)
1415 return (CUPS_BACKEND_AUTH_REQUIRED
);
1416 else if (ipp_status
== IPP_INTERNAL_ERROR
)
1417 return (CUPS_BACKEND_STOP
);
1418 else if (ipp_status
> IPP_OK_CONFLICT
)
1419 return (CUPS_BACKEND_FAILED
);
1422 _cupsLangPuts(stderr
, _("INFO: Ready to print.\n"));
1423 return (CUPS_BACKEND_OK
);
1429 * 'cancel_job()' - Cancel a print job.
1433 cancel_job(http_t
*http
, /* I - HTTP connection */
1434 const char *uri
, /* I - printer-uri */
1435 int id
, /* I - job-id */
1436 const char *resource
, /* I - Resource path */
1437 const char *user
, /* I - requesting-user-name */
1438 int version
) /* I - IPP version */
1440 ipp_t
*request
; /* Cancel-Job request */
1443 _cupsLangPuts(stderr
, _("INFO: Canceling print job...\n"));
1445 request
= ippNewRequest(IPP_CANCEL_JOB
);
1446 request
->request
.op
.version
[0] = version
/ 10;
1447 request
->request
.op
.version
[1] = version
% 10;
1449 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1451 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id", id
);
1453 if (user
&& user
[0])
1454 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1455 "requesting-user-name", NULL
, user
);
1461 ippDelete(cupsDoRequest(http
, request
, resource
));
1463 if (cupsLastError() > IPP_OK_CONFLICT
)
1464 _cupsLangPrintf(stderr
, _("ERROR: Unable to cancel job %d: %s\n"), id
,
1465 cupsLastErrorString());
1470 * 'check_printer_state()' - Check the printer state...
1474 check_printer_state(
1475 http_t
*http
, /* I - HTTP connection */
1476 const char *uri
, /* I - Printer URI */
1477 const char *resource
, /* I - Resource path */
1478 const char *user
, /* I - Username, if any */
1479 int version
, /* I - IPP version */
1480 int job_id
) /* I - Current job ID */
1482 ipp_t
*request
, /* IPP request */
1483 *response
; /* IPP response */
1484 static const char * const attrs
[] = /* Attributes we want */
1491 "printer-state-message",
1492 "printer-state-reasons"
1497 * Check on the printer state...
1500 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
1501 request
->request
.op
.version
[0] = version
/ 10;
1502 request
->request
.op
.version
[1] = version
% 10;
1504 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1507 if (user
&& user
[0])
1508 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1509 "requesting-user-name", NULL
, user
);
1511 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1512 "requested-attributes",
1513 (int)(sizeof(attrs
) / sizeof(attrs
[0])), NULL
, attrs
);
1519 if ((response
= cupsDoRequest(http
, request
, resource
)) != NULL
)
1521 report_printer_state(response
, job_id
);
1522 ippDelete(response
);
1529 * 'compress_files()' - Compress print files...
1533 compress_files(int num_files
, /* I - Number of files */
1534 char **files
) /* I - Files */
1536 int i
, /* Looping var */
1537 fd
; /* Temporary file descriptor */
1538 ssize_t bytes
; /* Bytes read/written */
1539 size_t total
; /* Total bytes read */
1540 cups_file_t
*in
, /* Input file */
1541 *out
; /* Output file */
1542 struct stat outinfo
; /* Output file information */
1543 char filename
[1024], /* Temporary filename */
1544 buffer
[32768]; /* Copy buffer */
1547 fprintf(stderr
, "DEBUG: Compressing %d job files...\n", num_files
);
1548 for (i
= 0; i
< num_files
; i
++)
1550 if ((fd
= cupsTempFd(filename
, sizeof(filename
))) < 0)
1552 _cupsLangPrintf(stderr
,
1553 _("ERROR: Unable to create temporary compressed print "
1554 "file: %s\n"), strerror(errno
));
1555 exit(CUPS_BACKEND_FAILED
);
1558 if ((out
= cupsFileOpenFd(fd
, "w9")) == NULL
)
1560 _cupsLangPrintf(stderr
,
1561 _("ERROR: Unable to open temporary compressed print "
1562 "file: %s\n"), strerror(errno
));
1563 exit(CUPS_BACKEND_FAILED
);
1566 if ((in
= cupsFileOpen(files
[i
], "r")) == NULL
)
1568 _cupsLangPrintf(stderr
,
1569 _("ERROR: Unable to open print file \"%s\": %s\n"),
1570 files
[i
], strerror(errno
));
1572 exit(CUPS_BACKEND_FAILED
);
1576 while ((bytes
= cupsFileRead(in
, buffer
, sizeof(buffer
))) > 0)
1577 if (cupsFileWrite(out
, buffer
, bytes
) < bytes
)
1579 _cupsLangPrintf(stderr
,
1580 _("ERROR: Unable to write %d bytes to \"%s\": %s\n"),
1581 (int)bytes
, filename
, strerror(errno
));
1584 exit(CUPS_BACKEND_FAILED
);
1592 files
[i
] = strdup(filename
);
1594 if (!stat(filename
, &outinfo
))
1596 "DEBUG: File %d compressed to %.1f%% of original size, "
1597 CUPS_LLFMT
" bytes...\n",
1598 i
+ 1, 100.0 * outinfo
.st_size
/ total
,
1599 CUPS_LLCAST outinfo
.st_size
);
1602 #endif /* HAVE_LIBZ */
1606 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
1609 static const char * /* O - Password */
1610 password_cb(const char *prompt
) /* I - Prompt (not used) */
1615 * Remember that we need to authenticate...
1618 auth_info_required
= "username,password";
1620 if (password
&& *password
&& password_tries
< 3)
1629 * Give up after 3 tries or if we don't have a password to begin with...
1638 * 'report_attr()' - Report an IPP attribute value.
1642 report_attr(ipp_attribute_t
*attr
) /* I - Attribute */
1644 int i
; /* Looping var */
1645 char value
[1024], /* Value string */
1646 *valptr
, /* Pointer into value string */
1647 *attrptr
; /* Pointer into attribute value */
1651 * Convert the attribute values into quoted strings...
1654 for (i
= 0, valptr
= value
;
1655 i
< attr
->num_values
&& valptr
< (value
+ sizeof(value
) - 10);
1661 switch (attr
->value_tag
)
1663 case IPP_TAG_INTEGER
:
1665 snprintf(valptr
, sizeof(value
) - (valptr
- value
), "%d",
1666 attr
->values
[i
].integer
);
1667 valptr
+= strlen(valptr
);
1672 case IPP_TAG_KEYWORD
:
1674 for (attrptr
= attr
->values
[i
].string
.text
;
1675 *attrptr
&& valptr
< (value
+ sizeof(value
) - 10);
1678 if (*attrptr
== '\\' || *attrptr
== '\"')
1681 *valptr
++ = *attrptr
;
1688 * Unsupported value type...
1698 * Tell the scheduler about the new values...
1701 fprintf(stderr
, "ATTR: %s=%s\n", attr
->name
, value
);
1706 * 'report_printer_state()' - Report the printer state.
1709 static int /* O - Number of reasons shown */
1710 report_printer_state(ipp_t
*ipp
, /* I - IPP response */
1711 int job_id
) /* I - Current job ID */
1713 int i
; /* Looping var */
1714 int count
; /* Count of reasons shown... */
1715 ipp_attribute_t
*psm
, /* printer-state-message */
1716 *reasons
, /* printer-state-reasons */
1717 *marker
; /* marker-* attributes */
1718 const char *reason
; /* Current reason */
1719 const char *prefix
; /* Prefix for STATE: line */
1720 char state
[1024]; /* State string */
1723 if ((psm
= ippFindAttribute(ipp
, "printer-state-message",
1724 IPP_TAG_TEXT
)) != NULL
)
1725 fprintf(stderr
, "INFO: %s\n", psm
->values
[0].string
.text
);
1727 if ((reasons
= ippFindAttribute(ipp
, "printer-state-reasons",
1728 IPP_TAG_KEYWORD
)) == NULL
)
1734 for (i
= 0, count
= 0; i
< reasons
->num_values
; i
++)
1736 reason
= reasons
->values
[i
].string
.text
;
1738 if (strcmp(reason
, "paused") &&
1739 strcmp(reason
, "com.apple.print.recoverable-warning"))
1741 strlcat(state
, prefix
, sizeof(state
));
1742 strlcat(state
, reason
, sizeof(state
));
1749 fprintf(stderr
, "%s\n", state
);
1752 * Relay the current marker-* attribute values...
1755 if ((marker
= ippFindAttribute(ipp
, "marker-colors", IPP_TAG_NAME
)) != NULL
)
1756 report_attr(marker
);
1757 if ((marker
= ippFindAttribute(ipp
, "marker-high-levels",
1758 IPP_TAG_INTEGER
)) != NULL
)
1759 report_attr(marker
);
1760 if ((marker
= ippFindAttribute(ipp
, "marker-levels",
1761 IPP_TAG_INTEGER
)) != NULL
)
1762 report_attr(marker
);
1763 if ((marker
= ippFindAttribute(ipp
, "marker-low-levels",
1764 IPP_TAG_INTEGER
)) != NULL
)
1765 report_attr(marker
);
1766 if ((marker
= ippFindAttribute(ipp
, "marker-message", IPP_TAG_TEXT
)) != NULL
)
1767 report_attr(marker
);
1768 if ((marker
= ippFindAttribute(ipp
, "marker-names", IPP_TAG_NAME
)) != NULL
)
1769 report_attr(marker
);
1770 if ((marker
= ippFindAttribute(ipp
, "marker-types", IPP_TAG_KEYWORD
)) != NULL
)
1771 report_attr(marker
);
1778 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
1782 sigterm_handler(int sig
) /* I - Signal */
1784 (void)sig
; /* remove compiler warnings... */
1789 * Flag that the job should be cancelled...
1801 * End of "$Id: ipp.c 7948 2008-09-17 00:04:12Z mike $".