X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=backend%2Fipp.c;h=7f8a17e1be25ae7d5d129cf1a17423f47ab2a7c0;hb=3dd9c340583c44d4dcb7223efbd0a815e0027ff7;hp=2c13360bcb441ec0c63a3ac71ea95f8ce672487d;hpb=321d8d57dacf00e4ae19558dc9444519f4d9ade1;p=thirdparty%2Fcups.git diff --git a/backend/ipp.c b/backend/ipp.c index 2c13360bc..7f8a17e1b 100644 --- a/backend/ipp.c +++ b/backend/ipp.c @@ -3,7 +3,7 @@ * * IPP backend for CUPS. * - * Copyright 2007-2011 by Apple Inc. + * Copyright 2007-2012 by Apple Inc. * Copyright 1997-2007 by Easy Software Products, all rights reserved. * * These coded instructions, statements, and computer programs are the @@ -16,18 +16,22 @@ * * Contents: * - * main() - Send a file to the printer or server. - * cancel_job() - Cancel a print job. + * main() - Send a file to the printer or server. + * cancel_job() - Cancel a print job. * check_printer_state() - Check the printer state. - * compress_files() - Compress print files. - * monitor_printer() - Monitor the printer state. - * new_request() - Create a new print creation or validation request. - * password_cb() - Disable the password prompt for - * cupsDoFileRequest(). - * report_attr() - Report an IPP attribute value. + * compress_files() - Compress print files. + * monitor_printer() - Monitor the printer state. + * new_request() - Create a new print creation or validation + * request. + * password_cb() - Disable the password prompt for + * cupsDoFileRequest(). + * quote_string() - Quote a string value. + * report_attr() - Report an IPP attribute value. * report_printer_state() - Report the printer state. - * run_as_user() - Run the IPP backend as the printing user. - * sigterm_handler() - Handle 'terminate' signals that stop the backend. + * run_as_user() - Run the IPP backend as the printing user. + * sigterm_handler() - Handle 'terminate' signals that stop the backend. + * timeout_cb() - Handle HTTP timeouts. + * update_reasons() - Update the printer-state-reasons values. */ /* @@ -44,8 +48,12 @@ # define kPMPrintUIToolAgent "com.apple.printuitool.agent" # define kPMStartJob 100 # define kPMWaitForJob 101 +# ifdef HAVE_XPC_PRIVATE_H +# include +# else extern void xpc_connection_set_target_uid(xpc_connection_t connection, uid_t uid); +# endif /* HAVE_XPC_PRIVATE_H */ #endif /* HAVE_GSSAPI && HAVE_XPC */ @@ -61,7 +69,9 @@ typedef struct _cups_monitor_s /**** Monitoring data ****/ *resource; /* Resource path */ int port, /* Port number */ version, /* IPP version */ - job_id; /* Job ID for submitted job */ + job_id, /* Job ID for submitted job */ + get_job_attrs; /* Support Get-Job-Attributes? */ + const char *job_name; /* Job name for submitted job */ http_encryption_t encryption; /* Use encryption? */ ipp_jstate_t job_state; /* Current job state */ ipp_pstate_t printer_state; /* Current printer state */ @@ -79,14 +89,19 @@ static int child_pid = 0; /* Child process ID */ #endif /* HAVE_GSSAPI && HAVE_XPC */ static const char * const jattrs[] = /* Job attributes we want */ { + "job-id", "job-impressions-completed", "job-media-sheets-completed", + "job-name", + "job-originating-user-name", "job-state", "job-state-reasons" }; static int job_canceled = 0; /* Job cancelled? */ -static char *password = NULL; +static char username[256] = "", + /* Username for device URI */ + *password = NULL; /* Password for device URI */ static int password_tries = 0; /* Password tries */ @@ -110,17 +125,17 @@ static const char * const pattrs[] = /* Printer attributes we want */ "printer-is-accepting-jobs", "printer-state", "printer-state-message", - "printer-state-reasons", + "printer-state-reasons" }; static const char * const remote_job_states[] = { /* Remote job state keywords */ - "cups-remote-pending", - "cups-remote-pending-held", - "cups-remote-processing", - "cups-remote-stopped", - "cups-remote-canceled", - "cups-remote-aborted", - "cups-remote-completed" + "+cups-remote-pending", + "+cups-remote-pending-held", + "+cups-remote-processing", + "+cups-remote-stopped", + "+cups-remote-canceled", + "+cups-remote-aborted", + "+cups-remote-completed" }; static _cups_mutex_t report_mutex = _CUPS_MUTEX_INITIALIZER; /* Mutex to control access */ @@ -154,14 +169,18 @@ static ipp_t *new_request(ipp_op_t op, int version, const char *uri, const char *format, _ppd_cache_t *pc, ipp_attribute_t *media_col_sup, ipp_attribute_t *doc_handling_sup); -static const char *password_cb(const char *); +static const char *password_cb(const char *prompt, http_t *http, + const char *method, const char *resource, + void *user_data); +static const char *quote_string(const char *s, char *q, size_t qsize); static void report_attr(ipp_attribute_t *attr); static void report_printer_state(ipp_t *ipp); #if defined(HAVE_GSSAPI) && defined(HAVE_XPC) -static int run_as_user(int argc, char *argv[], uid_t uid, +static int run_as_user(char *argv[], uid_t uid, const char *device_uri, int fd); #endif /* HAVE_GSSAPI && HAVE_XPC */ static void sigterm_handler(int sig); +static int timeout_cb(http_t *http, void *user_data); static void update_reasons(ipp_attribute_t *attr, const char *s); @@ -184,7 +203,6 @@ main(int argc, /* I - Number of command-line args */ const char *device_uri; /* Device URI */ char scheme[255], /* Scheme in URI */ hostname[1024], /* Hostname */ - username[255], /* Username info */ resource[1024], /* Resource info (printer name) */ addrname[256], /* Address name */ *optptr, /* Pointer to URI options */ @@ -192,6 +210,7 @@ main(int argc, /* I - Number of command-line args */ *value, /* Value of option */ sep; /* Separator character */ http_addrlist_t *addrlist; /* Address of printer */ + int snmp_enabled = 1; /* Is SNMP enabled? */ int snmp_fd, /* SNMP socket */ start_count, /* Page count via SNMP at start */ page_count, /* Page count via SNMP */ @@ -203,6 +222,7 @@ main(int argc, /* I - Number of command-line args */ int port; /* Port number (not used) */ char portname[255]; /* Port name */ char uri[HTTP_MAX_URI]; /* Updated URI without user/pass */ + char print_job_name[1024]; /* Update job-name for Print-Job */ http_status_t http_status; /* Status of HTTP request */ ipp_status_t ipp_status; /* Status of IPP request */ http_t *http; /* HTTP connection */ @@ -229,7 +249,10 @@ main(int argc, /* I - Number of command-line args */ ipp_attribute_t *doc_handling_sup; /* multiple-document-handling-supported */ ipp_attribute_t *printer_state; /* printer-state attribute */ ipp_attribute_t *printer_accepting; /* printer-is-accepting-jobs */ - int validate_job; /* Does printer support Validate-Job? */ + int create_job = 0, /* Does printer support Create-Job? */ + get_job_attrs = 0, /* Does printer support Get-Job-Attributes? */ + send_document = 0, /* Does printer support Send-Document? */ + validate_job = 0; /* Does printer support Validate-Job? */ int copies, /* Number of copies for job */ copies_remaining; /* Number of copies remaining */ const char *content_type, /* CONTENT_TYPE environment variable */ @@ -316,7 +339,7 @@ main(int argc, /* I - Number of command-line args */ if ((auth_info_required = getenv("AUTH_INFO_REQUIRED")) == NULL) auth_info_required = "none"; - state_reasons = _cupsArrayNewStrings(getenv("PRINTER_STATE_REASONS")); + state_reasons = _cupsArrayNewStrings(getenv("PRINTER_STATE_REASONS"), ','); #ifdef HAVE_GSSAPI /* @@ -333,7 +356,7 @@ main(int argc, /* I - Number of command-line args */ if (uid > 0) { if (argc == 6) - return (run_as_user(argc, argv, uid, device_uri, 0)); + return (run_as_user(argv, uid, device_uri, 0)); else { int status = 0; /* Exit status */ @@ -342,7 +365,7 @@ main(int argc, /* I - Number of command-line args */ { if ((fd = open(argv[i], O_RDONLY)) >= 0) { - status = run_as_user(argc, argv, uid, device_uri, fd); + status = run_as_user(argv, uid, device_uri, fd); close(fd); } else @@ -452,39 +475,39 @@ main(int argc, /* I - Number of command-line args */ * Process the option... */ - if (!strcasecmp(name, "waitjob")) + if (!_cups_strcasecmp(name, "waitjob")) { /* * Wait for job completion? */ - waitjob = !strcasecmp(value, "on") || - !strcasecmp(value, "yes") || - !strcasecmp(value, "true"); + waitjob = !_cups_strcasecmp(value, "on") || + !_cups_strcasecmp(value, "yes") || + !_cups_strcasecmp(value, "true"); } - else if (!strcasecmp(name, "waitprinter")) + else if (!_cups_strcasecmp(name, "waitprinter")) { /* * Wait for printer idle? */ - waitprinter = !strcasecmp(value, "on") || - !strcasecmp(value, "yes") || - !strcasecmp(value, "true"); + waitprinter = !_cups_strcasecmp(value, "on") || + !_cups_strcasecmp(value, "yes") || + !_cups_strcasecmp(value, "true"); } - else if (!strcasecmp(name, "encryption")) + else if (!_cups_strcasecmp(name, "encryption")) { /* * Enable/disable encryption? */ - if (!strcasecmp(value, "always")) + if (!_cups_strcasecmp(value, "always")) cupsSetEncryption(HTTP_ENCRYPT_ALWAYS); - else if (!strcasecmp(value, "required")) + else if (!_cups_strcasecmp(value, "required")) cupsSetEncryption(HTTP_ENCRYPT_REQUIRED); - else if (!strcasecmp(value, "never")) + else if (!_cups_strcasecmp(value, "never")) cupsSetEncryption(HTTP_ENCRYPT_NEVER); - else if (!strcasecmp(value, "ifrequested")) + else if (!_cups_strcasecmp(value, "ifrequested")) cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED); else { @@ -493,7 +516,17 @@ main(int argc, /* I - Number of command-line args */ value); } } - else if (!strcasecmp(name, "version")) + else if (!_cups_strcasecmp(name, "snmp")) + { + /* + * Enable/disable SNMP stuff... + */ + + snmp_enabled = !value[0] || !_cups_strcasecmp(value, "on") || + _cups_strcasecmp(value, "yes") || + _cups_strcasecmp(value, "true"); + } + else if (!_cups_strcasecmp(name, "version")) { if (!strcmp(value, "1.0")) version = 10; @@ -513,14 +546,14 @@ main(int argc, /* I - Number of command-line args */ } } #ifdef HAVE_LIBZ - else if (!strcasecmp(name, "compression")) + else if (!_cups_strcasecmp(name, "compression")) { - if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || - !strcasecmp(value, "on") || !strcasecmp(value, "gzip")) + if (!_cups_strcasecmp(value, "true") || !_cups_strcasecmp(value, "yes") || + !_cups_strcasecmp(value, "on") || !_cups_strcasecmp(value, "gzip")) compression = "gzip"; } #endif /* HAVE_LIBZ */ - else if (!strcasecmp(name, "contimeout")) + else if (!_cups_strcasecmp(name, "contimeout")) { /* * Set the connection timeout... @@ -551,9 +584,10 @@ main(int argc, /* I - Number of command-line args */ if (argc == 6) { num_files = 0; - send_options = !strcasecmp(final_content_type, "application/pdf") || - !strcasecmp(final_content_type, "application/vnd.cups-pdf") || - !strncasecmp(final_content_type, "image/", 6); + files = NULL; + send_options = !_cups_strcasecmp(final_content_type, "application/pdf") || + !_cups_strcasecmp(final_content_type, "application/vnd.cups-pdf") || + !_cups_strncasecmp(final_content_type, "image/", 6); fputs("DEBUG: Sending stdin for job...\n", stderr); } @@ -579,12 +613,12 @@ main(int argc, /* I - Number of command-line args */ * Set the authentication info, if any... */ - cupsSetPasswordCB(password_cb); + cupsSetPasswordCB2(password_cb, NULL); if (username[0]) { /* - * Use authenticaion information in the device URI... + * Use authentication information in the device URI... */ if ((password = strchr(username, ':')) != NULL) @@ -601,7 +635,10 @@ main(int argc, /* I - Number of command-line args */ const char *ptr = getenv("AUTH_USERNAME"); if (ptr) + { + strlcpy(username, ptr, sizeof(username)); cupsSetUser(ptr); + } password = getenv("AUTH_PASSWORD"); } @@ -628,19 +665,26 @@ main(int argc, /* I - Number of command-line args */ update_reasons(NULL, "-connecting-to-device"); return (CUPS_BACKEND_STOP); } + + if (job_canceled) + return (CUPS_BACKEND_OK); } http = _httpCreate(hostname, port, addrlist, cupsEncryption(), AF_UNSPEC); + httpSetTimeout(http, 30.0, timeout_cb, NULL); /* * See if the printer supports SNMP... */ - if ((snmp_fd = _cupsSNMPOpen(addrlist->addr.addr.sa_family)) >= 0) - { + if (snmp_enabled) + snmp_fd = _cupsSNMPOpen(addrlist->addr.addr.sa_family); + else + snmp_fd = -1; + + if (snmp_fd >= 0) have_supplies = !backendSNMPSupplies(snmp_fd, &(addrlist->addr), &start_count, NULL); - } else have_supplies = start_count = 0; @@ -731,7 +775,7 @@ main(int argc, /* I - Number of command-line args */ case ECONNREFUSED : default : _cupsLangPrintFilter(stderr, "WARNING", - _("The printer is busy.")); + _("The printer is in use.")); break; } @@ -754,7 +798,9 @@ main(int argc, /* I - Number of command-line args */ } while (http->fd < 0); - if (job_canceled || !http) + if (job_canceled) + return (CUPS_BACKEND_OK); + else if (!http) return (CUPS_BACKEND_FAILED); update_reasons(NULL, "-connecting-to-device"); @@ -785,7 +831,6 @@ main(int argc, /* I - Number of command-line args */ supported = NULL; operations_sup = NULL; doc_handling_sup = NULL; - validate_job = 0; do { @@ -830,7 +875,9 @@ main(int argc, /* I - Number of command-line args */ fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n", ippErrorString(ipp_status), cupsLastErrorString()); - if (ipp_status > IPP_OK_CONFLICT) + if (ipp_status <= IPP_OK_CONFLICT) + password_tries = 0; + else { fprintf(stderr, "DEBUG: Get-Printer-Attributes returned %s.\n", ippErrorString(ipp_status)); @@ -845,7 +892,7 @@ main(int argc, /* I - Number of command-line args */ return (CUPS_BACKEND_FAILED); } - _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy.")); + _cupsLangPrintFilter(stderr, "INFO", _("The printer is in use.")); report_printer_state(supported); @@ -863,14 +910,14 @@ main(int argc, /* I - Number of command-line args */ if (version >= 20) { _cupsLangPrintFilter(stderr, "INFO", - _("Printer does not support IPP/%d.%d, trying " + _("The printer does not support IPP/%d.%d, trying " "IPP/%s."), version / 10, version % 10, "1.1"); version = 11; } else { _cupsLangPrintFilter(stderr, "INFO", - _("Printer does not support IPP/%d.%d, trying " + _("The printer does not support IPP/%d.%d, trying " "IPP/%s."), version / 10, version % 10, "1.0"); version = 10; } @@ -887,20 +934,27 @@ main(int argc, /* I - Number of command-line args */ return (CUPS_BACKEND_STOP); } - else if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN) + else if (ipp_status == IPP_FORBIDDEN || + ipp_status == IPP_AUTHENTICATION_CANCELED) { - if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE), - "Negotiate", 9)) + const char *www_auth = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE); + /* WWW-Authenticate field value */ + + if (!strncmp(www_auth, "Negotiate", 9)) auth_info_required = "negotiate"; + else if (www_auth[0]) + auth_info_required = "username,password"; fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required); return (CUPS_BACKEND_AUTH_REQUIRED); } - else + else if (ipp_status != IPP_NOT_AUTHORIZED) { _cupsLangPrintFilter(stderr, "ERROR", _("Unable to get printer status.")); sleep(10); + + httpReconnect(http); } ippDelete(supported); @@ -925,7 +979,7 @@ main(int argc, /* I - Number of command-line args */ else if (!printer_accepting) update_reasons(NULL, "+cups-ipp-conformance-failure-report," "cups-ipp-missing-printer-is-accepting-jobs"); - + if ((printer_state = ippFindAttribute(supported, "printer-state-reasons", IPP_TAG_KEYWORD)) != NULL && !busy) @@ -946,7 +1000,7 @@ main(int argc, /* I - Number of command-line args */ if (busy) { - _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy.")); + _cupsLangPrintFilter(stderr, "INFO", _("The printer is in use.")); report_printer_state(supported); @@ -1038,11 +1092,26 @@ main(int argc, /* I - Number of command-line args */ "cups-ipp-missing-get-printer-attributes"); for (i = 0; i < operations_sup->num_values; i ++) + { if (operations_sup->values[i].integer == IPP_VALIDATE_JOB) - { validate_job = 1; - break; - } + else if (operations_sup->values[i].integer == IPP_CREATE_JOB) + create_job = 1; + else if (operations_sup->values[i].integer == IPP_SEND_DOCUMENT) + send_document = 1; + else if (operations_sup->values[i].integer == IPP_GET_JOB_ATTRIBUTES) + get_job_attrs = 1; + } + + if (create_job && !send_document) + { + fputs("DEBUG: Printer supports Create-Job but not Send-Document.\n", + stderr); + create_job = 0; + + update_reasons(NULL, "+cups-ipp-conformance-failure-report," + "cups-ipp-missing-send-document"); + } if (!validate_job) update_reasons(NULL, "+cups-ipp-conformance-failure-report," @@ -1058,7 +1127,10 @@ main(int argc, /* I - Number of command-line args */ report_printer_state(supported); } - while (ipp_status > IPP_OK_CONFLICT); + while (!job_canceled && ipp_status > IPP_OK_CONFLICT); + + if (job_canceled) + return (CUPS_BACKEND_OK); /* * See if the printer is accepting jobs and is not stopped; if either @@ -1109,12 +1181,7 @@ main(int argc, /* I - Number of command-line args */ copies = atoi(argv[4]); if (copies_sup || argc < 7) - { copies_remaining = 1; - - if (argc < 7 && !send_options) - copies = 1; - } else copies_remaining = copies; @@ -1149,7 +1216,8 @@ main(int argc, /* I - Number of command-line args */ if (format_sup != NULL) { for (i = 0; i < format_sup->num_values; i ++) - if (!strcasecmp(final_content_type, format_sup->values[i].string.text)) + if (!_cups_strcasecmp(final_content_type, + format_sup->values[i].string.text)) { document_format = final_content_type; break; @@ -1158,8 +1226,8 @@ main(int argc, /* I - Number of command-line args */ if (!document_format) { for (i = 0; i < format_sup->num_values; i ++) - if (!strcasecmp("application/octet-stream", - format_sup->values[i].string.text)) + if (!_cups_strcasecmp("application/octet-stream", + format_sup->values[i].string.text)) { document_format = "application/octet-stream"; break; @@ -1167,6 +1235,9 @@ main(int argc, /* I - Number of command-line args */ } } + fprintf(stderr, "DEBUG: final_content_type=\"%s\", document_format=\"%s\"\n", + final_content_type, document_format ? document_format : "(null)"); + /* * If the printer does not support HTTP/1.1 (which IPP requires), copy stdin * to a temporary file so that we can do a HTTP/1.0 submission... @@ -1184,8 +1255,17 @@ main(int argc, /* I - Number of command-line args */ _cupsLangPrintFilter(stderr, "INFO", _("Copying print data.")); - compatsize = backendRunLoop(-1, fd, snmp_fd, &(addrlist->addr), 0, 0, - backendNetworkSideCB); + if ((compatsize = write(fd, buffer, bytes)) < 0) + { + perror("DEBUG: Unable to write temporary file"); + return (CUPS_BACKEND_FAILED); + } + + if ((bytes = backendRunLoop(-1, fd, snmp_fd, &(addrlist->addr), 0, 0, + backendNetworkSideCB)) < 0) + return (CUPS_BACKEND_FAILED); + + compatsize += bytes; close(fd); @@ -1201,6 +1281,16 @@ main(int argc, /* I - Number of command-line args */ compatsize = fileinfo.st_size; } + /* + * If the printer only claims to support IPP/1.0, or if the user specifically + * included version=1.0 in the URI, then do not try to use Create-Job or + * Send-Document. This is another dreaded compatibility hack, but unfortunately + * there are enough broken printers out there that we need this for now... + */ + + if (version == 10) + create_job = send_document = 0; + /* * Start monitoring the printer in the background... */ @@ -1212,10 +1302,22 @@ main(int argc, /* I - Number of command-line args */ monitor.port = port; monitor.version = version; monitor.job_id = 0; + monitor.get_job_attrs = get_job_attrs; monitor.encryption = cupsEncryption(); monitor.job_state = IPP_JOB_PENDING; monitor.printer_state = IPP_PRINTER_IDLE; + if (create_job) + { + monitor.job_name = argv[3]; + } + else + { + snprintf(print_job_name, sizeof(print_job_name), "%s - %s", argv[1], + argv[3]); + monitor.job_name = print_job_name; + } + _cupsThreadCreate((_cups_thread_func_t)monitor_printer, &monitor); /* @@ -1224,8 +1326,8 @@ main(int argc, /* I - Number of command-line args */ while (!job_canceled && validate_job) { - request = new_request(IPP_VALIDATE_JOB, version, uri, argv[2], argv[3], - num_options, options, compression, + request = new_request(IPP_VALIDATE_JOB, version, uri, argv[2], + monitor.job_name, num_options, options, compression, copies_sup ? copies : 1, document_format, pc, media_col_sup, doc_handling_sup); @@ -1241,29 +1343,21 @@ main(int argc, /* I - Number of command-line args */ if (ipp_status == IPP_SERVICE_UNAVAILABLE || ipp_status == IPP_PRINTER_BUSY) { - _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy.")); + _cupsLangPrintFilter(stderr, "INFO", _("The printer is in use.")); sleep(10); } - else if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN || + else if (ipp_status == IPP_DOCUMENT_FORMAT) + goto cleanup; + else if (ipp_status == IPP_FORBIDDEN || ipp_status == IPP_AUTHENTICATION_CANCELED) { - /* - * Update auth-info-required as needed... - */ - - fprintf(stderr, "DEBUG: WWW-Authenticate=\"%s\"\n", - httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE)); + const char *www_auth = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE); + /* WWW-Authenticate field value */ - /* - * Normal authentication goes through the password callback, which sets - * auth_info_required to "username,password". Kerberos goes directly - * through GSSAPI, so look for Negotiate in the WWW-Authenticate header - * here and set auth_info_required as needed... - */ - - if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE), - "Negotiate", 9)) + if (!strncmp(www_auth, "Negotiate", 9)) auth_info_required = "negotiate"; + else if (www_auth[0]) + auth_info_required = "username,password"; goto cleanup; } @@ -1277,7 +1371,8 @@ main(int argc, /* I - Number of command-line args */ "cups-ipp-missing-validate-job"); break; } - else if (ipp_status < IPP_REDIRECTION_OTHER_SITE) + else if (ipp_status < IPP_REDIRECTION_OTHER_SITE || + ipp_status == IPP_BAD_REQUEST) break; } @@ -1302,16 +1397,17 @@ main(int argc, /* I - Number of command-line args */ if (job_canceled) break; - request = new_request(num_files > 1 ? IPP_CREATE_JOB : IPP_PRINT_JOB, - version, uri, argv[2], argv[3], num_options, options, - compression, copies_sup ? copies : 1, document_format, - pc, media_col_sup, doc_handling_sup); + request = new_request((num_files > 1 || create_job) ? IPP_CREATE_JOB : + IPP_PRINT_JOB, + version, uri, argv[2], monitor.job_name, num_options, + options, compression, copies_sup ? copies : 1, + document_format, pc, media_col_sup, doc_handling_sup); /* * Do the request... */ - if (num_files > 1) + if (num_files > 1 || create_job) response = cupsDoRequest(http, request, resource); else { @@ -1329,14 +1425,21 @@ main(int argc, /* I - Number of command-line args */ if (http_status == HTTP_CONTINUE && request->state == IPP_DATA) { if (num_files == 1) - fd = open(files[0], O_RDONLY); + { + if ((fd = open(files[0], O_RDONLY)) < 0) + { + _cupsLangPrintError("ERROR", _("Unable to open print file")); + return (CUPS_BACKEND_FAILED); + } + } else { fd = 0; http_status = cupsWriteRequestData(http, buffer, bytes); } - while (http_status == HTTP_CONTINUE) + while (http_status == HTTP_CONTINUE && + (!job_canceled || compatsize > 0)) { /* * Check for side-channel requests and more print data... @@ -1377,7 +1480,7 @@ main(int argc, /* I - Number of command-line args */ ipp_status = cupsLastError(); fprintf(stderr, "DEBUG: %s: %s (%s)\n", - num_files > 1 ? "Create-Job" : "Print-Job", + (num_files > 1 || create_job) ? "Create-Job" : "Print-Job", ippErrorString(ipp_status), cupsLastErrorString()); if (ipp_status > IPP_OK_CONFLICT) @@ -1388,9 +1491,10 @@ main(int argc, /* I - Number of command-line args */ break; if (ipp_status == IPP_SERVICE_UNAVAILABLE || + ipp_status == IPP_NOT_POSSIBLE || ipp_status == IPP_PRINTER_BUSY) { - _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy.")); + _cupsLangPrintFilter(stderr, "INFO", _("The printer is in use.")); sleep(10); if (num_files == 0) @@ -1403,6 +1507,9 @@ main(int argc, /* I - Number of command-line args */ goto cleanup; } } + else if (ipp_status == IPP_ERROR_JOB_CANCELED || + ipp_status == IPP_NOT_AUTHORIZED) + goto cleanup; else { /* @@ -1412,21 +1519,24 @@ main(int argc, /* I - Number of command-line args */ _cupsLangPrintFilter(stderr, "ERROR", _("Print file was not accepted.")); - if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN) + if (ipp_status == IPP_FORBIDDEN || + ipp_status == IPP_AUTHENTICATION_CANCELED) { - fprintf(stderr, "DEBUG: WWW-Authenticate=\"%s\"\n", - httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE)); + const char *www_auth = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE); + /* WWW-Authenticate field value */ - /* - * Normal authentication goes through the password callback, which sets - * auth_info_required to "username,password". Kerberos goes directly - * through GSSAPI, so look for Negotiate in the WWW-Authenticate header - * here and set auth_info_required as needed... + if (!strncmp(www_auth, "Negotiate", 9)) + auth_info_required = "negotiate"; + else if (www_auth[0]) + auth_info_required = "username,password"; + } + else if (ipp_status == IPP_REQUEST_VALUE) + { + /* + * Print file is too large, abort this job... */ - if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE), - "Negotiate", 9)) - auth_info_required = "negotiate"; + goto cleanup; } else sleep(10); @@ -1453,19 +1563,21 @@ main(int argc, /* I - Number of command-line args */ } else { + password_tries = 0; monitor.job_id = job_id = job_id_attr->values[0].integer; _cupsLangPrintFilter(stderr, "INFO", _("Print file accepted - job ID %d."), job_id); } + fprintf(stderr, "DEBUG: job-id=%d\n", job_id); ippDelete(response); if (job_canceled) break; - if (job_id && num_files > 1) + if (job_id && (num_files > 1 || create_job)) { - for (i = 0; i < num_files; i ++) + for (i = 0; num_files == 0 || i < num_files; i ++) { /* * Check for side-channel requests... @@ -1491,20 +1603,45 @@ main(int argc, /* I - Number of command-line args */ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, argv[2]); - if ((i + 1) == num_files) + if ((i + 1) >= num_files) ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, - "document-format", NULL, content_type); + if (document_format) + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, + "document-format", NULL, document_format); + + if (compression) + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, + "compression", NULL, compression); fprintf(stderr, "DEBUG: Sending file %d using chunking...\n", i + 1); http_status = cupsSendRequest(http, request, resource, 0); - if (http_status == HTTP_CONTINUE && request->state == IPP_DATA && - (fd = open(files[i], O_RDONLY)) >= 0) + if (http_status == HTTP_CONTINUE && request->state == IPP_DATA) { - while ((bytes = read(fd, buffer, sizeof(buffer))) > 0) + if (num_files == 0) + { + fd = 0; + http_status = cupsWriteRequestData(http, buffer, bytes); + } + else { - if (cupsWriteRequestData(http, buffer, bytes) != HTTP_CONTINUE) + if ((fd = open(files[i], O_RDONLY)) < 0) + { + _cupsLangPrintError("ERROR", _("Unable to open print file")); + return (CUPS_BACKEND_FAILED); + } + } + } + else + fd = -1; + + if (fd >= 0) + { + while (!job_canceled && http_status == HTTP_CONTINUE && + (bytes = read(fd, buffer, sizeof(buffer))) > 0) + { + if ((http_status = cupsWriteRequestData(http, buffer, bytes)) + != HTTP_CONTINUE) break; else { @@ -1516,7 +1653,8 @@ main(int argc, /* I - Number of command-line args */ } } - close(fd); + if (fd > 0) + close(fd); } ippDelete(cupsGetResponse(http, resource)); @@ -1533,6 +1671,13 @@ main(int argc, /* I - Number of command-line args */ _("Unable to add document to print job.")); break; } + else + { + password_tries = 0; + + if (num_files == 0 || fd < 0) + break; + } } } @@ -1542,8 +1687,39 @@ main(int argc, /* I - Number of command-line args */ copies_remaining --; } else if (ipp_status == IPP_SERVICE_UNAVAILABLE || + ipp_status == IPP_NOT_POSSIBLE || ipp_status == IPP_PRINTER_BUSY) continue; + else if (ipp_status == IPP_REQUEST_VALUE || + ipp_status == IPP_ERROR_JOB_CANCELED || + ipp_status == IPP_NOT_AUTHORIZED || + ipp_status == IPP_INTERNAL_ERROR) + { + /* + * Print file is too large, job was canceled, we need new + * authentication data, or we had some sort of error... + */ + + goto cleanup; + } + else if (ipp_status == IPP_NOT_FOUND) + { + /* + * Printer does not actually implement support for Create-Job/ + * Send-Document, so log the conformance issue and stop the printer. + */ + + fputs("DEBUG: This printer claims to support Create-Job and " + "Send-Document, but those operations failed.\n", stderr); + fputs("DEBUG: Add '?version=1.0' to the device URI to use legacy " + "compatibility mode.\n", stderr); + update_reasons(NULL, "+cups-ipp-conformance-failure-report," + "cups-ipp-missing-send-document"); + + ipp_status = IPP_INTERNAL_ERROR; /* Force queue to stop */ + + goto cleanup; + } else copies_remaining --; @@ -1551,7 +1727,7 @@ main(int argc, /* I - Number of command-line args */ * Wait for the job to complete... */ - if (!job_id || !waitjob) + if (!job_id || !waitjob || !get_job_attrs) continue; _cupsLangPrintFilter(stderr, "INFO", _("Waiting for job to complete.")); @@ -1594,7 +1770,7 @@ main(int argc, /* I - Number of command-line args */ response = cupsDoRequest(http, request, resource); ipp_status = cupsLastError(); - if (ipp_status == IPP_NOT_FOUND) + if (ipp_status == IPP_NOT_FOUND || ipp_status == IPP_NOT_POSSIBLE) { /* * Job has gone away and/or the server has no job history... @@ -1611,15 +1787,15 @@ main(int argc, /* I - Number of command-line args */ fprintf(stderr, "DEBUG: Get-Job-Attributes: %s (%s)\n", ippErrorString(ipp_status), cupsLastErrorString()); - if (ipp_status > IPP_OK_CONFLICT) + if (ipp_status <= IPP_OK_CONFLICT) + password_tries = 0; + else { if (ipp_status != IPP_SERVICE_UNAVAILABLE && ipp_status != IPP_PRINTER_BUSY) { ippDelete(response); - - _cupsLangPrintFilter(stderr, "ERROR", - _("Unable to get print job status.")); + ipp_status = IPP_OK; break; } } @@ -1661,7 +1837,9 @@ main(int argc, /* I - Number of command-line args */ break; } } - else + else if (ipp_status != IPP_SERVICE_UNAVAILABLE && + ipp_status != IPP_NOT_POSSIBLE && + ipp_status != IPP_PRINTER_BUSY) { /* * If the printer does not return a job-state attribute, it does not @@ -1692,7 +1870,7 @@ main(int argc, /* I - Number of command-line args */ * Cancel the job as needed... */ - if (job_canceled && job_id) + if (job_canceled > 0 && job_id > 0) cancel_job(http, uri, job_id, resource, argv[2], version); /* @@ -1761,16 +1939,25 @@ main(int argc, /* I - Number of command-line args */ return (CUPS_BACKEND_AUTH_REQUIRED); else if (ipp_status == IPP_INTERNAL_ERROR) return (CUPS_BACKEND_STOP); - else if (ipp_status == IPP_DOCUMENT_FORMAT || - ipp_status == IPP_CONFLICT) + else if (ipp_status == IPP_CONFLICT) return (CUPS_BACKEND_FAILED); - else if (ipp_status > IPP_OK_CONFLICT) + else if (ipp_status == IPP_REQUEST_VALUE || + ipp_status == IPP_DOCUMENT_FORMAT || job_canceled < 0) + { + if (ipp_status == IPP_REQUEST_VALUE) + _cupsLangPrintFilter(stderr, "ERROR", _("Print job too large.")); + else if (ipp_status == IPP_DOCUMENT_FORMAT) + _cupsLangPrintFilter(stderr, "ERROR", + _("Printer cannot print supplied content.")); + else + _cupsLangPrintFilter(stderr, "ERROR", _("Print job canceled at printer.")); + + return (CUPS_BACKEND_CANCEL); + } + else if (ipp_status > IPP_OK_CONFLICT && ipp_status != IPP_ERROR_JOB_CANCELED) return (CUPS_BACKEND_RETRY_CURRENT); else - { - _cupsLangPrintFilter(stderr, "INFO", _("Ready to print.")); return (CUPS_BACKEND_OK); - } } @@ -1866,6 +2053,9 @@ check_printer_state( fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n", ippErrorString(cupsLastError()), cupsLastErrorString()); + if (cupsLastError() <= IPP_OK_CONFLICT) + password_tries = 0; + /* * Return the printer-state value... */ @@ -1959,6 +2149,11 @@ monitor_printer( ipp_attribute_t *attr; /* Attribute in response */ int delay, /* Current delay */ prev_delay; /* Previous delay */ + ipp_op_t job_op; /* Operation to use */ + int job_id; /* Job ID */ + const char *job_name; /* Job name */ + ipp_jstate_t job_state; /* Job state */ + const char *job_user; /* Job originating user name */ /* @@ -1967,7 +2162,10 @@ monitor_printer( http = _httpCreate(monitor->hostname, monitor->port, NULL, monitor->encryption, AF_UNSPEC); - cupsSetPasswordCB(password_cb); + httpSetTimeout(http, 30.0, timeout_cb, NULL); + if (username[0]) + cupsSetUser(username); + cupsSetPasswordCB2(password_cb, NULL); /* * Loop until the job is canceled, aborted, or completed. @@ -1992,47 +2190,108 @@ monitor_printer( monitor->user, monitor->version); - if (monitor->job_id > 0) - { - /* - * Check the status of the job itself... - */ + /* + * Check the status of the job itself... + */ - request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES); - request->request.op.version[0] = monitor->version / 10; - request->request.op.version[1] = monitor->version % 10; + job_op = (monitor->job_id > 0 && monitor->get_job_attrs) ? + IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS; + request = ippNewRequest(job_op); + request->request.op.version[0] = monitor->version / 10; + request->request.op.version[1] = monitor->version % 10; - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", - NULL, monitor->uri); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", + NULL, monitor->uri); + if (job_op == IPP_GET_JOB_ATTRIBUTES) ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", - monitor->job_id); + monitor->job_id); - if (monitor->user && monitor->user[0]) - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, - "requesting-user-name", NULL, monitor->user); + if (monitor->user && monitor->user[0]) + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, + "requesting-user-name", NULL, monitor->user); - ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, - "requested-attributes", - (int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs); + ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, + "requested-attributes", + (int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs); - /* - * Do the request... - */ + /* + * Do the request... + */ - response = cupsDoRequest(http, request, monitor->resource); + response = cupsDoRequest(http, request, monitor->resource); - fprintf(stderr, "DEBUG: Get-Job-Attributes: %s (%s)\n", - ippErrorString(cupsLastError()), cupsLastErrorString()); + fprintf(stderr, "DEBUG: (monitor) %s: %s (%s)\n", ippOpString(job_op), + ippErrorString(cupsLastError()), cupsLastErrorString()); + + if (cupsLastError() <= IPP_OK_CONFLICT) + password_tries = 0; + if (job_op == IPP_GET_JOB_ATTRIBUTES) + { if ((attr = ippFindAttribute(response, "job-state", IPP_TAG_ENUM)) != NULL) monitor->job_state = (ipp_jstate_t)attr->values[0].integer; else monitor->job_state = IPP_JOB_COMPLETED; + } + else if (response) + { + for (attr = response->attrs; attr; attr = attr->next) + { + job_id = 0; + job_name = NULL; + job_state = IPP_JOB_PENDING; + job_user = NULL; + + while (attr && attr->group_tag != IPP_TAG_JOB) + attr = attr->next; - ippDelete(response); + if (!attr) + break; + + while (attr && attr->group_tag == IPP_TAG_JOB) + { + if (!strcmp(attr->name, "job-id") && + attr->value_tag == IPP_TAG_INTEGER) + job_id = attr->values[0].integer; + else if (!strcmp(attr->name, "job-name") && + (attr->value_tag == IPP_TAG_NAME || + attr->value_tag == IPP_TAG_NAMELANG)) + job_name = attr->values[0].string.text; + else if (!strcmp(attr->name, "job-state") && + attr->value_tag == IPP_TAG_ENUM) + job_state = attr->values[0].integer; + else if (!strcmp(attr->name, "job-originating-user-name") && + (attr->value_tag == IPP_TAG_NAME || + attr->value_tag == IPP_TAG_NAMELANG)) + job_user = attr->values[0].string.text; + + attr = attr->next; + } + + if (job_id > 0 && job_name && !strcmp(job_name, monitor->job_name) && + job_user && monitor->user && !strcmp(job_user, monitor->user)) + { + monitor->job_id = job_id; + monitor->job_state = job_state; + break; + } + + if (!attr) + break; + } } + ippDelete(response); + + fprintf(stderr, "DEBUG: (monitor) job-state=%s\n", + ippEnumString("job-state", monitor->job_state)); + + if (!job_canceled && + (monitor->job_state == IPP_JOB_CANCELED || + monitor->job_state == IPP_JOB_ABORTED)) + job_canceled = -1; + /* * Disconnect from the printer - we'll reconnect on the next poll... */ @@ -2049,6 +2308,15 @@ monitor_printer( delay = _cupsNextDelay(delay, &prev_delay); } + /* + * Cancel the job if necessary... + */ + + if (job_canceled > 0 && monitor->job_id > 0) + if (!httpReconnect(http)) + cancel_job(http, monitor->uri, monitor->job_id, monitor->resource, + monitor->user, monitor->version); + /* * Cleanup and return... */ @@ -2074,7 +2342,7 @@ new_request( cups_option_t *options, /* I - Options to send */ const char *compression, /* I - compression value or NULL */ int copies, /* I - copies value or 0 */ - const char *format, /* I - documet-format value or NULL */ + const char *format, /* I - document-format value or NULL */ _ppd_cache_t *pc, /* I - PPD cache and mapping data */ ipp_attribute_t *media_col_sup, /* I - media-col-supported values */ ipp_attribute_t *doc_handling_sup) /* I - multiple-document-handling-supported values */ @@ -2087,7 +2355,8 @@ new_request( *media_size; /* media-size value */ const char *media_source, /* media-source value */ *media_type, /* media-type value */ - *collate_str; /* multiple-document-handling value */ + *collate_str, /* multiple-document-handling value */ + *mandatory; /* Mandatory attributes */ /* @@ -2125,7 +2394,7 @@ new_request( fprintf(stderr, "DEBUG: job-name=\"%s\"\n", title); } - if (format) + if (format && op != IPP_CREATE_JOB) { ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, format); @@ -2133,7 +2402,7 @@ new_request( } #ifdef HAVE_LIBZ - if (compression) + if (compression && op != IPP_CREATE_JOB) { ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "compression", NULL, compression); @@ -2149,10 +2418,92 @@ new_request( { if (pc) { + int num_finishings = 0, /* Number of finishing values */ + finishings[10]; /* Finishing enum values */ + /* * Send standard IPP attributes... */ + if (pc->password && + (keyword = cupsGetOption("job-password", num_options, + options)) != NULL) + { + ippAddOctetString(request, IPP_TAG_OPERATION, "job-password", + keyword, strlen(keyword)); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, + "job-password-encryption", NULL, "none"); + } + + if (pc->account_id && + (keyword = cupsGetOption("job-account-id", num_options, + options)) != NULL) + ippAddString(request, IPP_TAG_JOB, IPP_TAG_NAME, "job-account-id", + NULL, keyword); + + if (pc->account_id && + (keyword = cupsGetOption("job-accounting-user-id", num_options, + options)) != NULL) + ippAddString(request, IPP_TAG_JOB, IPP_TAG_NAME, + "job-accounting-user-id", NULL, keyword); + + for (mandatory = (char *)cupsArrayFirst(pc->mandatory); + mandatory; + mandatory = (char *)cupsArrayNext(pc->mandatory)) + { + if (strcmp(mandatory, "copies") && + strcmp(mandatory, "destination-uris") && + strcmp(mandatory, "finishings") && + strcmp(mandatory, "job-account-id") && + strcmp(mandatory, "job-accounting-user-id") && + strcmp(mandatory, "job-password") && + strcmp(mandatory, "media") && + strncmp(mandatory, "media-col", 9) && + strcmp(mandatory, "multiple-document-handling") && + strcmp(mandatory, "output-bin") && + strcmp(mandatory, "print-color-mode") && + strcmp(mandatory, "print-quality") && + strcmp(mandatory, "sides") && + (keyword = cupsGetOption(mandatory, num_options, options)) != NULL) + { + _ipp_option_t *opt = _ippFindOption(mandatory); + /* Option type */ + ipp_tag_t value_tag = opt ? opt->value_tag : IPP_TAG_NAME; + /* Value type */ + + switch (value_tag) + { + case IPP_TAG_INTEGER : + case IPP_TAG_ENUM : + ippAddInteger(request, IPP_TAG_JOB, value_tag, mandatory, + atoi(keyword)); + break; + case IPP_TAG_BOOLEAN : + ippAddBoolean(request, IPP_TAG_JOB, mandatory, + !_cups_strcasecmp(keyword, "true")); + break; + case IPP_TAG_RANGE : + { + int lower, upper; /* Range */ + + if (sscanf(keyword, "%d-%d", &lower, &upper) != 2) + lower = upper = atoi(keyword); + + ippAddRange(request, IPP_TAG_JOB, mandatory, lower, upper); + } + break; + case IPP_TAG_STRING : + ippAddOctetString(request, IPP_TAG_JOB, mandatory, keyword, + strlen(keyword)); + break; + default : + ippAddString(request, IPP_TAG_JOB, value_tag, mandatory, + NULL, keyword); + break; + } + } + } + if ((keyword = cupsGetOption("PageSize", num_options, options)) == NULL) keyword = cupsGetOption("media", num_options, options); @@ -2187,15 +2538,15 @@ new_request( else if (!strcmp(media_col_sup->values[i].string.text, "media-bottom-margin")) ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, - "media-bottom-margin", size->left); + "media-bottom-margin", size->bottom); else if (!strcmp(media_col_sup->values[i].string.text, "media-right-margin")) ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, - "media-right-margin", size->left); + "media-right-margin", size->right); else if (!strcmp(media_col_sup->values[i].string.text, "media-top-margin")) ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER, - "media-top-margin", size->left); + "media-top-margin", size->top); else if (!strcmp(media_col_sup->values[i].string.text, "media-source") && media_source) ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD, @@ -2218,19 +2569,19 @@ new_request( ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-bin", NULL, keyword); - if ((keyword = cupsGetOption("output-mode", num_options, + if ((keyword = cupsGetOption("print-color-mode", num_options, options)) != NULL) - ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode", + ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "print-color-mode", NULL, keyword); else if ((keyword = cupsGetOption("ColorModel", num_options, options)) != NULL) { - if (!strcasecmp(keyword, "Gray")) - ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode", - NULL, "monochrome"); + if (!_cups_strcasecmp(keyword, "Gray")) + ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, + "print-color-mode", NULL, "monochrome"); else - ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-mode", - NULL, "color"); + ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, + "print-color-mode", NULL, "color"); } if ((keyword = cupsGetOption("print-quality", num_options, @@ -2240,13 +2591,13 @@ new_request( else if ((keyword = cupsGetOption("cupsPrintQuality", num_options, options)) != NULL) { - if (!strcasecmp(keyword, "draft")) + if (!_cups_strcasecmp(keyword, "draft")) ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", IPP_QUALITY_DRAFT); - else if (!strcasecmp(keyword, "normal")) + else if (!_cups_strcasecmp(keyword, "normal")) ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", IPP_QUALITY_NORMAL); - else if (!strcasecmp(keyword, "high")) + else if (!_cups_strcasecmp(keyword, "high")) ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality", IPP_QUALITY_HIGH); } @@ -2258,21 +2609,59 @@ new_request( (keyword = cupsGetOption(pc->sides_option, num_options, options)) != NULL) { - if (!strcasecmp(keyword, pc->sides_1sided)) + if (!_cups_strcasecmp(keyword, pc->sides_1sided)) ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, "one-sided"); - else if (!strcasecmp(keyword, pc->sides_2sided_long)) + else if (!_cups_strcasecmp(keyword, pc->sides_2sided_long)) ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, "two-sided-long-edge"); - if (!strcasecmp(keyword, pc->sides_2sided_short)) + if (!_cups_strcasecmp(keyword, pc->sides_2sided_short)) ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides", NULL, "two-sided-short-edge"); } - if (doc_handling_sup && - (keyword = cupsGetOption("collate", num_options, options)) != NULL) + if ((keyword = cupsGetOption("multiple-document-handling", + num_options, options)) != NULL) + { + if (strstr(keyword, "uncollated")) + keyword = "false"; + else + keyword = "true"; + } + else if ((keyword = cupsGetOption("collate", num_options, + options)) == NULL) + keyword = "true"; + + if (format) + { + if (!_cups_strcasecmp(format, "image/gif") || + !_cups_strcasecmp(format, "image/jp2") || + !_cups_strcasecmp(format, "image/jpeg") || + !_cups_strcasecmp(format, "image/png") || + !_cups_strcasecmp(format, "image/tiff") || + !_cups_strncasecmp(format, "image/x-", 8)) + { + /* + * Collation makes no sense for single page image formats... + */ + + keyword = "false"; + } + else if (!_cups_strncasecmp(format, "image/", 6) || + !_cups_strcasecmp(format, "application/vnd.cups-raster")) + { + /* + * Multi-page image formats will have copies applied by the upstream + * filters... + */ + + copies = 1; + } + } + + if (doc_handling_sup) { - if (!strcasecmp(keyword, "true")) + if (!_cups_strcasecmp(keyword, "true")) collate_str = "separate-documents-collated-copies"; else collate_str = "separate-documents-uncollated-copies"; @@ -2284,6 +2673,46 @@ new_request( "multiple-document-handling", NULL, collate_str); break; } + + if (i >= doc_handling_sup->num_values) + copies = 1; + } + + /* + * Map finishing options... + */ + + num_finishings = _ppdCacheGetFinishingValues(pc, num_options, options, + (int)(sizeof(finishings) / + sizeof(finishings[0])), + finishings); + if (num_finishings > 0) + ippAddIntegers(request, IPP_TAG_JOB, IPP_TAG_ENUM, "finishings", + num_finishings, finishings); + + /* + * Map FaxOut options... + */ + + if ((keyword = cupsGetOption("phone", num_options, options)) != NULL) + { + ipp_t *destination; /* destination collection */ + char tel_uri[1024]; /* tel: URI */ + + destination = ippNew(); + + httpAssembleURI(HTTP_URI_CODING_ALL, tel_uri, sizeof(tel_uri), "tel", + NULL, NULL, 0, keyword); + ippAddString(destination, IPP_TAG_JOB, IPP_TAG_URI, "destination-uri", + NULL, tel_uri); + + if ((keyword = cupsGetOption("faxPrefix", num_options, + options)) != NULL && *keyword) + ippAddString(destination, IPP_TAG_JOB, IPP_TAG_TEXT, + "pre-dial-string", NULL, keyword); + + ippAddCollection(request, IPP_TAG_JOB, "destination-uris", destination); + ippDelete(destination); } } else @@ -2292,10 +2721,10 @@ new_request( * When talking to another CUPS server, send all options... */ - cupsEncodeOptions(request, num_options, options); + cupsEncodeOptions2(request, num_options, options, IPP_TAG_JOB); } - if (copies > 1) + if (copies > 1 && (!pc || copies <= pc->max_copies)) ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies", copies); } @@ -2308,9 +2737,22 @@ new_request( */ static const char * /* O - Password */ -password_cb(const char *prompt) /* I - Prompt (not used) */ +password_cb(const char *prompt, /* I - Prompt (not used) */ + http_t *http, /* I - Connection */ + const char *method, /* I - Request method (not used) */ + const char *resource, /* I - Resource path (not used) */ + void *user_data) /* I - User data (not used) */ { + char def_username[HTTP_MAX_VALUE]; /* Default username */ + + + fprintf(stderr, "DEBUG: password_cb(prompt=\"%s\"), password=%p, " + "password_tries=%d\n", prompt, password, password_tries); + (void)prompt; + (void)method; + (void)resource; + (void)user_data; /* * Remember that we need to authenticate... @@ -2318,6 +2760,16 @@ password_cb(const char *prompt) /* I - Prompt (not used) */ auth_info_required = "username,password"; + if (httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "username", + def_username)) + { + char quoted[HTTP_MAX_VALUE * 2 + 4]; + /* Quoted string */ + + fprintf(stderr, "ATTR: auth-info-default=%s,\n", + quote_string(def_username, quoted, sizeof(quoted))); + } + if (password && *password && password_tries < 3) { password_tries ++; @@ -2335,6 +2787,56 @@ password_cb(const char *prompt) /* I - Prompt (not used) */ } +/* + * 'quote_string()' - Quote a string value. + */ + +static const char * /* O - Quoted string */ +quote_string(const char *s, /* I - String */ + char *q, /* I - Quoted string buffer */ + size_t qsize) /* I - Size of quoted string buffer */ +{ + char *qptr, /* Pointer into string buffer */ + *qend; /* End of string buffer */ + + + qptr = q; + qend = q + qsize - 5; + + if (qend < q) + { + *q = '\0'; + return (q); + } + + *qptr++ = '\''; + *qptr++ = '\"'; + + while (*s && qptr < qend) + { + if (*s == '\\' || *s == '\"' || *s == '\'') + { + if (q < (qend - 3)) + { + *qptr++ = '\\'; + *qptr++ = '\\'; + *qptr++ = '\\'; + } + else + break; + } + + *qptr++ = *s++; + } + + *qptr++ = '\"'; + *qptr++ = '\''; + *qptr = '\0'; + + return (q); +} + + /* * 'report_attr()' - Report an IPP attribute value. */ @@ -2344,8 +2846,7 @@ report_attr(ipp_attribute_t *attr) /* I - Attribute */ { int i; /* Looping var */ char value[1024], /* Value string */ - *valptr, /* Pointer into value string */ - *attrptr; /* Pointer into attribute value */ + *valptr; /* Pointer into value string */ const char *cached; /* Cached attribute */ @@ -2372,17 +2873,9 @@ report_attr(ipp_attribute_t *attr) /* I - Attribute */ case IPP_TAG_TEXT : case IPP_TAG_NAME : case IPP_TAG_KEYWORD : - *valptr++ = '\"'; - for (attrptr = attr->values[i].string.text; - *attrptr && valptr < (value + sizeof(value) - 10); - attrptr ++) - { - if (*attrptr == '\\' || *attrptr == '\"') - *valptr++ = '\\'; - - *valptr++ = *attrptr; - } - *valptr++ = '\"'; + quote_string(attr->values[i].string.text, valptr, + value + sizeof(value) - valptr); + valptr += strlen(valptr); break; default : @@ -2496,7 +2989,7 @@ report_printer_state(ipp_t *ipp) /* I - IPP response */ if ((ppd = ppdOpenFile(getenv("PPD"))) != NULL && (ppdattr = ppdFindAttr(ppd, "cupsIPPSupplies", NULL)) != NULL && - ppdattr->value && strcasecmp(ppdattr->value, "true")) + ppdattr->value && _cups_strcasecmp(ppdattr->value, "true")) ipp_supplies = 0; else ipp_supplies = 1; @@ -2539,12 +3032,12 @@ report_printer_state(ipp_t *ipp) /* I - IPP response */ */ static int /* O - Exit status */ -run_as_user(int argc, /* I - Number of command-line args */ - char *argv[], /* I - Command-line arguments */ +run_as_user(char *argv[], /* I - Command-line arguments */ uid_t uid, /* I - User ID */ const char *device_uri, /* I - Device URI */ int fd) /* I - File to print */ { + const char *auth_negotiate;/* AUTH_NEGOTIATE env var */ xpc_connection_t conn; /* Connection to XPC service */ xpc_object_t request; /* Request message dictionary */ __block xpc_object_t response; /* Response message dictionary */ @@ -2605,6 +3098,8 @@ run_as_user(int argc, /* I - Number of command-line args */ xpc_dictionary_set_string(request, "options", argv[5]); xpc_dictionary_set_string(request, "auth-info-required", getenv("AUTH_INFO_REQUIRED")); + if ((auth_negotiate = getenv("AUTH_NEGOTIATE")) != NULL) + xpc_dictionary_set_string(request, "auth-negotiate", auth_negotiate); xpc_dictionary_set_fd(request, "stdin", fd); xpc_dictionary_set_fd(request, "stderr", 2); xpc_dictionary_set_fd(request, "side-channel", CUPS_SC_FD); @@ -2728,6 +3223,8 @@ sigterm_handler(int sig) /* I - Signal */ { (void)sig; /* remove compiler warnings... */ + write(2, "DEBUG: Got SIGTERM.\n", 20); + #if defined(HAVE_GSSAPI) && defined(HAVE_XPC) if (child_pid) { @@ -2742,6 +3239,8 @@ sigterm_handler(int sig) /* I - Signal */ * Flag that the job should be canceled... */ + write(2, "DEBUG: job_canceled = 1.\n", 25); + job_canceled = 1; return; } @@ -2758,6 +3257,21 @@ sigterm_handler(int sig) /* I - Signal */ } +/* + * 'timeout_cb()' - Handle HTTP timeouts. + */ + +static int /* O - 1 to continue, 0 to cancel */ +timeout_cb(http_t *http, /* I - Connection to server (unused) */ + void *user_data) /* I - User data (unused) */ +{ + (void)http; + (void)user_data; + + return (!job_canceled); +} + + /* * 'update_reasons()' - Update the printer-state-reasons values. */ @@ -2799,6 +3313,7 @@ update_reasons(ipp_attribute_t *attr, /* I - printer-state-reasons or NULL */ if (strcmp(reason, "none") && strcmp(reason, "none-report") && strcmp(reason, "paused") && + strncmp(reason, "spool-area-full", 15) && strcmp(reason, "com.apple.print.recoverable-warning") && strncmp(reason, "cups-", 5)) cupsArrayAdd(new_reasons, reason); @@ -2811,7 +3326,7 @@ update_reasons(ipp_attribute_t *attr, /* I - printer-state-reasons or NULL */ else op = '\0'; - new_reasons = _cupsArrayNewStrings(s); + new_reasons = _cupsArrayNewStrings(s, ','); } else return;