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 - 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 */ 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); }