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 * monitor_printer() - Monitor the printer state...
24 * password_cb() - Disable the password prompt for
25 * cupsDoFileRequest().
26 * report_attr() - Report an IPP attribute value.
27 * report_printer_state() - Report the printer state.
28 * sigterm_handler() - Handle 'terminate' signals that stop the backend.
32 * Include necessary headers.
35 #include "backend-private.h"
36 #include <sys/types.h>
45 typedef struct _cups_monitor_s
/**** Monitoring data ****/
47 const char *uri
, /* Printer URI */
48 *hostname
, /* Hostname */
50 *resource
; /* Resource path */
51 int port
, /* Port number */
52 version
, /* IPP version */
53 job_id
; /* Job ID for submitted job */
54 http_encryption_t encryption
; /* Use encryption? */
55 ipp_jstate_t job_state
; /* Current job state */
56 ipp_pstate_t printer_state
; /* Current printer state */
64 static const char *auth_info_required
= "none";
65 /* New auth-info-required value */
66 static const char * const jattrs
[] = /* Job attributes we want */
68 "job-media-sheets-completed",
72 static int job_canceled
= 0; /* Job cancelled? */
73 static char *password
= NULL
; /* Password for device URI */
74 static int password_tries
= 0; /* Password tries */
75 static const char * const pattrs
[] = /* Printer attributes we want */
79 "document-format-supported",
87 "media-col-supported",
89 "printer-alert-description",
90 "printer-is-accepting-jobs",
92 "printer-state-message",
93 "printer-state-reasons",
101 static void cancel_job(http_t
*http
, const char *uri
, int id
,
102 const char *resource
, const char *user
,
104 static ipp_pstate_t
check_printer_state(http_t
*http
, const char *uri
,
105 const char *resource
,
106 const char *user
, int version
,
109 static void compress_files(int num_files
, char **files
);
110 #endif /* HAVE_LIBZ */
111 static void *monitor_printer(_cups_monitor_t
*monitor
);
112 static const char *password_cb(const char *);
113 static void report_attr(ipp_attribute_t
*attr
);
114 static int report_printer_state(ipp_t
*ipp
, int job_id
);
115 static void sigterm_handler(int sig
);
119 * 'main()' - Send a file to the printer or server.
123 * printer-uri job-id user title copies options [file]
126 int /* O - Exit status */
127 main(int argc
, /* I - Number of command-line args */
128 char *argv
[]) /* I - Command-line arguments */
130 int i
; /* Looping var */
131 int send_options
; /* Send job options? */
132 int num_options
; /* Number of printer options */
133 cups_option_t
*options
; /* Printer options */
134 const char *device_uri
; /* Device URI */
135 char scheme
[255], /* Scheme in URI */
136 hostname
[1024], /* Hostname */
137 username
[255], /* Username info */
138 resource
[1024], /* Resource info (printer name) */
139 addrname
[256], /* Address name */
140 *optptr
, /* Pointer to URI options */
141 *name
, /* Name of option */
142 *value
, /* Value of option */
143 sep
; /* Separator character */
144 int snmp_fd
, /* SNMP socket */
145 start_count
, /* Page count via SNMP at start */
146 page_count
, /* Page count via SNMP */
147 have_supplies
; /* Printer supports supply levels? */
148 int num_files
; /* Number of files to print */
149 char **files
; /* Files to print */
150 int port
; /* Port number (not used) */
151 char uri
[HTTP_MAX_URI
]; /* Updated URI without user/pass */
152 http_status_t http_status
; /* Status of HTTP request */
153 ipp_status_t ipp_status
; /* Status of IPP request */
154 http_t
*http
; /* HTTP connection */
155 ipp_t
*request
, /* IPP request */
156 *response
, /* IPP response */
157 *supported
; /* get-printer-attributes response */
158 time_t start_time
; /* Time of first connect */
159 int contimeout
; /* Connection timeout */
160 int delay
; /* Delay for retries... */
161 int compression
, /* Do compression of the job data? */
162 waitjob
, /* Wait for job complete? */
163 waitprinter
; /* Wait for printer ready? */
164 _cups_monitor_t monitor
; /* Monitoring data */
165 ipp_attribute_t
*job_id_attr
; /* job-id attribute */
166 int job_id
; /* job-id value */
167 ipp_attribute_t
*job_sheets
; /* job-media-sheets-completed */
168 ipp_attribute_t
*job_state
; /* job-state */
169 ipp_attribute_t
*copies_sup
; /* copies-supported */
170 ipp_attribute_t
*cups_version
; /* cups-version */
171 ipp_attribute_t
*format_sup
; /* document-format-supported */
172 ipp_attribute_t
*media_col_sup
; /* media-col-supported */
173 ipp_attribute_t
*printer_state
; /* printer-state attribute */
174 ipp_attribute_t
*printer_accepting
; /* printer-is-accepting-jobs */
175 int copies
, /* Number of copies for job */
176 copies_remaining
; /* Number of copies remaining */
177 const char *content_type
, /* CONTENT_TYPE environment variable */
178 *final_content_type
; /* FINAL_CONTENT_TYPE environment var */
179 int fd
; /* File descriptor */
180 off_t bytes
; /* Bytes copied */
181 char buffer
[16384]; /* Copy buffer */
182 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
183 struct sigaction action
; /* Actions for POSIX signals */
184 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
185 int version
; /* IPP version */
189 * Make sure status messages are not buffered...
192 setbuf(stderr
, NULL
);
195 * Ignore SIGPIPE and catch SIGTERM signals...
199 sigset(SIGPIPE
, SIG_IGN
);
200 sigset(SIGTERM
, sigterm_handler
);
201 #elif defined(HAVE_SIGACTION)
202 memset(&action
, 0, sizeof(action
));
203 action
.sa_handler
= SIG_IGN
;
204 sigaction(SIGPIPE
, &action
, NULL
);
206 sigemptyset(&action
.sa_mask
);
207 sigaddset(&action
.sa_mask
, SIGTERM
);
208 action
.sa_handler
= sigterm_handler
;
209 sigaction(SIGTERM
, &action
, NULL
);
211 signal(SIGPIPE
, SIG_IGN
);
212 signal(SIGTERM
, sigterm_handler
);
213 #endif /* HAVE_SIGSET */
216 * Check command-line...
223 if ((s
= strrchr(argv
[0], '/')) != NULL
)
228 printf("network %s \"Unknown\" \"%s (%s)\"\n",
229 s
, _cupsLangString(cupsLangDefault(),
230 _("Internet Printing Protocol")), s
);
231 return (CUPS_BACKEND_OK
);
235 _cupsLangPrintf(stderr
,
236 _("Usage: %s job-id user title copies options [file]\n"),
238 return (CUPS_BACKEND_STOP
);
242 * Get the (final) content type...
245 if ((content_type
= getenv("CONTENT_TYPE")) == NULL
)
246 content_type
= "application/octet-stream";
248 if ((final_content_type
= getenv("FINAL_CONTENT_TYPE")) == NULL
)
250 final_content_type
= content_type
;
252 if (!strncmp(final_content_type
, "printer/", 8))
253 final_content_type
= "application/vnd.cups-raw";
257 * Extract the hostname and printer name from the URI...
260 if ((device_uri
= cupsBackendDeviceURI(argv
)) == NULL
)
261 return (CUPS_BACKEND_FAILED
);
263 httpSeparateURI(HTTP_URI_CODING_ALL
, device_uri
, scheme
, sizeof(scheme
),
264 username
, sizeof(username
), hostname
, sizeof(hostname
), &port
,
265 resource
, sizeof(resource
));
268 port
= IPP_PORT
; /* Default to port 631 */
270 if (!strcmp(scheme
, "https"))
271 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS
);
273 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED
);
276 * See if there are any options...
283 contimeout
= 7 * 24 * 60 * 60;
285 if ((optptr
= strchr(resource
, '?')) != NULL
)
288 * Yup, terminate the device name string and move to the first
289 * character of the optptr...
295 * Then parse the optptr...
306 while (*optptr
&& *optptr
!= '=' && *optptr
!= '+' && *optptr
!= '&')
309 if ((sep
= *optptr
) != '\0')
320 while (*optptr
&& *optptr
!= '+' && *optptr
!= '&')
330 * Process the option...
333 if (!strcasecmp(name
, "waitjob"))
336 * Wait for job completion?
339 waitjob
= !strcasecmp(value
, "on") ||
340 !strcasecmp(value
, "yes") ||
341 !strcasecmp(value
, "true");
343 else if (!strcasecmp(name
, "waitprinter"))
346 * Wait for printer idle?
349 waitprinter
= !strcasecmp(value
, "on") ||
350 !strcasecmp(value
, "yes") ||
351 !strcasecmp(value
, "true");
353 else if (!strcasecmp(name
, "encryption"))
356 * Enable/disable encryption?
359 if (!strcasecmp(value
, "always"))
360 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS
);
361 else if (!strcasecmp(value
, "required"))
362 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED
);
363 else if (!strcasecmp(value
, "never"))
364 cupsSetEncryption(HTTP_ENCRYPT_NEVER
);
365 else if (!strcasecmp(value
, "ifrequested"))
366 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED
);
369 _cupsLangPrintf(stderr
,
370 _("ERROR: Unknown encryption option value \"%s\"\n"),
374 else if (!strcasecmp(name
, "version"))
376 if (!strcmp(value
, "1.0"))
378 else if (!strcmp(value
, "1.1"))
380 else if (!strcmp(value
, "2.0"))
382 else if (!strcmp(value
, "2.1"))
386 _cupsLangPrintf(stderr
,
387 _("ERROR: Unknown version option value \"%s\"\n"),
392 else if (!strcasecmp(name
, "compression"))
394 compression
= !strcasecmp(value
, "true") ||
395 !strcasecmp(value
, "yes") ||
396 !strcasecmp(value
, "on") ||
397 !strcasecmp(value
, "gzip");
399 #endif /* HAVE_LIBZ */
400 else if (!strcasecmp(name
, "contimeout"))
403 * Set the connection timeout...
407 contimeout
= atoi(value
);
415 _cupsLangPrintf(stderr
,
416 _("ERROR: Unknown option \"%s\" with value \"%s\"\n"),
423 * If we have 7 arguments, print the file named on the command-line.
424 * Otherwise, copy stdin to a temporary file and print the temporary
431 send_options
= !strcasecmp(final_content_type
, "application/pdf") ||
432 !strcasecmp(final_content_type
, "application/vnd.cups-pdf") ||
433 !strcasecmp(final_content_type
, "image/jpeg") ||
434 !strcasecmp(final_content_type
, "image/png");
436 fputs("DEBUG: Sending stdin for job...\n", stderr
);
441 * Point to the files on the command-line...
444 num_files
= argc
- 6;
450 compress_files(num_files
, files
);
451 #endif /* HAVE_LIBZ */
453 fprintf(stderr
, "DEBUG: %d files to send in job...\n", num_files
);
457 * Set the authentication info, if any...
460 cupsSetPasswordCB(password_cb
);
465 * Use authenticaion information in the device URI...
468 if ((password
= strchr(username
, ':')) != NULL
)
471 cupsSetUser(username
);
476 * Try loading authentication information from the environment.
479 const char *ptr
= getenv("AUTH_USERNAME");
484 password
= getenv("AUTH_PASSWORD");
488 * Try connecting to the remote server...
492 start_time
= time(NULL
);
494 fputs("STATE: +connecting-to-device\n", stderr
);
498 fprintf(stderr
, "DEBUG: Connecting to %s:%d\n", hostname
, port
);
499 _cupsLangPuts(stderr
, _("INFO: Connecting to printer...\n"));
501 if ((http
= httpConnectEncrypt(hostname
, port
, cupsEncryption())) == NULL
)
503 #if 0 /* These need to go in here someplace when we see HTTP_PKI_ERROR or IPP_PKI_ERROR */
504 fputs("STATE: +cups-certificate-error\n", stderr
);
505 fputs("STATE: -cups-certificate-error\n", stderr
);
508 int error
= errno
; /* Connection error */
513 if (getenv("CLASS") != NULL
)
516 * If the CLASS environment variable is set, the job was submitted
517 * to a class and not to a specific queue. In this case, we want
518 * to abort immediately so that the job can be requeued on the next
519 * available printer in the class.
522 _cupsLangPuts(stderr
,
523 _("INFO: Unable to contact printer, queuing on next "
524 "printer in class...\n"));
527 * Sleep 5 seconds to keep the job from requeuing too rapidly...
532 return (CUPS_BACKEND_FAILED
);
535 fprintf(stderr
, "DEBUG: Connection error: %s\n", strerror(errno
));
537 if (errno
== ECONNREFUSED
|| errno
== EHOSTDOWN
||
538 errno
== EHOSTUNREACH
)
540 if (contimeout
&& (time(NULL
) - start_time
) > contimeout
)
542 _cupsLangPuts(stderr
, _("ERROR: The printer is not responding.\n"));
543 return (CUPS_BACKEND_FAILED
);
549 _cupsLangPrintf(stderr
,
550 _("WARNING: Network printer \'%s\' may not exist "
551 "or is unavailable at this time.\n"),
556 _cupsLangPrintf(stderr
,
557 _("WARNING: Network printer \'%s\' is "
558 "unreachable at this time.\n"),
564 _cupsLangPrintf(stderr
,
565 _("WARNING: Network printer \'%s\' is busy.\n"),
577 _cupsLangPrintf(stderr
,
578 _("ERROR: Unable to locate network printer \'%s\'.\n"),
580 return (CUPS_BACKEND_STOP
);
584 _cupsLangPrintf(stderr
, _("ERROR: Network printer \'%s\' is not "
585 "responding.\n"), hostname
);
593 while (http
== NULL
);
595 if (job_canceled
|| !http
)
596 return (CUPS_BACKEND_FAILED
);
598 fputs("STATE: -connecting-to-device\n", stderr
);
599 _cupsLangPuts(stderr
, _("INFO: Connected to printer...\n"));
602 if (http
->hostaddr
->addr
.sa_family
== AF_INET6
)
603 fprintf(stderr
, "DEBUG: Connected to [%s]:%d (IPv6)...\n",
604 httpAddrString(http
->hostaddr
, addrname
, sizeof(addrname
)),
605 ntohs(http
->hostaddr
->ipv6
.sin6_port
));
607 #endif /* AF_INET6 */
608 if (http
->hostaddr
->addr
.sa_family
== AF_INET
)
609 fprintf(stderr
, "DEBUG: Connected to %s:%d (IPv4)...\n",
610 httpAddrString(http
->hostaddr
, addrname
, sizeof(addrname
)),
611 ntohs(http
->hostaddr
->ipv4
.sin_port
));
614 * See if the printer supports SNMP...
617 if ((snmp_fd
= _cupsSNMPOpen(http
->hostaddr
->addr
.sa_family
)) >= 0)
618 have_supplies
= !backendSNMPSupplies(snmp_fd
, http
->hostaddr
, &start_count
,
621 have_supplies
= start_count
= 0;
624 * Build a URI for the printer and fill the standard IPP attributes for
625 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
626 * might contain username:password information...
629 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), scheme
, NULL
, hostname
,
633 * First validate the destination and see if the device supports multiple
640 media_col_sup
= NULL
;
646 * Check for side-channel requests...
649 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
652 * Build the IPP request...
655 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
656 request
->request
.op
.version
[0] = version
/ 10;
657 request
->request
.op
.version
[1] = version
% 10;
659 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
662 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
663 "requested-attributes", sizeof(pattrs
) / sizeof(pattrs
[0]),
670 fputs("DEBUG: Getting supported attributes...\n", stderr
);
672 if (http
->version
< HTTP_1_1
)
674 fprintf(stderr
, "DEBUG: Printer responded with HTTP version %d.%d.\n",
675 http
->version
/ 100, http
->version
% 100);
677 _cupsLangPuts(stderr
,
678 _("ERROR: Unable to print: the printer does not conform to "
679 "the IPP standard.\n"));
680 exit(CUPS_BACKEND_STOP
);
683 supported
= cupsDoRequest(http
, request
, resource
);
684 ipp_status
= cupsLastError();
686 if (ipp_status
> IPP_OK_CONFLICT
)
688 fprintf(stderr
, "DEBUG: Get-Printer-Attributes returned %s.\n",
689 ippErrorString(ipp_status
));
691 if (ipp_status
== IPP_PRINTER_BUSY
||
692 ipp_status
== IPP_SERVICE_UNAVAILABLE
)
694 if (contimeout
&& (time(NULL
) - start_time
) > contimeout
)
696 _cupsLangPuts(stderr
, _("ERROR: The printer is not responding.\n"));
697 return (CUPS_BACKEND_FAILED
);
700 _cupsLangPrintf(stderr
,
701 _("WARNING: Network host \'%s\' is busy; will retry in "
702 "%d seconds...\n"), hostname
, delay
);
704 report_printer_state(supported
, 0);
711 else if ((ipp_status
== IPP_BAD_REQUEST
||
712 ipp_status
== IPP_VERSION_NOT_SUPPORTED
) && version
> 10)
715 * Switch to IPP/1.1 or IPP/1.0...
720 _cupsLangPrintf(stderr
,
721 _("INFO: Printer does not support IPP/%d.%d, trying "
722 "IPP/%s...\n"), version
/ 10, version
% 10, "1.1");
727 _cupsLangPrintf(stderr
,
728 _("INFO: Printer does not support IPP/%d.%d, trying "
729 "IPP/%s...\n"), version
/ 10, version
% 10, "1.0");
735 else if (ipp_status
== IPP_NOT_FOUND
)
737 _cupsLangPuts(stderr
,
738 _("ERROR: The printer URI is incorrect or no longer "
742 ippDelete(supported
);
744 return (CUPS_BACKEND_STOP
);
746 else if (ipp_status
== IPP_NOT_AUTHORIZED
|| ipp_status
== IPP_FORBIDDEN
)
748 if (!strncmp(httpGetField(http
, HTTP_FIELD_WWW_AUTHENTICATE
),
750 auth_info_required
= "negotiate";
752 fprintf(stderr
, "ATTR: auth-info-required=%s\n", auth_info_required
);
753 return (CUPS_BACKEND_AUTH_REQUIRED
);
757 _cupsLangPrintf(stderr
,
758 _("ERROR: Unable to get printer status (%s)\n"),
759 cupsLastErrorString());
764 ippDelete(supported
);
770 * Check for supported attributes...
773 if ((copies_sup
= ippFindAttribute(supported
, "copies-supported",
774 IPP_TAG_RANGE
)) != NULL
)
777 * Has the "copies-supported" attribute - does it have an upper
781 fprintf(stderr
, "DEBUG: copies-supported=%d-%d\n",
782 copies_sup
->values
[0].range
.lower
,
783 copies_sup
->values
[0].range
.upper
);
785 if (copies_sup
->values
[0].range
.upper
<= 1)
786 copies_sup
= NULL
; /* No */
789 cups_version
= ippFindAttribute(supported
, "cups-version", IPP_TAG_TEXT
);
791 if ((format_sup
= ippFindAttribute(supported
, "document-format-supported",
792 IPP_TAG_MIMETYPE
)) != NULL
)
794 fprintf(stderr
, "DEBUG: document-format-supported (%d values)\n",
795 format_sup
->num_values
);
796 for (i
= 0; i
< format_sup
->num_values
; i
++)
797 fprintf(stderr
, "DEBUG: [%d] = \"%s\"\n", i
,
798 format_sup
->values
[i
].string
.text
);
801 if ((media_col_sup
= ippFindAttribute(supported
, "media-col-supported",
802 IPP_TAG_KEYWORD
)) != NULL
)
804 fprintf(stderr
, "DEBUG: media-col-supported (%d values)\n",
805 media_col_sup
->num_values
);
806 for (i
= 0; i
< media_col_sup
->num_values
; i
++)
807 fprintf(stderr
, "DEBUG: [%d] = \"%s\"\n", i
,
808 media_col_sup
->values
[i
].string
.text
);
811 report_printer_state(supported
, 0);
813 while (ipp_status
> IPP_OK_CONFLICT
);
816 * See if the printer is accepting jobs and is not stopped; if either
817 * condition is true and we are printing to a class, requeue the job...
820 if (getenv("CLASS") != NULL
)
822 printer_state
= ippFindAttribute(supported
, "printer-state",
824 printer_accepting
= ippFindAttribute(supported
, "printer-is-accepting-jobs",
827 if (printer_state
== NULL
||
828 (printer_state
->values
[0].integer
> IPP_PRINTER_PROCESSING
&&
830 printer_accepting
== NULL
||
831 !printer_accepting
->values
[0].boolean
)
834 * If the CLASS environment variable is set, the job was submitted
835 * to a class and not to a specific queue. In this case, we want
836 * to abort immediately so that the job can be requeued on the next
837 * available printer in the class.
840 _cupsLangPuts(stderr
,
841 _("INFO: Unable to contact printer, queuing on next "
842 "printer in class...\n"));
844 ippDelete(supported
);
848 * Sleep 5 seconds to keep the job from requeuing too rapidly...
853 return (CUPS_BACKEND_FAILED
);
858 * See if the printer supports multiple copies...
861 copies
= atoi(argv
[4]);
863 if (copies_sup
|| argc
< 7)
865 copies_remaining
= 1;
871 copies_remaining
= copies
;
874 * Start monitoring the printer in the background...
878 monitor
.hostname
= hostname
;
879 monitor
.user
= argv
[2];
880 monitor
.resource
= resource
;
882 monitor
.version
= version
;
884 monitor
.encryption
= cupsEncryption();
885 monitor
.job_state
= IPP_JOB_PENDING
;
886 monitor
.printer_state
= IPP_PRINTER_IDLE
;
888 _cupsThreadCreate((_cups_thread_func_t
)monitor_printer
, &monitor
);
891 * Then issue the print-job request...
896 while (copies_remaining
> 0)
899 * Check for side-channel requests...
902 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
905 * Build the IPP request...
912 request
= ippNewRequest(IPP_CREATE_JOB
);
914 request
= ippNewRequest(IPP_PRINT_JOB
);
916 request
->request
.op
.version
[0] = version
/ 10;
917 request
->request
.op
.version
[1] = version
% 10;
919 fprintf(stderr
, "DEBUG: %s IPP/%d.%d\n",
920 ippOpString(request
->request
.op
.operation_id
),
921 request
->request
.op
.version
[0],
922 request
->request
.op
.version
[1]);
924 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
926 fprintf(stderr
, "DEBUG: printer-uri=\"%s\"\n", uri
);
930 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
931 "requesting-user-name", NULL
, argv
[2]);
932 fprintf(stderr
, "DEBUG: requesting-user-name=\"%s\"\n", argv
[2]);
937 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "job-name", NULL
,
939 fprintf(stderr
, "DEBUG: job-name=\"%s\"\n", argv
[3]);
944 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
945 "compression", NULL
, "gzip");
946 #endif /* HAVE_LIBZ */
949 * Handle options on the command-line...
953 num_options
= cupsParseOptions(argv
[5], 0, &options
);
955 if (format_sup
!= NULL
)
957 for (i
= 0; i
< format_sup
->num_values
; i
++)
958 if (!strcasecmp(final_content_type
, format_sup
->values
[i
].string
.text
))
961 if (i
< format_sup
->num_values
)
962 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
,
963 "document-format", NULL
, final_content_type
);
968 if (cups_version
|| !media_col_sup
)
971 * When talking to another CUPS server, send all options...
974 cupsEncodeOptions(request
, num_options
, options
);
979 * Otherwise send standard IPP attributes...
982 char cachefile
[1024];/* Printer PWG cache file */
983 const char *cups_cachedir
; /* Location of cache file */
984 _pwg_t
*pwg
; /* PWG mapping data */
985 const char *keyword
; /* PWG keyword */
986 _pwg_size_t
*size
; /* PWG media size */
989 if ((cups_cachedir
= getenv("CUPS_CACHEDIR")) == NULL
)
990 cups_cachedir
= CUPS_CACHEDIR
;
992 snprintf(cachefile
, sizeof(cachefile
), "%s/%s.pwg", cups_cachedir
,
995 if ((keyword
= cupsGetOption("PageSize", num_options
, options
)) == NULL
)
996 keyword
= cupsGetOption("media", num_options
, options
);
998 pwg
= _pwgCreateWithFile(cachefile
);
999 size
= _pwgGetSize(pwg
, keyword
);
1001 if (media_col_sup
&& size
)
1003 ipp_t
*media_col
, /* media-col value */
1004 *media_size
; /* media-size value */
1005 const char *media_source
, /* media-source value */
1006 *media_type
; /* media-type value */
1008 media_size
= ippNew();
1009 ippAddInteger(media_size
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
1010 "x-dimension", size
->width
);
1011 ippAddInteger(media_size
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
1012 "y-dimension", size
->length
);
1014 media_col
= ippNew();
1015 ippAddCollection(media_col
, IPP_TAG_ZERO
, "media-size", media_size
);
1017 media_source
= _pwgGetSource(pwg
, cupsGetOption("InputSlot",
1020 media_type
= _pwgGetType(pwg
, cupsGetOption("MediaType",
1021 num_options
, options
));
1023 for (i
= 0; i
< media_col_sup
->num_values
; i
++)
1025 if (!strcmp(media_col_sup
->values
[i
].string
.text
,
1026 "media-left-margin"))
1027 ippAddInteger(media_col
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
1028 "media-left-margin", size
->left
);
1029 else if (!strcmp(media_col_sup
->values
[i
].string
.text
,
1030 "media-bottom-margin"))
1031 ippAddInteger(media_col
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
1032 "media-bottom-margin", size
->left
);
1033 else if (!strcmp(media_col_sup
->values
[i
].string
.text
,
1034 "media-right-margin"))
1035 ippAddInteger(media_col
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
1036 "media-right-margin", size
->left
);
1037 else if (!strcmp(media_col_sup
->values
[i
].string
.text
,
1038 "media-top-margin"))
1039 ippAddInteger(media_col
, IPP_TAG_ZERO
, IPP_TAG_INTEGER
,
1040 "media-top-margin", size
->left
);
1041 else if (!strcmp(media_col_sup
->values
[i
].string
.text
,
1042 "media-source") && media_source
)
1043 ippAddString(media_col
, IPP_TAG_ZERO
, IPP_TAG_KEYWORD
,
1044 "media-source", NULL
, media_source
);
1045 else if (!strcmp(media_col_sup
->values
[i
].string
.text
,
1046 "media-type") && media_type
)
1047 ippAddString(media_col
, IPP_TAG_ZERO
, IPP_TAG_KEYWORD
,
1048 "media-type", NULL
, media_type
);
1051 ippAddCollection(request
, IPP_TAG_JOB
, "media-col", media_col
);
1054 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "media",
1055 NULL
, size
->map
.pwg
);
1058 _pwg_media_t
*found
; /* PWG media */
1061 if ((found
= _pwgMediaForPPD(keyword
)) != NULL
)
1062 keyword
= found
->pwg
;
1063 else if ((found
= _pwgMediaForLegacy(keyword
)) != NULL
)
1064 keyword
= found
->pwg
;
1066 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "media",
1070 if ((keyword
= cupsGetOption("output-bin", num_options
,
1072 keyword
= _pwgGetBin(pwg
, cupsGetOption("OutputBin", num_options
,
1076 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "output-bin",
1079 if ((keyword
= cupsGetOption("output-mode", num_options
,
1081 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "output-mode",
1083 else if ((keyword
= cupsGetOption("ColorModel", num_options
,
1086 if (!strcasecmp(keyword
, "Gray"))
1087 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "output-mode",
1088 NULL
, "monochrome");
1089 else if (!strcasecmp(keyword
, "Color"))
1090 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "output-mode",
1094 if ((keyword
= cupsGetOption("print-quality", num_options
,
1096 ippAddInteger(request
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "print-quality",
1098 else if ((keyword
= cupsGetOption("cupsPrintQuality", num_options
,
1101 if (!strcasecmp(keyword
, "draft"))
1102 ippAddInteger(request
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "print-quality",
1104 else if (!strcasecmp(keyword
, "normal"))
1105 ippAddInteger(request
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "print-quality",
1106 IPP_QUALITY_NORMAL
);
1107 else if (!strcasecmp(keyword
, "high"))
1108 ippAddInteger(request
, IPP_TAG_JOB
, IPP_TAG_ENUM
, "print-quality",
1112 if ((keyword
= cupsGetOption("sides", num_options
, options
)) != NULL
)
1113 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "sides",
1115 else if ((keyword
= cupsGetOption("Duplex", num_options
,
1118 if (!strcasecmp(keyword
, "None"))
1119 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "sides",
1121 else if (!strcasecmp(keyword
, "DuplexNoTumble"))
1122 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "sides",
1123 NULL
, "two-sided-long-edge");
1124 if (!strcasecmp(keyword
, "DuplexTumble"))
1125 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "sides",
1126 NULL
, "two-sided-short-edge");
1132 if (copies_sup
&& copies
> 1)
1133 ippAddInteger(request
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "copies", copies
);
1136 cupsFreeOptions(num_options
, options
);
1143 response
= cupsDoRequest(http
, request
, resource
);
1146 fputs("DEBUG: Sending file using chunking...\n", stderr
);
1147 http_status
= cupsSendRequest(http
, request
, resource
, 0);
1148 if (http_status
== HTTP_CONTINUE
&& request
->state
== IPP_DATA
)
1151 fd
= open(files
[0], O_RDONLY
);
1155 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
1157 fprintf(stderr
, "DEBUG: Read %d bytes...\n", (int)bytes
);
1159 if (cupsWriteRequestData(http
, buffer
, bytes
) != HTTP_CONTINUE
)
1164 * Check for side-channel requests...
1167 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
1175 response
= cupsGetResponse(http
, resource
);
1179 ipp_status
= cupsLastError();
1181 if (ipp_status
> IPP_OK_CONFLICT
)
1188 if (ipp_status
== IPP_SERVICE_UNAVAILABLE
||
1189 ipp_status
== IPP_PRINTER_BUSY
)
1191 _cupsLangPuts(stderr
,
1192 _("INFO: Printer busy; will retry in 10 seconds...\n"));
1198 * Update auth-info-required as needed...
1201 _cupsLangPrintf(stderr
, _("ERROR: Print file was not accepted (%s)\n"),
1202 cupsLastErrorString());
1204 if (ipp_status
== IPP_NOT_AUTHORIZED
|| ipp_status
== IPP_FORBIDDEN
)
1206 fprintf(stderr
, "DEBUG: WWW-Authenticate=\"%s\"\n",
1207 httpGetField(http
, HTTP_FIELD_WWW_AUTHENTICATE
));
1210 * Normal authentication goes through the password callback, which sets
1211 * auth_info_required to "username,password". Kerberos goes directly
1212 * through GSSAPI, so look for Negotiate in the WWW-Authenticate header
1213 * here and set auth_info_required as needed...
1216 if (!strncmp(httpGetField(http
, HTTP_FIELD_WWW_AUTHENTICATE
),
1218 auth_info_required
= "negotiate";
1222 else if ((job_id_attr
= ippFindAttribute(response
, "job-id",
1223 IPP_TAG_INTEGER
)) == NULL
)
1225 _cupsLangPuts(stderr
,
1226 _("NOTICE: Print file accepted - job ID unknown.\n"));
1231 monitor
.job_id
= job_id
= job_id_attr
->values
[0].integer
;
1232 _cupsLangPrintf(stderr
, _("NOTICE: Print file accepted - job ID %d.\n"),
1236 ippDelete(response
);
1241 if (job_id
&& num_files
> 1)
1243 for (i
= 0; i
< num_files
; i
++)
1246 * Check for side-channel requests...
1249 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
1252 * Send the next file in the job...
1255 request
= ippNewRequest(IPP_SEND_DOCUMENT
);
1256 request
->request
.op
.version
[0] = version
/ 10;
1257 request
->request
.op
.version
[1] = version
% 10;
1259 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1262 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id",
1266 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1267 "requesting-user-name", NULL
, argv
[2]);
1269 if ((i
+ 1) == num_files
)
1270 ippAddBoolean(request
, IPP_TAG_OPERATION
, "last-document", 1);
1272 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
,
1273 "document-format", NULL
, content_type
);
1275 fprintf(stderr
, "DEBUG: Sending file %d using chunking...\n", i
+ 1);
1276 http_status
= cupsSendRequest(http
, request
, resource
, 0);
1277 if (http_status
== HTTP_CONTINUE
&& request
->state
== IPP_DATA
&&
1278 (fd
= open(files
[i
], O_RDONLY
)) >= 0)
1280 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
1282 if (cupsWriteRequestData(http
, buffer
, bytes
) != HTTP_CONTINUE
)
1287 * Check for side-channel requests...
1290 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
1297 ippDelete(cupsGetResponse(http
, resource
));
1300 if (cupsLastError() > IPP_OK_CONFLICT
)
1302 ipp_status
= cupsLastError();
1304 _cupsLangPrintf(stderr
,
1305 _("ERROR: Unable to add file %d to job: %s\n"),
1306 job_id
, cupsLastErrorString());
1312 if (ipp_status
<= IPP_OK_CONFLICT
&& argc
> 6)
1314 fprintf(stderr
, "PAGE: 1 %d\n", copies_sup
? atoi(argv
[4]) : 1);
1315 copies_remaining
--;
1317 else if (ipp_status
== IPP_SERVICE_UNAVAILABLE
||
1318 ipp_status
== IPP_PRINTER_BUSY
)
1321 copies_remaining
--;
1324 * Wait for the job to complete...
1327 if (!job_id
|| !waitjob
)
1330 _cupsLangPuts(stderr
, _("INFO: Waiting for job to complete...\n"));
1332 for (delay
= 1; !job_canceled
;)
1335 * Check for side-channel requests...
1338 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
1341 * Build an IPP_GET_JOB_ATTRIBUTES request...
1344 request
= ippNewRequest(IPP_GET_JOB_ATTRIBUTES
);
1345 request
->request
.op
.version
[0] = version
/ 10;
1346 request
->request
.op
.version
[1] = version
% 10;
1348 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1351 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id",
1355 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1356 "requesting-user-name", NULL
, argv
[2]);
1358 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1359 "requested-attributes", sizeof(jattrs
) / sizeof(jattrs
[0]),
1366 response
= cupsDoRequest(http
, request
, resource
);
1367 ipp_status
= cupsLastError();
1369 if (ipp_status
== IPP_NOT_FOUND
)
1372 * Job has gone away and/or the server has no job history...
1375 ippDelete(response
);
1377 ipp_status
= IPP_OK
;
1381 if (ipp_status
> IPP_OK_CONFLICT
)
1383 if (ipp_status
!= IPP_SERVICE_UNAVAILABLE
&&
1384 ipp_status
!= IPP_PRINTER_BUSY
)
1386 ippDelete(response
);
1388 _cupsLangPrintf(stderr
,
1389 _("ERROR: Unable to get job %d attributes (%s)\n"),
1390 job_id
, cupsLastErrorString());
1397 if ((job_state
= ippFindAttribute(response
, "job-state",
1398 IPP_TAG_ENUM
)) != NULL
)
1401 * Stop polling if the job is finished or pending-held...
1404 if (job_state
->values
[0].integer
> IPP_JOB_STOPPED
)
1406 if ((job_sheets
= ippFindAttribute(response
,
1407 "job-media-sheets-completed",
1408 IPP_TAG_INTEGER
)) != NULL
)
1409 fprintf(stderr
, "PAGE: total %d\n",
1410 job_sheets
->values
[0].integer
);
1412 ippDelete(response
);
1419 * If the printer does not return a job-state attribute, it does not
1420 * conform to the IPP specification - break out immediately and fail
1424 fputs("DEBUG: No job-state available from printer - stopping queue.\n",
1426 ipp_status
= IPP_INTERNAL_ERROR
;
1431 ippDelete(response
);
1435 * Check the printer state and report it if necessary...
1438 check_printer_state(http
, uri
, resource
, argv
[2], version
, job_id
);
1442 * Wait 1-10 seconds before polling again...
1454 * Cancel the job as needed...
1457 if (job_canceled
&& job_id
)
1458 cancel_job(http
, uri
, job_id
, resource
, argv
[2], version
);
1461 * Check the printer state and report it if necessary...
1464 check_printer_state(http
, uri
, resource
, argv
[2], version
, job_id
);
1467 * Collect the final page count as needed...
1470 if (have_supplies
&&
1471 !backendSNMPSupplies(snmp_fd
, http
->hostaddr
, &page_count
, NULL
) &&
1472 page_count
> start_count
)
1473 fprintf(stderr
, "PAGE: total %d\n", page_count
- start_count
);
1477 * See if we used Kerberos at all...
1481 auth_info_required
= "negotiate";
1482 #endif /* HAVE_GSSAPI */
1490 ippDelete(supported
);
1493 * Remove the temporary file(s) if necessary...
1499 for (i
= 0; i
< num_files
; i
++)
1502 #endif /* HAVE_LIBZ */
1505 * Return the queue status...
1508 fprintf(stderr
, "ATTR: auth-info-required=%s\n", auth_info_required
);
1510 if (ipp_status
== IPP_NOT_AUTHORIZED
|| ipp_status
== IPP_FORBIDDEN
)
1511 return (CUPS_BACKEND_AUTH_REQUIRED
);
1512 else if (ipp_status
== IPP_INTERNAL_ERROR
)
1513 return (CUPS_BACKEND_STOP
);
1514 else if (ipp_status
> IPP_OK_CONFLICT
)
1515 return (CUPS_BACKEND_FAILED
);
1518 _cupsLangPuts(stderr
, _("INFO: Ready to print.\n"));
1519 return (CUPS_BACKEND_OK
);
1525 * 'cancel_job()' - Cancel a print job.
1529 cancel_job(http_t
*http
, /* I - HTTP connection */
1530 const char *uri
, /* I - printer-uri */
1531 int id
, /* I - job-id */
1532 const char *resource
, /* I - Resource path */
1533 const char *user
, /* I - requesting-user-name */
1534 int version
) /* I - IPP version */
1536 ipp_t
*request
; /* Cancel-Job request */
1539 _cupsLangPuts(stderr
, _("INFO: Canceling print job...\n"));
1541 request
= ippNewRequest(IPP_CANCEL_JOB
);
1542 request
->request
.op
.version
[0] = version
/ 10;
1543 request
->request
.op
.version
[1] = version
% 10;
1545 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1547 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id", id
);
1549 if (user
&& user
[0])
1550 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1551 "requesting-user-name", NULL
, user
);
1557 ippDelete(cupsDoRequest(http
, request
, resource
));
1559 if (cupsLastError() > IPP_OK_CONFLICT
)
1560 _cupsLangPrintf(stderr
, _("ERROR: Unable to cancel job %d: %s\n"), id
,
1561 cupsLastErrorString());
1566 * 'check_printer_state()' - Check the printer state.
1569 static ipp_pstate_t
/* O - Current printer-state */
1570 check_printer_state(
1571 http_t
*http
, /* I - HTTP connection */
1572 const char *uri
, /* I - Printer URI */
1573 const char *resource
, /* I - Resource path */
1574 const char *user
, /* I - Username, if any */
1575 int version
, /* I - IPP version */
1578 ipp_t
*request
, /* IPP request */
1579 *response
; /* IPP response */
1580 ipp_attribute_t
*attr
; /* Attribute in response */
1581 ipp_pstate_t printer_state
= IPP_PRINTER_STOPPED
;
1582 /* Current printer-state */
1586 * Send a Get-Printer-Attributes request and log the results...
1589 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
1590 request
->request
.op
.version
[0] = version
/ 10;
1591 request
->request
.op
.version
[1] = version
% 10;
1593 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1596 if (user
&& user
[0])
1597 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1598 "requesting-user-name", NULL
, user
);
1600 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1601 "requested-attributes",
1602 (int)(sizeof(pattrs
) / sizeof(pattrs
[0])), NULL
, pattrs
);
1604 if ((response
= cupsDoRequest(http
, request
, resource
)) != NULL
)
1606 report_printer_state(response
, job_id
);
1608 if ((attr
= ippFindAttribute(response
, "printer-state",
1609 IPP_TAG_ENUM
)) != NULL
)
1610 printer_state
= (ipp_pstate_t
)attr
->values
[0].integer
;
1612 ippDelete(response
);
1616 * Return the printer-state value...
1619 return (printer_state
);
1625 * 'compress_files()' - Compress print files...
1629 compress_files(int num_files
, /* I - Number of files */
1630 char **files
) /* I - Files */
1632 int i
, /* Looping var */
1633 fd
; /* Temporary file descriptor */
1634 ssize_t bytes
; /* Bytes read/written */
1635 size_t total
; /* Total bytes read */
1636 cups_file_t
*in
, /* Input file */
1637 *out
; /* Output file */
1638 struct stat outinfo
; /* Output file information */
1639 char filename
[1024], /* Temporary filename */
1640 buffer
[32768]; /* Copy buffer */
1643 fprintf(stderr
, "DEBUG: Compressing %d job files...\n", num_files
);
1644 for (i
= 0; i
< num_files
; i
++)
1646 if ((fd
= cupsTempFd(filename
, sizeof(filename
))) < 0)
1648 _cupsLangPrintf(stderr
,
1649 _("ERROR: Unable to create temporary compressed print "
1650 "file: %s\n"), strerror(errno
));
1651 exit(CUPS_BACKEND_FAILED
);
1654 if ((out
= cupsFileOpenFd(fd
, "w9")) == NULL
)
1656 _cupsLangPrintf(stderr
,
1657 _("ERROR: Unable to open temporary compressed print "
1658 "file: %s\n"), strerror(errno
));
1659 exit(CUPS_BACKEND_FAILED
);
1662 if ((in
= cupsFileOpen(files
[i
], "r")) == NULL
)
1664 _cupsLangPrintf(stderr
,
1665 _("ERROR: Unable to open print file \"%s\": %s\n"),
1666 files
[i
], strerror(errno
));
1668 exit(CUPS_BACKEND_FAILED
);
1672 while ((bytes
= cupsFileRead(in
, buffer
, sizeof(buffer
))) > 0)
1673 if (cupsFileWrite(out
, buffer
, bytes
) < bytes
)
1675 _cupsLangPrintf(stderr
,
1676 _("ERROR: Unable to write %d bytes to \"%s\": %s\n"),
1677 (int)bytes
, filename
, strerror(errno
));
1680 exit(CUPS_BACKEND_FAILED
);
1688 files
[i
] = strdup(filename
);
1690 if (!stat(filename
, &outinfo
))
1692 "DEBUG: File %d compressed to %.1f%% of original size, "
1693 CUPS_LLFMT
" bytes...\n",
1694 i
+ 1, 100.0 * outinfo
.st_size
/ total
,
1695 CUPS_LLCAST outinfo
.st_size
);
1698 #endif /* HAVE_LIBZ */
1702 * 'monitor_printer()' - Monitor the printer state...
1705 static void * /* O - Thread exit code */
1707 _cups_monitor_t
*monitor
) /* I - Monitoring data */
1709 http_t
*http
; /* Connection to printer */
1710 ipp_t
*request
, /* IPP request */
1711 *response
; /* IPP response */
1712 ipp_attribute_t
*attr
; /* Attribute in response */
1713 int delay
, /* Current delay */
1714 prev_delay
, /* Previous delay */
1715 temp_delay
; /* Temporary delay value */
1719 * Make a copy of the printer connection...
1722 http
= _httpCreate(monitor
->hostname
, monitor
->port
, monitor
->encryption
,
1724 cupsSetPasswordCB(password_cb
);
1727 * Loop until the job is canceled, aborted, or completed.
1733 while (monitor
->job_state
< IPP_JOB_CANCELED
&& !job_canceled
)
1736 * Reconnect to the printer...
1739 if (!httpReconnect(http
))
1742 * Connected, so check on the printer state...
1745 monitor
->printer_state
= check_printer_state(http
, monitor
->uri
,
1751 if (monitor
->job_id
> 0)
1754 * Check the status of the job itself...
1757 request
= ippNewRequest(IPP_GET_JOB_ATTRIBUTES
);
1758 request
->request
.op
.version
[0] = monitor
->version
/ 10;
1759 request
->request
.op
.version
[1] = monitor
->version
% 10;
1761 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1762 NULL
, monitor
->uri
);
1763 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id",
1766 if (monitor
->user
&& monitor
->user
[0])
1767 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1768 "requesting-user-name", NULL
, monitor
->user
);
1770 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1771 "requested-attributes",
1772 (int)(sizeof(jattrs
) / sizeof(jattrs
[0])), NULL
, jattrs
);
1778 response
= cupsDoRequest(http
, request
, monitor
->resource
);
1780 if ((attr
= ippFindAttribute(response
, "job-state",
1781 IPP_TAG_ENUM
)) != NULL
)
1782 monitor
->job_state
= (ipp_jstate_t
)attr
->values
[0].integer
;
1784 monitor
->job_state
= IPP_JOB_COMPLETED
;
1786 ippDelete(response
);
1790 * Disconnect from the printer - we'll reconnect on the next poll...
1793 _httpDisconnect(http
);
1797 * Sleep for N seconds, and then update the next sleep time using a
1798 * Fibonacci series (1 1 2 3 5 8)...
1804 delay
= (delay
+ prev_delay
) % 12;
1805 prev_delay
= delay
< temp_delay
? 0 : temp_delay
;
1809 * Cleanup and return...
1819 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
1822 static const char * /* O - Password */
1823 password_cb(const char *prompt
) /* I - Prompt (not used) */
1828 * Remember that we need to authenticate...
1831 auth_info_required
= "username,password";
1833 if (password
&& *password
&& password_tries
< 3)
1842 * Give up after 3 tries or if we don't have a password to begin with...
1851 * 'report_attr()' - Report an IPP attribute value.
1855 report_attr(ipp_attribute_t
*attr
) /* I - Attribute */
1857 int i
; /* Looping var */
1858 char value
[1024], /* Value string */
1859 *valptr
, /* Pointer into value string */
1860 *attrptr
; /* Pointer into attribute value */
1864 * Convert the attribute values into quoted strings...
1867 for (i
= 0, valptr
= value
;
1868 i
< attr
->num_values
&& valptr
< (value
+ sizeof(value
) - 10);
1874 switch (attr
->value_tag
)
1876 case IPP_TAG_INTEGER
:
1878 snprintf(valptr
, sizeof(value
) - (valptr
- value
), "%d",
1879 attr
->values
[i
].integer
);
1880 valptr
+= strlen(valptr
);
1885 case IPP_TAG_KEYWORD
:
1887 for (attrptr
= attr
->values
[i
].string
.text
;
1888 *attrptr
&& valptr
< (value
+ sizeof(value
) - 10);
1891 if (*attrptr
== '\\' || *attrptr
== '\"')
1894 *valptr
++ = *attrptr
;
1901 * Unsupported value type...
1911 * Tell the scheduler about the new values...
1914 fprintf(stderr
, "ATTR: %s=%s\n", attr
->name
, value
);
1919 * 'report_printer_state()' - Report the printer state.
1922 static int /* O - Number of reasons shown */
1923 report_printer_state(ipp_t
*ipp
, /* I - IPP response */
1924 int job_id
) /* I - Current job ID */
1926 int i
; /* Looping var */
1927 int count
; /* Count of reasons shown... */
1928 ipp_attribute_t
*pa
, /* printer-alert */
1929 *pam
, /* printer-alert-message */
1930 *psm
, /* printer-state-message */
1931 *reasons
, /* printer-state-reasons */
1932 *marker
; /* marker-* attributes */
1933 const char *reason
; /* Current reason */
1934 const char *prefix
; /* Prefix for STATE: line */
1935 char value
[1024], /* State/message string */
1936 *valptr
; /* Pointer into string */
1940 * Report alerts and messages...
1943 if ((pa
= ippFindAttribute(ipp
, "printer-alert", IPP_TAG_TEXT
)) != NULL
)
1946 if ((pam
= ippFindAttribute(ipp
, "printer-alert-message",
1947 IPP_TAG_TEXT
)) != NULL
)
1950 if ((psm
= ippFindAttribute(ipp
, "printer-state-message",
1951 IPP_TAG_TEXT
)) != NULL
)
1953 char *ptr
; /* Pointer into message */
1956 strlcpy(value
, "INFO: ", sizeof(value
));
1957 for (ptr
= psm
->values
[0].string
.text
, valptr
= value
+ 6;
1958 *ptr
&& valptr
< (value
+ sizeof(value
) - 6);
1961 if (*ptr
< ' ' && *ptr
> 0 && *ptr
!= '\t')
1964 * Substitute "<XX>" for the control character; sprintf is safe because
1965 * we always leave 6 chars free at the end...
1968 sprintf(valptr
, "<%02X>", *ptr
);
1978 fputs(value
, stderr
);
1982 * Now report printer-state-reasons, filtering out some of the reasons we never
1986 if ((reasons
= ippFindAttribute(ipp
, "printer-state-reasons",
1987 IPP_TAG_KEYWORD
)) == NULL
)
1993 for (i
= 0, count
= 0, valptr
= value
; i
< reasons
->num_values
; i
++)
1995 reason
= reasons
->values
[i
].string
.text
;
1997 if (strcmp(reason
, "paused") &&
1998 strcmp(reason
, "com.apple.print.recoverable-warning"))
2000 strlcpy(valptr
, prefix
, sizeof(value
) - (valptr
- value
) - 1);
2001 valptr
+= strlen(valptr
);
2002 strlcpy(valptr
, reason
, sizeof(value
) - (valptr
- value
) - 1);
2003 valptr
+= strlen(valptr
);
2013 fputs(value
, stderr
);
2017 * Relay the current marker-* attribute values...
2020 if ((marker
= ippFindAttribute(ipp
, "marker-colors", IPP_TAG_NAME
)) != NULL
)
2021 report_attr(marker
);
2022 if ((marker
= ippFindAttribute(ipp
, "marker-high-levels",
2023 IPP_TAG_INTEGER
)) != NULL
)
2024 report_attr(marker
);
2025 if ((marker
= ippFindAttribute(ipp
, "marker-levels",
2026 IPP_TAG_INTEGER
)) != NULL
)
2027 report_attr(marker
);
2028 if ((marker
= ippFindAttribute(ipp
, "marker-low-levels",
2029 IPP_TAG_INTEGER
)) != NULL
)
2030 report_attr(marker
);
2031 if ((marker
= ippFindAttribute(ipp
, "marker-message", IPP_TAG_TEXT
)) != NULL
)
2032 report_attr(marker
);
2033 if ((marker
= ippFindAttribute(ipp
, "marker-names", IPP_TAG_NAME
)) != NULL
)
2034 report_attr(marker
);
2035 if ((marker
= ippFindAttribute(ipp
, "marker-types", IPP_TAG_KEYWORD
)) != NULL
)
2036 report_attr(marker
);
2043 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
2047 sigterm_handler(int sig
) /* I - Signal */
2049 (void)sig
; /* remove compiler warnings... */
2054 * Flag that the job should be cancelled...
2066 * End of "$Id: ipp.c 7948 2008-09-17 00:04:12Z mike $".