2 * IPP backend for CUPS.
4 * Copyright © 2021-2025 by OpenPrinting
5 * Copyright © 2007-2021 by Apple Inc.
6 * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
8 * Licensed under Apache License v2.0. See the file "LICENSE" for more
12 #include "backend-private.h"
13 #include <cups/ppd-private.h>
14 #include <sys/types.h>
17 #if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
19 # define kPMPrintUIToolAgent "com.apple.printuitool.agent"
20 # define kPMStartJob 100
21 # define kPMWaitForJob 101
22 extern void xpc_connection_set_target_uid(xpc_connection_t connection
,
24 #endif /* HAVE_GSSAPI && HAVE_XPC */
28 * Bits for job-state-reasons we care about...
31 #define _CUPS_JSR_ACCOUNT_AUTHORIZATION_FAILED 0x01
32 #define _CUPS_JSR_ACCOUNT_CLOSED 0x02
33 #define _CUPS_JSR_ACCOUNT_INFO_NEEDED 0x04
34 #define _CUPS_JSR_ACCOUNT_LIMIT_REACHED 0x08
35 #define _CUPS_JSR_JOB_PASSWORD_WAIT 0x10
36 #define _CUPS_JSR_JOB_RELEASE_WAIT 0x20
37 #define _CUPS_JSR_DOCUMENT_FORMAT_ERROR 0x40
38 #define _CUPS_JSR_DOCUMENT_UNPRINTABLE 0x80
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 job_reasons
, /* Job state reasons bits */
55 create_job
, /* Support Create-Job? */
56 get_job_attrs
; /* Support Get-Job-Attributes? */
57 const char *job_name
; /* Job name for submitted job */
58 http_encryption_t encryption
; /* Use encryption? */
59 ipp_jstate_t job_state
; /* Current job state */
60 ipp_pstate_t printer_state
; /* Current printer state */
61 int retryable
; /* Is this a job that should be retried? */
68 #if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
69 static pid_t child_pid
= 0; /* Child process ID */
70 #endif /* HAVE_GSSAPI && HAVE_XPC */
71 static const char * const jattrs
[] = /* Job attributes we want */
74 "job-impressions-completed",
75 "job-media-sheets-completed",
77 "job-originating-user-name",
81 static int job_canceled
= 0,
84 /* Credentials supplied in URI? */
85 static char device_username
[256] = "",
86 /* Username for device URI */
87 *device_password
= NULL
;
88 /* Password for device URI */
89 static const char * const pattrs
[] = /* Printer attributes we want */
91 "compression-supported",
94 "document-format-supported",
95 "job-password-encryption-supported",
103 "media-col-supported",
104 "multiple-document-handling-supported",
105 "operations-supported",
106 "print-color-mode-supported",
107 "print-scaling-supported",
109 "printer-alert-description",
110 "printer-is-accepting-jobs",
111 "printer-mandatory-job-attributes",
113 "printer-state-message",
114 "printer-state-reasons"
116 static const char * const remote_job_states
[] =
117 { /* Remote job state keywords */
118 "+cups-remote-pending",
119 "+cups-remote-pending-held",
120 "+cups-remote-processing",
121 "+cups-remote-stopped",
122 "+cups-remote-canceled",
123 "+cups-remote-aborted",
124 "+cups-remote-completed"
126 static cups_mutex_t report_mutex
= CUPS_MUTEX_INITIALIZER
;
127 /* Mutex to control access */
128 static int num_attr_cache
= 0;
129 /* Number of cached attributes */
130 static cups_option_t
*attr_cache
= NULL
;
131 /* Cached attributes */
132 static cups_array_t
*state_reasons
; /* Array of printe-state-reasons keywords */
133 static char tmpfilename
[1024] = "";
134 /* Temporary spool file name */
135 static char mandatory_attrs
[1024] = "";
136 /* cupsMandatory value */
143 static int adjust_options(int num_options
, cups_option_t
**options
);
144 static void cancel_job(http_t
*http
, const char *uri
, int id
,
145 const char *resource
, const char *user
,
147 static ipp_pstate_t
check_printer_state(http_t
*http
, const char *uri
,
148 const char *resource
,
149 const char *user
, int version
);
150 static void debug_attributes(ipp_t
*ipp
);
151 static void *monitor_printer(_cups_monitor_t
*monitor
);
152 static ipp_t
*new_request(ipp_op_t op
, int version
, const char *uri
,
153 const char *user
, const char *title
,
154 int num_options
, cups_option_t
*options
,
155 const char *compression
, int copies
,
156 const char *format
, _ppd_cache_t
*pc
,
158 ipp_attribute_t
*media_col_sup
,
159 ipp_attribute_t
*doc_handling_sup
,
160 ipp_attribute_t
*print_color_mode_sup
,
161 ipp_attribute_t
*print_scaling_sup
);
162 static const char *password_cb(const char *prompt
, http_t
*http
,
163 const char *method
, const char *resource
,
165 static const char *quote_string(const char *s
, char *q
, size_t qsize
);
166 static void report_attr(ipp_attribute_t
*attr
);
167 static void report_printer_state(ipp_t
*ipp
);
168 #if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
169 static int run_as_user(char *argv
[], uid_t uid
,
170 const char *device_uri
, int fd
);
171 #endif /* HAVE_GSSAPI && HAVE_XPC */
172 static void sigterm_handler(int sig
);
173 static int timeout_cb(http_t
*http
, void *user_data
);
174 static void update_reasons(ipp_attribute_t
*attr
, const char *s
);
178 * 'main()' - Send a file to the printer or server.
182 * printer-uri job-id user title copies options [file]
185 int /* O - Exit status */
186 main(int argc
, /* I - Number of command-line args */
187 char *argv
[]) /* I - Command-line arguments */
189 int i
; /* Looping var */
190 int send_options
; /* Send job options? */
191 int num_options
; /* Number of printer options */
192 cups_option_t
*options
; /* Printer options */
193 const char *device_uri
; /* Device URI */
194 char scheme
[255], /* Scheme in URI */
195 hostname
[1024], /* Hostname */
196 resource
[1024], /* Resource info (printer name) */
197 addrname
[256], /* Address name */
198 username
[IPP_MAX_NAME
], /* Requesting user name */
199 *optptr
, /* Pointer to URI options */
200 *name
, /* Name of option */
201 *value
, /* Value of option */
202 sep
; /* Separator character */
203 int password_tries
= 0; /* Password tries */
204 http_addrlist_t
*addrlist
; /* Address of printer */
205 int snmp_enabled
= 1; /* Is SNMP enabled? */
206 int snmp_fd
, /* SNMP socket */
207 start_count
, /* Page count via SNMP at start */
208 page_count
, /* Page count via SNMP */
209 have_supplies
; /* Printer supports supply levels? */
210 int num_files
; /* Number of files to print */
211 char **files
, /* Files to print */
212 *compatfile
= NULL
; /* Compatibility filename */
213 off_t compatsize
= 0; /* Size of compatibility file */
214 int port
; /* Port number (not used) */
215 char uri
[HTTP_MAX_URI
]; /* Updated URI without user/pass */
216 char print_job_name
[256]; /* Update job-name for Print-Job */
217 http_status_t http_status
; /* Status of HTTP request */
218 ipp_status_t ipp_status
; /* Status of IPP request */
219 http_t
*http
; /* HTTP connection */
220 ipp_t
*request
, /* IPP request */
221 *response
, /* IPP response */
222 *supported
; /* get-printer-attributes response */
223 time_t start_time
; /* Time of first connect */
224 int contimeout
; /* Connection timeout */
225 int delay
, /* Delay for retries */
226 prev_delay
; /* Previous delay */
227 const char *compression
; /* Compression mode */
228 int waitjob
, /* Wait for job complete? */
229 waitjob_tries
= 0, /* Number of times we've waited */
230 waitprinter
; /* Wait for printer ready? */
231 _cups_monitor_t monitor
; /* Monitoring data */
232 ipp_attribute_t
*job_id_attr
; /* job-id attribute */
233 int job_id
; /* job-id value */
234 ipp_attribute_t
*job_sheets
; /* job-media-sheets-completed */
235 ipp_attribute_t
*job_state
; /* job-state */
236 ipp_attribute_t
*compression_sup
; /* compression-supported */
237 ipp_attribute_t
*copies_sup
; /* copies-supported */
238 ipp_attribute_t
*cups_version
; /* cups-version */
239 ipp_attribute_t
*encryption_sup
; /* job-password-encryption-supported */
240 ipp_attribute_t
*format_sup
; /* document-format-supported */
241 ipp_attribute_t
*job_auth
; /* job-authorization-uri */
242 ipp_attribute_t
*media_col_sup
; /* media-col-supported */
243 ipp_attribute_t
*operations_sup
; /* operations-supported */
244 ipp_attribute_t
*doc_handling_sup
; /* multiple-document-handling-supported */
245 ipp_attribute_t
*printer_state
; /* printer-state attribute */
246 ipp_attribute_t
*printer_accepting
; /* printer-is-accepting-jobs */
247 ipp_attribute_t
*print_color_mode_sup
;/* Does printer support print-color-mode? */
248 ipp_attribute_t
*print_scaling_sup
; /* print-scaling-supported */
249 int create_job
= 0, /* Does printer support Create-Job? */
250 get_job_attrs
= 0, /* Does printer support Get-Job-Attributes? */
251 send_document
= 0, /* Does printer support Send-Document? */
252 validate_job
= 0, /* Does printer support Validate-Job? */
253 validate_retried
= 0, /* Was Validate-Job request retried? */
254 copies
, /* Number of copies for job */
255 copies_remaining
; /* Number of copies remaining */
256 const char *auth_info_required
, /* New auth-info-required value */
257 *content_type
, /* CONTENT_TYPE environment variable */
258 *final_content_type
, /* FINAL_CONTENT_TYPE environment var */
259 *document_format
; /* document-format value */
260 int fd
; /* File descriptor */
261 off_t bytes
= 0; /* Bytes copied */
262 char buffer
[16384]; /* Copy buffer */
263 struct sigaction action
; /* Actions for POSIX signals */
264 int version
; /* IPP version */
265 ppd_file_t
*ppd
= NULL
; /* PPD file */
266 _ppd_cache_t
*pc
= NULL
; /* PPD cache and mapping data */
267 fd_set input
; /* Input set for select() */
271 * Make sure status messages are not buffered...
274 setbuf(stderr
, NULL
);
277 * Ignore SIGPIPE and catch SIGTERM signals...
280 memset(&action
, 0, sizeof(action
));
281 action
.sa_handler
= SIG_IGN
;
282 sigaction(SIGPIPE
, &action
, NULL
);
284 sigemptyset(&action
.sa_mask
);
285 sigaddset(&action
.sa_mask
, SIGTERM
);
286 action
.sa_handler
= sigterm_handler
;
287 sigaction(SIGTERM
, &action
, NULL
);
290 * Check command-line...
297 if ((s
= strrchr(argv
[0], '/')) != NULL
)
302 printf("network %s \"Unknown\" \"%s (%s)\"\n",
303 s
, _cupsLangString(cupsLangDefault(),
304 _("Internet Printing Protocol")), s
);
305 return (CUPS_BACKEND_OK
);
309 _cupsLangPrintf(stderr
,
310 _("Usage: %s job-id user title copies options [file]"),
312 return (CUPS_BACKEND_STOP
);
315 cupsCopyString(username
, argv
[2], sizeof(username
));
318 * Get the device URI...
321 while ((device_uri
= cupsBackendDeviceURI(argv
)) == NULL
)
323 _cupsLangPrintFilter(stderr
, "INFO", _("Unable to locate printer."));
326 if (getenv("CLASS") != NULL
)
327 return (CUPS_BACKEND_FAILED
);
330 if ((auth_info_required
= getenv("AUTH_INFO_REQUIRED")) == NULL
)
331 auth_info_required
= "none";
333 state_reasons
= cupsArrayNewStrings(getenv("PRINTER_STATE_REASONS"), ',');
337 * For Kerberos, become the printing user (if we can) to get the credentials
341 if (!getuid() && (value
= getenv("AUTH_UID")) != NULL
)
343 uid_t uid
= (uid_t
)strtoul(value
, NULL
, 10);
350 return (run_as_user(argv
, uid
, device_uri
, 0));
353 int status
= 0; /* Exit status */
355 for (i
= 6; i
< argc
&& !status
&& !job_canceled
; i
++)
357 if ((fd
= open(argv
[i
], O_RDONLY
)) >= 0)
359 status
= run_as_user(argv
, uid
, device_uri
, fd
);
364 _cupsLangPrintError("ERROR", _("Unable to open print file"));
365 status
= CUPS_BACKEND_FAILED
;
373 # else /* No XPC, just try to run as the user ID */
376 # endif /* HAVE_XPC */
378 #endif /* HAVE_GSSAPI */
381 * Get the (final) content type...
384 if ((content_type
= getenv("CONTENT_TYPE")) == NULL
)
385 content_type
= "application/octet-stream";
387 if ((final_content_type
= getenv("FINAL_CONTENT_TYPE")) == NULL
)
389 final_content_type
= content_type
;
391 if (!strncmp(final_content_type
, "printer/", 8))
392 final_content_type
= "application/vnd.cups-raw";
396 * Extract the hostname and printer name from the URI...
399 httpSeparateURI(HTTP_URI_CODING_ALL
, device_uri
, scheme
, sizeof(scheme
),
400 device_username
, sizeof(device_username
), hostname
, sizeof(hostname
), &port
,
401 resource
, sizeof(resource
));
404 port
= IPP_PORT
; /* Default to port 631 */
406 if (!strcmp(scheme
, "https") || !strcmp(scheme
, "ipps"))
407 cupsSetEncryption(HTTP_ENCRYPTION_ALWAYS
);
409 cupsSetEncryption(HTTP_ENCRYPTION_IF_REQUESTED
);
411 if (!strcmp(auth_info_required
, "negotiate") &&
412 (isdigit(hostname
[0] & 255) || hostname
[0] == '['))
415 * IP addresses are not allowed with Kerberos...
418 _cupsLangPrintFilter(stderr
, "ERROR",
419 _("IP address is not allowed as hostname when using Negotiate - use FQDN."));
420 update_reasons(NULL
, "-connecting-to-device");
421 return (CUPS_BACKEND_FAILED
);
425 * See if there are any options...
432 contimeout
= 7 * 24 * 60 * 60;
434 if ((optptr
= strchr(resource
, '?')) != NULL
)
437 * Yup, terminate the device name string and move to the first
438 * character of the optptr...
444 * Then parse the optptr...
455 while (*optptr
&& *optptr
!= '=' && *optptr
!= '+' && *optptr
!= '&')
458 if ((sep
= *optptr
) != '\0')
469 while (*optptr
&& *optptr
!= '+' && *optptr
!= '&')
479 * Process the option...
482 if (!_cups_strcasecmp(name
, "waitjob"))
485 * Wait for job completion?
488 waitjob
= !_cups_strcasecmp(value
, "on") ||
489 !_cups_strcasecmp(value
, "yes") ||
490 !_cups_strcasecmp(value
, "true");
492 else if (!_cups_strcasecmp(name
, "waitprinter"))
495 * Wait for printer idle?
498 waitprinter
= !_cups_strcasecmp(value
, "on") ||
499 !_cups_strcasecmp(value
, "yes") ||
500 !_cups_strcasecmp(value
, "true");
502 else if (!_cups_strcasecmp(name
, "encryption"))
505 * Enable/disable encryption?
508 if (!_cups_strcasecmp(value
, "always"))
509 cupsSetEncryption(HTTP_ENCRYPTION_ALWAYS
);
510 else if (!_cups_strcasecmp(value
, "required"))
511 cupsSetEncryption(HTTP_ENCRYPTION_REQUIRED
);
512 else if (!_cups_strcasecmp(value
, "never"))
513 cupsSetEncryption(HTTP_ENCRYPTION_NEVER
);
514 else if (!_cups_strcasecmp(value
, "ifrequested"))
515 cupsSetEncryption(HTTP_ENCRYPTION_IF_REQUESTED
);
518 _cupsLangPrintFilter(stderr
, "ERROR",
519 _("Unknown encryption option value: \"%s\"."),
523 else if (!_cups_strcasecmp(name
, "snmp"))
526 * Enable/disable SNMP stuff...
529 snmp_enabled
= !value
[0] || !_cups_strcasecmp(value
, "on") ||
530 !_cups_strcasecmp(value
, "yes") ||
531 !_cups_strcasecmp(value
, "true");
533 else if (!_cups_strcasecmp(name
, "version"))
535 if (!strcmp(value
, "1.0"))
537 else if (!strcmp(value
, "1.1"))
539 else if (!strcmp(value
, "2.0"))
541 else if (!strcmp(value
, "2.1"))
543 else if (!strcmp(value
, "2.2"))
547 _cupsLangPrintFilter(stderr
, "ERROR",
548 _("Unknown version option value: \"%s\"."),
552 else if (!_cups_strcasecmp(name
, "compression"))
554 if (!_cups_strcasecmp(value
, "true") || !_cups_strcasecmp(value
, "yes") ||
555 !_cups_strcasecmp(value
, "on") || !_cups_strcasecmp(value
, "gzip"))
556 compression
= "gzip";
557 else if (!_cups_strcasecmp(value
, "deflate"))
558 compression
= "deflate";
559 else if (!_cups_strcasecmp(value
, "false") ||
560 !_cups_strcasecmp(value
, "no") ||
561 !_cups_strcasecmp(value
, "off") ||
562 !_cups_strcasecmp(value
, "none"))
563 compression
= "none";
565 else if (!_cups_strcasecmp(name
, "contimeout"))
567 int value_int
= atoi(value
);
569 * Set the connection timeout...
573 contimeout
= value_int
;
581 _cupsLangPrintFilter(stderr
, "ERROR",
582 _("Unknown option \"%s\" with value \"%s\"."),
589 * If we have 7 arguments, print the file named on the command-line.
590 * Otherwise, copy stdin to a temporary file and print the temporary
598 send_options
= !_cups_strcasecmp(final_content_type
, "application/pdf") ||
599 !_cups_strcasecmp(final_content_type
, "application/vnd.cups-pdf") ||
600 !_cups_strncasecmp(final_content_type
, "image/", 6);
602 fputs("DEBUG: Sending stdin for job...\n", stderr
);
607 * Point to the files on the command-line...
610 num_files
= argc
- 6;
614 fprintf(stderr
, "DEBUG: %d files to send in job...\n", num_files
);
618 * Set the authentication info, if any...
621 cupsSetPasswordCB2((cups_password_cb2_t
)password_cb
, &password_tries
);
623 if (device_username
[0])
626 * Use authentication information in the device URI...
629 if ((device_password
= strchr(device_username
, ':')) != NULL
)
630 *device_password
++ = '\0';
632 cupsSetUser(device_username
);
638 * Try loading authentication information from the environment.
641 const char *ptr
= getenv("AUTH_USERNAME");
645 cupsCopyString(device_username
, ptr
, sizeof(device_username
));
649 device_password
= getenv("AUTH_PASSWORD");
653 * Try finding the remote server...
656 start_time
= time(NULL
);
658 addrlist
= backendLookup(hostname
, port
, &job_canceled
);
660 http
= httpConnect2(hostname
, port
, addrlist
, AF_UNSPEC
, cupsEncryption(), 1,
662 httpSetTimeout(http
, 30.0, timeout_cb
, NULL
);
665 * See if the printer supports SNMP...
669 snmp_fd
= _cupsSNMPOpen(addrlist
->addr
.addr
.sa_family
);
674 have_supplies
= !backendSNMPSupplies(snmp_fd
, &(addrlist
->addr
),
677 have_supplies
= start_count
= 0;
680 * Wait for data from the filter...
685 if (!backendWaitLoop(snmp_fd
, &(addrlist
->addr
), 0, backendNetworkSideCB
))
686 return (CUPS_BACKEND_OK
);
687 else if ((bytes
= read(0, buffer
, sizeof(buffer
))) <= 0)
688 return (CUPS_BACKEND_OK
);
692 * Try connecting to the remote server...
695 delay
= _cupsNextDelay(0, &prev_delay
);
699 fprintf(stderr
, "DEBUG: Connecting to %s:%d\n", hostname
, port
);
700 _cupsLangPrintFilter(stderr
, "INFO", _("Connecting to printer."));
702 if (httpReconnect2(http
, 30000, NULL
))
704 int error
= errno
; /* Connection error */
706 if (http
->status
== HTTP_STATUS_CUPS_PKI_ERROR
)
707 update_reasons(NULL
, "+cups-certificate-error");
712 if (getenv("CLASS") != NULL
)
715 * If the CLASS environment variable is set, the job was submitted
716 * to a class and not to a specific queue. In this case, we want
717 * to abort immediately so that the job can be requeued on the next
718 * available printer in the class.
721 _cupsLangPrintFilter(stderr
, "INFO",
722 _("Unable to contact printer, queuing on next "
723 "printer in class."));
726 * Sleep 5 seconds to keep the job from requeuing too rapidly...
731 update_reasons(NULL
, "-connecting-to-device");
733 return (CUPS_BACKEND_FAILED
);
736 fprintf(stderr
, "DEBUG: Connection error: %s\n", strerror(errno
));
738 if (errno
== ECONNREFUSED
|| errno
== EHOSTDOWN
|| errno
== EHOSTUNREACH
|| errno
== ETIMEDOUT
|| errno
== ENOTCONN
)
740 if (contimeout
&& (time(NULL
) - start_time
) > contimeout
)
742 _cupsLangPrintFilter(stderr
, "ERROR",
743 _("The printer is not responding."));
744 update_reasons(NULL
, "-connecting-to-device");
745 return (CUPS_BACKEND_FAILED
);
751 _cupsLangPrintFilter(stderr
, "WARNING",
752 _("The printer may not exist or "
753 "is unavailable at this time."));
758 _cupsLangPrintFilter(stderr
, "WARNING",
759 _("The printer is unreachable at this "
764 _cupsLangPrintFilter(stderr
, "WARNING",
765 _("The printer is in use."));
769 sleep((unsigned)delay
);
771 delay
= _cupsNextDelay(delay
, &prev_delay
);
775 _cupsLangPrintFilter(stderr
, "ERROR",
776 _("The printer is not responding."));
784 update_reasons(NULL
, "-cups-certificate-error");
786 while (http
->fd
< 0);
789 return (CUPS_BACKEND_OK
);
791 if (httpIsEncrypted(http
))
794 * Validate TLS credentials...
797 char *creds
; /* TLS credentials */
798 char *lcreds
= NULL
; /* Loaded credentials */
799 http_trust_t trust
; /* Trust level */
800 char credinfo
[1024], /* Information on credentials */
801 lcredinfo
[1024];/* Information on saved credentials */
802 static const char * const trusts
[] = { NULL
, "+cups-pki-invalid", "+cups-pki-changed", "+cups-pki-expired", NULL
, "+cups-pki-unknown" };
804 static const char * const trust_msgs
[] =
806 _("Credentials are OK/trusted."),
807 _("Credentials are invalid."),
808 _("Credentials have changed."),
809 _("Credentials are expired."),
810 _("Credentials have been renewed."),
811 _("Credentials are unknown/new.")
814 fputs("DEBUG: Connection is encrypted.\n", stderr
);
816 if ((creds
= httpCopyPeerCredentials(http
)) != NULL
)
818 trust
= cupsGetCredentialsTrust(NULL
, hostname
, creds
, /*require_ca*/false);
819 cupsGetCredentialsInfo(creds
, credinfo
, sizeof(credinfo
));
821 fprintf(stderr
, "DEBUG: %s (%s)\n", trust_msgs
[trust
], cupsGetErrorString());
822 fprintf(stderr
, "DEBUG: Printer credentials: %s\n", credinfo
);
824 if ((lcreds
= cupsCopyCredentials(NULL
, hostname
)) != NULL
)
826 cupsGetCredentialsInfo(lcreds
, lcredinfo
, sizeof(lcredinfo
));
827 fprintf(stderr
, "DEBUG: Stored credentials: %s\n", lcredinfo
);
831 fputs("DEBUG: No stored credentials.\n", stderr
);
834 update_reasons(NULL
, "-cups-pki-invalid,cups-pki-changed,cups-pki-expired,cups-pki-unknown");
837 update_reasons(NULL
, trusts
[trust
]);
838 _cupsLangPrintFilter(stderr
, "ALERT", "%s", trust_msgs
[trust
]);
839 return (CUPS_BACKEND_STOP
);
843 * Save the credentials we have so we can detect changes...
846 cupsSaveCredentials(NULL
, hostname
, creds
, /*key*/NULL
);
853 fputs("DEBUG: No printer credentials.\n", stderr
);
855 update_reasons(NULL
, "cups-pki-unknown");
856 return (CUPS_BACKEND_STOP
);
860 update_reasons(NULL
, "-connecting-to-device");
861 _cupsLangPrintFilter(stderr
, "INFO", _("Connected to printer."));
863 fprintf(stderr
, "DEBUG: Connected to %s:%d...\n",
864 httpAddrGetString(http
->hostaddr
, addrname
, sizeof(addrname
)),
865 httpAddrGetPort(http
->hostaddr
));
868 * Build a URI for the printer and fill the standard IPP attributes for
869 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
870 * might contain username:password information...
873 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), scheme
, NULL
, hostname
,
877 * First validate the destination and see if the device supports multiple
881 compression_sup
= NULL
;
884 encryption_sup
= NULL
;
886 media_col_sup
= NULL
;
888 operations_sup
= NULL
;
889 doc_handling_sup
= NULL
;
890 print_color_mode_sup
= NULL
;
891 print_scaling_sup
= NULL
;
896 * Check for side-channel requests...
899 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
902 * Build the IPP request...
905 request
= ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES
);
906 ippSetVersion(request
, version
/ 10, version
% 10);
907 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
910 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
911 "requested-attributes", sizeof(pattrs
) / sizeof(pattrs
[0]),
918 fputs("DEBUG: Getting supported attributes...\n", stderr
);
920 if (http
->version
< HTTP_VERSION_1_1
)
922 fprintf(stderr
, "DEBUG: Printer responded with HTTP version %d.%d.\n",
923 http
->version
/ 100, http
->version
% 100);
924 update_reasons(NULL
, "+cups-ipp-conformance-failure-report,"
925 "cups-ipp-wrong-http-version");
928 supported
= cupsDoRequest(http
, request
, resource
);
929 ipp_status
= cupsGetError();
931 fprintf(stderr
, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
932 ippErrorString(ipp_status
), cupsGetErrorString());
934 if (ipp_status
<= IPP_STATUS_OK_CONFLICTING
)
938 fprintf(stderr
, "DEBUG: Get-Printer-Attributes returned %s.\n",
939 ippErrorString(ipp_status
));
941 if (ipp_status
== IPP_STATUS_ERROR_BUSY
||
942 ipp_status
== IPP_STATUS_ERROR_SERVICE_UNAVAILABLE
)
944 if (contimeout
&& (time(NULL
) - start_time
) > contimeout
)
946 _cupsLangPrintFilter(stderr
, "ERROR",
947 _("The printer is not responding."));
948 return (CUPS_BACKEND_FAILED
);
951 _cupsLangPrintFilter(stderr
, "INFO", _("The printer is in use."));
953 report_printer_state(supported
);
955 sleep((unsigned)delay
);
957 delay
= _cupsNextDelay(delay
, &prev_delay
);
959 else if ((ipp_status
== IPP_STATUS_ERROR_BAD_REQUEST
||
960 ipp_status
== IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
) && version
> 10)
963 * Switch to IPP/1.1 or IPP/1.0...
968 _cupsLangPrintFilter(stderr
, "INFO", _("Preparing to print."));
970 "DEBUG: The printer does not support IPP/%d.%d, trying "
971 "IPP/1.1.\n", version
/ 10, version
% 10);
976 _cupsLangPrintFilter(stderr
, "INFO", _("Preparing to print."));
978 "DEBUG: The printer does not support IPP/%d.%d, trying "
979 "IPP/1.0.\n", version
/ 10, version
% 10);
983 httpReconnect2(http
, 30000, NULL
);
985 else if (ipp_status
== IPP_STATUS_ERROR_NOT_FOUND
)
987 _cupsLangPrintFilter(stderr
, "ERROR",
988 _("The printer configuration is incorrect or the "
989 "printer no longer exists."));
991 ippDelete(supported
);
993 return (CUPS_BACKEND_STOP
);
995 else if (ipp_status
== IPP_STATUS_ERROR_FORBIDDEN
||
996 ipp_status
== IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED
)
998 const char *www_auth
= httpGetField(http
, HTTP_FIELD_WWW_AUTHENTICATE
);
999 /* WWW-Authenticate field value */
1001 if (!strncmp(www_auth
, "Negotiate", 9))
1002 auth_info_required
= "negotiate";
1003 else if (www_auth
[0])
1004 auth_info_required
= "username,password";
1006 fprintf(stderr
, "ATTR: auth-info-required=%s\n", auth_info_required
);
1007 return (CUPS_BACKEND_AUTH_REQUIRED
);
1009 else if (ipp_status
!= IPP_STATUS_ERROR_NOT_AUTHORIZED
)
1011 _cupsLangPrintFilter(stderr
, "ERROR",
1012 _("Unable to get printer status."));
1015 httpReconnect2(http
, 30000, NULL
);
1018 ippDelete(supported
);
1023 if (!getenv("CLASS"))
1026 * Check printer-is-accepting-jobs = false and printer-state-reasons for the
1027 * "spool-area-full" keyword...
1032 if ((printer_accepting
= ippFindAttribute(supported
,
1033 "printer-is-accepting-jobs",
1034 IPP_TAG_BOOLEAN
)) != NULL
&&
1035 !printer_accepting
->values
[0].boolean
)
1037 else if (!printer_accepting
)
1038 update_reasons(NULL
, "+cups-ipp-conformance-failure-report,"
1039 "cups-ipp-missing-printer-is-accepting-jobs");
1041 if ((printer_state
= ippFindAttribute(supported
,
1042 "printer-state-reasons",
1043 IPP_TAG_KEYWORD
)) == NULL
)
1045 update_reasons(NULL
, "+cups-ipp-conformance-failure-report,"
1046 "cups-ipp-missing-printer-state-reasons");
1050 for (i
= 0; i
< printer_state
->num_values
; i
++)
1052 if (!strcmp(printer_state
->values
[0].string
.text
,
1053 "spool-area-full") ||
1054 !strncmp(printer_state
->values
[0].string
.text
, "spool-area-full-",
1065 _cupsLangPrintFilter(stderr
, "INFO", _("The printer is in use."));
1067 report_printer_state(supported
);
1069 sleep((unsigned)delay
);
1071 delay
= _cupsNextDelay(delay
, &prev_delay
);
1073 ippDelete(supported
);
1075 ipp_status
= IPP_STATUS_ERROR_BUSY
;
1081 * Check for supported attributes...
1084 if ((compression_sup
= ippFindAttribute(supported
, "compression-supported", IPP_TAG_KEYWORD
)) != NULL
)
1087 * Check whether the requested compression is supported and/or default to
1088 * compression if supported...
1091 if (compression
&& !ippContainsString(compression_sup
, compression
))
1093 fprintf(stderr
, "DEBUG: Printer does not support the requested "
1094 "compression value \"%s\".\n", compression
);
1097 else if (!compression
&& (!strcmp(final_content_type
, "image/pwg-raster") || !strcmp(final_content_type
, "image/urf")))
1099 if (ippContainsString(compression_sup
, "gzip"))
1100 compression
= "gzip";
1101 else if (ippContainsString(compression_sup
, "deflate"))
1102 compression
= "deflate";
1105 fprintf(stderr
, "DEBUG: Automatically using \"%s\" compression.\n",
1110 if ((copies_sup
= ippFindAttribute(supported
, "copies-supported",
1111 IPP_TAG_RANGE
)) != NULL
)
1114 * Has the "copies-supported" attribute - does it have an upper
1118 fprintf(stderr
, "DEBUG: copies-supported=%d-%d\n",
1119 copies_sup
->values
[0].range
.lower
,
1120 copies_sup
->values
[0].range
.upper
);
1122 if (copies_sup
->values
[0].range
.upper
<= 1)
1123 copies_sup
= NULL
; /* No */
1126 if ((cups_version
= ippFindAttribute(supported
, "cups-version", IPP_TAG_TEXT
)) != NULL
)
1128 const char *val
= ippGetString(cups_version
, 0, NULL
);
1130 fprintf(stderr
, "DEBUG: cups-version = \"%s\"\n", val
);
1131 if (!strcmp(val
, "cups-version"))
1132 cups_version
= NULL
; /* Bogus cups-version value returned by buggy printers! */
1135 encryption_sup
= ippFindAttribute(supported
, "job-password-encryption-supported", IPP_TAG_KEYWORD
);
1137 if ((format_sup
= ippFindAttribute(supported
, "document-format-supported",
1138 IPP_TAG_MIMETYPE
)) != NULL
)
1140 fprintf(stderr
, "DEBUG: document-format-supported (%d values)\n",
1141 format_sup
->num_values
);
1142 for (i
= 0; i
< format_sup
->num_values
; i
++)
1143 fprintf(stderr
, "DEBUG: [%d] = \"%s\"\n", i
,
1144 format_sup
->values
[i
].string
.text
);
1147 if ((media_col_sup
= ippFindAttribute(supported
, "media-col-supported",
1148 IPP_TAG_KEYWORD
)) != NULL
)
1150 fprintf(stderr
, "DEBUG: media-col-supported (%d values)\n",
1151 media_col_sup
->num_values
);
1152 for (i
= 0; i
< media_col_sup
->num_values
; i
++)
1153 fprintf(stderr
, "DEBUG: [%d] = \"%s\"\n", i
,
1154 media_col_sup
->values
[i
].string
.text
);
1157 print_color_mode_sup
= ippFindAttribute(supported
, "print-color-mode-supported", IPP_TAG_KEYWORD
);
1159 if ((print_scaling_sup
= ippFindAttribute(supported
, "print-scaling-supported", IPP_TAG_KEYWORD
)) != NULL
)
1161 int count
= ippGetCount(print_scaling_sup
);
1163 fprintf(stderr
, "DEBUG: print-scaling-supported (%d values)\n", count
);
1164 for (i
= 0; i
< count
; i
++)
1165 fprintf(stderr
, "DEBUG: [%d] = %s\n", i
, ippGetString(print_scaling_sup
, i
, NULL
));
1168 if ((operations_sup
= ippFindAttribute(supported
, "operations-supported",
1169 IPP_TAG_ENUM
)) != NULL
)
1171 fprintf(stderr
, "DEBUG: operations-supported (%d values)\n",
1172 operations_sup
->num_values
);
1173 for (i
= 0; i
< operations_sup
->num_values
; i
++)
1174 fprintf(stderr
, "DEBUG: [%d] = %s\n", i
,
1175 ippOpString(operations_sup
->values
[i
].integer
));
1177 for (i
= 0; i
< operations_sup
->num_values
; i
++)
1178 if (operations_sup
->values
[i
].integer
== IPP_OP_PRINT_JOB
)
1181 if (i
>= operations_sup
->num_values
)
1182 update_reasons(NULL
, "+cups-ipp-conformance-failure-report,"
1183 "cups-ipp-missing-print-job");
1185 for (i
= 0; i
< operations_sup
->num_values
; i
++)
1186 if (operations_sup
->values
[i
].integer
== IPP_OP_CANCEL_JOB
)
1189 if (i
>= operations_sup
->num_values
)
1190 update_reasons(NULL
, "+cups-ipp-conformance-failure-report,"
1191 "cups-ipp-missing-cancel-job");
1193 for (i
= 0; i
< operations_sup
->num_values
; i
++)
1194 if (operations_sup
->values
[i
].integer
== IPP_OP_GET_JOB_ATTRIBUTES
)
1197 if (i
>= operations_sup
->num_values
)
1198 update_reasons(NULL
, "+cups-ipp-conformance-failure-report,"
1199 "cups-ipp-missing-get-job-attributes");
1201 for (i
= 0; i
< operations_sup
->num_values
; i
++)
1202 if (operations_sup
->values
[i
].integer
== IPP_OP_GET_PRINTER_ATTRIBUTES
)
1205 if (i
>= operations_sup
->num_values
)
1206 update_reasons(NULL
, "+cups-ipp-conformance-failure-report,"
1207 "cups-ipp-missing-get-printer-attributes");
1209 for (i
= 0; i
< operations_sup
->num_values
; i
++)
1211 if (operations_sup
->values
[i
].integer
== IPP_OP_VALIDATE_JOB
)
1213 else if (operations_sup
->values
[i
].integer
== IPP_OP_CREATE_JOB
)
1215 else if (operations_sup
->values
[i
].integer
== IPP_OP_SEND_DOCUMENT
)
1217 else if (operations_sup
->values
[i
].integer
== IPP_OP_GET_JOB_ATTRIBUTES
)
1221 if (create_job
&& !send_document
)
1223 fputs("DEBUG: Printer supports Create-Job but not Send-Document.\n",
1227 update_reasons(NULL
, "+cups-ipp-conformance-failure-report,"
1228 "cups-ipp-missing-send-document");
1232 update_reasons(NULL
, "+cups-ipp-conformance-failure-report,"
1233 "cups-ipp-missing-validate-job");
1236 update_reasons(NULL
, "+cups-ipp-conformance-failure-report,"
1237 "cups-ipp-missing-operations-supported");
1239 doc_handling_sup
= ippFindAttribute(supported
,
1240 "multiple-document-handling-supported",
1243 report_printer_state(supported
);
1245 while (!job_canceled
&& ipp_status
> IPP_STATUS_OK_CONFLICTING
);
1248 return (CUPS_BACKEND_OK
);
1251 * See if the printer is accepting jobs and is not stopped; if either
1252 * condition is true and we are printing to a class, requeue the job...
1255 if (getenv("CLASS") != NULL
)
1257 printer_state
= ippFindAttribute(supported
, "printer-state",
1259 printer_accepting
= ippFindAttribute(supported
, "printer-is-accepting-jobs",
1262 if (printer_state
== NULL
||
1263 (printer_state
->values
[0].integer
> IPP_PSTATE_PROCESSING
&&
1265 printer_accepting
== NULL
||
1266 !printer_accepting
->values
[0].boolean
)
1269 * If the CLASS environment variable is set, the job was submitted
1270 * to a class and not to a specific queue. In this case, we want
1271 * to abort immediately so that the job can be requeued on the next
1272 * available printer in the class.
1275 _cupsLangPrintFilter(stderr
, "INFO",
1276 _("Unable to contact printer, queuing on next "
1277 "printer in class."));
1279 ippDelete(supported
);
1283 * Sleep 5 seconds to keep the job from requeuing too rapidly...
1288 return (CUPS_BACKEND_FAILED
);
1293 * See if the printer supports multiple copies...
1296 copies
= atoi(argv
[4]);
1298 if (copies_sup
|| argc
< 7)
1299 copies_remaining
= 1;
1301 copies_remaining
= copies
;
1304 * Prepare remaining printing options...
1311 num_options
= cupsParseOptions(argv
[5], 0, &options
);
1313 if (!cups_version
&& media_col_sup
)
1316 * Load the PPD file and generate PWG attribute mapping information...
1319 ppd_attr_t
*mandatory
; /* cupsMandatory value */
1321 ppd
= ppdOpenFile(getenv("PPD"));
1322 pc
= _ppdCacheCreateWithPPD(NULL
, ppd
);
1324 ppdMarkDefaults(ppd
);
1325 cupsMarkOptions(ppd
, num_options
, options
);
1327 if ((mandatory
= ppdFindAttr(ppd
, "cupsMandatory", NULL
)) != NULL
)
1328 cupsCopyString(mandatory_attrs
, mandatory
->value
, sizeof(mandatory_attrs
));
1332 * Validate job-password/-encryption...
1335 if (cupsGetOption("job-password", num_options
, options
))
1337 const char *keyword
; /* job-password-encryption value */
1338 static const char * const hashes
[] =
1339 { /* List of supported hash algorithms, in order of preference */
1350 if ((keyword
= cupsGetOption("job-password-encryption", num_options
, options
)) == NULL
|| !ippContainsString(encryption_sup
, keyword
))
1353 * Either no job-password-encryption or the value isn't supported by
1358 for (j
= 0; j
< (sizeof(hashes
) / sizeof(hashes
[0])); j
++)
1359 if (ippContainsString(encryption_sup
, hashes
[j
]))
1361 num_options
= cupsAddOption("job-password-encryption", hashes
[j
], num_options
, &options
);
1370 document_format
= NULL
;
1372 if (format_sup
!= NULL
)
1374 if (ippContainsString(format_sup
, final_content_type
))
1375 document_format
= final_content_type
;
1376 else if (ippContainsString(format_sup
, "application/octet-stream"))
1377 document_format
= "application/octet-stream";
1380 fprintf(stderr
, "DEBUG: final_content_type=\"%s\", document_format=\"%s\"\n",
1381 final_content_type
, document_format
? document_format
: "(null)");
1384 * If the printer does not support HTTP/1.1 (which IPP requires), copy stdin
1385 * to a temporary file so that we can do a HTTP/1.0 submission...
1387 * (I hate compatibility hacks!)
1390 if (http
->version
< HTTP_VERSION_1_1
&& num_files
== 0)
1392 if ((fd
= cupsTempFd(tmpfilename
, sizeof(tmpfilename
))) < 0)
1394 perror("DEBUG: Unable to create temporary file");
1395 return (CUPS_BACKEND_FAILED
);
1398 _cupsLangPrintFilter(stderr
, "INFO", _("Copying print data."));
1400 if ((compatsize
= write(fd
, buffer
, (size_t)bytes
)) < 0)
1402 perror("DEBUG: Unable to write temporary file");
1403 return (CUPS_BACKEND_FAILED
);
1406 if ((bytes
= backendRunLoop(-1, fd
, snmp_fd
, &(addrlist
->addr
), 0, 0,
1407 backendNetworkSideCB
)) < 0)
1408 return (CUPS_BACKEND_FAILED
);
1410 compatsize
+= bytes
;
1414 compatfile
= tmpfilename
;
1415 files
= &compatfile
;
1418 else if (http
->version
< HTTP_VERSION_1_1
&& num_files
== 1)
1420 struct stat fileinfo
; /* File information */
1422 if (!stat(files
[0], &fileinfo
))
1423 compatsize
= fileinfo
.st_size
;
1427 * If the printer only claims to support IPP/1.0, or if the user specifically
1428 * included version=1.0 in the URI, then do not try to use Create-Job or
1429 * Send-Document. This is another dreaded compatibility hack, but
1430 * unfortunately there are enough broken printers out there that we need
1438 * Start monitoring the printer in the background...
1442 monitor
.hostname
= hostname
;
1443 monitor
.user
= username
;
1444 monitor
.resource
= resource
;
1445 monitor
.port
= port
;
1446 monitor
.version
= version
;
1448 monitor
.create_job
= create_job
;
1449 monitor
.get_job_attrs
= get_job_attrs
;
1450 monitor
.encryption
= cupsEncryption();
1451 monitor
.job_state
= IPP_JSTATE_PENDING
;
1452 monitor
.printer_state
= IPP_PSTATE_IDLE
;
1453 monitor
.retryable
= argc
== 6 && document_format
&& strcmp(document_format
, "image/pwg-raster") && strcmp(document_format
, "image/urf");
1455 fprintf(stderr
, "DEBUG: retryable=%d\n", monitor
.retryable
);
1459 monitor
.job_name
= argv
[3];
1464 * TODO: make this compatible with UTF-8 - possible UTF-8 truncation here..
1467 snprintf(print_job_name
, sizeof(print_job_name
), "%s - %s", argv
[1],
1469 monitor
.job_name
= print_job_name
;
1472 cupsThreadCreate((cups_thread_func_t
)monitor_printer
, &monitor
);
1475 * Validate access to the printer...
1478 while (!job_canceled
&& validate_job
)
1480 request
= new_request(IPP_OP_VALIDATE_JOB
, version
, uri
, username
,
1481 monitor
.job_name
, num_options
, options
, compression
,
1482 copies_sup
? copies
: 1, document_format
, pc
, ppd
,
1483 media_col_sup
, doc_handling_sup
, print_color_mode_sup
, print_scaling_sup
);
1485 response
= cupsDoRequest(http
, request
, resource
);
1487 ipp_status
= cupsGetError();
1489 fprintf(stderr
, "DEBUG: Validate-Job: %s (%s)\n",
1490 ippErrorString(ipp_status
), cupsGetErrorString());
1491 debug_attributes(response
);
1493 if ((job_auth
= ippFindAttribute(response
, "job-authorization-uri",
1494 IPP_TAG_URI
)) != NULL
)
1495 num_options
= cupsAddOption("job-authorization-uri",
1496 ippGetString(job_auth
, 0, NULL
), num_options
,
1499 if (ipp_status
== IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED
|| ipp_status
== IPP_STATUS_OK_CONFLICTING
)
1502 * One or more options are not supported...
1505 if (ippFindAttribute(response
, "sides", IPP_TAG_ZERO
))
1508 * The sides value is not supported, revert to one-sided as needed...
1511 const char *sides
= cupsGetOption("sides", num_options
, options
);
1513 if (!sides
|| !strncmp(sides
, "two-sided-", 10))
1515 fputs("DEBUG: Unable to do two-sided printing, setting sides to 'one-sided'.\n", stderr
);
1516 num_options
= cupsAddOption("sides", "one-sided", num_options
, &options
);
1520 else if ((ipp_status
== IPP_STATUS_ERROR_BAD_REQUEST
|| ipp_status
== IPP_STATUS_ERROR_INTERNAL
) && !strcmp(username
, argv
[2]))
1523 * Issue #1145: Some printers have trouble with valid character in the
1524 * requesting-user-name attribute. Sanitize the username and try again
1528 char *argptr
= argv
[2], /* Pointer into local username */
1529 *userptr
= username
; /* Pointer into requesting-user-name value */
1531 fputs("DEBUG: Trying sanitized requesting-user-name value.\n", stderr
);
1533 while (*argptr
&& userptr
< (username
+ sizeof(username
) - 1))
1535 if (isalnum(*argptr
& 255))
1536 *userptr
++ = *argptr
;
1543 ippDelete(response
);
1548 if (ipp_status
== IPP_STATUS_ERROR_SERVICE_UNAVAILABLE
||
1549 ipp_status
== IPP_STATUS_ERROR_BUSY
)
1551 _cupsLangPrintFilter(stderr
, "INFO", _("The printer is in use."));
1554 else if (ipp_status
== IPP_STATUS_ERROR_DOCUMENT_FORMAT_NOT_SUPPORTED
||
1555 ipp_status
== IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
||
1556 ipp_status
== IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED
||
1557 ipp_status
== IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED
||
1558 ipp_status
== IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED
||
1559 ipp_status
== IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED
)
1561 else if (ipp_status
== IPP_STATUS_ERROR_FORBIDDEN
||
1562 ipp_status
== IPP_STATUS_ERROR_NOT_AUTHORIZED
||
1563 ipp_status
== IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED
)
1565 const char *www_auth
= httpGetField(http
, HTTP_FIELD_WWW_AUTHENTICATE
);
1566 /* WWW-Authenticate field value */
1568 if (!strncmp(www_auth
, "Negotiate", 9))
1569 auth_info_required
= "negotiate";
1570 else if (www_auth
[0])
1571 auth_info_required
= "username,password";
1575 else if (ipp_status
== IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
)
1578 * This is all too common...
1581 update_reasons(NULL
, "+cups-ipp-conformance-failure-report,"
1582 "cups-ipp-missing-validate-job");
1585 else if (ipp_status
< IPP_STATUS_REDIRECTION_OTHER_SITE
||
1586 ipp_status
== IPP_STATUS_ERROR_BAD_REQUEST
)
1588 else if (job_auth
== NULL
&& ipp_status
> IPP_STATUS_ERROR_BAD_REQUEST
)
1590 if (!validate_retried
)
1592 // Retry Validate-Job operation once, to work around known printer bug...
1593 validate_retried
= 1;
1603 * Then issue the print-job request...
1608 while (!job_canceled
&& copies_remaining
> 0)
1611 * Check for side-channel requests...
1614 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
1617 * Build the IPP job creation request...
1623 request
= new_request((num_files
> 1 || create_job
) ? IPP_OP_CREATE_JOB
:
1625 version
, uri
, username
, monitor
.job_name
, num_options
,
1626 options
, compression
, copies_sup
? copies
: 1,
1627 document_format
, pc
, ppd
, media_col_sup
,
1628 doc_handling_sup
, print_color_mode_sup
, print_scaling_sup
);
1634 if (num_files
> 1 || create_job
)
1635 response
= cupsDoRequest(http
, request
, resource
);
1638 size_t length
= 0; /* Length of request */
1642 fputs("DEBUG: Sending file using HTTP/1.0 Content-Length...\n", stderr
);
1643 length
= ippLength(request
) + (size_t)compatsize
;
1646 fputs("DEBUG: Sending file using HTTP/1.1 chunking...\n", stderr
);
1648 http_status
= cupsSendRequest(http
, request
, resource
, length
);
1649 if (http_status
== HTTP_STATUS_CONTINUE
&& request
->state
== IPP_STATE_DATA
)
1651 if (compression
&& strcmp(compression
, "none"))
1652 httpSetField(http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
1656 if ((fd
= open(files
[0], O_RDONLY
)) < 0)
1658 _cupsLangPrintError("ERROR", _("Unable to open print file"));
1659 return (CUPS_BACKEND_FAILED
);
1665 http_status
= cupsWriteRequestData(http
, buffer
, (size_t)bytes
);
1668 while (http_status
== HTTP_STATUS_CONTINUE
&&
1669 (!job_canceled
|| compatsize
> 0))
1672 * Check for side-channel requests and more print data...
1677 FD_SET(snmp_fd
, &input
);
1678 FD_SET(CUPS_SC_FD
, &input
);
1680 while (select(fd
> snmp_fd
? fd
+ 1 : snmp_fd
+ 1, &input
, NULL
, NULL
,
1681 NULL
) <= 0 && !job_canceled
);
1683 if (FD_ISSET(snmp_fd
, &input
))
1684 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
1686 if (FD_ISSET(fd
, &input
))
1688 if ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
1690 fprintf(stderr
, "DEBUG: Read %d bytes...\n", (int)bytes
);
1692 if ((http_status
= cupsWriteRequestData(http
, buffer
, (size_t)bytes
))
1693 != HTTP_STATUS_CONTINUE
)
1696 else if (bytes
== 0 || (errno
!= EINTR
&& errno
!= EAGAIN
))
1701 if (http_status
== HTTP_STATUS_ERROR
)
1702 fprintf(stderr
, "DEBUG: Error writing document data for "
1703 "Print-Job: %s\n", strerror(httpError(http
)));
1709 response
= cupsGetResponse(http
, resource
);
1713 ipp_status
= cupsGetError();
1715 fprintf(stderr
, "DEBUG: %s: %s (%s)\n",
1716 (num_files
> 1 || create_job
) ? "Create-Job" : "Print-Job",
1717 ippErrorString(ipp_status
), cupsGetErrorString());
1718 debug_attributes(response
);
1720 if (ipp_status
> IPP_STATUS_OK_CONFLICTING
)
1727 if (ipp_status
== IPP_STATUS_ERROR_SERVICE_UNAVAILABLE
||
1728 ipp_status
== IPP_STATUS_ERROR_NOT_POSSIBLE
||
1729 ipp_status
== IPP_STATUS_ERROR_BUSY
)
1731 _cupsLangPrintFilter(stderr
, "INFO", _("The printer is in use."));
1737 * We can't re-submit when we have no files to print, so exit
1738 * immediately with the right status code...
1744 else if (ipp_status
== IPP_STATUS_ERROR_JOB_CANCELED
||
1745 ipp_status
== IPP_STATUS_ERROR_NOT_AUTHORIZED
||
1746 ipp_status
== IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
||
1747 ipp_status
== IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED
||
1748 ipp_status
== IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED
||
1749 ipp_status
== IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED
||
1750 ipp_status
== IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED
)
1755 * Update auth-info-required as needed...
1758 _cupsLangPrintFilter(stderr
, "ERROR",
1759 _("Print job was not accepted."));
1761 if (ipp_status
== IPP_STATUS_ERROR_FORBIDDEN
||
1762 ipp_status
== IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED
)
1764 const char *www_auth
= httpGetField(http
, HTTP_FIELD_WWW_AUTHENTICATE
);
1765 /* WWW-Authenticate field value */
1767 if (!strncmp(www_auth
, "Negotiate", 9))
1768 auth_info_required
= "negotiate";
1769 else if (www_auth
[0])
1770 auth_info_required
= "username,password";
1772 else if (ipp_status
== IPP_STATUS_ERROR_REQUEST_VALUE
)
1775 * Print file is too large, abort this job...
1786 * We can't re-submit when we have no files to print, so exit
1787 * immediately with the right status code...
1794 else if ((job_id_attr
= ippFindAttribute(response
, "job-id",
1795 IPP_TAG_INTEGER
)) == NULL
)
1797 fputs("DEBUG: Print job accepted - job ID unknown.\n", stderr
);
1798 update_reasons(NULL
, "+cups-ipp-conformance-failure-report,"
1799 "cups-ipp-missing-job-id");
1805 monitor
.job_id
= job_id
= job_id_attr
->values
[0].integer
;
1806 fprintf(stderr
, "DEBUG: Print job accepted - job ID %d.\n", job_id
);
1809 ippDelete(response
);
1814 if (job_id
&& (num_files
> 1 || create_job
))
1816 for (i
= 0; num_files
== 0 || i
< num_files
; i
++)
1819 * Check for side-channel requests...
1822 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
1825 * Send the next file in the job...
1828 request
= ippNewRequest(IPP_OP_SEND_DOCUMENT
);
1829 ippSetVersion(request
, version
/ 10, version
% 10);
1831 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1834 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id",
1838 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1839 "requesting-user-name", NULL
, username
);
1841 ippAddBoolean(request
, IPP_TAG_OPERATION
, "last-document",
1842 (i
+ 1) >= num_files
);
1844 if (document_format
)
1845 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
,
1846 "document-format", NULL
, document_format
);
1849 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1850 "compression", NULL
, compression
);
1852 fprintf(stderr
, "DEBUG: Sending file %d using chunking...\n", i
+ 1);
1853 fprintf(stderr
, "DEBUG: IPP/%d.%d %s #%d\n", version
/ 10, version
% 10, ippOpString(ippGetOperation(request
)), ippGetRequestId(request
));
1854 debug_attributes(request
);
1856 http_status
= cupsSendRequest(http
, request
, resource
, 0);
1857 if (http_status
== HTTP_STATUS_CONTINUE
&& request
->state
== IPP_STATE_DATA
)
1859 if (compression
&& strcmp(compression
, "none"))
1860 httpSetField(http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
1865 http_status
= cupsWriteRequestData(http
, buffer
, (size_t)bytes
);
1869 if ((fd
= open(files
[i
], O_RDONLY
)) < 0)
1871 _cupsLangPrintError("ERROR", _("Unable to open print file"));
1872 return (CUPS_BACKEND_FAILED
);
1881 while (!job_canceled
&& http_status
== HTTP_STATUS_CONTINUE
&&
1882 (bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
1884 if ((http_status
= cupsWriteRequestData(http
, buffer
, (size_t)bytes
))
1885 != HTTP_STATUS_CONTINUE
)
1890 * Check for side-channel requests...
1893 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
1901 if (http_status
== HTTP_STATUS_ERROR
)
1902 fprintf(stderr
, "DEBUG: Error writing document data for "
1903 "Send-Document: %s\n", strerror(httpError(http
)));
1905 response
= cupsGetResponse(http
, resource
);
1908 fprintf(stderr
, "DEBUG: Send-Document: %s (%s)\n", ippErrorString(cupsGetError()), cupsGetErrorString());
1909 debug_attributes(response
);
1911 if (cupsGetError() > IPP_STATUS_OK_CONFLICTING
&& !job_canceled
)
1913 ipp_attribute_t
*reasons
= ippFindAttribute(response
, "job-state-reasons", IPP_TAG_KEYWORD
);
1914 /* job-state-reasons values */
1916 ipp_status
= cupsGetError();
1918 if (ippContainsString(reasons
, "document-format-error"))
1919 ipp_status
= IPP_STATUS_ERROR_DOCUMENT_FORMAT_ERROR
;
1920 else if (ippContainsString(reasons
, "document-unprintable-error"))
1921 ipp_status
= IPP_STATUS_ERROR_DOCUMENT_UNPRINTABLE
;
1923 ippDelete(response
);
1924 _cupsLangPrintFilter(stderr
, "ERROR", _("Unable to add document to print job."));
1929 ippDelete(response
);
1933 if (num_files
== 0 || fd
< 0)
1942 if (ipp_status
<= IPP_STATUS_OK_CONFLICTING
&& argc
> 6)
1944 fprintf(stderr
, "PAGE: 1 %d\n", copies_sup
? atoi(argv
[4]) : 1);
1945 copies_remaining
--;
1947 else if ((ipp_status
== IPP_STATUS_ERROR_DOCUMENT_FORMAT_NOT_SUPPORTED
|| ipp_status
== IPP_STATUS_ERROR_DOCUMENT_FORMAT_ERROR
|| ipp_status
== IPP_STATUS_ERROR_DOCUMENT_UNPRINTABLE
) &&
1949 document_format
&& strcmp(document_format
, "image/pwg-raster") && strcmp(document_format
, "image/urf"))
1952 * Need to reprocess the job as raster...
1955 fputs("JOBSTATE: cups-retry-as-raster\n", stderr
);
1957 cancel_job(http
, uri
, job_id
, resource
, username
, version
);
1961 else if (ipp_status
== IPP_STATUS_ERROR_SERVICE_UNAVAILABLE
||
1962 ipp_status
== IPP_STATUS_ERROR_NOT_POSSIBLE
||
1963 ipp_status
== IPP_STATUS_ERROR_BUSY
)
1968 * Need to reprocess the entire job; if we have a job ID, cancel the
1973 cancel_job(http
, uri
, job_id
, resource
, username
, version
);
1979 else if (ipp_status
== IPP_STATUS_ERROR_REQUEST_VALUE
||
1980 ipp_status
== IPP_STATUS_ERROR_JOB_CANCELED
||
1981 ipp_status
== IPP_STATUS_ERROR_NOT_AUTHORIZED
||
1982 ipp_status
== IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED
||
1983 ipp_status
== IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED
||
1984 ipp_status
== IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED
||
1985 ipp_status
== IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED
||
1986 ipp_status
== IPP_STATUS_ERROR_INTERNAL
)
1989 * Print file is too large, job was canceled, we need new
1990 * authentication data, or we had some sort of error...
1995 else if (ipp_status
== IPP_STATUS_ERROR_CUPS_UPGRADE_REQUIRED
)
1998 * Server is configured incorrectly; the policy for Create-Job and
1999 * Send-Document has to be the same (auth or no auth, encryption or
2000 * no encryption). Force the queue to stop since printing will never
2004 fputs("DEBUG: The server or printer is configured incorrectly.\n",
2006 fputs("DEBUG: The policy for Create-Job and Send-Document must have the "
2007 "same authentication and encryption requirements.\n", stderr
);
2009 ipp_status
= IPP_STATUS_ERROR_INTERNAL
;
2012 cancel_job(http
, uri
, job_id
, resource
, username
, version
);
2016 else if (ipp_status
== IPP_STATUS_ERROR_NOT_FOUND
)
2019 * Printer does not actually implement support for Create-Job/
2020 * Send-Document, so log the conformance issue and stop the printer.
2023 fputs("DEBUG: This printer claims to support Create-Job and "
2024 "Send-Document, but those operations failed.\n", stderr
);
2025 fputs("DEBUG: Add '?version=1.0' to the device URI to use legacy "
2026 "compatibility mode.\n", stderr
);
2027 update_reasons(NULL
, "+cups-ipp-conformance-failure-report,"
2028 "cups-ipp-missing-send-document");
2030 ipp_status
= IPP_STATUS_ERROR_INTERNAL
; /* Force queue to stop */
2035 copies_remaining
--;
2038 * Wait for the job to complete...
2041 if (!job_id
|| !waitjob
|| !get_job_attrs
)
2044 fputs("STATE: +cups-waiting-for-job-completed\n", stderr
);
2046 _cupsLangPrintFilter(stderr
, "INFO", _("Waiting for job to complete."));
2048 for (delay
= _cupsNextDelay(0, &prev_delay
); !job_canceled
;)
2051 * Check for side-channel requests...
2054 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
2057 * Check printer state...
2060 check_printer_state(http
, uri
, resource
, username
, version
);
2062 if (cupsGetError() <= IPP_STATUS_OK_CONFLICTING
)
2066 * Build an IPP_OP_GET_JOB_ATTRIBUTES request...
2069 request
= ippNewRequest(IPP_OP_GET_JOB_ATTRIBUTES
);
2070 ippSetVersion(request
, version
/ 10, version
% 10);
2072 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
2075 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id",
2079 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
2080 "requesting-user-name", NULL
, username
);
2082 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
2083 "requested-attributes", sizeof(jattrs
) / sizeof(jattrs
[0]),
2086 fprintf(stderr
, "DEBUG: IPP/%d.%d %s #%d\n", version
/ 10, version
% 10, ippOpString(ippGetOperation(request
)), ippGetRequestId(request
));
2087 debug_attributes(request
);
2093 httpReconnect2(http
, 30000, NULL
);
2094 response
= cupsDoRequest(http
, request
, resource
);
2095 ipp_status
= cupsGetError();
2097 if (ipp_status
== IPP_STATUS_ERROR_NOT_FOUND
|| ipp_status
== IPP_STATUS_ERROR_NOT_POSSIBLE
)
2100 * Job has gone away and/or the server has no job history...
2103 update_reasons(NULL
, "+cups-ipp-conformance-failure-report,"
2104 "cups-ipp-missing-job-history");
2105 ippDelete(response
);
2107 ipp_status
= IPP_STATUS_OK
;
2111 fprintf(stderr
, "DEBUG: Get-Job-Attributes: %s (%s)\n",
2112 ippErrorString(ipp_status
), cupsGetErrorString());
2113 debug_attributes(response
);
2115 if (ipp_status
<= IPP_STATUS_OK_CONFLICTING
)
2119 if (ipp_status
!= IPP_STATUS_ERROR_SERVICE_UNAVAILABLE
&&
2120 ipp_status
!= IPP_STATUS_ERROR_BUSY
)
2122 ippDelete(response
);
2123 ipp_status
= IPP_STATUS_OK
;
2126 else if (ipp_status
== IPP_STATUS_ERROR_INTERNAL
)
2130 if (waitjob_tries
> 4)
2132 ippDelete(response
);
2133 ipp_status
= IPP_STATUS_OK
;
2141 if ((job_state
= ippFindAttribute(response
, "job-state",
2142 IPP_TAG_ENUM
)) != NULL
)
2145 * Reflect the remote job state in the local queue...
2149 job_state
->values
[0].integer
>= IPP_JSTATE_PENDING
&&
2150 job_state
->values
[0].integer
<= IPP_JSTATE_COMPLETED
)
2151 update_reasons(NULL
,
2152 remote_job_states
[job_state
->values
[0].integer
-
2153 IPP_JSTATE_PENDING
]);
2155 if ((job_sheets
= ippFindAttribute(response
, "job-impressions-completed", IPP_TAG_INTEGER
)) == NULL
)
2156 job_sheets
= ippFindAttribute(response
, "job-media-sheets-completed", IPP_TAG_INTEGER
);
2159 fprintf(stderr
, "PAGE: total %d\n",
2160 job_sheets
->values
[0].integer
);
2163 * Stop polling if the job is finished or pending-held...
2166 if (job_state
->values
[0].integer
> IPP_JSTATE_STOPPED
|| job_state
->values
[0].integer
== IPP_JSTATE_HELD
)
2168 ippDelete(response
);
2172 else if (ipp_status
!= IPP_STATUS_ERROR_SERVICE_UNAVAILABLE
&&
2173 ipp_status
!= IPP_STATUS_ERROR_NOT_POSSIBLE
&&
2174 ipp_status
!= IPP_STATUS_ERROR_BUSY
)
2177 * If the printer does not return a job-state attribute, it does not
2178 * conform to the IPP specification - break out immediately and fail
2182 update_reasons(NULL
, "+cups-ipp-conformance-failure-report,"
2183 "cups-ipp-missing-job-state");
2184 ipp_status
= IPP_STATUS_ERROR_INTERNAL
;
2189 ippDelete(response
);
2192 * Wait before polling again...
2195 sleep((unsigned)delay
);
2197 delay
= _cupsNextDelay(delay
, &prev_delay
);
2202 * Cancel the job as needed...
2205 if (job_canceled
> 0 && job_id
> 0)
2207 cancel_job(http
, uri
, job_id
, resource
, username
, version
);
2209 if (cupsGetError() > IPP_STATUS_OK_CONFLICTING
)
2210 _cupsLangPrintFilter(stderr
, "ERROR", _("Unable to cancel print job."));
2214 * Check the printer state and report it if necessary...
2217 check_printer_state(http
, uri
, resource
, username
, version
);
2219 if (cupsGetError() <= IPP_STATUS_OK_CONFLICTING
)
2223 * Collect the final page count as needed...
2226 if (have_supplies
&&
2227 !backendSNMPSupplies(snmp_fd
, &(http
->addrlist
->addr
), &page_count
,
2229 page_count
> start_count
)
2230 fprintf(stderr
, "PAGE: total %d\n", page_count
- start_count
);
2234 * See if we used Kerberos at all...
2238 auth_info_required
= "negotiate";
2239 #endif /* HAVE_GSSAPI */
2247 cupsFreeOptions(num_options
, options
);
2248 _ppdCacheDestroy(pc
);
2253 ippDelete(supported
);
2256 * Remove the temporary file(s) if necessary...
2260 unlink(tmpfilename
);
2263 * Return the queue status...
2266 if (ipp_status
== IPP_STATUS_ERROR_NOT_AUTHORIZED
|| ipp_status
== IPP_STATUS_ERROR_FORBIDDEN
||
2267 ipp_status
== IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED
||
2268 ipp_status
<= IPP_STATUS_OK_CONFLICTING
)
2269 fprintf(stderr
, "ATTR: auth-info-required=%s\n", auth_info_required
);
2271 if (ipp_status
== IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED
)
2272 fputs("JOBSTATE: account-info-needed\n", stderr
);
2273 else if (ipp_status
== IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED
)
2274 fputs("JOBSTATE: account-closed\n", stderr
);
2275 else if (ipp_status
== IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED
)
2276 fputs("JOBSTATE: account-limit-reached\n", stderr
);
2277 else if (ipp_status
== IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED
)
2278 fputs("JOBSTATE: account-authorization-failed\n", stderr
);
2280 // job_canceled can be -1 which should not be treated as CUPS_BACKEND_OK
2281 if (job_canceled
> 0)
2282 return (CUPS_BACKEND_OK
);
2283 else if (ipp_status
== IPP_STATUS_ERROR_NOT_AUTHORIZED
|| ipp_status
== IPP_STATUS_ERROR_FORBIDDEN
|| ipp_status
== IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED
)
2284 return (CUPS_BACKEND_AUTH_REQUIRED
);
2285 else if (ipp_status
== IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED
||
2286 ipp_status
== IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED
||
2287 ipp_status
== IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED
||
2288 ipp_status
== IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED
)
2289 return (CUPS_BACKEND_HOLD
);
2290 else if (ipp_status
== IPP_STATUS_ERROR_INTERNAL
)
2291 return (CUPS_BACKEND_STOP
);
2292 else if (ipp_status
== IPP_STATUS_ERROR_CONFLICTING
|| ipp_status
== IPP_STATUS_ERROR_REQUEST_ENTITY
|| ipp_status
== IPP_STATUS_ERROR_REQUEST_VALUE
)
2293 return (CUPS_BACKEND_FAILED
);
2294 else if (ipp_status
== IPP_STATUS_ERROR_REQUEST_VALUE
||
2295 ipp_status
== IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
||
2296 ipp_status
== IPP_STATUS_ERROR_DOCUMENT_FORMAT_NOT_SUPPORTED
|| job_canceled
< 0)
2298 if (ipp_status
== IPP_STATUS_ERROR_REQUEST_VALUE
)
2299 _cupsLangPrintFilter(stderr
, "ERROR", _("Print job too large."));
2300 else if (ipp_status
== IPP_STATUS_ERROR_DOCUMENT_FORMAT_NOT_SUPPORTED
)
2301 _cupsLangPrintFilter(stderr
, "ERROR",
2302 _("Printer cannot print supplied content."));
2303 else if (ipp_status
== IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
)
2304 _cupsLangPrintFilter(stderr
, "ERROR",
2305 _("Printer cannot print with supplied options."));
2307 _cupsLangPrintFilter(stderr
, "ERROR", _("Print job canceled at printer."));
2309 return (CUPS_BACKEND_CANCEL
);
2311 else if (ipp_status
> IPP_STATUS_OK_CONFLICTING
&& ipp_status
!= IPP_STATUS_ERROR_JOB_CANCELED
)
2312 return (CUPS_BACKEND_RETRY_CURRENT
);
2314 return (CUPS_BACKEND_OK
);
2319 * 'adjust_options()' - Adjust options which have the same meaning.
2321 * In case the backend gets PPD option and IPP attribute of the same meaning
2322 * among options array, adjust their values to reflect the same choices,
2323 * if the values differ (preferring PPD option values).
2325 * Support for each PPD x IPP option pair is added adhoc, based on demand.
2328 static int /* O - New number of options */
2329 adjust_options(int num_options
, /* I - Number of options */
2330 cups_option_t
**options
) /* I - Array of job options */
2332 const char *ppd_option_value
= NULL
; /* PPD option value */
2333 const char *ipp_attr_value
= NULL
; /* IPP attribute value */
2336 fprintf(stderr
, "DEBUG: adjust_options()\n");
2338 if (options
== NULL
|| num_options
< 2)
2340 fprintf(stderr
, "DEBUG: adjust_options(): Invalid values.\n");
2341 return (num_options
);
2345 * PPD option ColorModel and IPP attribute print-color-mode
2348 ppd_option_value
= cupsGetOption("ColorModel", num_options
, *options
);
2349 ipp_attr_value
= cupsGetOption("print-color-mode", num_options
, *options
);
2351 if (!ppd_option_value
|| !ipp_attr_value
)
2352 return (num_options
);
2354 if (strcmp(ipp_attr_value
, "monochrome") && (!strcmp(ppd_option_value
, "Gray")
2355 || !strcmp(ppd_option_value
, "FastGray")
2356 || !strcmp(ppd_option_value
, "DeviceGray")))
2358 fprintf(stderr
, "DEBUG: adjust_options(): Adjusting print-color-mode to monochrome.\n");
2359 num_options
= cupsAddOption("print-color-mode", "monochrome", num_options
, options
);
2361 else if (strcmp(ipp_attr_value
, "color") && (!strcmp(ppd_option_value
, "CMY")
2362 || !strcmp(ppd_option_value
, "CMYK")
2363 || !strcmp(ppd_option_value
, "RGB")))
2365 fprintf(stderr
, "DEBUG: adjust_options(): Adjusting print-color-mode to color.\n");
2366 num_options
= cupsAddOption("print-color-mode", "color", num_options
, options
);
2369 return (num_options
);
2374 * 'cancel_job()' - Cancel a print job.
2378 cancel_job(http_t
*http
, /* I - HTTP connection */
2379 const char *uri
, /* I - printer-uri */
2380 int id
, /* I - job-id */
2381 const char *resource
, /* I - Resource path */
2382 const char *user
, /* I - requesting-user-name */
2383 int version
) /* I - IPP version */
2385 ipp_t
*request
; /* Cancel-Job request */
2388 _cupsLangPrintFilter(stderr
, "INFO", _("Canceling print job."));
2390 request
= ippNewRequest(IPP_OP_CANCEL_JOB
);
2391 ippSetVersion(request
, version
/ 10, version
% 10);
2393 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
2395 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id", id
);
2397 if (user
&& user
[0])
2398 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
2399 "requesting-user-name", NULL
, user
);
2405 ippDelete(cupsDoRequest(http
, request
, resource
));
2410 * 'check_printer_state()' - Check the printer state.
2413 static ipp_pstate_t
/* O - Current printer-state */
2414 check_printer_state(
2415 http_t
*http
, /* I - HTTP connection */
2416 const char *uri
, /* I - Printer URI */
2417 const char *resource
, /* I - Resource path */
2418 const char *user
, /* I - Username, if any */
2419 int version
) /* I - IPP version */
2421 ipp_t
*request
, /* IPP request */
2422 *response
; /* IPP response */
2423 ipp_attribute_t
*attr
; /* Attribute in response */
2424 ipp_pstate_t printer_state
= IPP_PSTATE_STOPPED
;
2425 /* Current printer-state */
2429 * Send a Get-Printer-Attributes request and log the results...
2432 request
= ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES
);
2433 ippSetVersion(request
, version
/ 10, version
% 10);
2435 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
2438 if (user
&& user
[0])
2439 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
2440 "requesting-user-name", NULL
, user
);
2442 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
2443 "requested-attributes",
2444 (int)(sizeof(pattrs
) / sizeof(pattrs
[0])), NULL
, pattrs
);
2446 fprintf(stderr
, "DEBUG: IPP/%d.%d %s #%d\n", version
/ 10, version
% 10, ippOpString(ippGetOperation(request
)), ippGetRequestId(request
));
2447 debug_attributes(request
);
2449 if ((response
= cupsDoRequest(http
, request
, resource
)) != NULL
)
2451 report_printer_state(response
);
2453 if ((attr
= ippFindAttribute(response
, "printer-state",
2454 IPP_TAG_ENUM
)) != NULL
)
2455 printer_state
= (ipp_pstate_t
)attr
->values
[0].integer
;
2458 fprintf(stderr
, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
2459 ippErrorString(cupsGetError()), cupsGetErrorString());
2460 debug_attributes(response
);
2461 ippDelete(response
);
2464 * Return the printer-state value...
2467 return (printer_state
);
2472 * 'debug_attributes()' - Print out the request or response attributes as DEBUG
2477 debug_attributes(ipp_t
*ipp
) /* I - Request or response message */
2479 ipp_tag_t group
; /* Current group */
2480 ipp_attribute_t
*attr
; /* Current attribute */
2481 char buffer
[1024]; /* Value buffer */
2484 for (group
= IPP_TAG_ZERO
, attr
= ippFirstAttribute(ipp
);
2486 attr
= ippNextAttribute(ipp
))
2488 const char *name
= ippGetName(attr
);
2492 group
= IPP_TAG_ZERO
;
2496 if (group
!= ippGetGroupTag(attr
))
2498 group
= ippGetGroupTag(attr
);
2499 fprintf(stderr
, "DEBUG: ---- %s ----\n", ippTagString(group
));
2502 if (!strcmp(name
, "job-password"))
2503 cupsCopyString(buffer
, "---", sizeof(buffer
));
2505 ippAttributeString(attr
, buffer
, sizeof(buffer
));
2507 fprintf(stderr
, "DEBUG: %s %s%s %s\n", name
,
2508 ippGetCount(attr
) > 1 ? "1setOf " : "",
2509 ippTagString(ippGetValueTag(attr
)), buffer
);
2512 fprintf(stderr
, "DEBUG: ---- %s ----\n", ippTagString(IPP_TAG_END
));
2517 * 'monitor_printer()' - Monitor the printer state.
2520 static void * /* O - Thread exit code */
2522 _cups_monitor_t
*monitor
) /* I - Monitoring data */
2524 http_t
*http
; /* Connection to printer */
2525 ipp_t
*request
, /* IPP request */
2526 *response
; /* IPP response */
2527 ipp_attribute_t
*attr
; /* Attribute in response */
2528 int delay
, /* Current delay */
2529 prev_delay
; /* Previous delay */
2530 ipp_op_t job_op
; /* Operation to use */
2531 int job_id
; /* Job ID */
2532 const char *job_name
; /* Job name */
2533 ipp_jstate_t job_state
; /* Job state */
2534 const char *job_user
; /* Job originating user name */
2535 int password_tries
= 0; /* Password tries */
2539 * Make a copy of the printer connection...
2542 http
= httpConnect2(monitor
->hostname
, monitor
->port
, NULL
, AF_UNSPEC
,
2543 monitor
->encryption
, 1, 0, NULL
);
2544 httpSetTimeout(http
, 30.0, timeout_cb
, NULL
);
2545 if (device_username
[0])
2546 cupsSetUser(device_username
);
2548 cupsSetPasswordCB2((cups_password_cb2_t
)password_cb
, &password_tries
);
2551 * Loop until the job is canceled, aborted, or completed.
2554 delay
= _cupsNextDelay(0, &prev_delay
);
2556 monitor
->job_reasons
= 0;
2558 while (monitor
->job_state
< IPP_JSTATE_CANCELED
&& !job_canceled
)
2561 * Reconnect to the printer as needed...
2564 if (httpGetFd(http
) < 0)
2565 httpReconnect2(http
, 30000, NULL
);
2567 if (httpGetFd(http
) >= 0)
2570 * Connected, so check on the printer state...
2573 monitor
->printer_state
= check_printer_state(http
, monitor
->uri
,
2577 if (cupsGetError() <= IPP_STATUS_OK_CONFLICTING
)
2580 if (monitor
->job_id
== 0 && monitor
->create_job
)
2583 * No job-id yet, so continue...
2590 * Check the status of the job itself...
2593 job_op
= (monitor
->job_id
> 0 && monitor
->get_job_attrs
) ?
2594 IPP_OP_GET_JOB_ATTRIBUTES
: IPP_OP_GET_JOBS
;
2595 request
= ippNewRequest(job_op
);
2596 ippSetVersion(request
, monitor
->version
/ 10, monitor
->version
% 10);
2598 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
2599 NULL
, monitor
->uri
);
2600 if (job_op
== IPP_OP_GET_JOB_ATTRIBUTES
)
2601 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id",
2604 if (monitor
->user
&& monitor
->user
[0])
2605 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
2606 "requesting-user-name", NULL
, monitor
->user
);
2608 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
2609 "requested-attributes",
2610 (int)(sizeof(jattrs
) / sizeof(jattrs
[0])), NULL
, jattrs
);
2616 response
= cupsDoRequest(http
, request
, monitor
->resource
);
2618 fprintf(stderr
, "DEBUG: (monitor) %s: %s (%s)\n", ippOpString(job_op
),
2619 ippErrorString(cupsGetError()), cupsGetErrorString());
2621 if (cupsGetError() <= IPP_STATUS_OK_CONFLICTING
)
2624 if (job_op
== IPP_OP_GET_JOB_ATTRIBUTES
)
2626 if ((attr
= ippFindAttribute(response
, "job-state",
2627 IPP_TAG_ENUM
)) != NULL
)
2628 monitor
->job_state
= (ipp_jstate_t
)attr
->values
[0].integer
;
2630 monitor
->job_state
= IPP_JSTATE_COMPLETED
;
2634 for (attr
= response
->attrs
; attr
; attr
= attr
->next
)
2638 job_state
= IPP_JSTATE_PENDING
;
2641 while (attr
&& attr
->group_tag
!= IPP_TAG_JOB
)
2647 while (attr
&& attr
->group_tag
== IPP_TAG_JOB
)
2649 if (!strcmp(attr
->name
, "job-id") &&
2650 attr
->value_tag
== IPP_TAG_INTEGER
)
2651 job_id
= attr
->values
[0].integer
;
2652 else if (!strcmp(attr
->name
, "job-name") &&
2653 (attr
->value_tag
== IPP_TAG_NAME
||
2654 attr
->value_tag
== IPP_TAG_NAMELANG
))
2655 job_name
= attr
->values
[0].string
.text
;
2656 else if (!strcmp(attr
->name
, "job-state") &&
2657 attr
->value_tag
== IPP_TAG_ENUM
)
2658 job_state
= (ipp_jstate_t
)attr
->values
[0].integer
;
2659 else if (!strcmp(attr
->name
, "job-originating-user-name") &&
2660 (attr
->value_tag
== IPP_TAG_NAME
||
2661 attr
->value_tag
== IPP_TAG_NAMELANG
))
2662 job_user
= attr
->values
[0].string
.text
;
2667 if (job_id
> 0 && job_name
&& !strcmp(job_name
, monitor
->job_name
) &&
2668 job_user
&& monitor
->user
&& !strcmp(job_user
, monitor
->user
))
2670 monitor
->job_id
= job_id
;
2671 monitor
->job_state
= job_state
;
2680 fprintf(stderr
, "DEBUG: (monitor) job-state = %s\n", ippEnumString("job-state", (int)monitor
->job_state
));
2682 if (!job_canceled
&&
2683 (monitor
->job_state
== IPP_JSTATE_CANCELED
||
2684 monitor
->job_state
== IPP_JSTATE_ABORTED
))
2687 fprintf(stderr
, "DEBUG: (monitor) job_canceled = -1\n");
2690 if ((attr
= ippFindAttribute(response
, "job-state-reasons",
2691 IPP_TAG_KEYWORD
)) != NULL
)
2693 int i
, new_reasons
= 0; /* Looping var, new reasons */
2695 for (i
= 0; i
< attr
->num_values
; i
++)
2697 if (!strcmp(attr
->values
[i
].string
.text
, "account-authorization-failed"))
2698 new_reasons
|= _CUPS_JSR_ACCOUNT_AUTHORIZATION_FAILED
;
2699 else if (!strcmp(attr
->values
[i
].string
.text
, "account-closed"))
2700 new_reasons
|= _CUPS_JSR_ACCOUNT_CLOSED
;
2701 else if (!strcmp(attr
->values
[i
].string
.text
, "account-info-needed"))
2702 new_reasons
|= _CUPS_JSR_ACCOUNT_INFO_NEEDED
;
2703 else if (!strcmp(attr
->values
[i
].string
.text
, "account-limit-reached"))
2704 new_reasons
|= _CUPS_JSR_ACCOUNT_LIMIT_REACHED
;
2705 else if (!strcmp(attr
->values
[i
].string
.text
, "job-password-wait"))
2706 new_reasons
|= _CUPS_JSR_JOB_PASSWORD_WAIT
;
2707 else if (!strcmp(attr
->values
[i
].string
.text
, "job-release-wait"))
2708 new_reasons
|= _CUPS_JSR_JOB_RELEASE_WAIT
;
2709 else if (!strcmp(attr
->values
[i
].string
.text
, "document-format-error"))
2710 new_reasons
|= _CUPS_JSR_DOCUMENT_FORMAT_ERROR
;
2711 else if (!strcmp(attr
->values
[i
].string
.text
, "document-unprintable-error"))
2712 new_reasons
|= _CUPS_JSR_DOCUMENT_UNPRINTABLE
;
2714 if (!job_canceled
&& (!strncmp(attr
->values
[i
].string
.text
, "job-canceled-", 13) || !strcmp(attr
->values
[i
].string
.text
, "aborted-by-system")))
2718 if (new_reasons
!= monitor
->job_reasons
)
2720 if (new_reasons
& _CUPS_JSR_ACCOUNT_AUTHORIZATION_FAILED
)
2721 fputs("JOBSTATE: account-authorization-failed\n", stderr
);
2722 else if (new_reasons
& _CUPS_JSR_ACCOUNT_CLOSED
)
2723 fputs("JOBSTATE: account-closed\n", stderr
);
2724 else if (new_reasons
& _CUPS_JSR_ACCOUNT_INFO_NEEDED
)
2725 fputs("JOBSTATE: account-info-needed\n", stderr
);
2726 else if (new_reasons
& _CUPS_JSR_ACCOUNT_LIMIT_REACHED
)
2727 fputs("JOBSTATE: account-limit-reached\n", stderr
);
2728 else if (new_reasons
& _CUPS_JSR_JOB_PASSWORD_WAIT
)
2729 fputs("JOBSTATE: job-password-wait\n", stderr
);
2730 else if (new_reasons
& _CUPS_JSR_JOB_RELEASE_WAIT
)
2731 fputs("JOBSTATE: job-release-wait\n", stderr
);
2732 else if (new_reasons
& (_CUPS_JSR_DOCUMENT_FORMAT_ERROR
| _CUPS_JSR_DOCUMENT_UNPRINTABLE
))
2734 if (monitor
->retryable
)
2737 * Can't print this, so retry as raster...
2741 fputs("JOBSTATE: cups-retry-as-raster\n", stderr
);
2743 else if (new_reasons
& _CUPS_JSR_DOCUMENT_FORMAT_ERROR
)
2745 fputs("JOBSTATE: document-format-error\n", stderr
);
2749 fputs("JOBSTATE: document-unprintable\n", stderr
);
2753 fputs("JOBSTATE: job-printing\n", stderr
);
2755 monitor
->job_reasons
= new_reasons
;
2759 ippDelete(response
);
2761 fprintf(stderr
, "DEBUG: (monitor) job-state = %s\n", ippEnumString("job-state", (int)monitor
->job_state
));
2763 if (!job_canceled
&&
2764 (monitor
->job_state
== IPP_JSTATE_CANCELED
||
2765 monitor
->job_state
== IPP_JSTATE_ABORTED
))
2770 * Sleep for N seconds...
2775 sleep((unsigned)delay
);
2777 delay
= _cupsNextDelay(delay
, &prev_delay
);
2781 * Cancel the job if necessary...
2784 if (job_canceled
> 0 && monitor
->job_id
> 0)
2786 if (httpGetFd(http
) < 0)
2787 httpReconnect2(http
, 30000, NULL
);
2789 if (httpGetFd(http
) >= 0)
2791 cancel_job(http
, monitor
->uri
, monitor
->job_id
, monitor
->resource
,
2792 monitor
->user
, monitor
->version
);
2794 if (cupsGetError() > IPP_STATUS_OK_CONFLICTING
)
2796 fprintf(stderr
, "DEBUG: (monitor) cancel_job() = %s\n", cupsGetErrorString());
2797 _cupsLangPrintFilter(stderr
, "ERROR", _("Unable to cancel print job."));
2803 * Cleanup and return...
2813 * 'new_request()' - Create a new print creation or validation request.
2816 static ipp_t
* /* O - Request data */
2818 ipp_op_t op
, /* I - IPP operation code */
2819 int version
, /* I - IPP version number */
2820 const char *uri
, /* I - printer-uri value */
2821 const char *user
, /* I - requesting-user-name value */
2822 const char *title
, /* I - job-name value */
2823 int num_options
, /* I - Number of options to send */
2824 cups_option_t
*options
, /* I - Options to send */
2825 const char *compression
, /* I - compression value or NULL */
2826 int copies
, /* I - copies value or 0 */
2827 const char *format
, /* I - document-format value or NULL */
2828 _ppd_cache_t
*pc
, /* I - PPD cache and mapping data */
2829 ppd_file_t
*ppd
, /* I - PPD file data */
2830 ipp_attribute_t
*media_col_sup
, /* I - media-col-supported values */
2831 ipp_attribute_t
*doc_handling_sup
, /* I - multiple-document-handling-supported values */
2832 ipp_attribute_t
*print_color_mode_sup
,
2833 /* I - Printer supports print-color-mode? */
2834 ipp_attribute_t
*print_scaling_sup
) /* I - print-scaling-supported values */
2836 ipp_t
*request
; /* Request data */
2837 const char *keyword
; /* PWG keyword */
2841 * Create the IPP request...
2844 request
= ippNewRequest(op
);
2845 ippSetVersion(request
, version
/ 10, version
% 10);
2847 fprintf(stderr
, "DEBUG: %s IPP/%d.%d\n",
2848 ippOpString(request
->request
.op
.operation_id
),
2849 request
->request
.op
.version
[0],
2850 request
->request
.op
.version
[1]);
2853 * Add standard attributes...
2856 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri", NULL
, uri
);
2857 fprintf(stderr
, "DEBUG: printer-uri=\"%s\"\n", uri
);
2861 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name", NULL
, user
);
2862 fprintf(stderr
, "DEBUG: requesting-user-name=\"%s\"\n", user
);
2865 if (title
&& *title
)
2867 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "job-name", NULL
, title
);
2868 fprintf(stderr
, "DEBUG: job-name=\"%s\"\n", title
);
2871 if (format
&& op
!= IPP_OP_CREATE_JOB
)
2873 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
, "document-format", NULL
, format
);
2874 fprintf(stderr
, "DEBUG: document-format=\"%s\"\n", format
);
2877 if (compression
&& op
!= IPP_OP_CREATE_JOB
&& op
!= IPP_OP_VALIDATE_JOB
)
2879 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
, "compression", NULL
, compression
);
2880 fprintf(stderr
, "DEBUG: compression=\"%s\"\n", compression
);
2884 * Handle options on the command-line...
2887 if (num_options
> 0)
2892 * Send standard IPP attributes...
2895 fputs("DEBUG: Adding standard IPP operation/job attributes.\n", stderr
);
2897 copies
= _cupsConvertOptions(request
, ppd
, pc
, media_col_sup
, doc_handling_sup
, print_color_mode_sup
, user
, format
, copies
, num_options
, options
);
2899 if ((keyword
= cupsGetOption("print-scaling", num_options
, options
)) != NULL
&& ippContainsString(print_scaling_sup
, keyword
))
2900 ippAddString(request
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
, "print-scaling", NULL
, keyword
);
2903 * Map FaxOut options...
2906 if ((keyword
= cupsGetOption("phone", num_options
, options
)) != NULL
)
2908 ipp_t
*destination
; /* destination collection */
2909 char phone
[1024], /* Phone number string */
2910 *ptr
, /* Pointer into string */
2911 tel_uri
[1024]; /* tel: URI */
2912 static const char * const allowed
= "0123456789#*-+.()pw";
2913 /* Allowed characters */
2916 * Unescape and filter out spaces and other characters that are not
2917 * allowed in a tel: URI.
2920 _httpDecodeURI(phone
, keyword
, sizeof(phone
));
2924 * Weed out "Custom." in the beginning, this allows to put the
2925 * "phone" option as custom string option into the PPD so that
2926 * print dialogs not supporting fax display the option and
2927 * allow entering the phone number. Print dialogs also send "None"
2928 * if no phone number got entered, filter this, too.
2930 if (!_cups_strcasecmp(phone
, "None"))
2932 if (!_cups_strncasecmp(phone
, "Custom.", 7))
2933 _cups_strcpy(ptr
, ptr
+ 7);
2939 else if (!strchr(allowed
, *ptr
))
2940 _cups_strcpy(ptr
, ptr
+ 1);
2945 if (strlen(phone
) > 0)
2947 destination
= ippNew();
2949 httpAssembleURI(HTTP_URI_CODING_ALL
, tel_uri
, sizeof(tel_uri
), "tel", NULL
, NULL
, 0, phone
);
2950 ippAddString(destination
, IPP_TAG_JOB
, IPP_TAG_URI
, "destination-uri", NULL
, tel_uri
);
2951 fprintf(stderr
, "DEBUG: Faxing to phone %s; destination-uri: %s\n", phone
, tel_uri
);
2953 if ((keyword
= cupsGetOption("faxPrefix", num_options
, options
)) != NULL
&& *keyword
)
2955 char predial
[1024]; /* Pre-dial string */
2957 _httpDecodeURI(predial
, keyword
, sizeof(predial
));
2959 if (!_cups_strcasecmp(ptr
, "None"))
2961 if (!_cups_strncasecmp(ptr
, "Custom.", 7))
2963 if (strlen(ptr
) > 0)
2965 ippAddString(destination
, IPP_TAG_JOB
, IPP_TAG_TEXT
, "pre-dial-string", NULL
, ptr
);
2966 fprintf(stderr
, "DEBUG: Pre-dialing %s; pre-dial-string: %s\n", ptr
, ptr
);
2969 fprintf(stderr
, "WARNING: Pre-dial number for fax not valid! Sending fax without pre-dial number.\n");
2972 ippAddCollection(request
, IPP_TAG_JOB
, "destination-uris", destination
);
2973 ippDelete(destination
);
2976 fprintf(stderr
, "ERROR: Phone number for fax not valid! Fax cannot be sent.\n");
2982 * When talking to another CUPS server, send all options...
2985 fputs("DEBUG: Adding all operation/job attributes.\n", stderr
);
2986 num_options
= adjust_options(num_options
, &options
);
2988 if (format
&& (!strcmp(format
, "image/pwg-raster") || !strcmp(format
, "image/urf")))
2989 num_options
= cupsRemoveOption("copies", num_options
, &options
);
2991 cupsEncodeOptions2(request
, num_options
, options
, IPP_TAG_OPERATION
);
2992 cupsEncodeOptions2(request
, num_options
, options
, IPP_TAG_JOB
);
2995 if (copies
> 1 && (!pc
|| copies
<= pc
->max_copies
))
2996 ippAddInteger(request
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "copies", copies
);
2999 fprintf(stderr
, "DEBUG: IPP/%d.%d %s #%d\n", version
/ 10, version
% 10, ippOpString(ippGetOperation(request
)), ippGetRequestId(request
));
3000 debug_attributes(request
);
3007 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
3010 static const char * /* O - Password */
3011 password_cb(const char *prompt
, /* I - Prompt (not used) */
3012 http_t
*http
, /* I - Connection */
3013 const char *method
, /* I - Request method (not used) */
3014 const char *resource
, /* I - Resource path (not used) */
3015 int *password_tries
) /* I - Password tries */
3017 char def_username
[HTTP_MAX_VALUE
]; /* Default username */
3020 fprintf(stderr
, "DEBUG: password_cb(prompt=\"%s\", http=%p, method=\"%s\", "
3021 "resource=\"%s\", password_tries=%p(%d)), device_password=%p\n",
3022 prompt
, (void *)http
, method
, resource
, (void *)password_tries
, *password_tries
,
3023 (void *)device_password
);
3029 if (!uri_credentials
)
3032 * Remember that we need to authenticate...
3035 if (httpGetSubField(http
, HTTP_FIELD_WWW_AUTHENTICATE
, "username",
3038 char quoted
[HTTP_MAX_VALUE
* 2 + 4];
3041 fprintf(stderr
, "ATTR: auth-info-default=%s,\n",
3042 quote_string(def_username
, quoted
, sizeof(quoted
)));
3046 if (device_password
&& *device_password
&& *password_tries
< 3)
3048 (*password_tries
) ++;
3050 cupsSetUser(device_username
);
3052 return (device_password
);
3057 * Give up after 3 tries or if we don't have a password to begin with...
3066 * 'quote_string()' - Quote a string value.
3069 static const char * /* O - Quoted string */
3070 quote_string(const char *s
, /* I - String */
3071 char *q
, /* I - Quoted string buffer */
3072 size_t qsize
) /* I - Size of quoted string buffer */
3074 char *qptr
, /* Pointer into string buffer */
3075 *qend
; /* End of string buffer */
3079 qend
= q
+ qsize
- 5;
3090 while (*s
&& qptr
< qend
)
3092 if (*s
== '\\' || *s
== '\"' || *s
== '\'')
3094 if (qptr
< (qend
- 4))
3116 * 'report_attr()' - Report an IPP attribute value.
3120 report_attr(ipp_attribute_t
*attr
) /* I - Attribute */
3122 int i
; /* Looping var */
3123 char value
[1024], /* Value string */
3124 *valptr
; /* Pointer into value string */
3125 const char *cached
; /* Cached attribute */
3129 * Convert the attribute values into quoted strings...
3132 for (i
= 0, valptr
= value
;
3133 i
< attr
->num_values
&& valptr
< (value
+ sizeof(value
) - 10);
3139 switch (attr
->value_tag
)
3141 case IPP_TAG_INTEGER
:
3143 snprintf(valptr
, sizeof(value
) - (size_t)(valptr
- value
), "%d", attr
->values
[i
].integer
);
3144 valptr
+= strlen(valptr
);
3149 case IPP_TAG_KEYWORD
:
3150 quote_string(attr
->values
[i
].string
.text
, valptr
, (size_t)(value
+ sizeof(value
) - valptr
));
3151 valptr
+= strlen(valptr
);
3156 * Unsupported value type...
3165 cupsMutexLock(&report_mutex
);
3167 if ((cached
= cupsGetOption(attr
->name
, num_attr_cache
,
3168 attr_cache
)) == NULL
|| strcmp(cached
, value
))
3171 * Tell the scheduler about the new values...
3174 num_attr_cache
= cupsAddOption(attr
->name
, value
, num_attr_cache
,
3176 fprintf(stderr
, "ATTR: %s=%s\n", attr
->name
, value
);
3179 cupsMutexUnlock(&report_mutex
);
3184 * 'report_printer_state()' - Report the printer state.
3188 report_printer_state(ipp_t
*ipp
) /* I - IPP response */
3190 ipp_attribute_t
*pa
, /* printer-alert */
3191 *pam
, /* printer-alert-message */
3192 *pmja
, /* printer-mandatory-job-attributes */
3193 *psm
, /* printer-state-message */
3194 *reasons
, /* printer-state-reasons */
3195 *marker
; /* marker-* attributes */
3196 char value
[1024], /* State/message string */
3197 *valptr
; /* Pointer into string */
3198 static int ipp_supplies
= -1;
3199 /* Report supply levels? */
3203 * Report alerts and messages...
3206 if ((pa
= ippFindAttribute(ipp
, "printer-alert", IPP_TAG_STRING
)) != NULL
)
3209 if ((pam
= ippFindAttribute(ipp
, "printer-alert-message",
3210 IPP_TAG_TEXT
)) != NULL
)
3213 if ((pmja
= ippFindAttribute(ipp
, "printer-mandatory-job-attributes", IPP_TAG_KEYWORD
)) != NULL
)
3215 int i
, /* Looping var */
3216 count
= ippGetCount(pmja
); /* Number of values */
3218 for (i
= 0, valptr
= value
; i
< count
; i
++, valptr
+= strlen(valptr
))
3221 snprintf(valptr
, sizeof(value
) - (size_t)(valptr
- value
), " %s", ippGetString(pmja
, i
, NULL
));
3223 cupsCopyString(value
, ippGetString(pmja
, i
, NULL
), sizeof(value
));
3226 if (strcmp(value
, mandatory_attrs
))
3228 cupsCopyString(mandatory_attrs
, value
, sizeof(mandatory_attrs
));
3229 fprintf(stderr
, "PPD: cupsMandatory=\"%s\"\n", value
);
3233 if ((psm
= ippFindAttribute(ipp
, "printer-state-message",
3234 IPP_TAG_TEXT
)) != NULL
)
3236 char *ptr
; /* Pointer into message */
3239 cupsCopyString(value
, "INFO: ", sizeof(value
));
3240 for (ptr
= psm
->values
[0].string
.text
, valptr
= value
+ 6;
3241 *ptr
&& valptr
< (value
+ sizeof(value
) - 6);
3244 if (*ptr
< ' ' && *ptr
> 0 && *ptr
!= '\t')
3247 * Substitute "<XX>" for the control character...
3250 snprintf(valptr
, sizeof(value
) - (size_t)(valptr
- value
), "<%02X>", *ptr
);
3260 fputs(value
, stderr
);
3264 * Now report printer-state-reasons, filtering out some of the reasons we never
3268 if ((reasons
= ippFindAttribute(ipp
, "printer-state-reasons",
3269 IPP_TAG_KEYWORD
)) == NULL
)
3272 update_reasons(reasons
, NULL
);
3275 * Relay the current marker-* attribute values...
3278 if (ipp_supplies
< 0)
3280 ppd_file_t
*ppd
; /* PPD file */
3281 ppd_attr_t
*ppdattr
; /* Attribute in PPD file */
3283 if ((ppd
= ppdOpenFile(getenv("PPD"))) != NULL
&&
3284 (ppdattr
= ppdFindAttr(ppd
, "cupsIPPSupplies", NULL
)) != NULL
&&
3285 ppdattr
->value
&& _cups_strcasecmp(ppdattr
->value
, "true"))
3293 if (ipp_supplies
> 0)
3295 if ((marker
= ippFindAttribute(ipp
, "marker-colors", IPP_TAG_NAME
)) != NULL
)
3296 report_attr(marker
);
3297 if ((marker
= ippFindAttribute(ipp
, "marker-high-levels",
3298 IPP_TAG_INTEGER
)) != NULL
)
3299 report_attr(marker
);
3300 if ((marker
= ippFindAttribute(ipp
, "marker-levels",
3301 IPP_TAG_INTEGER
)) != NULL
)
3302 report_attr(marker
);
3303 if ((marker
= ippFindAttribute(ipp
, "marker-low-levels",
3304 IPP_TAG_INTEGER
)) != NULL
)
3305 report_attr(marker
);
3306 if ((marker
= ippFindAttribute(ipp
, "marker-message",
3307 IPP_TAG_TEXT
)) != NULL
)
3308 report_attr(marker
);
3309 if ((marker
= ippFindAttribute(ipp
, "marker-names", IPP_TAG_NAME
)) != NULL
)
3310 report_attr(marker
);
3311 if ((marker
= ippFindAttribute(ipp
, "marker-types",
3312 IPP_TAG_KEYWORD
)) != NULL
)
3313 report_attr(marker
);
3318 #if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
3320 * 'run_as_user()' - Run the IPP backend as the printing user.
3322 * This function uses an XPC-based user agent to run the backend as the printing
3323 * user. We need to do this in order to have access to the user's Kerberos
3327 static int /* O - Exit status */
3328 run_as_user(char *argv
[], /* I - Command-line arguments */
3329 uid_t uid
, /* I - User ID */
3330 const char *device_uri
, /* I - Device URI */
3331 int fd
) /* I - File to print */
3333 const char *auth_negotiate
,/* AUTH_NEGOTIATE env var */
3334 *content_type
, /* [FINAL_]CONTENT_TYPE env vars */
3335 *auth_info_required
; /* New auth-info-required value */
3336 xpc_connection_t conn
; /* Connection to XPC service */
3337 xpc_object_t request
; /* Request message dictionary */
3338 __block xpc_object_t response
; /* Response message dictionary */
3339 int status
= CUPS_BACKEND_FAILED
;
3340 /* Status of request */
3343 fprintf(stderr
, "DEBUG: Running IPP backend as UID %u.\n", (unsigned)uid
);
3346 * Connect to the user agent for the specified UID...
3349 conn
= xpc_connection_create_mach_service(kPMPrintUIToolAgent
,
3350 dispatch_get_global_queue(0, 0), 0);
3353 _cupsLangPrintFilter(stderr
, "ERROR",
3354 _("Unable to start backend process."));
3355 fputs("DEBUG: Unable to create connection to agent.\n", stderr
);
3359 xpc_connection_set_event_handler(conn
,
3360 ^(xpc_object_t event
)
3362 xpc_type_t messageType
= xpc_get_type(event
);
3364 if (messageType
== XPC_TYPE_ERROR
)
3366 if (event
== XPC_ERROR_CONNECTION_INTERRUPTED
)
3367 fprintf(stderr
, "DEBUG: Interrupted connection to service %s.\n",
3368 xpc_connection_get_name(conn
));
3369 else if (event
== XPC_ERROR_CONNECTION_INVALID
)
3370 fprintf(stderr
, "DEBUG: Connection invalid for service %s.\n",
3371 xpc_connection_get_name(conn
));
3373 fprintf(stderr
, "DEBUG: Unexpected error for service %s: %s\n",
3374 xpc_connection_get_name(conn
),
3375 xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
));
3378 xpc_connection_set_target_uid(conn
, uid
);
3379 xpc_connection_resume(conn
);
3382 * Try starting the backend...
3385 request
= xpc_dictionary_create_empty();
3386 xpc_dictionary_set_int64(request
, "command", kPMStartJob
);
3387 xpc_dictionary_set_string(request
, "device-uri", device_uri
);
3388 xpc_dictionary_set_string(request
, "job-id", argv
[1]);
3389 xpc_dictionary_set_string(request
, "user", argv
[2]);
3390 xpc_dictionary_set_string(request
, "title", argv
[3]);
3391 xpc_dictionary_set_string(request
, "copies", argv
[4]);
3392 xpc_dictionary_set_string(request
, "options", argv
[5]);
3393 if ((auth_info_required
= getenv("AUTH_INFO_REQUIRED")) != NULL
)
3394 xpc_dictionary_set_string(request
, "auth-info-required",
3395 auth_info_required
);
3396 if ((auth_negotiate
= getenv("AUTH_NEGOTIATE")) != NULL
)
3397 xpc_dictionary_set_string(request
, "auth-negotiate", auth_negotiate
);
3398 if ((content_type
= getenv("CONTENT_TYPE")) != NULL
)
3399 xpc_dictionary_set_string(request
, "content-type", content_type
);
3400 if ((content_type
= getenv("FINAL_CONTENT_TYPE")) != NULL
)
3401 xpc_dictionary_set_string(request
, "final-content-type", content_type
);
3402 xpc_dictionary_set_fd(request
, "stdin", fd
);
3403 xpc_dictionary_set_fd(request
, "stderr", 2);
3404 xpc_dictionary_set_fd(request
, "side-channel", CUPS_SC_FD
);
3406 response
= xpc_connection_send_message_with_reply_sync(conn
, request
);
3408 xpc_release(request
);
3412 child_pid
= (pid_t
)xpc_dictionary_get_int64(response
, "child-pid");
3414 xpc_release(response
);
3417 fprintf(stderr
, "DEBUG: Child PID=%d.\n", (int)child_pid
);
3420 _cupsLangPrintFilter(stderr
, "ERROR",
3421 _("Unable to start backend process."));
3422 fputs("DEBUG: No child PID.\n", stderr
);
3428 _cupsLangPrintFilter(stderr
, "ERROR",
3429 _("Unable to start backend process."));
3430 fputs("DEBUG: No reply from agent.\n", stderr
);
3435 * Then wait for the backend to finish...
3438 request
= xpc_dictionary_create_empty();
3439 xpc_dictionary_set_int64(request
, "command", kPMWaitForJob
);
3440 xpc_dictionary_set_fd(request
, "stderr", 2);
3442 response
= xpc_connection_send_message_with_reply_sync(conn
, request
);
3443 xpc_release(request
);
3447 status
= (int)xpc_dictionary_get_int64(response
, "status");
3449 if (status
== SIGTERM
|| status
== SIGKILL
|| status
== SIGPIPE
)
3451 fprintf(stderr
, "DEBUG: Child terminated on signal %d.\n", status
);
3452 status
= CUPS_BACKEND_FAILED
;
3454 else if (WIFSIGNALED(status
))
3456 fprintf(stderr
, "DEBUG: Child crashed on signal %d.\n", status
);
3457 status
= CUPS_BACKEND_STOP
;
3459 else if (WIFEXITED(status
))
3461 status
= WEXITSTATUS(status
);
3462 fprintf(stderr
, "DEBUG: Child exited with status %d.\n", status
);
3465 xpc_release(response
);
3468 _cupsLangPrintFilter(stderr
, "ERROR",
3469 _("Unable to get backend exit status."));
3475 xpc_connection_cancel(conn
);
3481 #endif /* HAVE_GSSAPI && HAVE_XPC */
3485 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
3489 sigterm_handler(int sig
) /* I - Signal */
3491 (void)sig
; /* remove compiler warnings... */
3493 backendMessage("DEBUG: Got SIGTERM.\n");
3495 #if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
3498 kill(child_pid
, sig
);
3501 #endif /* HAVE_GSSAPI && HAVE_XPC */
3506 * Flag that the job should be canceled...
3509 backendMessage("DEBUG: sigterm_handler: job_canceled = 1.\n");
3516 * The scheduler already tried to cancel us once, now just terminate
3517 * after removing our temp file!
3521 unlink(tmpfilename
);
3528 * 'timeout_cb()' - Handle HTTP timeouts.
3531 static int /* O - 1 to continue, 0 to cancel */
3532 timeout_cb(http_t
*http
, /* I - Connection to server (unused) */
3533 void *user_data
) /* I - User data (unused) */
3538 return (!job_canceled
);
3543 * 'update_reasons()' - Update the printer-state-reasons values.
3547 update_reasons(ipp_attribute_t
*attr
, /* I - printer-state-reasons or NULL */
3548 const char *s
) /* I - STATE: string or NULL */
3550 char op
; /* Add (+), remove (-), replace (\0) */
3551 cups_array_t
*new_reasons
; /* New reasons array */
3552 char *reason
, /* Current reason */
3553 add
[2048], /* Reasons added string */
3554 *addptr
, /* Pointer into add string */
3555 rem
[2048], /* Reasons removed string */
3556 *remptr
; /* Pointer into remove string */
3557 const char *addprefix
, /* Current add string prefix */
3558 *remprefix
; /* Current remove string prefix */
3561 fprintf(stderr
, "DEBUG: update_reasons(attr=%d(%s%s), s=\"%s\")\n",
3562 attr
? attr
->num_values
: 0, attr
? attr
->values
[0].string
.text
: "",
3563 attr
&& attr
->num_values
> 1 ? ",..." : "", s
? s
: "(null)");
3566 * Create an array of new reason keyword strings...
3571 int i
; /* Looping var */
3573 new_reasons
= cupsArrayNew((cups_array_func_t
)_cupsArrayStrcmp
, NULL
);
3576 for (i
= 0; i
< attr
->num_values
; i
++)
3578 reason
= attr
->values
[i
].string
.text
;
3580 if (strcmp(reason
, "none") &&
3581 strcmp(reason
, "none-report") &&
3582 strcmp(reason
, "paused") &&
3583 strncmp(reason
, "spool-area-full", 15) &&
3584 strcmp(reason
, "com.apple.print.recoverable-warning") &&
3585 strncmp(reason
, "cups-", 5))
3586 cupsArrayAdd(new_reasons
, reason
);
3591 if (*s
== '+' || *s
== '-')
3596 new_reasons
= cupsArrayNewStrings(s
, ',');
3602 * Compute the changes...
3606 addprefix
= "STATE: +";
3609 remprefix
= "STATE: -";
3612 cupsMutexLock(&report_mutex
);
3614 fprintf(stderr
, "DEBUG2: op='%c', new_reasons=%d, state_reasons=%d\n",
3615 op
? op
: ' ', cupsArrayCount(new_reasons
),
3616 cupsArrayCount(state_reasons
));
3624 for (reason
= (char *)cupsArrayFirst(new_reasons
);
3626 reason
= (char *)cupsArrayNext(new_reasons
))
3628 if (!cupsArrayFind(state_reasons
, reason
))
3630 if (!strncmp(reason
, "cups-remote-", 12))
3633 * If we are setting cups-remote-xxx, remove all other cups-remote-xxx
3637 char *temp
; /* Current reason in state_reasons */
3639 cupsArraySave(state_reasons
);
3641 for (temp
= (char *)cupsArrayFirst(state_reasons
);
3643 temp
= (char *)cupsArrayNext(state_reasons
))
3644 if (!strncmp(temp
, "cups-remote-", 12))
3646 snprintf(remptr
, sizeof(rem
) - (size_t)(remptr
- rem
), "%s%s", remprefix
, temp
);
3647 remptr
+= strlen(remptr
);
3650 cupsArrayRemove(state_reasons
, temp
);
3654 cupsArrayRestore(state_reasons
);
3657 cupsArrayAdd(state_reasons
, reason
);
3659 snprintf(addptr
, sizeof(add
) - (size_t)(addptr
- add
), "%s%s", addprefix
, reason
);
3660 addptr
+= strlen(addptr
);
3671 for (reason
= (char *)cupsArrayFirst(new_reasons
);
3673 reason
= (char *)cupsArrayNext(new_reasons
))
3675 if (cupsArrayFind(state_reasons
, reason
))
3677 snprintf(remptr
, sizeof(rem
) - (size_t)(remptr
- rem
), "%s%s", remprefix
, reason
);
3678 remptr
+= strlen(remptr
);
3681 cupsArrayRemove(state_reasons
, reason
);
3688 * Replace reasons...
3691 for (reason
= (char *)cupsArrayFirst(state_reasons
);
3693 reason
= (char *)cupsArrayNext(state_reasons
))
3695 if (strncmp(reason
, "cups-", 5) && !cupsArrayFind(new_reasons
, reason
))
3697 snprintf(remptr
, sizeof(rem
) - (size_t)(remptr
- rem
), "%s%s", remprefix
, reason
);
3698 remptr
+= strlen(remptr
);
3701 cupsArrayRemove(state_reasons
, reason
);
3705 for (reason
= (char *)cupsArrayFirst(new_reasons
);
3707 reason
= (char *)cupsArrayNext(new_reasons
))
3709 if (!cupsArrayFind(state_reasons
, reason
))
3711 cupsArrayAdd(state_reasons
, reason
);
3713 snprintf(addptr
, sizeof(add
) - (size_t)(addptr
- add
), "%s%s", addprefix
, reason
);
3714 addptr
+= strlen(addptr
);
3720 cupsArrayDelete(new_reasons
);
3722 cupsMutexUnlock(&report_mutex
);
3725 * Report changes and return...
3728 if (add
[0] && rem
[0])
3729 fprintf(stderr
, "%s\n%s\n", add
, rem
);
3731 fprintf(stderr
, "%s\n", add
);
3733 fprintf(stderr
, "%s\n", rem
);