static void _get_send (GtkCupsRequest *request) { GTK_NOTE (PRINTING, g_print ("CUPS Backend: %s\n", G_STRFUNC)); request->poll_state = GTK_CUPS_HTTP_WRITE; if (request->data_io == NULL) { gtk_cups_result_set_error (request->result, GTK_CUPS_ERROR_IO, G_IO_STATUS_ERROR, G_IO_CHANNEL_ERROR_FAILED, "Get requires an open io channel"); request->state = GTK_CUPS_GET_DONE; request->poll_state = GTK_CUPS_HTTP_IDLE; return; } httpClearFields (request->http); #ifdef HAVE_HTTPGETAUTHSTRING httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http)); #else #ifdef HAVE_HTTP_AUTHSTRING httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring); #endif #endif if (httpGet (request->http, request->resource)) { if (httpReconnect (request->http)) { request->state = GTK_CUPS_GET_DONE; request->poll_state = GTK_CUPS_HTTP_IDLE; /* TODO: should add a status or error code for failed GET */ gtk_cups_result_set_error (request->result, GTK_CUPS_ERROR_GENERAL, 0, 0, "Failed Get"); } request->attempts++; return; } if (httpCheck (request->http)) request->last_status = httpUpdate (request->http); request->attempts = 0; request->state = GTK_CUPS_GET_CHECK; request->poll_state = GTK_CUPS_HTTP_READ; ippSetState (request->ipp_request, IPP_IDLE); }
gboolean gtk_cups_request_read_write (GtkCupsRequest *request, gboolean connect_only) { if (connect_only && request->state != GTK_CUPS_REQUEST_START) return FALSE; do { if (request->type == GTK_CUPS_POST) post_states[request->state] (request); else if (request->type == GTK_CUPS_GET) get_states[request->state] (request); if (gtk_cups_result_is_error (request->result)) request->state = GTK_CUPS_REQUEST_DONE; if (request->attempts > _GTK_CUPS_MAX_ATTEMPTS && request->state != GTK_CUPS_REQUEST_DONE) { /* TODO: should add a status or error code for too many failed attempts */ gtk_cups_result_set_error (request->result, GTK_CUPS_ERROR_GENERAL, 0, 0, "Too many failed attempts"); request->state = GTK_CUPS_REQUEST_DONE; } if (request->state == GTK_CUPS_REQUEST_DONE) { request->poll_state = GTK_CUPS_HTTP_IDLE; return TRUE; } } /* We need to recheck using httpCheck if the poll_state is read, because * Cups has an internal read buffer. And if this buffer is filled, we may * never get a poll event again. */ while (request->poll_state == GTK_CUPS_HTTP_READ && request->http && httpCheck(request->http)); return FALSE; }
static void _post_check (GtkCupsRequest *request) { http_status_t http_status; http_status = request->last_status; GTK_NOTE (PRINTING, g_print ("CUPS Backend: %s - status %i\n", G_STRFUNC, http_status)); request->poll_state = GTK_CUPS_HTTP_READ; if (http_status == HTTP_CONTINUE) { goto again; } else if (http_status == HTTP_UNAUTHORIZED) { int auth_result = -1; httpFlush (request->http); if (request->password_state == GTK_CUPS_PASSWORD_APPLIED) { request->poll_state = GTK_CUPS_HTTP_IDLE; request->password_state = GTK_CUPS_PASSWORD_NOT_VALID; request->state = GTK_CUPS_POST_AUTH; request->need_password = TRUE; return; } /* Negotiate */ if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0) { auth_result = cupsDoAuthentication (request->http, "POST", request->resource); } /* Basic, BasicDigest, Digest and PeerCred */ else { if (request->password_state == GTK_CUPS_PASSWORD_NONE) { cups_username = request->username; cupsSetPasswordCB (passwordCB); /* This call success for PeerCred authentication */ auth_result = cupsDoAuthentication (request->http, "POST", request->resource); if (auth_result != 0) { /* move to AUTH state to let the backend * ask for a password */ request->poll_state = GTK_CUPS_HTTP_IDLE; request->state = GTK_CUPS_POST_AUTH; request->need_password = TRUE; return; } } else { cups_password = request->password; cups_username = request->username; auth_result = cupsDoAuthentication (request->http, "POST", request->resource); if (cups_password != NULL) return; if (request->password != NULL) { memset (request->password, 0, strlen (request->password)); g_free (request->password); request->password = NULL; } request->password_state = GTK_CUPS_PASSWORD_APPLIED; } } if (auth_result || httpReconnect (request->http)) { /* if the password has been used, reset password_state * so that we ask for a new one next time around */ if (cups_password == NULL) request->password_state = GTK_CUPS_PASSWORD_NONE; request->state = GTK_CUPS_POST_DONE; request->poll_state = GTK_CUPS_HTTP_IDLE; gtk_cups_result_set_error (request->result, GTK_CUPS_ERROR_AUTH, 0, 0, "Not authorized"); return; } if (request->data_io != NULL) g_io_channel_seek_position (request->data_io, 0, G_SEEK_SET, NULL); request->state = GTK_CUPS_POST_CONNECT; request->poll_state = GTK_CUPS_HTTP_WRITE; } else if (http_status == HTTP_ERROR) { int error = httpError (request->http); #ifdef G_OS_WIN32 if (error != WSAENETDOWN && error != WSAENETUNREACH) #else if (error != ENETDOWN && error != ENETUNREACH) #endif /* G_OS_WIN32 */ { request->attempts++; goto again; } else { request->state = GTK_CUPS_POST_DONE; request->poll_state = GTK_CUPS_HTTP_IDLE; gtk_cups_result_set_error (request->result, GTK_CUPS_ERROR_HTTP, http_status, error, "Unknown HTTP error"); return; } } else if (http_status == HTTP_UPGRADE_REQUIRED) { /* Flush any error message... */ httpFlush (request->http); cupsSetEncryption (HTTP_ENCRYPT_REQUIRED); request->state = GTK_CUPS_POST_CONNECT; /* Reconnect... */ httpReconnect (request->http); /* Upgrade with encryption... */ httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED); request->attempts++; goto again; } else if (http_status != HTTP_OK) { int http_errno; http_errno = httpError (request->http); if (http_errno == EPIPE) request->state = GTK_CUPS_POST_CONNECT; else { request->state = GTK_CUPS_POST_DONE; gtk_cups_result_set_error (request->result, GTK_CUPS_ERROR_HTTP, http_status, http_errno, "HTTP Error in POST %s", g_strerror (http_errno)); request->poll_state = GTK_CUPS_HTTP_IDLE; httpFlush (request->http); return; } request->poll_state = GTK_CUPS_HTTP_IDLE; request->last_status = HTTP_CONTINUE; httpFlush (request->http); if (request->own_http) httpClose (request->http); request->http = NULL; return; } else { request->state = GTK_CUPS_POST_READ_RESPONSE; return; } again: http_status = HTTP_CONTINUE; if (httpCheck (request->http)) http_status = httpUpdate (request->http); request->last_status = http_status; }
static void _post_write_data (GtkCupsRequest *request) { gsize bytes; char buffer[_GTK_CUPS_MAX_CHUNK_SIZE]; http_status_t http_status; GTK_NOTE (PRINTING, g_print ("CUPS Backend: %s\n", G_STRFUNC)); request->poll_state = GTK_CUPS_HTTP_WRITE; if (httpCheck (request->http)) http_status = httpUpdate (request->http); else http_status = request->last_status; request->last_status = http_status; if (http_status == HTTP_CONTINUE || http_status == HTTP_OK) { GIOStatus io_status; GError *error; error = NULL; /* send data */ io_status = g_io_channel_read_chars (request->data_io, buffer, _GTK_CUPS_MAX_CHUNK_SIZE, &bytes, &error); if (io_status == G_IO_STATUS_ERROR) { request->state = GTK_CUPS_POST_DONE; request->poll_state = GTK_CUPS_HTTP_IDLE; gtk_cups_result_set_error (request->result, GTK_CUPS_ERROR_IO, io_status, error->code, "Error reading from cache file: %s", error->message); g_error_free (error); return; } else if (bytes == 0 && io_status == G_IO_STATUS_EOF) { request->state = GTK_CUPS_POST_CHECK; request->poll_state = GTK_CUPS_HTTP_READ; request->attempts = 0; return; } if (httpWrite2 (request->http, buffer, bytes) < bytes) { int http_errno; http_errno = httpError (request->http); request->state = GTK_CUPS_POST_DONE; request->poll_state = GTK_CUPS_HTTP_IDLE; gtk_cups_result_set_error (request->result, GTK_CUPS_ERROR_HTTP, http_status, http_errno, "Error writing to socket in Post %s", g_strerror (http_errno)); return; } } else if (http_status == HTTP_UNAUTHORIZED) { request->state = GTK_CUPS_POST_CHECK; request->poll_state = GTK_CUPS_HTTP_READ; request->attempts = 0; return; } else { request->attempts++; } }
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 - 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 */ 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); }