http_status_t /* O - @code HTTP_STATUS_CONTINUE@ if OK or HTTP status on error */ cupsWriteRequestData( http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ const char *buffer, /* I - Bytes to write */ size_t length) /* I - Number of bytes to write */ { int wused; /* Previous bytes in buffer */ /* * Get the default connection as needed... */ DEBUG_printf(("cupsWriteRequestData(http=%p, buffer=%p, " "length=" CUPS_LLFMT ")", http, buffer, CUPS_LLCAST length)); if (!http) { _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */ if ((http = cg->http) == NULL) { _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No active connection"), 1); DEBUG_puts("1cupsWriteRequestData: Returning HTTP_STATUS_ERROR."); return (HTTP_STATUS_ERROR); } } /* * Then write to the HTTP connection... */ wused = http->wused; if (httpWrite2(http, buffer, length) < 0) { DEBUG_puts("1cupsWriteRequestData: Returning HTTP_STATUS_ERROR."); _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(http->error), 0); return (HTTP_STATUS_ERROR); } /* * Finally, check if we have any pending data from the server... */ if (length >= HTTP_MAX_BUFFER || http->wused < wused || (wused > 0 && http->wused == length)) { /* * We've written something to the server, so check for response data... */ if (_httpWait(http, 0, 1)) { http_status_t status; /* Status from _httpUpdate */ _httpUpdate(http, &status); if (status >= HTTP_STATUS_MULTIPLE_CHOICES) { _cupsSetHTTPError(status); do { status = httpUpdate(http); } while (status != HTTP_STATUS_ERROR && http->state == HTTP_STATE_POST_RECV); httpFlush(http); } DEBUG_printf(("1cupsWriteRequestData: Returning %d.\n", status)); return (status); } } DEBUG_puts("1cupsWriteRequestData: Returning HTTP_STATUS_CONTINUE."); return (HTTP_STATUS_CONTINUE); }
ipp_t * /* O - Response data */ cupsDoIORequest(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ ipp_t *request, /* I - IPP request */ const char *resource, /* I - HTTP resource for POST */ int infile, /* I - File to read from or -1 for none */ int outfile) /* I - File to write to or -1 for none */ { ipp_t *response = NULL; /* IPP response data */ size_t length = 0; /* Content-Length value */ http_status_t status; /* Status of HTTP request */ struct stat fileinfo; /* File information */ int bytes; /* Number of bytes read/written */ char buffer[32768]; /* Output buffer */ DEBUG_printf(("cupsDoIORequest(http=%p, request=%p(%s), resource=\"%s\", " "infile=%d, outfile=%d)", http, request, request ? ippOpString(request->request.op.operation_id) : "?", resource, infile, outfile)); /* * Range check input... */ if (!request || !resource) { ippDelete(request); _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); return (NULL); } /* * Get the default connection as needed... */ if (!http) if ((http = _cupsConnect()) == NULL) { ippDelete(request); return (NULL); } /* * See if we have a file to send... */ if (infile >= 0) { if (fstat(infile, &fileinfo)) { /* * Can't get file information! */ _cupsSetError(errno == EBADF ? IPP_STATUS_ERROR_NOT_FOUND : IPP_STATUS_ERROR_NOT_AUTHORIZED, NULL, 0); ippDelete(request); return (NULL); } #ifdef WIN32 if (fileinfo.st_mode & _S_IFDIR) #else if (S_ISDIR(fileinfo.st_mode)) #endif /* WIN32 */ { /* * Can't send a directory... */ ippDelete(request); _cupsSetError(IPP_STATUS_ERROR_NOT_POSSIBLE, strerror(EISDIR), 0); return (NULL); } #ifndef WIN32 if (!S_ISREG(fileinfo.st_mode)) length = 0; /* Chunk when piping */ else #endif /* !WIN32 */ length = ippLength(request) + fileinfo.st_size; } else length = ippLength(request); DEBUG_printf(("2cupsDoIORequest: Request length=%ld, total length=%ld", (long)ippLength(request), (long)length)); /* * Clear any "Local" authentication data since it is probably stale... */ if (http->authstring && !strncmp(http->authstring, "Local ", 6)) httpSetAuthString(http, NULL, NULL); /* * Loop until we can send the request without authorization problems. */ while (response == NULL) { DEBUG_puts("2cupsDoIORequest: setup..."); /* * Send the request... */ status = cupsSendRequest(http, request, resource, length); DEBUG_printf(("2cupsDoIORequest: status=%d", status)); if (status == HTTP_STATUS_CONTINUE && request->state == IPP_STATE_DATA && infile >= 0) { DEBUG_puts("2cupsDoIORequest: file write..."); /* * Send the file with the request... */ #ifndef WIN32 if (S_ISREG(fileinfo.st_mode)) #endif /* WIN32 */ lseek(infile, 0, SEEK_SET); while ((bytes = (int)read(infile, buffer, sizeof(buffer))) > 0) { if ((status = cupsWriteRequestData(http, buffer, bytes)) != HTTP_STATUS_CONTINUE) break; } } /* * Get the server's response... */ if (status != HTTP_STATUS_ERROR) { response = cupsGetResponse(http, resource); status = httpGetStatus(http); } DEBUG_printf(("2cupsDoIORequest: status=%d", status)); if (status == HTTP_STATUS_ERROR || (status >= HTTP_STATUS_BAD_REQUEST && status != HTTP_STATUS_UNAUTHORIZED && status != HTTP_STATUS_UPGRADE_REQUIRED)) { _cupsSetHTTPError(status); break; } if (response && outfile >= 0) { /* * Write trailing data to file... */ while ((bytes = (int)httpRead2(http, buffer, sizeof(buffer))) > 0) if (write(outfile, buffer, bytes) < bytes) break; } if (http->state != HTTP_STATE_WAITING) { /* * Flush any remaining data... */ httpFlush(http); } } /* * Delete the original request and return the response... */ ippDelete(request); return (response); }
http_status_t /* O - Initial HTTP status */ cupsSendRequest(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ ipp_t *request, /* I - IPP request */ const char *resource, /* I - Resource path */ size_t length) /* I - Length of data to follow or @code CUPS_LENGTH_VARIABLE@ */ { http_status_t status; /* Status of HTTP request */ int got_status; /* Did we get the status? */ ipp_state_t state; /* State of IPP processing */ http_status_t expect; /* Expect: header to use */ DEBUG_printf(("cupsSendRequest(http=%p, request=%p(%s), resource=\"%s\", " "length=" CUPS_LLFMT ")", http, request, request ? ippOpString(request->request.op.operation_id) : "?", resource, CUPS_LLCAST length)); /* * Range check input... */ if (!request || !resource) { _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); return (HTTP_STATUS_ERROR); } /* * Get the default connection as needed... */ if (!http) if ((http = _cupsConnect()) == NULL) return (HTTP_STATUS_SERVICE_UNAVAILABLE); /* * If the prior request was not flushed out, do so now... */ if (http->state == HTTP_STATE_GET_SEND || http->state == HTTP_STATE_POST_SEND) { DEBUG_puts("2cupsSendRequest: Flush prior response."); httpFlush(http); } else if (http->state != HTTP_STATE_WAITING) { DEBUG_printf(("1cupsSendRequest: Unknown HTTP state (%d), " "reconnecting.", http->state)); if (httpReconnect2(http, 30000, NULL)) return (HTTP_STATUS_ERROR); } #ifdef HAVE_SSL /* * See if we have an auth-info attribute and are communicating over * a non-local link. If so, encrypt the link so that we can pass * the authentication information securely... */ if (ippFindAttribute(request, "auth-info", IPP_TAG_TEXT) && !httpAddrLocalhost(http->hostaddr) && !http->tls && httpEncryption(http, HTTP_ENCRYPTION_REQUIRED)) { DEBUG_puts("1cupsSendRequest: Unable to encrypt connection."); return (HTTP_STATUS_SERVICE_UNAVAILABLE); } #endif /* HAVE_SSL */ /* * Reconnect if the last response had a "Connection: close"... */ if (!_cups_strcasecmp(http->fields[HTTP_FIELD_CONNECTION], "close")) { DEBUG_puts("2cupsSendRequest: Connection: close"); httpClearFields(http); if (httpReconnect2(http, 30000, NULL)) { DEBUG_puts("1cupsSendRequest: Unable to reconnect."); return (HTTP_STATUS_SERVICE_UNAVAILABLE); } } /* * Loop until we can send the request without authorization problems. */ expect = HTTP_STATUS_CONTINUE; for (;;) { DEBUG_puts("2cupsSendRequest: Setup..."); /* * Setup the HTTP variables needed... */ httpClearFields(http); httpSetExpect(http, expect); httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp"); httpSetLength(http, length); #ifdef HAVE_GSSAPI if (http->authstring && !strncmp(http->authstring, "Negotiate", 9)) { /* * Do not use cached Kerberos credentials since they will look like a * "replay" attack... */ _cupsSetNegotiateAuthString(http, "POST", resource); } #endif /* HAVE_GSSAPI */ httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring); DEBUG_printf(("2cupsSendRequest: authstring=\"%s\"", http->authstring)); /* * Try the request... */ DEBUG_puts("2cupsSendRequest: Sending HTTP POST..."); if (httpPost(http, resource)) { DEBUG_puts("2cupsSendRequest: POST failed, reconnecting."); if (httpReconnect2(http, 30000, NULL)) { DEBUG_puts("1cupsSendRequest: Unable to reconnect."); return (HTTP_STATUS_SERVICE_UNAVAILABLE); } else continue; } /* * Send the IPP data... */ DEBUG_puts("2cupsSendRequest: Writing IPP request..."); request->state = IPP_STATE_IDLE; status = HTTP_STATUS_CONTINUE; got_status = 0; while ((state = ippWrite(http, request)) != IPP_STATE_DATA) if (state == IPP_STATE_ERROR) break; else if (httpCheck(http)) { got_status = 1; _httpUpdate(http, &status); if (status >= HTTP_STATUS_MULTIPLE_CHOICES) break; } if (state == IPP_STATE_ERROR) { DEBUG_puts("1cupsSendRequest: Unable to send IPP request."); http->status = HTTP_STATUS_ERROR; http->state = HTTP_STATE_WAITING; return (HTTP_STATUS_ERROR); } /* * Wait up to 1 second to get the 100-continue response as needed... */ if (!got_status) { if (expect == HTTP_STATUS_CONTINUE) { DEBUG_puts("2cupsSendRequest: Waiting for 100-continue..."); if (httpWait(http, 1000)) _httpUpdate(http, &status); } else if (httpCheck(http)) _httpUpdate(http, &status); } DEBUG_printf(("2cupsSendRequest: status=%d", status)); /* * Process the current HTTP status... */ if (status >= HTTP_STATUS_MULTIPLE_CHOICES) { int temp_status; /* Temporary status */ _cupsSetHTTPError(status); do { temp_status = httpUpdate(http); } while (temp_status != HTTP_STATUS_ERROR && http->state == HTTP_STATE_POST_RECV); httpFlush(http); } switch (status) { case HTTP_STATUS_CONTINUE : case HTTP_STATUS_OK : case HTTP_STATUS_ERROR : DEBUG_printf(("1cupsSendRequest: Returning %d.", status)); return (status); case HTTP_STATUS_UNAUTHORIZED : if (cupsDoAuthentication(http, "POST", resource)) { DEBUG_puts("1cupsSendRequest: Returning HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED."); return (HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED); } DEBUG_puts("2cupsSendRequest: Reconnecting after HTTP_STATUS_UNAUTHORIZED."); if (httpReconnect2(http, 30000, NULL)) { DEBUG_puts("1cupsSendRequest: Unable to reconnect."); return (HTTP_STATUS_SERVICE_UNAVAILABLE); } break; #ifdef HAVE_SSL case HTTP_STATUS_UPGRADE_REQUIRED : /* * Flush any error message, reconnect, and then upgrade with * encryption... */ DEBUG_puts("2cupsSendRequest: Reconnecting after " "HTTP_STATUS_UPGRADE_REQUIRED."); if (httpReconnect2(http, 30000, NULL)) { DEBUG_puts("1cupsSendRequest: Unable to reconnect."); return (HTTP_STATUS_SERVICE_UNAVAILABLE); } DEBUG_puts("2cupsSendRequest: Upgrading to TLS."); if (httpEncryption(http, HTTP_ENCRYPTION_REQUIRED)) { DEBUG_puts("1cupsSendRequest: Unable to encrypt connection."); return (HTTP_STATUS_SERVICE_UNAVAILABLE); } break; #endif /* HAVE_SSL */ case HTTP_STATUS_EXPECTATION_FAILED : /* * Don't try using the Expect: header the next time around... */ expect = (http_status_t)0; DEBUG_puts("2cupsSendRequest: Reconnecting after " "HTTP_EXPECTATION_FAILED."); if (httpReconnect2(http, 30000, NULL)) { DEBUG_puts("1cupsSendRequest: Unable to reconnect."); return (HTTP_STATUS_SERVICE_UNAVAILABLE); } break; default : /* * Some other error... */ return (status); } } }
http_status_t /* O - HTTP status */ cupsGetFd(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ const char *resource, /* I - Resource name */ int fd) /* I - File descriptor */ { ssize_t bytes; /* Number of bytes read */ char buffer[8192]; /* Buffer for file */ http_status_t status; /* HTTP status from server */ char if_modified_since[HTTP_MAX_VALUE]; /* If-Modified-Since header */ int new_auth = 0; /* Using new auth information? */ int digest; /* Are we using Digest authentication? */ /* * Range check input... */ DEBUG_printf(("cupsGetFd(http=%p, resource=\"%s\", fd=%d)", (void *)http, resource, fd)); if (!resource || fd < 0) { if (http) http->error = EINVAL; return (HTTP_STATUS_ERROR); } if (!http) if ((http = _cupsConnect()) == NULL) return (HTTP_STATUS_SERVICE_UNAVAILABLE); /* * Then send GET requests to the HTTP server... */ strlcpy(if_modified_since, httpGetField(http, HTTP_FIELD_IF_MODIFIED_SINCE), sizeof(if_modified_since)); do { if (!_cups_strcasecmp(httpGetField(http, HTTP_FIELD_CONNECTION), "close")) { httpClearFields(http); if (httpReconnect2(http, 30000, NULL)) { status = HTTP_STATUS_ERROR; break; } } httpClearFields(http); httpSetField(http, HTTP_FIELD_IF_MODIFIED_SINCE, if_modified_since); digest = http->authstring && !strncmp(http->authstring, "Digest ", 7); if (digest && !new_auth) { /* * Update the Digest authentication string... */ _httpSetDigestAuthString(http, http->nextnonce, "GET", resource); } #ifdef HAVE_GSSAPI if (http->authstring && !strncmp(http->authstring, "Negotiate", 9) && !new_auth) { /* * Do not use cached Kerberos credentials since they will look like a * "replay" attack... */ _cupsSetNegotiateAuthString(http, "GET", resource); } #endif /* HAVE_GSSAPI */ httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring); if (httpGet(http, resource)) { if (httpReconnect2(http, 30000, NULL)) { status = HTTP_STATUS_ERROR; break; } else { status = HTTP_STATUS_UNAUTHORIZED; continue; } } new_auth = 0; while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE); if (status == HTTP_STATUS_UNAUTHORIZED) { /* * Flush any error message... */ httpFlush(http); /* * See if we can do authentication... */ new_auth = 1; if (cupsDoAuthentication(http, "GET", resource)) { status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED; break; } if (httpReconnect2(http, 30000, NULL)) { status = HTTP_STATUS_ERROR; break; } continue; } #ifdef HAVE_SSL else if (status == HTTP_STATUS_UPGRADE_REQUIRED) { /* Flush any error message... */ httpFlush(http); /* Reconnect... */ if (httpReconnect2(http, 30000, NULL)) { status = HTTP_STATUS_ERROR; break; } /* Upgrade with encryption... */ httpEncryption(http, HTTP_ENCRYPTION_REQUIRED); /* Try again, this time with encryption enabled... */ continue; } #endif /* HAVE_SSL */ } while (status == HTTP_STATUS_UNAUTHORIZED || status == HTTP_STATUS_UPGRADE_REQUIRED); /* * See if we actually got the file or an error... */ if (status == HTTP_STATUS_OK) { /* * Yes, copy the file... */ while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0) write(fd, buffer, (size_t)bytes); } else { _cupsSetHTTPError(status); httpFlush(http); } /* * Return the request status... */ DEBUG_printf(("1cupsGetFd: Returning %d...", status)); return (status); }
ipp_status_t /* O - Request status - @code IPP_OK@ on success. */ cupsGetDevices( http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ int timeout, /* I - Timeout in seconds or @code CUPS_TIMEOUT_DEFAULT@ */ const char *include_schemes, /* I - Comma-separated URI schemes to include or @code CUPS_INCLUDE_ALL@ */ const char *exclude_schemes, /* I - Comma-separated URI schemes to exclude or @code CUPS_EXCLUDE_NONE@ */ cups_device_cb_t callback, /* I - Callback function */ void *user_data) /* I - User data pointer */ { ipp_t *request, /* CUPS-Get-Devices request */ *response; /* CUPS-Get-Devices response */ ipp_attribute_t *attr; /* Current attribute */ const char *device_class, /* device-class value */ *device_id, /* device-id value */ *device_info, /* device-info value */ *device_location, /* device-location value */ *device_make_and_model, /* device-make-and-model value */ *device_uri; /* device-uri value */ int blocking; /* Current blocking-IO mode */ cups_option_t option; /* in/exclude-schemes option */ http_status_t status; /* HTTP status of request */ ipp_state_t state; /* IPP response state */ /* * Range check input... */ DEBUG_printf(("cupsGetDevices(http=%p, timeout=%d, include_schemes=\"%s\", exclude_schemes=\"%s\", callback=%p, user_data=%p)", (void *)http, timeout, include_schemes, exclude_schemes, (void *)callback, user_data)); if (!callback) return (IPP_STATUS_ERROR_INTERNAL); if (!http) http = _cupsConnect(); if (!http) return (IPP_STATUS_ERROR_SERVICE_UNAVAILABLE); /* * Create a CUPS-Get-Devices request... */ request = ippNewRequest(IPP_OP_CUPS_GET_DEVICES); if (timeout > 0) ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "timeout", timeout); if (include_schemes) { option.name = "include-schemes"; option.value = (char *)include_schemes; cupsEncodeOptions2(request, 1, &option, IPP_TAG_OPERATION); } if (exclude_schemes) { option.name = "exclude-schemes"; option.value = (char *)exclude_schemes; cupsEncodeOptions2(request, 1, &option, IPP_TAG_OPERATION); } /* * Send the request and do any necessary authentication... */ do { DEBUG_puts("2cupsGetDevices: Sending request..."); status = cupsSendRequest(http, request, "/", ippLength(request)); DEBUG_puts("2cupsGetDevices: Waiting for response status..."); while (status == HTTP_STATUS_CONTINUE) status = httpUpdate(http); if (status != HTTP_STATUS_OK) { httpFlush(http); if (status == HTTP_STATUS_UNAUTHORIZED) { /* * See if we can do authentication... */ DEBUG_puts("2cupsGetDevices: Need authorization..."); if (!cupsDoAuthentication(http, "POST", "/")) httpReconnect2(http, 30000, NULL); else { status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED; break; } } #ifdef HAVE_SSL else if (status == HTTP_STATUS_UPGRADE_REQUIRED) { /* * Force a reconnect with encryption... */ DEBUG_puts("2cupsGetDevices: Need encryption..."); if (!httpReconnect2(http, 30000, NULL)) httpEncryption(http, HTTP_ENCRYPTION_REQUIRED); } #endif /* HAVE_SSL */ } } while (status == HTTP_STATUS_UNAUTHORIZED || status == HTTP_STATUS_UPGRADE_REQUIRED); DEBUG_printf(("2cupsGetDevices: status=%d", status)); ippDelete(request); if (status != HTTP_STATUS_OK) { _cupsSetHTTPError(status); return (cupsLastError()); } /* * Read the response in non-blocking mode... */ blocking = httpGetBlocking(http); httpBlocking(http, 0); response = ippNew(); device_class = NULL; device_id = NULL; device_info = NULL; device_location = ""; device_make_and_model = NULL; device_uri = NULL; attr = NULL; DEBUG_puts("2cupsGetDevices: Reading response..."); do { if ((state = ippRead(http, response)) == IPP_STATE_ERROR) break; DEBUG_printf(("2cupsGetDevices: state=%d, response->last=%p", state, (void *)response->last)); if (!response->attrs) continue; while (attr != response->last) { if (!attr) attr = response->attrs; else attr = attr->next; DEBUG_printf(("2cupsGetDevices: attr->name=\"%s\", attr->value_tag=%d", attr->name, attr->value_tag)); if (!attr->name) { if (device_class && device_id && device_info && device_make_and_model && device_uri) (*callback)(device_class, device_id, device_info, device_make_and_model, device_uri, device_location, user_data); device_class = NULL; device_id = NULL; device_info = NULL; device_location = ""; device_make_and_model = NULL; device_uri = NULL; } else if (!strcmp(attr->name, "device-class") && attr->value_tag == IPP_TAG_KEYWORD) device_class = attr->values[0].string.text; else if (!strcmp(attr->name, "device-id") && attr->value_tag == IPP_TAG_TEXT) device_id = attr->values[0].string.text; else if (!strcmp(attr->name, "device-info") && attr->value_tag == IPP_TAG_TEXT) device_info = attr->values[0].string.text; else if (!strcmp(attr->name, "device-location") && attr->value_tag == IPP_TAG_TEXT) device_location = attr->values[0].string.text; else if (!strcmp(attr->name, "device-make-and-model") && attr->value_tag == IPP_TAG_TEXT) device_make_and_model = attr->values[0].string.text; else if (!strcmp(attr->name, "device-uri") && attr->value_tag == IPP_TAG_URI) device_uri = attr->values[0].string.text; } } while (state != IPP_STATE_DATA); DEBUG_printf(("2cupsGetDevices: state=%d, response->last=%p", state, (void *)response->last)); if (device_class && device_id && device_info && device_make_and_model && device_uri) (*callback)(device_class, device_id, device_info, device_make_and_model, device_uri, device_location, user_data); /* * Set the IPP status and return... */ httpBlocking(http, blocking); httpFlush(http); if (status == HTTP_STATUS_ERROR) _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(http->error), 0); else { attr = ippFindAttribute(response, "status-message", IPP_TAG_TEXT); DEBUG_printf(("cupsGetDevices: status-code=%s, status-message=\"%s\"", ippErrorString(response->request.status.status_code), attr ? attr->values[0].string.text : "")); _cupsSetError(response->request.status.status_code, attr ? attr->values[0].string.text : ippErrorString(response->request.status.status_code), 0); } ippDelete(response); return (cupsLastError()); }
http_status_t /* O - HTTP status */ cupsPutFd(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ const char *resource, /* I - Resource name */ int fd) /* I - File descriptor */ { ssize_t bytes; /* Number of bytes read */ int retries; /* Number of retries */ char buffer[8192]; /* Buffer for file */ http_status_t status; /* HTTP status from server */ int new_auth = 0; /* Using new auth information? */ int digest; /* Are we using Digest authentication? */ /* * Range check input... */ DEBUG_printf(("cupsPutFd(http=%p, resource=\"%s\", fd=%d)", (void *)http, resource, fd)); if (!resource || fd < 0) { if (http) http->error = EINVAL; return (HTTP_STATUS_ERROR); } if (!http) if ((http = _cupsConnect()) == NULL) return (HTTP_STATUS_SERVICE_UNAVAILABLE); /* * Then send PUT requests to the HTTP server... */ retries = 0; do { if (!_cups_strcasecmp(httpGetField(http, HTTP_FIELD_CONNECTION), "close")) { httpClearFields(http); if (httpReconnect2(http, 30000, NULL)) { status = HTTP_STATUS_ERROR; break; } } DEBUG_printf(("2cupsPutFd: starting attempt, authstring=\"%s\"...", http->authstring)); httpClearFields(http); httpSetField(http, HTTP_FIELD_TRANSFER_ENCODING, "chunked"); httpSetExpect(http, HTTP_STATUS_CONTINUE); digest = http->authstring && !strncmp(http->authstring, "Digest ", 7); if (digest && !new_auth) { /* * Update the Digest authentication string... */ _httpSetDigestAuthString(http, http->nextnonce, "PUT", resource); } #ifdef HAVE_GSSAPI if (http->authstring && !strncmp(http->authstring, "Negotiate", 9) && !new_auth) { /* * Do not use cached Kerberos credentials since they will look like a * "replay" attack... */ _cupsSetNegotiateAuthString(http, "PUT", resource); } #endif /* HAVE_GSSAPI */ httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring); if (httpPut(http, resource)) { if (httpReconnect2(http, 30000, NULL)) { status = HTTP_STATUS_ERROR; break; } else { status = HTTP_STATUS_UNAUTHORIZED; continue; } } /* * Wait up to 1 second for a 100-continue response... */ if (httpWait(http, 1000)) status = httpUpdate(http); else status = HTTP_STATUS_CONTINUE; if (status == HTTP_STATUS_CONTINUE) { /* * Copy the file... */ lseek(fd, 0, SEEK_SET); while ((bytes = read(fd, buffer, sizeof(buffer))) > 0) if (httpCheck(http)) { if ((status = httpUpdate(http)) != HTTP_STATUS_CONTINUE) break; } else httpWrite2(http, buffer, (size_t)bytes); } if (status == HTTP_STATUS_CONTINUE) { httpWrite2(http, buffer, 0); while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE); } if (status == HTTP_STATUS_ERROR && !retries) { DEBUG_printf(("2cupsPutFd: retry on status %d", status)); retries ++; /* Flush any error message... */ httpFlush(http); /* Reconnect... */ if (httpReconnect2(http, 30000, NULL)) { status = HTTP_STATUS_ERROR; break; } /* Try again... */ continue; } DEBUG_printf(("2cupsPutFd: status=%d", status)); new_auth = 0; if (status == HTTP_STATUS_UNAUTHORIZED) { /* * Flush any error message... */ httpFlush(http); /* * See if we can do authentication... */ new_auth = 1; if (cupsDoAuthentication(http, "PUT", resource)) { status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED; break; } if (httpReconnect2(http, 30000, NULL)) { status = HTTP_STATUS_ERROR; break; } continue; } #ifdef HAVE_SSL else if (status == HTTP_STATUS_UPGRADE_REQUIRED) { /* Flush any error message... */ httpFlush(http); /* Reconnect... */ if (httpReconnect2(http, 30000, NULL)) { status = HTTP_STATUS_ERROR; break; } /* Upgrade with encryption... */ httpEncryption(http, HTTP_ENCRYPTION_REQUIRED); /* Try again, this time with encryption enabled... */ continue; } #endif /* HAVE_SSL */ } while (status == HTTP_STATUS_UNAUTHORIZED || status == HTTP_STATUS_UPGRADE_REQUIRED || (status == HTTP_STATUS_ERROR && retries < 2)); /* * See if we actually put the file or an error... */ if (status != HTTP_STATUS_CREATED) { _cupsSetHTTPError(status); httpFlush(http); } DEBUG_printf(("1cupsPutFd: Returning %d...", status)); return (status); }
http_status_t /* O - HTTP status */ cupsGetFd(http_t *http, /* I - HTTP connection to server */ const char *resource, /* I - Resource name */ int fd) /* I - File descriptor */ { int bytes; /* Number of bytes read */ char buffer[8192]; /* Buffer for file */ http_status_t status; /* HTTP status from server */ char if_modified_since[HTTP_MAX_VALUE]; /* If-Modified-Since header */ /* * Range check input... */ DEBUG_printf(("cupsGetFd(http=%p, resource=\"%s\", fd=%d)\n", http, resource, fd)); if (!http || !resource || fd < 0) { if (http) http->error = EINVAL; return (HTTP_ERROR); } /* * Then send GET requests to the HTTP server... */ strlcpy(if_modified_since, httpGetField(http, HTTP_FIELD_IF_MODIFIED_SINCE), sizeof(if_modified_since)); do { httpClearFields(http); httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring); httpSetField(http, HTTP_FIELD_IF_MODIFIED_SINCE, if_modified_since); if (httpGet(http, resource)) { if (httpReconnect(http)) { status = HTTP_ERROR; break; } else { status = HTTP_UNAUTHORIZED; continue; } } while ((status = httpUpdate(http)) == HTTP_CONTINUE); if (status == HTTP_UNAUTHORIZED) { /* * Flush any error message... */ httpFlush(http); /* * See if we can do authentication... */ if (cupsDoAuthentication(http, "GET", resource)) break; if (httpReconnect(http)) { status = HTTP_ERROR; break; } continue; } #ifdef HAVE_SSL else if (status == HTTP_UPGRADE_REQUIRED) { /* Flush any error message... */ httpFlush(http); /* Reconnect... */ if (httpReconnect(http)) { status = HTTP_ERROR; break; } /* Upgrade with encryption... */ httpEncryption(http, HTTP_ENCRYPT_REQUIRED); /* Try again, this time with encryption enabled... */ continue; } #endif /* HAVE_SSL */ } while (status == HTTP_UNAUTHORIZED || status == HTTP_UPGRADE_REQUIRED); /* * See if we actually got the file or an error... */ if (status == HTTP_OK) { /* * Yes, copy the file... */ while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0) write(fd, buffer, bytes); } else { _cupsSetHTTPError(status); httpFlush(http); } /* * Return the request status... */ return (status); }
http_status_t /* O - HTTP status */ cupsPutFd(http_t *http, /* I - HTTP connection to server */ const char *resource, /* I - Resource name */ int fd) /* I - File descriptor */ { int bytes, /* Number of bytes read */ retries; /* Number of retries */ char buffer[8192]; /* Buffer for file */ http_status_t status; /* HTTP status from server */ /* * Range check input... */ DEBUG_printf(("cupsPutFd(http=%p, resource=\"%s\", fd=%d)\n", http, resource, fd)); if (!http || !resource || fd < 0) { if (http) http->error = EINVAL; return (HTTP_ERROR); } /* * Then send PUT requests to the HTTP server... */ retries = 0; do { DEBUG_printf(("cupsPutFd: starting attempt, authstring=\"%s\"...\n", http->authstring)); httpClearFields(http); httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring); httpSetField(http, HTTP_FIELD_TRANSFER_ENCODING, "chunked"); httpSetExpect(http, HTTP_CONTINUE); if (httpPut(http, resource)) { if (httpReconnect(http)) { status = HTTP_ERROR; break; } else { status = HTTP_UNAUTHORIZED; continue; } } /* * Wait up to 1 second for a 100-continue response... */ if (httpWait(http, 1000)) status = httpUpdate(http); else status = HTTP_CONTINUE; if (status == HTTP_CONTINUE) { /* * Copy the file... */ lseek(fd, 0, SEEK_SET); while ((bytes = read(fd, buffer, sizeof(buffer))) > 0) if (httpCheck(http)) { if ((status = httpUpdate(http)) != HTTP_CONTINUE) break; } else httpWrite2(http, buffer, bytes); } if (status == HTTP_CONTINUE) { httpWrite2(http, buffer, 0); while ((status = httpUpdate(http)) == HTTP_CONTINUE); } if (status == HTTP_ERROR && !retries) { DEBUG_printf(("cupsPutFd: retry on status %d\n", status)); retries ++; /* Flush any error message... */ httpFlush(http); /* Reconnect... */ if (httpReconnect(http)) { status = HTTP_ERROR; break; } /* Try again... */ continue; } DEBUG_printf(("cupsPutFd: status=%d\n", status)); if (status == HTTP_UNAUTHORIZED) { /* * Flush any error message... */ httpFlush(http); /* * See if we can do authentication... */ if (cupsDoAuthentication(http, "PUT", resource)) break; if (httpReconnect(http)) { status = HTTP_ERROR; break; } continue; } #ifdef HAVE_SSL else if (status == HTTP_UPGRADE_REQUIRED) { /* Flush any error message... */ httpFlush(http); /* Reconnect... */ if (httpReconnect(http)) { status = HTTP_ERROR; break; } /* Upgrade with encryption... */ httpEncryption(http, HTTP_ENCRYPT_REQUIRED); /* Try again, this time with encryption enabled... */ continue; } #endif /* HAVE_SSL */ } while (status == HTTP_UNAUTHORIZED || status == HTTP_UPGRADE_REQUIRED || (status == HTTP_ERROR && retries < 2)); /* * See if we actually put the file or an error... */ if (status != HTTP_CREATED) { _cupsSetHTTPError(status); httpFlush(http); } return (status); }
int /* O - 0 on success, -1 on failure */ _httpTLSStart(http_t *http) /* I - Connection to server */ { char hostname[256], /* Hostname */ *hostptr; /* Pointer into hostname */ int status; /* Status of handshake */ gnutls_certificate_credentials_t *credentials; /* TLS credentials */ char priority_string[1024]; /* Priority string */ DEBUG_printf(("3_httpTLSStart(http=%p)", http)); if (tls_options < 0) { DEBUG_puts("4_httpTLSStart: Setting defaults."); _cupsSetDefaults(); DEBUG_printf(("4_httpTLSStart: tls_options=%x", tls_options)); } if (http->mode == _HTTP_MODE_SERVER && !tls_keypath) { DEBUG_puts("4_httpTLSStart: cupsSetServerCredentials not called."); http->error = errno = EINVAL; http->status = HTTP_STATUS_ERROR; _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Server credentials not set."), 1); return (-1); } credentials = (gnutls_certificate_credentials_t *) malloc(sizeof(gnutls_certificate_credentials_t)); if (credentials == NULL) { DEBUG_printf(("8_httpStartTLS: Unable to allocate credentials: %s", strerror(errno))); http->error = errno; http->status = HTTP_STATUS_ERROR; _cupsSetHTTPError(HTTP_STATUS_ERROR); return (-1); } gnutls_certificate_allocate_credentials(credentials); status = gnutls_init(&http->tls, http->mode == _HTTP_MODE_CLIENT ? GNUTLS_CLIENT : GNUTLS_SERVER); if (!status) status = gnutls_set_default_priority(http->tls); if (status) { http->error = EIO; http->status = HTTP_STATUS_ERROR; DEBUG_printf(("4_httpTLSStart: Unable to initialize common TLS parameters: %s", gnutls_strerror(status))); _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, gnutls_strerror(status), 0); gnutls_deinit(http->tls); gnutls_certificate_free_credentials(*credentials); free(credentials); http->tls = NULL; return (-1); } if (http->mode == _HTTP_MODE_CLIENT) { /* * Client: get the hostname to use for TLS... */ if (httpAddrLocalhost(http->hostaddr)) { strlcpy(hostname, "localhost", sizeof(hostname)); } else { /* * Otherwise make sure the hostname we have does not end in a trailing dot. */ strlcpy(hostname, http->hostname, sizeof(hostname)); if ((hostptr = hostname + strlen(hostname) - 1) >= hostname && *hostptr == '.') *hostptr = '\0'; } status = gnutls_server_name_set(http->tls, GNUTLS_NAME_DNS, hostname, strlen(hostname)); } else { /* * Server: get certificate and private key... */ char crtfile[1024], /* Certificate file */ keyfile[1024]; /* Private key file */ int have_creds = 0; /* Have credentials? */ if (http->fields[HTTP_FIELD_HOST][0]) { /* * Use hostname for TLS upgrade... */ strlcpy(hostname, http->fields[HTTP_FIELD_HOST], sizeof(hostname)); } else { /* * Resolve hostname from connection address... */ http_addr_t addr; /* Connection address */ socklen_t addrlen; /* Length of address */ addrlen = sizeof(addr); if (getsockname(http->fd, (struct sockaddr *)&addr, &addrlen)) { DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno))); hostname[0] = '\0'; } else if (httpAddrLocalhost(&addr)) hostname[0] = '\0'; else { httpAddrLookup(&addr, hostname, sizeof(hostname)); DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname)); } } if (isdigit(hostname[0] & 255) || hostname[0] == '[') hostname[0] = '\0'; /* Don't allow numeric addresses */ if (hostname[0]) { http_gnutls_make_path(crtfile, sizeof(crtfile), tls_keypath, hostname, "crt"); http_gnutls_make_path(keyfile, sizeof(keyfile), tls_keypath, hostname, "key"); have_creds = !access(crtfile, 0) && !access(keyfile, 0); } else if (tls_common_name) { http_gnutls_make_path(crtfile, sizeof(crtfile), tls_keypath, tls_common_name, "crt"); http_gnutls_make_path(keyfile, sizeof(keyfile), tls_keypath, tls_common_name, "key"); have_creds = !access(crtfile, 0) && !access(keyfile, 0); } if (!have_creds && tls_auto_create && (hostname[0] || tls_common_name)) { DEBUG_printf(("4_httpTLSStart: Auto-create credentials for \"%s\".", hostname[0] ? hostname : tls_common_name)); if (!cupsMakeServerCredentials(tls_keypath, hostname[0] ? hostname : tls_common_name, 0, NULL, time(NULL) + 365 * 86400)) { DEBUG_puts("4_httpTLSStart: cupsMakeServerCredentials failed."); http->error = errno = EINVAL; http->status = HTTP_STATUS_ERROR; _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create server credentials."), 1); return (-1); } } DEBUG_printf(("4_httpTLSStart: Using certificate \"%s\" and private key \"%s\".", crtfile, keyfile)); status = gnutls_certificate_set_x509_key_file(*credentials, crtfile, keyfile, GNUTLS_X509_FMT_PEM); } if (!status) status = gnutls_credentials_set(http->tls, GNUTLS_CRD_CERTIFICATE, *credentials); if (status) { http->error = EIO; http->status = HTTP_STATUS_ERROR; DEBUG_printf(("4_httpTLSStart: Unable to complete client/server setup: %s", gnutls_strerror(status))); _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, gnutls_strerror(status), 0); gnutls_deinit(http->tls); gnutls_certificate_free_credentials(*credentials); free(credentials); http->tls = NULL; return (-1); } strlcpy(priority_string, "NORMAL", sizeof(priority_string)); if (tls_options & _HTTP_TLS_DENY_TLS10) strlcat(priority_string, ":+VERS-TLS-ALL:-VERS-TLS1.0:-VERS-SSL3.0", sizeof(priority_string)); else if (tls_options & _HTTP_TLS_ALLOW_SSL3) strlcat(priority_string, ":+VERS-TLS-ALL", sizeof(priority_string)); else strlcat(priority_string, ":+VERS-TLS-ALL:-VERS-SSL3.0", sizeof(priority_string)); if (!(tls_options & _HTTP_TLS_ALLOW_RC4)) strlcat(priority_string, ":-ARCFOUR-128", sizeof(priority_string)); if (!(tls_options & _HTTP_TLS_ALLOW_DH)) strlcat(priority_string, ":!ANON-DH", sizeof(priority_string)); #ifdef HAVE_GNUTLS_PRIORITY_SET_DIRECT gnutls_priority_set_direct(http->tls, priority_string, NULL); #else gnutls_priority_t priority; /* Priority */ gnutls_priority_init(&priority, priority_string, NULL); gnutls_priority_set(http->tls, priority); gnutls_priority_deinit(priority); #endif /* HAVE_GNUTLS_PRIORITY_SET_DIRECT */ gnutls_transport_set_ptr(http->tls, (gnutls_transport_ptr_t)http); gnutls_transport_set_pull_function(http->tls, http_gnutls_read); #ifdef HAVE_GNUTLS_TRANSPORT_SET_PULL_TIMEOUT_FUNCTION gnutls_transport_set_pull_timeout_function(http->tls, (gnutls_pull_timeout_func)httpWait); #endif /* HAVE_GNUTLS_TRANSPORT_SET_PULL_TIMEOUT_FUNCTION */ gnutls_transport_set_push_function(http->tls, http_gnutls_write); while ((status = gnutls_handshake(http->tls)) != GNUTLS_E_SUCCESS) { DEBUG_printf(("5_httpStartTLS: gnutls_handshake returned %d (%s)", status, gnutls_strerror(status))); if (gnutls_error_is_fatal(status)) { http->error = EIO; http->status = HTTP_STATUS_ERROR; _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, gnutls_strerror(status), 0); gnutls_deinit(http->tls); gnutls_certificate_free_credentials(*credentials); free(credentials); http->tls = NULL; return (-1); } } http->tls_credentials = credentials; return (0); }
http_status_t /* O - HTTP status */ cupsGetPPD3(http_t *http, /* I - HTTP connection or @code CUPS_HTTP_DEFAULT@ */ const char *name, /* I - Destination name */ time_t *modtime, /* IO - Modification time */ char *buffer, /* I - Filename buffer */ size_t bufsize) /* I - Size of filename buffer */ { int http_port; /* Port number */ char http_hostname[HTTP_MAX_HOST]; /* Hostname associated with connection */ http_t *http2; /* Alternate HTTP connection */ int fd; /* PPD file */ char localhost[HTTP_MAX_URI],/* Local hostname */ hostname[HTTP_MAX_URI], /* Hostname */ resource[HTTP_MAX_URI]; /* Resource name */ int port; /* Port number */ http_status_t status; /* HTTP status from server */ char tempfile[1024] = ""; /* Temporary filename */ _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */ /* * Range check input... */ DEBUG_printf(("cupsGetPPD3(http=%p, name=\"%s\", modtime=%p(%d), buffer=%p, " "bufsize=%d)", http, name, modtime, modtime ? (int)*modtime : 0, buffer, (int)bufsize)); if (!name) { _cupsSetError(IPP_INTERNAL_ERROR, _("No printer name"), 1); return (HTTP_NOT_ACCEPTABLE); } if (!modtime) { _cupsSetError(IPP_INTERNAL_ERROR, _("No modification time"), 1); return (HTTP_NOT_ACCEPTABLE); } if (!buffer || bufsize <= 1) { _cupsSetError(IPP_INTERNAL_ERROR, _("Bad filename buffer"), 1); return (HTTP_NOT_ACCEPTABLE); } #ifndef WIN32 /* * See if the PPD file is available locally... */ if (!cg->servername[0]) cupsServer(); if (!_cups_strcasecmp(cg->servername, "localhost")) { char ppdname[1024]; /* PPD filename */ struct stat ppdinfo; /* PPD file information */ snprintf(ppdname, sizeof(ppdname), "%s/ppd/%s.ppd", cg->cups_serverroot, name); if (!stat(ppdname, &ppdinfo)) { /* * OK, the file exists, use it! */ if (buffer[0]) { unlink(buffer); if (symlink(ppdname, buffer) && errno != EEXIST) { _cupsSetError(IPP_INTERNAL_ERROR, NULL, 0); return (HTTP_SERVER_ERROR); } } else { int tries; /* Number of tries */ const char *tmpdir; /* TMPDIR environment variable */ struct timeval curtime; /* Current time */ /* * Previously we put root temporary files in the default CUPS temporary * directory under /var/spool/cups. However, since the scheduler cleans * out temporary files there and runs independently of the user apps, we * don't want to use it unless specifically told to by cupsd. */ if ((tmpdir = getenv("TMPDIR")) == NULL) # ifdef __APPLE__ tmpdir = "/private/tmp"; /* /tmp is a symlink to /private/tmp */ # else tmpdir = "/tmp"; # endif /* __APPLE__ */ /* * Make the temporary name using the specified directory... */ tries = 0; do { /* * Get the current time of day... */ gettimeofday(&curtime, NULL); /* * Format a string using the hex time values... */ snprintf(buffer, bufsize, "%s/%08lx%05lx", tmpdir, (unsigned long)curtime.tv_sec, (unsigned long)curtime.tv_usec); /* * Try to make a symlink... */ if (!symlink(ppdname, buffer)) break; tries ++; } while (tries < 1000); if (tries >= 1000) { _cupsSetError(IPP_INTERNAL_ERROR, NULL, 0); return (HTTP_SERVER_ERROR); } } if (*modtime >= ppdinfo.st_mtime) return (HTTP_NOT_MODIFIED); else { *modtime = ppdinfo.st_mtime; return (HTTP_OK); } } } #endif /* !WIN32 */ /* * Try finding a printer URI for this printer... */ if (!http) if ((http = _cupsConnect()) == NULL) return (HTTP_SERVICE_UNAVAILABLE); if (!cups_get_printer_uri(http, name, hostname, sizeof(hostname), &port, resource, sizeof(resource), 0)) return (HTTP_NOT_FOUND); DEBUG_printf(("2cupsGetPPD3: Printer hostname=\"%s\", port=%d", hostname, port)); /* * Remap local hostname to localhost... */ httpGetHostname(NULL, localhost, sizeof(localhost)); DEBUG_printf(("2cupsGetPPD3: Local hostname=\"%s\"", localhost)); if (!_cups_strcasecmp(localhost, hostname)) strcpy(hostname, "localhost"); /* * Get the hostname and port number we are connected to... */ httpGetHostname(http, http_hostname, sizeof(http_hostname)); http_port = _httpAddrPort(http->hostaddr); DEBUG_printf(("2cupsGetPPD3: Connection hostname=\"%s\", port=%d", http_hostname, http_port)); /* * Reconnect to the correct server as needed... */ if (!_cups_strcasecmp(http_hostname, hostname) && port == http_port) http2 = http; else if ((http2 = httpConnectEncrypt(hostname, port, cupsEncryption())) == NULL) { DEBUG_puts("1cupsGetPPD3: Unable to connect to server"); return (HTTP_SERVICE_UNAVAILABLE); } /* * Get a temp file... */ if (buffer[0]) fd = open(buffer, O_CREAT | O_TRUNC | O_WRONLY, 0600); else fd = cupsTempFd(tempfile, sizeof(tempfile)); if (fd < 0) { /* * Can't open file; close the server connection and return NULL... */ _cupsSetError(IPP_INTERNAL_ERROR, NULL, 0); if (http2 != http) httpClose(http2); return (HTTP_SERVER_ERROR); } /* * And send a request to the HTTP server... */ strlcat(resource, ".ppd", sizeof(resource)); if (*modtime > 0) httpSetField(http2, HTTP_FIELD_IF_MODIFIED_SINCE, httpGetDateString(*modtime)); status = cupsGetFd(http2, resource, fd); close(fd); /* * See if we actually got the file or an error... */ if (status == HTTP_OK) { *modtime = httpGetDateTime(httpGetField(http2, HTTP_FIELD_DATE)); if (tempfile[0]) strlcpy(buffer, tempfile, bufsize); } else if (status != HTTP_NOT_MODIFIED) { _cupsSetHTTPError(status); if (buffer[0]) unlink(buffer); else if (tempfile[0]) unlink(tempfile); } else if (tempfile[0]) unlink(tempfile); if (http2 != http) httpClose(http2); /* * Return the PPD file... */ DEBUG_printf(("1cupsGetPPD3: Returning status %d", status)); return (status); }