X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=cups%2Fhttp.c;h=83396ea4191a748e800ff6f8a7e8824c67a34726;hb=781996c9136ff4ec23b858a3611c21698a6bb317;hp=a2370c9ef0f12dd6cdc27757a1f25c6f8c45fbaf;hpb=bdbf4417b7027f46a44ec57e062407941f215200;p=thirdparty%2Fcups.git diff --git a/cups/http.c b/cups/http.c index a2370c9ef..83396ea41 100644 --- a/cups/http.c +++ b/cups/http.c @@ -1,9 +1,9 @@ /* - * "$Id: http.c,v 1.122 2003/08/04 18:45:51 swdev Exp $" + * "$Id$" * * HTTP routines for the Common UNIX Printing System (CUPS). * - * Copyright 1997-2003 by Easy Software Products, all rights reserved. + * Copyright 1997-2006 by Easy Software Products, all rights reserved. * * These coded instructions, statements, and computer programs are the * property of Easy Software Products and are protected by Federal @@ -15,9 +15,9 @@ * Attn: CUPS Licensing Information * Easy Software Products * 44141 Airport View Drive, Suite 204 - * Hollywood, Maryland 20636-3111 USA + * Hollywood, Maryland 20636 USA * - * Voice: (301) 373-9603 + * Voice: (301) 373-9600 * EMail: cups-info@cups.org * WWW: http://www.cups.org * @@ -25,50 +25,63 @@ * * Contents: * - * httpInitialize() - Initialize the HTTP interface library and set the - * default HTTP proxy (if any). + * httpBlocking() - Set blocking/non-blocking behavior on a connection. * httpCheck() - Check to see if there is a pending response from * the server. * httpClearCookie() - Clear the cookie value(s). + * httpClearFields() - Clear HTTP request fields. * httpClose() - Close an HTTP connection... * httpConnect() - Connect to a HTTP server. * httpConnectEncrypt() - Connect to a HTTP server using encryption. - * httpEncryption() - Set the required encryption on the link. - * httpReconnect() - Reconnect to a HTTP server... - * httpGetSubField() - Get a sub-field value. - * httpSetField() - Set the value of an HTTP header. * httpDelete() - Send a DELETE request to the server. + * httpEncryption() - Set the required encryption on the link. + * httpError() - Get the last error on a connection. + * httpFlush() - Flush data from a HTTP connection. + * httpFlushWrite() - Flush data in write buffer. * httpGet() - Send a GET request to the server. + * httpGetBlocking() - Get the blocking/non-block state of a connection. + * httpGetCookie() - Get any cookie data from the response. + * httpGetFd() - Get the file descriptor associated with a + * connection. + * httpGetField() - Get a field value from a request/response. + * httpGetLength() - Get the amount of data remaining from the + * content-length or transfer-encoding fields. + * httpGetLength2() - Get the amount of data remaining from the + * content-length or transfer-encoding fields. + * httpGetStatus() - Get the status of the last HTTP request. + * httpGetSubField() - Get a sub-field value. + * httpGets() - Get a line of text from a HTTP connection. * httpHead() - Send a HEAD request to the server. + * httpInitialize() - Initialize the HTTP interface library and set the + * default HTTP proxy (if any). * httpOptions() - Send an OPTIONS request to the server. * httpPost() - Send a POST request to the server. + * httpPrintf() - Print a formatted string to a HTTP connection. * httpPut() - Send a PUT request to the server. - * httpTrace() - Send an TRACE request to the server. - * httpFlush() - Flush data from a HTTP connection. * httpRead() - Read data from a HTTP connection. + * httpRead2() - Read data from a HTTP connection. + * _httpReadCDSA() - Read function for CDSA decryption code. + * httpReconnect() - Reconnect to a HTTP server... * httpSetCookie() - Set the cookie value(s)... + * httpSetExpect() - Set the Expect: header in a request. + * httpSetField() - Set the value of an HTTP header. + * httpSetLength() - Set the content-length and transfer-encoding. + * httpTrace() - Send an TRACE request to the server. + * httpUpdate() - Update the current HTTP state for incoming data. * httpWait() - Wait for data available on a connection. * httpWrite() - Write data to a HTTP connection. - * httpGets() - Get a line of text from a HTTP connection. - * httpPrintf() - Print a formatted string to a HTTP connection. - * httpGetDateString() - Get a formatted date/time string from a time value. - * httpGetDateTime() - Get a time value from a formatted date/time string. - * httpUpdate() - Update the current HTTP state for incoming data. - * httpDecode64() - Base64-decode a string. - * httpEncode64() - Base64-encode a string. - * httpGetLength() - Get the amount of data remaining from the - * content-length or transfer-encoding fields. + * httpWrite2() - Write data to a HTTP connection. + * _httpWriteCDSA() - Write function for CDSA encryption code. * http_field() - Return the field index for a field name. + * http_read_ssl() - Read from a SSL/TLS connection. * http_send() - Send a request with all fields and the trailing * blank line. - * http_wait() - Wait for data available on a connection. - * http_upgrade() - Force upgrade to TLS encryption. * http_setup_ssl() - Set up SSL/TLS on a connection. * http_shutdown_ssl() - Shut down SSL/TLS on a connection. - * http_read_ssl() - Read from a SSL/TLS connection. + * http_upgrade() - Force upgrade to TLS encryption. + * http_wait() - Wait for data available on a connection. + * http_write() - Write data to a connection. * http_write_ssl() - Write to a SSL/TLS connection. - * CDSAReadFunc() - Read function for CDSA decryption code. - * CDSAWriteFunc() - Write function for CDSA encryption code. */ /* @@ -76,18 +89,11 @@ */ #include "http-private.h" - -#include +#include "globals.h" +#include "debug.h" #include -#include -#include -#include "string.h" #include #include - -#include "http.h" -#include "debug.h" - #ifndef WIN32 # include # include @@ -113,16 +119,16 @@ static http_field_t http_field(const char *name); static int http_send(http_t *http, http_state_t request, const char *uri); static int http_wait(http_t *http, int msec); +static int http_write(http_t *http, const char *buffer, + int length); +static int http_write_chunk(http_t *http, const char *buffer, + int length); #ifdef HAVE_SSL -static int http_upgrade(http_t *http); +static int http_read_ssl(http_t *http, char *buf, int len); static int http_setup_ssl(http_t *http); static void http_shutdown_ssl(http_t *http); -static int http_read_ssl(http_t *http, char *buf, int len); +static int http_upgrade(http_t *http); static int http_write_ssl(http_t *http, const char *buf, int len); -# ifdef HAVE_CDSASSL -static OSStatus CDSAReadFunc(SSLConnectionRef connection, void *data, size_t *dataLength); -static OSStatus CDSAWriteFunc(SSLConnectionRef connection, const void *data, size_t *dataLength); -# endif /* HAVE_CDSASSL */ #endif /* HAVE_SSL */ @@ -160,97 +166,18 @@ static const char * const http_fields[] = "User-Agent", "WWW-Authenticate" }; -static const char * const days[7] = - { - "Sun", - "Mon", - "Tue", - "Wed", - "Thu", - "Fri", - "Sat" - }; -static const char * const months[12] = - { - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec" - }; /* - * 'httpInitialize()' - Initialize the HTTP interface library and set the - * default HTTP proxy (if any). + * 'httpBlocking()' - Set blocking/non-blocking behavior on a connection. */ void -httpInitialize(void) +httpBlocking(http_t *http, /* I - HTTP connection */ + int b) /* I - 1 = blocking, 0 = non-blocking */ { -#ifdef HAVE_LIBSSL -# ifndef WIN32 - struct timeval curtime; /* Current time in microseconds */ -# endif /* !WIN32 */ - int i; /* Looping var */ - unsigned char data[1024]; /* Seed data */ -#endif /* HAVE_LIBSSL */ - -#ifdef WIN32 - WSADATA winsockdata; /* WinSock data */ - static int initialized = 0;/* Has WinSock been initialized? */ - - - if (!initialized) - WSAStartup(MAKEWORD(1,1), &winsockdata); -#elif defined(HAVE_SIGSET) - sigset(SIGPIPE, SIG_IGN); -#elif defined(HAVE_SIGACTION) - struct sigaction action; /* POSIX sigaction data */ - - - /* - * Ignore SIGPIPE signals... - */ - - memset(&action, 0, sizeof(action)); - action.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &action, NULL); -#else - signal(SIGPIPE, SIG_IGN); -#endif /* WIN32 */ - -#ifdef HAVE_GNUTLS - gnutls_global_init(); -#endif /* HAVE_GNUTLS */ - -#ifdef HAVE_LIBSSL - SSL_load_error_strings(); - SSL_library_init(); - - /* - * Using the current time is a dubious random seed, but on some systems - * it is the best we can do (on others, this seed isn't even used...) - */ - -#ifdef WIN32 -#else - gettimeofday(&curtime, NULL); - srand(curtime.tv_sec + curtime.tv_usec); -#endif /* WIN32 */ - - for (i = 0; i < sizeof(data); i ++) - data[i] = rand(); /* Yes, this is a poor source of random data... */ - - RAND_seed(&data, sizeof(data)); -#endif /* HAVE_LIBSSL */ + if (http) + http->blocking = b; } @@ -258,8 +185,8 @@ httpInitialize(void) * 'httpCheck()' - Check to see if there is a pending response from the server. */ -int /* O - 0 = no data, 1 = data available */ -httpCheck(http_t *http) /* I - HTTP connection */ +int /* O - 0 = no data, 1 = data available */ +httpCheck(http_t *http) /* I - HTTP connection */ { return (httpWait(http, 0)); } @@ -267,10 +194,12 @@ httpCheck(http_t *http) /* I - HTTP connection */ /* * 'httpClearCookie()' - Clear the cookie value(s). + * + * @since CUPS 1.1.19@ */ void -httpClearCookie(http_t *http) /* I - Connection */ +httpClearCookie(http_t *http) /* I - HTTP connection */ { if (!http) return; @@ -283,16 +212,37 @@ httpClearCookie(http_t *http) /* I - Connection */ } +/* + * 'httpClearFields()' - Clear HTTP request fields. + */ + +void +httpClearFields(http_t *http) /* I - HTTP connection */ +{ + if (http) + { + memset(http->fields, 0, sizeof(http->fields)); + httpSetField(http, HTTP_FIELD_HOST, http->hostname); + + http->expect = (http_status_t)0; + } +} + + /* * 'httpClose()' - Close an HTTP connection... */ void -httpClose(http_t *http) /* I - Connection to close */ +httpClose(http_t *http) /* I - HTTP connection */ { + DEBUG_printf(("httpClose(http=%p)\n", http)); + if (!http) return; + httpAddrFreeList(http->addrlist); + if (http->input_set) free(http->input_set); @@ -318,11 +268,11 @@ httpClose(http_t *http) /* I - Connection to close */ * 'httpConnect()' - Connect to a HTTP server. */ -http_t * /* O - New HTTP connection */ -httpConnect(const char *host, /* I - Host to connect to */ - int port) /* I - Port number */ +http_t * /* O - New HTTP connection */ +httpConnect(const char *host, /* I - Host to connect to */ + int port) /* I - Port number */ { - http_encryption_t encrypt;/* Type of encryption to use */ + http_encryption_t encryption; /* Type of encryption to use */ /* @@ -330,11 +280,11 @@ httpConnect(const char *host, /* I - Host to connect to */ */ if (port == 443) - encrypt = HTTP_ENCRYPT_ALWAYS; + encryption = HTTP_ENCRYPT_ALWAYS; else - encrypt = HTTP_ENCRYPT_IF_REQUESTED; + encryption = HTTP_ENCRYPT_IF_REQUESTED; - return (httpConnectEncrypt(host, port, encrypt)); + return (httpConnectEncrypt(host, port, encryption)); } @@ -343,17 +293,20 @@ httpConnect(const char *host, /* I - Host to connect to */ */ http_t * /* O - New HTTP connection */ -httpConnectEncrypt(const char *host, /* I - Host to connect to */ - int port, /* I - Port number */ - http_encryption_t encrypt) - /* I - Type of encryption to use */ +httpConnectEncrypt( + const char *host, /* I - Host to connect to */ + int port, /* I - Port number */ + http_encryption_t encryption) /* I - Type of encryption to use */ { - int i; /* Looping var */ http_t *http; /* New HTTP connection */ - struct hostent *hostaddr; /* Host address data */ + http_addrlist_t *addrlist; /* Host address data */ + char service[255]; /* Service name */ + + DEBUG_printf(("httpConnectEncrypt(host=\"%s\", port=%d, encryption=%d)\n", + host ? host : "(null)", port, encryption)); - if (host == NULL) + if (!host) return (NULL); httpInitialize(); @@ -362,24 +315,9 @@ httpConnectEncrypt(const char *host, /* I - Host to connect to */ * Lookup the host... */ - if ((hostaddr = httpGetHostByName(host)) == NULL) - { - /* - * This hack to make users that don't have a localhost entry in - * their hosts file or DNS happy... - */ - - if (strcasecmp(host, "localhost") != 0) - return (NULL); - else if ((hostaddr = httpGetHostByName("127.0.0.1")) == NULL) - return (NULL); - } - - /* - * Verify that it is an IPv4 address (IPv6 support will come in CUPS 1.2...) - */ + sprintf(service, "%d", port); - if (hostaddr->h_addrtype != AF_INET || hostaddr->h_length != 4) + if ((addrlist = httpAddrGetList(host, AF_UNSPEC, service)) == NULL) return (NULL); /* @@ -395,26 +333,14 @@ httpConnectEncrypt(const char *host, /* I - Host to connect to */ http->activity = time(NULL); http->fd = -1; - /* - * Copy the hostname and port and then "reconnect"... - */ - - strlcpy(http->hostname, host, sizeof(http->hostname)); - http->hostaddr.sin_family = hostaddr->h_addrtype; -#ifdef WIN32 - http->hostaddr.sin_port = htons((u_short)port); -#else - http->hostaddr.sin_port = htons(port); -#endif /* WIN32 */ - /* * Set the encryption status... */ - if (port == 443) /* Always use encryption for https */ + if (port == 443) /* Always use encryption for https */ http->encryption = HTTP_ENCRYPT_ALWAYS; else - http->encryption = encrypt; + http->encryption = encryption; /* * Loop through the addresses we have until one of them connects... @@ -422,40 +348,49 @@ httpConnectEncrypt(const char *host, /* I - Host to connect to */ strlcpy(http->hostname, host, sizeof(http->hostname)); - for (i = 0; hostaddr->h_addr_list[i]; i ++) - { - /* - * Load the address... - */ - - memcpy((char *)&(http->hostaddr.sin_addr), hostaddr->h_addr_list[i], - hostaddr->h_length); + /* + * Connect to the remote system... + */ - /* - * Connect to the remote system... - */ + http->addrlist = addrlist; - if (!httpReconnect(http)) - return (http); - } + if (!httpReconnect(http)) + return (http); /* * Could not connect to any known address - bail out! */ + httpAddrFreeList(addrlist); + free(http); + return (NULL); } +/* + * 'httpDelete()' - Send a DELETE request to the server. + */ + +int /* O - Status of call (0 = success) */ +httpDelete(http_t *http, /* I - HTTP connection */ + const char *uri) /* I - URI to delete */ +{ + return (http_send(http, HTTP_DELETE, uri)); +} + + /* * 'httpEncryption()' - Set the required encryption on the link. */ int /* O - -1 on error, 0 on success */ -httpEncryption(http_t *http, /* I - HTTP data */ +httpEncryption(http_t *http, /* I - HTTP connection */ http_encryption_t e) /* I - New encryption preference */ { + DEBUG_printf(("httpEncryption(http=%p, e=%d)\n", http, e)); + #ifdef HAVE_SSL if (!http) return (0); @@ -479,157 +414,319 @@ httpEncryption(http_t *http, /* I - HTTP data */ /* - * 'httpReconnect()' - Reconnect to a HTTP server... + * 'httpError()' - Get the last error on a connection. */ -int /* O - 0 on success, non-zero on failure */ -httpReconnect(http_t *http) /* I - HTTP data */ +int /* O - Error code (errno) value */ +httpError(http_t *http) /* I - HTTP connection */ { - int val; /* Socket option value */ - -#ifdef HAVE_SSL - if (http->tls) - http_shutdown_ssl(http); -#endif /* HAVE_SSL */ - - /* - * Close any previously open socket... - */ - - if (http->fd >= 0) -#ifdef WIN32 - closesocket(http->fd); -#else - close(http->fd); -#endif /* WIN32 */ + if (http) + return (http->error); + else + return (EINVAL); +} - /* - * Create the socket and set options to allow reuse. - */ - if ((http->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) - { -#ifdef WIN32 - http->error = WSAGetLastError(); -#else - http->error = errno; -#endif /* WIN32 */ - http->status = HTTP_ERROR; - return (-1); - } +/* + * 'httpFlush()' - Flush data from a HTTP connection. + */ -#ifdef FD_CLOEXEC - fcntl(http->fd, F_SETFD, FD_CLOEXEC); /* Close this socket when starting * - * other processes... */ -#endif /* FD_CLOEXEC */ +void +httpFlush(http_t *http) /* I - HTTP connection */ +{ + char buffer[8192]; /* Junk buffer */ + int blocking; /* To block or not to block */ - val = 1; - setsockopt(http->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val)); -#ifdef SO_REUSEPORT - val = 1; - setsockopt(http->fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)); -#endif /* SO_REUSEPORT */ + DEBUG_printf(("httpFlush(http=%p), state=%d\n", http, http->state)); /* - * Using TCP_NODELAY improves responsiveness, especially on systems - * with a slow loopback interface... Since we write large buffers - * when sending print files and requests, there shouldn't be any - * performance penalty for this... + * Temporarily set non-blocking mode so we don't get stuck in httpRead()... */ - val = 1; - setsockopt(http->fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); + blocking = http->blocking; + http->blocking = 0; /* - * Connect to the server... + * Read any data we can... */ - if (connect(http->fd, (struct sockaddr *)&(http->hostaddr), - sizeof(http->hostaddr)) < 0) - { -#ifdef WIN32 - http->error = WSAGetLastError(); -#else - http->error = errno; -#endif /* WIN32 */ - http->status = HTTP_ERROR; - -#ifdef WIN32 - closesocket(http->fd); -#else - close(http->fd); -#endif + while (httpRead2(http, buffer, sizeof(buffer)) > 0); - http->fd = -1; - - return (-1); - } + /* + * Restore blocking and reset the connection if we didn't get all of + * the remaining data... + */ - http->error = 0; - http->status = HTTP_CONTINUE; + http->blocking = blocking; -#ifdef HAVE_SSL - if (http->encryption == HTTP_ENCRYPT_ALWAYS) + if (http->state != HTTP_WAITING && http->fd >= 0) { /* - * Always do encryption via SSL. + * Didn't get the data back, so close the current connection. */ - if (http_setup_ssl(http) != 0) - { + http->state = HTTP_WAITING; + +#ifdef HAVE_SSL + if (http->tls) + http_shutdown_ssl(http); +#endif /* HAVE_SSL */ + #ifdef WIN32 - closesocket(http->fd); + closesocket(http->fd); #else - close(http->fd); + close(http->fd); #endif /* WIN32 */ - return (-1); - } + http->fd = -1; } - else if (http->encryption == HTTP_ENCRYPT_REQUIRED) - return (http_upgrade(http)); -#endif /* HAVE_SSL */ - - return (0); } /* - * 'httpGetSubField()' - Get a sub-field value. + * 'httpFlushWrite()' - Flush data in write buffer. + * + * @since CUPS 1.2@ */ -char * /* O - Value or NULL */ -httpGetSubField(http_t *http, /* I - HTTP data */ - http_field_t field, /* I - Field index */ - const char *name, /* I - Name of sub-field */ - char *value) /* O - Value string */ +int /* O - Bytes written or -1 on error */ +httpFlushWrite(http_t *http) /* I - HTTP connection */ { - const char *fptr; /* Pointer into field */ - char temp[HTTP_MAX_VALUE], /* Temporary buffer for name */ - *ptr; /* Pointer into string buffer */ + int bytes; /* Bytes written */ - if (http == NULL || - field < HTTP_FIELD_ACCEPT_LANGUAGE || - field > HTTP_FIELD_WWW_AUTHENTICATE || - name == NULL || value == NULL) - return (NULL); + DEBUG_printf(("httpFlushWrite(http=%p)\n", http)); - DEBUG_printf(("httpGetSubField(%p, %d, \"%s\", %p)\n", - http, field, name, value)); + if (!http || !http->wused) + return (0); - for (fptr = http->fields[field]; *fptr;) - { - /* - * Skip leading whitespace... - */ + if (http->data_encoding == HTTP_ENCODE_CHUNKED) + bytes = http_write_chunk(http, http->wbuffer, http->wused); + else + bytes = http_write(http, http->wbuffer, http->wused); - while (isspace(*fptr)) - fptr ++; + http->wused = 0; - if (*fptr == ',') - { + return (bytes); +} + + +/* + * 'httpGet()' - Send a GET request to the server. + */ + +int /* O - Status of call (0 = success) */ +httpGet(http_t *http, /* I - HTTP connection */ + const char *uri) /* I - URI to get */ +{ + return (http_send(http, HTTP_GET, uri)); +} + + +/* + * 'httpGetBlocking()' - Get the blocking/non-block state of a connection. + * + * @since CUPS 1.2@ + */ + +int /* O - 1 if blocking, 0 if non-blocking */ +httpGetBlocking(http_t *http) /* I - HTTP connection */ +{ + return (http ? http->blocking : 0); +} + + +/* + * 'httpGetCookie()' - Get any cookie data from the response. + */ + +const char * /* O - Cookie data or NULL */ +httpGetCookie(http_t *http) /* I - HTTP connecion */ +{ + return (http ? http->cookie : NULL); +} + + +/* + * 'httpGetFd()' - Get the file descriptor associated with a connection. + * + * @since CUPS 1.2@ + */ + +int /* O - File descriptor or -1 if none */ +httpGetFd(http_t *http) /* I - HTTP connection */ +{ + return (http ? http->fd : -1); +} + + +/* + * 'httpGetField()' - Get a field value from a request/response. + */ + +const char * /* O - Field value */ +httpGetField(http_t *http, /* I - HTTP connection */ + http_field_t field) /* I - Field to get */ +{ + if (!http || field <= HTTP_FIELD_UNKNOWN || field >= HTTP_FIELD_MAX) + return (NULL); + else + return (http->fields[field]); +} + + +/* + * 'httpGetLength()' - Get the amount of data remaining from the + * content-length or transfer-encoding fields. + * + * This function is deprecated and will not return lengths larger than + * 2^31 - 1; use httpGetLength2() instead. + * + * @deprecated@ + */ + +int /* O - Content length */ +httpGetLength(http_t *http) /* I - HTTP connection */ +{ + /* + * Get the read content length and return the 32-bit value. + */ + + if (http) + { + httpGetLength2(http); + + return (http->_data_remaining); + } + else + return (-1); +} + + +/* + * 'httpGetLength2()' - Get the amount of data remaining from the + * content-length or transfer-encoding fields. + * + * This function returns the complete content length, even for + * content larger than 2^31 - 1. + * + * @since CUPS 1.2@ + */ + +off_t /* O - Content length */ +httpGetLength2(http_t *http) /* I - HTTP connection */ +{ + DEBUG_printf(("httpGetLength2(http=%p), state=%d\n", http, http->state)); + + if (!http) + return (-1); + + if (!strcasecmp(http->fields[HTTP_FIELD_TRANSFER_ENCODING], "chunked")) + { + DEBUG_puts("httpGetLength2: chunked request!"); + + http->data_encoding = HTTP_ENCODE_CHUNKED; + http->data_remaining = 0; + } + else + { + http->data_encoding = HTTP_ENCODE_LENGTH; + + /* + * The following is a hack for HTTP servers that don't send a + * content-length or transfer-encoding field... + * + * If there is no content-length then the connection must close + * after the transfer is complete... + */ + + if (http->fields[HTTP_FIELD_CONTENT_LENGTH][0] == '\0') + http->data_remaining = 2147483647; + else + http->data_remaining = strtoll(http->fields[HTTP_FIELD_CONTENT_LENGTH], + NULL, 10); + + DEBUG_printf(("httpGetLength2: content_length=" CUPS_LLFMT "\n", + CUPS_LLCAST http->data_remaining)); + } + + if (http->data_remaining <= INT_MAX) + http->_data_remaining = (int)http->data_remaining; + else + http->_data_remaining = INT_MAX; + + return (http->data_remaining); +} + + +/* + * 'httpGetStatus()' - Get the status of the last HTTP request. + * + * @since CUPS 1.2@ + */ + +http_status_t /* O - HTTP status */ +httpGetStatus(http_t *http) /* I - HTTP connection */ +{ + return (http ? http->status : HTTP_ERROR); +} + + +/* + * 'httpGetSubField()' - Get a sub-field value. + * + * @deprecated@ + */ + +char * /* O - Value or NULL */ +httpGetSubField(http_t *http, /* I - HTTP connection */ + http_field_t field, /* I - Field index */ + const char *name, /* I - Name of sub-field */ + char *value) /* O - Value string */ +{ + return (httpGetSubField2(http, field, name, value, HTTP_MAX_VALUE)); +} + + +/* + * 'httpGetSubField2()' - Get a sub-field value. + * + * @since CUPS 1.2@ + */ + +char * /* O - Value or NULL */ +httpGetSubField2(http_t *http, /* I - HTTP connection */ + http_field_t field, /* I - Field index */ + const char *name, /* I - Name of sub-field */ + char *value, /* O - Value string */ + int valuelen) /* I - Size of value buffer */ +{ + const char *fptr; /* Pointer into field */ + char temp[HTTP_MAX_VALUE], /* Temporary buffer for name */ + *ptr, /* Pointer into string buffer */ + *end; /* End of value buffer */ + + DEBUG_printf(("httpGetSubField2(http=%p, field=%d, name=\"%s\", value=%p, valuelen=%d)\n", + http, field, name, value, valuelen)); + + if (!http || !name || !value || valuelen < 2 || + field <= HTTP_FIELD_UNKNOWN || field >= HTTP_FIELD_MAX) + return (NULL); + + end = value + valuelen - 1; + + for (fptr = http->fields[field]; *fptr;) + { + /* + * Skip leading whitespace... + */ + + while (isspace(*fptr & 255)) + fptr ++; + + if (*fptr == ',') + { fptr ++; continue; } @@ -639,18 +736,19 @@ httpGetSubField(http_t *http, /* I - HTTP data */ */ for (ptr = temp; - *fptr && *fptr != '=' && !isspace(*fptr) && ptr < (temp + sizeof(temp) - 1); + *fptr && *fptr != '=' && !isspace(*fptr & 255) && + ptr < (temp + sizeof(temp) - 1); *ptr++ = *fptr++); *ptr = '\0'; - DEBUG_printf(("name = \"%s\"\n", temp)); + DEBUG_printf(("httpGetSubField: name=\"%s\"\n", temp)); /* * Skip trailing chars up to the '='... */ - while (isspace(*fptr)) + while (isspace(*fptr & 255)) fptr ++; if (!*fptr) @@ -665,7 +763,7 @@ httpGetSubField(http_t *http, /* I - HTTP data */ fptr ++; - while (isspace(*fptr)) + while (isspace(*fptr & 255)) fptr ++; if (*fptr == '\"') @@ -675,7 +773,7 @@ httpGetSubField(http_t *http, /* I - HTTP data */ */ for (ptr = value, fptr ++; - *fptr && *fptr != '\"' && ptr < (value + HTTP_MAX_VALUE - 1); + *fptr && *fptr != '\"' && ptr < end; *ptr++ = *fptr++); *ptr = '\0'; @@ -693,22 +791,22 @@ httpGetSubField(http_t *http, /* I - HTTP data */ */ for (ptr = value; - *fptr && !isspace(*fptr) && *fptr != ',' && ptr < (value + HTTP_MAX_VALUE - 1); + *fptr && !isspace(*fptr & 255) && *fptr != ',' && ptr < end; *ptr++ = *fptr++); *ptr = '\0'; - while (*fptr && !isspace(*fptr) && *fptr != ',') + while (*fptr && !isspace(*fptr & 255) && *fptr != ',') fptr ++; } - DEBUG_printf(("value = \"%s\"\n", value)); + DEBUG_printf(("httpGetSubField: value=\"%s\"\n", value)); /* * See if this is the one... */ - if (strcmp(name, temp) == 0) + if (!strcmp(name, temp)) return (value); } @@ -719,143 +817,342 @@ httpGetSubField(http_t *http, /* I - HTTP data */ /* - * 'httpSetField()' - Set the value of an HTTP header. + * 'httpGets()' - Get a line of text from a HTTP connection. */ -void -httpSetField(http_t *http, /* I - HTTP data */ - http_field_t field, /* I - Field index */ - const char *value) /* I - Value */ +char * /* O - Line or NULL */ +httpGets(char *line, /* I - Line to read into */ + int length, /* I - Max length of buffer */ + http_t *http) /* I - HTTP connection */ { - if (http == NULL || - field < HTTP_FIELD_ACCEPT_LANGUAGE || - field > HTTP_FIELD_WWW_AUTHENTICATE || - value == NULL) - return; + char *lineptr, /* Pointer into line */ + *lineend, /* End of line */ + *bufptr, /* Pointer into input buffer */ + *bufend; /* Pointer to end of buffer */ + int bytes, /* Number of bytes read */ + eol; /* End-of-line? */ - strlcpy(http->fields[field], value, HTTP_MAX_VALUE); -} + DEBUG_printf(("httpGets(line=%p, length=%d, http=%p)\n", line, length, http)); -/* - * 'httpDelete()' - Send a DELETE request to the server. - */ + if (http == NULL || line == NULL) + return (NULL); -int /* O - Status of call (0 = success) */ -httpDelete(http_t *http, /* I - HTTP data */ - const char *uri) /* I - URI to delete */ -{ - return (http_send(http, HTTP_DELETE, uri)); -} + /* + * Read a line from the buffer... + */ + + lineptr = line; + lineend = line + length - 1; + eol = 0; + while (lineptr < lineend) + { + /* + * Pre-load the buffer as needed... + */ -/* - * 'httpGet()' - Send a GET request to the server. - */ +#ifdef WIN32 + WSASetLastError(0); +#else + errno = 0; +#endif /* WIN32 */ -int /* O - Status of call (0 = success) */ -httpGet(http_t *http, /* I - HTTP data */ - const char *uri) /* I - URI to get */ -{ - return (http_send(http, HTTP_GET, uri)); -} + while (http->used == 0) + { + /* + * No newline; see if there is more data to be read... + */ + if (!http->blocking && !http_wait(http, 1000)) + return (NULL); -/* - * 'httpHead()' - Send a HEAD request to the server. - */ +#ifdef HAVE_SSL + if (http->tls) + bytes = http_read_ssl(http, http->buffer + http->used, + HTTP_MAX_BUFFER - http->used); + else +#endif /* HAVE_SSL */ + bytes = recv(http->fd, http->buffer + http->used, + HTTP_MAX_BUFFER - http->used, 0); -int /* O - Status of call (0 = success) */ -httpHead(http_t *http, /* I - HTTP data */ - const char *uri) /* I - URI for head */ -{ - return (http_send(http, HTTP_HEAD, uri)); -} + DEBUG_printf(("httpGets: read %d bytes...\n", bytes)); + if (bytes < 0) + { + /* + * Nope, can't get a line this time... + */ -/* - * 'httpOptions()' - Send an OPTIONS request to the server. - */ +#ifdef WIN32 + if (WSAGetLastError() != http->error) + { + http->error = WSAGetLastError(); + continue; + } -int /* O - Status of call (0 = success) */ -httpOptions(http_t *http, /* I - HTTP data */ - const char *uri) /* I - URI for options */ -{ - return (http_send(http, HTTP_OPTIONS, uri)); -} + DEBUG_printf(("httpGets: recv() error %d!\n", WSAGetLastError())); +#else + DEBUG_printf(("httpGets: recv() error %d!\n", errno)); + if (errno == EINTR) + continue; + else if (errno != http->error) + { + http->error = errno; + continue; + } +#endif /* WIN32 */ -/* - * 'httpPost()' - Send a POST request to the server. - */ + return (NULL); + } + else if (bytes == 0) + { + http->error = EPIPE; -int /* O - Status of call (0 = success) */ -httpPost(http_t *http, /* I - HTTP data */ - const char *uri) /* I - URI for post */ -{ - httpGetLength(http); + return (NULL); + } - return (http_send(http, HTTP_POST, uri)); + /* + * Yup, update the amount used... + */ + + http->used += bytes; + } + + /* + * Now copy as much of the current line as possible... + */ + + for (bufptr = http->buffer, bufend = http->buffer + http->used; + lineptr < lineend && bufptr < bufend;) + { + if (*bufptr == 0x0a) + { + eol = 1; + bufptr ++; + break; + } + else if (*bufptr == 0x0d) + bufptr ++; + else + *lineptr++ = *bufptr++; + } + + http->used -= bufptr - http->buffer; + if (http->used > 0) + memmove(http->buffer, bufptr, http->used); + + if (eol) + { + /* + * End of line... + */ + + http->activity = time(NULL); + + *lineptr = '\0'; + + DEBUG_printf(("httpGets: Returning \"%s\"\n", line)); + + return (line); + } + } + + DEBUG_puts("httpGets: No new line available!"); + + return (NULL); } /* - * 'httpPut()' - Send a PUT request to the server. + * 'httpHead()' - Send a HEAD request to the server. */ int /* O - Status of call (0 = success) */ -httpPut(http_t *http, /* I - HTTP data */ - const char *uri) /* I - URI to put */ +httpHead(http_t *http, /* I - HTTP connection */ + const char *uri) /* I - URI for head */ { - httpGetLength(http); + return (http_send(http, HTTP_HEAD, uri)); +} - return (http_send(http, HTTP_PUT, uri)); + +/* + * 'httpInitialize()' - Initialize the HTTP interface library and set the + * default HTTP proxy (if any). + */ + +void +httpInitialize(void) +{ +#ifdef HAVE_LIBSSL +# ifndef WIN32 + struct timeval curtime; /* Current time in microseconds */ +# endif /* !WIN32 */ + int i; /* Looping var */ + unsigned char data[1024]; /* Seed data */ +#endif /* HAVE_LIBSSL */ + +#ifdef WIN32 + WSADATA winsockdata; /* WinSock data */ + static int initialized = 0; /* Has WinSock been initialized? */ + + + if (!initialized) + WSAStartup(MAKEWORD(1,1), &winsockdata); +#elif !defined(SO_NOSIGPIPE) + /* + * Ignore SIGPIPE signals... + */ + +# ifdef HAVE_SIGSET + sigset(SIGPIPE, SIG_IGN); +# elif defined(HAVE_SIGACTION) + struct sigaction action; /* POSIX sigaction data */ + + + memset(&action, 0, sizeof(action)); + action.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &action, NULL); +# else + signal(SIGPIPE, SIG_IGN); +# endif /* !SO_NOSIGPIPE */ +#endif /* WIN32 */ + +#ifdef HAVE_GNUTLS + gnutls_global_init(); +#endif /* HAVE_GNUTLS */ + +#ifdef HAVE_LIBSSL + SSL_load_error_strings(); + SSL_library_init(); + + /* + * Using the current time is a dubious random seed, but on some systems + * it is the best we can do (on others, this seed isn't even used...) + */ + +#ifdef WIN32 +#else + gettimeofday(&curtime, NULL); + srand(curtime.tv_sec + curtime.tv_usec); +#endif /* WIN32 */ + + for (i = 0; i < sizeof(data); i ++) + data[i] = rand(); /* Yes, this is a poor source of random data... */ + + RAND_seed(&data, sizeof(data)); +#endif /* HAVE_LIBSSL */ } /* - * 'httpTrace()' - Send an TRACE request to the server. + * 'httpOptions()' - Send an OPTIONS request to the server. */ int /* O - Status of call (0 = success) */ -httpTrace(http_t *http, /* I - HTTP data */ - const char *uri) /* I - URI for trace */ +httpOptions(http_t *http, /* I - HTTP connection */ + const char *uri) /* I - URI for options */ { - return (http_send(http, HTTP_TRACE, uri)); + return (http_send(http, HTTP_OPTIONS, uri)); } /* - * 'httpFlush()' - Flush data from a HTTP connection. + * 'httpPost()' - Send a POST request to the server. */ -void -httpFlush(http_t *http) /* I - HTTP data */ +int /* O - Status of call (0 = success) */ +httpPost(http_t *http, /* I - HTTP connection */ + const char *uri) /* I - URI for post */ { - char buffer[8192]; /* Junk buffer */ + return (http_send(http, HTTP_POST, uri)); +} + + +/* + * 'httpPrintf()' - Print a formatted string to a HTTP connection. + * + * @private@ + */ + +int /* O - Number of bytes written */ +httpPrintf(http_t *http, /* I - HTTP connection */ + const char *format, /* I - printf-style format string */ + ...) /* I - Additional args as needed */ +{ + int bytes; /* Number of bytes to write */ + char buf[16384]; /* Buffer for formatted string */ + va_list ap; /* Variable argument pointer */ + + + DEBUG_printf(("httpPrintf(http=%p, format=\"%s\", ...)\n", http, format)); + va_start(ap, format); + bytes = vsnprintf(buf, sizeof(buf), format, ap); + va_end(ap); + + DEBUG_printf(("httpPrintf: %s", buf)); - if (http->state != HTTP_WAITING) + if (http->wused) { - while (httpRead(http, buffer, sizeof(buffer)) > 0); + DEBUG_puts(" flushing existing data..."); + + if (httpFlushWrite(http) < 0) + return (-1); } + + return (http_write(http, buf, bytes)); +} + + +/* + * 'httpPut()' - Send a PUT request to the server. + */ + +int /* O - Status of call (0 = success) */ +httpPut(http_t *http, /* I - HTTP connection */ + const char *uri) /* I - URI to put */ +{ + return (http_send(http, HTTP_PUT, uri)); } /* * 'httpRead()' - Read data from a HTTP connection. + * + * This function is deprecated. Use the httpRead2() function which can + * read more than 2GB of data. + * + * @deprecated@ */ int /* O - Number of bytes read */ -httpRead(http_t *http, /* I - HTTP data */ +httpRead(http_t *http, /* I - HTTP connection */ char *buffer, /* I - Buffer for data */ int length) /* I - Maximum number of bytes */ { - int bytes; /* Bytes read */ + return ((int)httpRead2(http, buffer, length)); +} + + +/* + * 'httpRead2()' - Read data from a HTTP connection. + * + * @since CUPS 1.2@ + */ + +ssize_t /* O - Number of bytes read */ +httpRead2(http_t *http, /* I - HTTP connection */ + char *buffer, /* I - Buffer for data */ + size_t length) /* I - Maximum number of bytes */ +{ + ssize_t bytes; /* Bytes read */ char len[32]; /* Length string */ - DEBUG_printf(("httpRead(%p, %p, %d)\n", http, buffer, length)); + DEBUG_printf(("httpRead(http=%p, buffer=%p, length=%d)\n", + http, buffer, length)); if (http == NULL || buffer == NULL) return (-1); @@ -868,23 +1165,24 @@ httpRead(http_t *http, /* I - HTTP data */ if (http->data_encoding == HTTP_ENCODE_CHUNKED && http->data_remaining <= 0) { - DEBUG_puts("httpRead: Getting chunk length..."); + DEBUG_puts("httpRead2: Getting chunk length..."); if (httpGets(len, sizeof(len), http) == NULL) { - DEBUG_puts("httpRead: Could not get length!"); + DEBUG_puts("httpRead2: Could not get length!"); return (0); } - http->data_remaining = strtol(len, NULL, 16); + http->data_remaining = strtoll(len, NULL, 16); if (http->data_remaining < 0) { - DEBUG_puts("httpRead: Negative chunk length!"); + DEBUG_puts("httpRead2: Negative chunk length!"); return (0); } } - DEBUG_printf(("httpRead: data_remaining = %d\n", http->data_remaining)); + DEBUG_printf(("httpRead2: data_remaining=" CUPS_LLFMT "\n", + CUPS_LLCAST http->data_remaining)); if (http->data_remaining <= 0) { @@ -901,6 +1199,12 @@ httpRead(http_t *http, /* I - HTTP data */ else http->state = HTTP_WAITING; + /* + * Prevent future reads for this request... + */ + + http->data_encoding = HTTP_ENCODE_LENGTH; + return (0); } else if (length > http->data_remaining) @@ -926,12 +1230,12 @@ httpRead(http_t *http, /* I - HTTP data */ else #endif /* HAVE_SSL */ { - DEBUG_printf(("httpRead: reading %d bytes from socket into buffer...\n", + DEBUG_printf(("httpRead2: reading %d bytes from socket into buffer...\n", bytes)); bytes = recv(http->fd, http->buffer, bytes, 0); - DEBUG_printf(("httpRead: read %d bytes from socket into buffer...\n", + DEBUG_printf(("httpRead2: read %d bytes from socket into buffer...\n", bytes)); } @@ -964,7 +1268,7 @@ httpRead(http_t *http, /* I - HTTP data */ bytes = length; - DEBUG_printf(("httpRead: grabbing %d bytes from input buffer...\n", bytes)); + DEBUG_printf(("httpRead2: grabbing %d bytes from input buffer...\n", bytes)); memcpy(buffer, http->buffer, length); http->used -= length; @@ -986,13 +1290,24 @@ httpRead(http_t *http, /* I - HTTP data */ if (!http->blocking && !httpWait(http, 1000)) return (0); - DEBUG_printf(("httpRead: reading %d bytes from socket...\n", length)); - bytes = recv(http->fd, buffer, length, 0); - DEBUG_printf(("httpRead: read %d bytes from socket...\n", bytes)); + DEBUG_printf(("httpRead2: reading %d bytes from socket...\n", length)); + + while ((bytes = recv(http->fd, buffer, length, 0)) < 0) + if (errno != EINTR) + break; + + DEBUG_printf(("httpRead2: read %d bytes from socket...\n", bytes)); } if (bytes > 0) + { http->data_remaining -= bytes; + + if (http->data_remaining <= INT_MAX) + http->_data_remaining = (int)http->data_remaining; + else + http->_data_remaining = INT_MAX; + } else if (bytes < 0) { #ifdef WIN32 @@ -1027,7 +1342,7 @@ httpRead(http_t *http, /* I - HTTP data */ #ifdef DEBUG { int i, j, ch; - printf("httpRead: Read %d bytes:\n", bytes); + printf("httpRead2: Read %d bytes:\n", bytes); for (i = 0; i < bytes; i += 16) { printf(" "); @@ -1046,7 +1361,7 @@ httpRead(http_t *http, /* I - HTTP data */ { ch = buffer[i + j] & 255; - if (ch < ' ' || ch == 127) + if (ch < ' ' || ch >= 127) ch = '.'; putchar(ch); @@ -1060,453 +1375,230 @@ httpRead(http_t *http, /* I - HTTP data */ } +#if defined(HAVE_SSL) && defined(HAVE_CDSASSL) /* - * 'httpSetCookie()' - Set the cookie value(s)... + * '_httpReadCDSA()' - Read function for CDSA decryption code. */ -void -httpSetCookie(http_t *http, /* I - Connection */ - const char *cookie) /* I - Cookie string */ +OSStatus /* O - -1 on error, 0 on success */ +_httpReadCDSA( + SSLConnectionRef connection, /* I - SSL/TLS connection */ + void *data, /* I - Data buffer */ + size_t *dataLength) /* IO - Number of bytes */ { - if (!http) - return; + OSStatus result; /* Return value */ + ssize_t bytes; /* Number of bytes read */ - if (http->cookie) - free(http->cookie); + do + bytes = recv((int)connection, data, *dataLength, 0); + while (bytes == -1 && errno == EINTR); - if (cookie) - http->cookie = strdup(cookie); + if (bytes == *dataLength) + result = 0; + else if (bytes > 0) + { + *dataLength = bytes; + result = errSSLWouldBlock; + } else - http->cookie = NULL; + { + *dataLength = 0; + + if (bytes == 0) + result = errSSLClosedAbort; + else if (errno == EAGAIN) + result = errSSLWouldBlock; + else if (errno == EPIPE) + result = errSSLClosedAbort; + else + result = errSSLInternal; + } + + return result; } +#endif /* HAVE_SSL && HAVE_CDSASSL */ /* - * 'httpWait()' - Wait for data available on a connection. + * 'httpReconnect()' - Reconnect to a HTTP server. */ -int /* O - 1 if data is available, 0 otherwise */ -httpWait(http_t *http, /* I - HTTP data */ - int msec) /* I - Milliseconds to wait */ +int /* O - 0 on success, non-zero on failure */ +httpReconnect(http_t *http) /* I - HTTP connection */ { - /* - * First see if there is data in the buffer... - */ + http_addrlist_t *addr; /* Connected address */ - if (http == NULL) - return (0); - if (http->used) - return (1); + DEBUG_printf(("httpReconnect(http=%p)\n", http)); + + if (!http) + return (-1); + +#ifdef HAVE_SSL + if (http->tls) + http_shutdown_ssl(http); +#endif /* HAVE_SSL */ /* - * If not, check the SSL/TLS buffers and do a select() on the connection... + * Close any previously open socket... */ - return (http_wait(http, msec)); -} + if (http->fd >= 0) + { +#ifdef WIN32 + closesocket(http->fd); +#else + close(http->fd); +#endif /* WIN32 */ + http->fd = -1; + } -/* - * 'httpWrite()' - Write data to a HTTP connection. - */ - -int /* O - Number of bytes written */ -httpWrite(http_t *http, /* I - HTTP data */ - const char *buffer, /* I - Buffer for data */ - int length) /* I - Number of bytes to write */ -{ - int tbytes, /* Total bytes sent */ - bytes; /* Bytes sent */ + /* + * Connect to the server... + */ + if ((addr = httpAddrConnect(http->addrlist, &(http->fd))) == NULL) + { + /* + * Unable to connect... + */ + +#ifdef WIN32 + http->error = WSAGetLastError(); +#else + http->error = errno; +#endif /* WIN32 */ + http->status = HTTP_ERROR; - if (http == NULL || buffer == NULL) return (-1); + } - http->activity = time(NULL); + http->hostaddr = &(addr->addr); + http->error = 0; + http->status = HTTP_CONTINUE; - if (http->data_encoding == HTTP_ENCODE_CHUNKED) +#ifdef HAVE_SSL + if (http->encryption == HTTP_ENCRYPT_ALWAYS) { - if (httpPrintf(http, "%x\r\n", length) < 0) - return (-1); + /* + * Always do encryption via SSL. + */ - if (length == 0) - { - /* - * A zero-length chunk ends a transfer; unless we are sending POST - * data, go idle... - */ - - DEBUG_puts("httpWrite: changing states..."); - - if (http->state == HTTP_POST_RECV) - http->state ++; - else if (http->state == HTTP_PUT_RECV) - http->state = HTTP_STATUS; - else - http->state = HTTP_WAITING; - - if (httpPrintf(http, "\r\n") < 0) - return (-1); - - return (0); - } - } - - tbytes = 0; - - while (length > 0) - { -#ifdef HAVE_SSL - if (http->tls) - bytes = http_write_ssl(http, buffer, length); - else -#endif /* HAVE_SSL */ - bytes = send(http->fd, buffer, length, 0); - - if (bytes < 0) + if (http_setup_ssl(http) != 0) { #ifdef WIN32 - if (WSAGetLastError() != http->error) - { - http->error = WSAGetLastError(); - continue; - } + closesocket(http->fd); #else - if (errno == EINTR) - continue; - else if (errno != http->error) - { - http->error = errno; - continue; - } + close(http->fd); #endif /* WIN32 */ - DEBUG_puts("httpWrite: error writing data...\n"); - return (-1); } - - buffer += bytes; - tbytes += bytes; - length -= bytes; - if (http->data_encoding == HTTP_ENCODE_LENGTH) - http->data_remaining -= bytes; - } - - if (http->data_encoding == HTTP_ENCODE_CHUNKED) - if (httpPrintf(http, "\r\n") < 0) - return (-1); - - if (http->data_remaining == 0 && http->data_encoding == HTTP_ENCODE_LENGTH) - { - /* - * Finished with the transfer; unless we are sending POST data, go idle... - */ - - DEBUG_puts("httpWrite: changing states..."); - - if (http->state == HTTP_POST_RECV) - http->state ++; - else - http->state = HTTP_WAITING; } + else if (http->encryption == HTTP_ENCRYPT_REQUIRED) + return (http_upgrade(http)); +#endif /* HAVE_SSL */ -#ifdef DEBUG - { - int i, j, ch; - printf("httpWrite: wrote %d bytes: \n", tbytes); - for (i = 0, buffer -= tbytes; i < tbytes; i += 16) - { - printf(" "); - - for (j = 0; j < 16 && (i + j) < tbytes; j ++) - printf(" %02X", buffer[i + j] & 255); - - while (j < 16) - { - printf(" "); - j ++; - } - - printf(" "); - for (j = 0; j < 16 && (i + j) < tbytes; j ++) - { - ch = buffer[i + j] & 255; - - if (ch < ' ' || ch == 127) - ch = '.'; - - putchar(ch); - } - putchar('\n'); - } - } -#endif /* DEBUG */ - return (tbytes); + return (0); } /* - * 'httpGets()' - Get a line of text from a HTTP connection. + * 'httpSetCookie()' - Set the cookie value(s)... + * + * @since CUPS 1.1.19@ */ -char * /* O - Line or NULL */ -httpGets(char *line, /* I - Line to read into */ - int length, /* I - Max length of buffer */ - http_t *http) /* I - HTTP data */ +void +httpSetCookie(http_t *http, /* I - Connection */ + const char *cookie) /* I - Cookie string */ { - char *lineptr, /* Pointer into line */ - *bufptr, /* Pointer into input buffer */ - *bufend; /* Pointer to end of buffer */ - int bytes; /* Number of bytes read */ - - - DEBUG_printf(("httpGets(%p, %d, %p)\n", line, length, http)); - - if (http == NULL || line == NULL) - return (NULL); - - /* - * Pre-scan the buffer and see if there is a newline in there... - */ - -#ifdef WIN32 - WSASetLastError(0); -#else - errno = 0; -#endif /* WIN32 */ - - do - { - bufptr = http->buffer; - bufend = http->buffer + http->used; - - while (bufptr < bufend) - if (*bufptr == 0x0a) - break; - else - bufptr ++; - - if (bufptr >= bufend && http->used < HTTP_MAX_BUFFER) - { - /* - * No newline; see if there is more data to be read... - */ - - if (!http->blocking && !http_wait(http, 1000)) - return (NULL); - -#ifdef HAVE_SSL - if (http->tls) - bytes = http_read_ssl(http, bufend, HTTP_MAX_BUFFER - http->used); - else -#endif /* HAVE_SSL */ - bytes = recv(http->fd, bufend, HTTP_MAX_BUFFER - http->used, 0); - - if (bytes < 0) - { - /* - * Nope, can't get a line this time... - */ - -#ifdef WIN32 - if (WSAGetLastError() != http->error) - { - http->error = WSAGetLastError(); - continue; - } - - DEBUG_printf(("httpGets(): recv() error %d!\n", WSAGetLastError())); -#else - if (errno == EINTR) - continue; - else if (errno != http->error) - { - http->error = errno; - continue; - } - - DEBUG_printf(("httpGets(): recv() error %d!\n", errno)); -#endif /* WIN32 */ - - return (NULL); - } - else if (bytes == 0) - { - http->error = EPIPE; - - return (NULL); - } - - /* - * Yup, update the amount used and the end pointer... - */ - - http->used += bytes; - bufend += bytes; - bufptr = bufend; - } - } - while (bufptr >= bufend && http->used < HTTP_MAX_BUFFER); - - http->activity = time(NULL); - - /* - * Read a line from the buffer... - */ - - lineptr = line; - bufptr = http->buffer; - bytes = 0; - length --; - - while (bufptr < bufend && bytes < length) - { - bytes ++; - - if (*bufptr == 0x0a) - { - bufptr ++; - break; - } - else if (*bufptr == 0x0d) - bufptr ++; - else - *lineptr++ = *bufptr++; - } - - if (bytes > 0) - { - *lineptr = '\0'; - - http->used -= bytes; - if (http->used > 0) - memmove(http->buffer, bufptr, http->used); - - DEBUG_printf(("httpGets(): Returning \"%s\"\n", line)); - return (line); - } + if (!http) + return; - DEBUG_puts("httpGets(): No new line available!"); + if (http->cookie) + free(http->cookie); - return (NULL); + if (cookie) + http->cookie = strdup(cookie); + else + http->cookie = NULL; } /* - * 'httpPrintf()' - Print a formatted string to a HTTP connection. + * 'httpSetExpect()' - Set the Expect: header in a request. + * + * Currently only HTTP_CONTINUE is supported for the "expect" argument. + * + * @since CUPS 1.2@ */ -int /* O - Number of bytes written */ -httpPrintf(http_t *http, /* I - HTTP data */ - const char *format, /* I - printf-style format string */ - ...) /* I - Additional args as needed */ +void +httpSetExpect(http_t *http, /* I - HTTP connection */ + http_status_t expect) /* I - HTTP status to expect (HTTP_CONTINUE) */ { - int bytes, /* Number of bytes to write */ - nbytes, /* Number of bytes written */ - tbytes; /* Number of bytes all together */ - char buf[HTTP_MAX_BUFFER], /* Buffer for formatted string */ - *bufptr; /* Pointer into buffer */ - va_list ap; /* Variable argument pointer */ - - - va_start(ap, format); - bytes = vsnprintf(buf, sizeof(buf), format, ap); - va_end(ap); - - DEBUG_printf(("httpPrintf: %s", buf)); - - for (tbytes = 0, bufptr = buf; tbytes < bytes; tbytes += nbytes, bufptr += nbytes) - { -#ifdef HAVE_SSL - if (http->tls) - nbytes = http_write_ssl(http, bufptr, bytes - tbytes); - else -#endif /* HAVE_SSL */ - nbytes = send(http->fd, bufptr, bytes - tbytes, 0); - - if (nbytes < 0) - { - nbytes = 0; - -#ifdef WIN32 - if (WSAGetLastError() != http->error) - { - http->error = WSAGetLastError(); - continue; - } -#else - if (errno == EINTR) - continue; - else if (errno != http->error) - { - http->error = errno; - continue; - } -#endif /* WIN32 */ - - return (-1); - } - } - - return (bytes); + if (http) + http->expect = expect; } /* - * 'httpGetDateString()' - Get a formatted date/time string from a time value. + * 'httpSetField()' - Set the value of an HTTP header. */ -const char * /* O - Date/time string */ -httpGetDateString(time_t t) /* I - UNIX time */ +void +httpSetField(http_t *http, /* I - HTTP connection */ + http_field_t field, /* I - Field index */ + const char *value) /* I - Value */ { - struct tm *tdate; - static char datetime[256]; - - - tdate = gmtime(&t); - snprintf(datetime, sizeof(datetime), "%s, %02d %s %d %02d:%02d:%02d GMT", - days[tdate->tm_wday], tdate->tm_mday, months[tdate->tm_mon], - tdate->tm_year + 1900, tdate->tm_hour, tdate->tm_min, tdate->tm_sec); + if (http == NULL || + field < HTTP_FIELD_ACCEPT_LANGUAGE || + field > HTTP_FIELD_WWW_AUTHENTICATE || + value == NULL) + return; - return (datetime); + strlcpy(http->fields[field], value, HTTP_MAX_VALUE); } /* - * 'httpGetDateTime()' - Get a time value from a formatted date/time string. + * 'httpSetLength()' - Set the content-length and content-encoding. + * + * @since CUPS 1.2@ */ -time_t /* O - UNIX time */ -httpGetDateTime(const char *s) /* I - Date/time string */ +void +httpSetLength(http_t *http, /* I - HTTP connection */ + size_t length) /* I - Length (0 for chunked) */ { - int i; /* Looping var */ - struct tm tdate; /* Time/date structure */ - char mon[16]; /* Abbreviated month name */ - int day, year; /* Day of month and year */ - int hour, min, sec; /* Time */ - - - if (sscanf(s, "%*s%d%15s%d%d:%d:%d", &day, mon, &year, &hour, &min, &sec) < 6) - return (0); + if (!http) + return; - for (i = 0; i < 12; i ++) - if (strcasecmp(mon, months[i]) == 0) - break; + if (!length) + { + strcpy(http->fields[HTTP_FIELD_TRANSFER_ENCODING], "chunked"); + http->fields[HTTP_FIELD_CONTENT_LENGTH][0] = '\0'; + } + else + { + http->fields[HTTP_FIELD_TRANSFER_ENCODING][0] = '\0'; + snprintf(http->fields[HTTP_FIELD_CONTENT_LENGTH], HTTP_MAX_VALUE, + CUPS_LLFMT, CUPS_LLCAST length); + } +} - if (i >= 12) - return (0); - tdate.tm_mon = i; - tdate.tm_mday = day; - tdate.tm_year = year - 1900; - tdate.tm_hour = hour; - tdate.tm_min = min; - tdate.tm_sec = sec; - tdate.tm_isdst = 0; +/* + * 'httpTrace()' - Send an TRACE request to the server. + */ - return (mktime(&tdate)); +int /* O - Status of call (0 = success) */ +httpTrace(http_t *http, /* I - HTTP connection */ + const char *uri) /* I - URI for trace */ +{ + return (http_send(http, HTTP_TRACE, uri)); } @@ -1515,16 +1607,28 @@ httpGetDateTime(const char *s) /* I - Date/time string */ */ http_status_t /* O - HTTP status */ -httpUpdate(http_t *http) /* I - HTTP data */ +httpUpdate(http_t *http) /* I - HTTP connection */ { - char line[1024], /* Line from connection... */ + char line[32768], /* Line from connection... */ *value; /* Pointer to value on line */ http_field_t field; /* Field index */ - int major, minor; /* HTTP version numbers */ - http_status_t status; /* Authorization status */ + int major, minor, /* HTTP version numbers */ + status; /* Request status */ - DEBUG_printf(("httpUpdate(%p)\n", http)); + DEBUG_printf(("httpUpdate(http=%p), state=%d\n", http, http->state)); + + /* + * Flush pending data, if any... + */ + + if (http->wused) + { + DEBUG_puts(" flushing buffer..."); + + if (httpFlushWrite(http) < 0) + return (HTTP_ERROR); + } /* * If we haven't issued any commands, then there is nothing to "update"... @@ -1539,7 +1643,7 @@ httpUpdate(http_t *http) /* I - HTTP data */ while (httpGets(line, sizeof(line), http) != NULL) { - DEBUG_puts(line); + DEBUG_printf(("httpUpdate: Got \"%s\"\n", line)); if (line[0] == '\0') { @@ -1555,16 +1659,19 @@ httpUpdate(http_t *http) /* I - HTTP data */ if (http->status == HTTP_CONTINUE) return (http->status); + if (http->status < HTTP_BAD_REQUEST) + http->digest_tries = 0; + #ifdef HAVE_SSL if (http->status == HTTP_SWITCHING_PROTOCOLS && !http->tls) { if (http_setup_ssl(http) != 0) { -#ifdef WIN32 +# ifdef WIN32 closesocket(http->fd); -#else +# else close(http->fd); -#endif +# endif /* WIN32 */ return (HTTP_ERROR); } @@ -1573,7 +1680,7 @@ httpUpdate(http_t *http) /* I - HTTP data */ } #endif /* HAVE_SSL */ - httpGetLength(http); + httpGetLength2(http); switch (http->state) { @@ -1598,11 +1705,11 @@ httpUpdate(http_t *http) /* I - HTTP data */ * Got the beginning of a response... */ - if (sscanf(line, "HTTP/%d.%d%d", &major, &minor, (int *)&status) != 3) + if (sscanf(line, "HTTP/%d.%d%d", &major, &minor, &status) != 3) return (HTTP_ERROR); http->version = (http_version_t)(major * 100 + minor); - http->status = status; + http->status = (http_status_t)status; } else if ((value = strchr(line, ':')) != NULL) { @@ -1611,7 +1718,7 @@ httpUpdate(http_t *http) /* I - HTTP data */ */ *value++ = '\0'; - while (isspace(*value)) + while (isspace(*value & 255)) value ++; /* @@ -1653,8 +1760,13 @@ httpUpdate(http_t *http) /* I - HTTP data */ * See if there was an error... */ + if (http->error == EPIPE && http->status > HTTP_CONTINUE) + return (http->status); + if (http->error) { + DEBUG_printf(("httpUpdate: socket error %d - %s\n", http->error, + strerror(http->error))); http->status = HTTP_ERROR; return (HTTP_ERROR); } @@ -1668,169 +1780,214 @@ httpUpdate(http_t *http) /* I - HTTP data */ /* - * 'httpDecode64()' - Base64-decode a string. + * 'httpWait()' - Wait for data available on a connection. + * + * @since CUPS 1.1.19@ */ -char * /* O - Decoded string */ -httpDecode64(char *out, /* I - String to write to */ - const char *in) /* I - String to read from */ +int /* O - 1 if data is available, 0 otherwise */ +httpWait(http_t *http, /* I - HTTP connection */ + int msec) /* I - Milliseconds to wait */ { - int pos, /* Bit position */ - base64; /* Value of this character */ - char *outptr; /* Output pointer */ - - - for (outptr = out, pos = 0; *in != '\0'; in ++) - { - /* - * Decode this character into a number from 0 to 63... - */ - - if (*in >= 'A' && *in <= 'Z') - base64 = *in - 'A'; - else if (*in >= 'a' && *in <= 'z') - base64 = *in - 'a' + 26; - else if (*in >= '0' && *in <= '9') - base64 = *in - '0' + 52; - else if (*in == '+') - base64 = 62; - else if (*in == '/') - base64 = 63; - else if (*in == '=') - break; - else - continue; - - /* - * Store the result in the appropriate chars... - */ + /* + * First see if there is data in the buffer... + */ - switch (pos) - { - case 0 : - *outptr = base64 << 2; - pos ++; - break; - case 1 : - *outptr++ |= (base64 >> 4) & 3; - *outptr = (base64 << 4) & 255; - pos ++; - break; - case 2 : - *outptr++ |= (base64 >> 2) & 15; - *outptr = (base64 << 6) & 255; - pos ++; - break; - case 3 : - *outptr++ |= base64; - pos = 0; - break; - } - } + if (http == NULL) + return (0); - *outptr = '\0'; + if (http->used) + return (1); /* - * Return the decoded string... + * If not, check the SSL/TLS buffers and do a select() on the connection... */ - return (out); + return (http_wait(http, msec)); } /* - * 'httpEncode64()' - Base64-encode a string. + * 'httpWrite()' - Write data to a HTTP connection. + * + * This function is deprecated. Use the httpWrite2() function which can + * write more than 2GB of data. + * + * @deprecated@ */ + +int /* O - Number of bytes written */ +httpWrite(http_t *http, /* I - HTTP connection */ + const char *buffer, /* I - Buffer for data */ + int length) /* I - Number of bytes to write */ +{ + return ((int)httpWrite2(http, buffer, length)); +} -char * /* O - Encoded string */ -httpEncode64(char *out, /* I - String to write to */ - const char *in) /* I - String to read from */ + +/* + * 'httpWrite2()' - Write data to a HTTP connection. + * + * @since CUPS 1.2@ + */ + +ssize_t /* O - Number of bytes written */ +httpWrite2(http_t *http, /* I - HTTP connection */ + const char *buffer, /* I - Buffer for data */ + size_t length) /* I - Number of bytes to write */ { - char *outptr; /* Output pointer */ - static const char base64[] = /* Base64 characters... */ - { - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789" - "+/" - }; + ssize_t bytes; /* Bytes written */ - for (outptr = out; *in != '\0'; in ++) + DEBUG_printf(("httpWrite(http=%p, buffer=%p, length=%d)\n", http, + buffer, length)); + + /* + * Range check input... + */ + + if (http == NULL || buffer == NULL) + return (-1); + + /* + * Mark activity on the connection... + */ + + http->activity = time(NULL); + + /* + * Buffer small writes for better performance... + */ + + if (length > 0) { - /* - * Encode the up to 3 characters as 4 Base64 numbers... - */ + if (http->wused && (length + http->wused) > sizeof(http->wbuffer)) + { + DEBUG_printf((" flushing buffer (wused=%d, length=%d)\n", + http->wused, length)); - *outptr ++ = base64[in[0] >> 2]; - *outptr ++ = base64[((in[0] << 4) | (in[1] >> 4)) & 63]; + httpFlushWrite(http); + } - in ++; - if (*in == '\0') + if ((length + http->wused) <= sizeof(http->wbuffer)) { - *outptr ++ = '='; - break; + /* + * Write to buffer... + */ + + DEBUG_printf((" copying %d bytes to wbuffer...\n", length)); + + memcpy(http->wbuffer + http->wused, buffer, length); + http->wused += length; + bytes = length; } + else + { + /* + * Otherwise write the data directly... + */ - *outptr ++ = base64[((in[0] << 2) | (in[1] >> 6)) & 63]; + DEBUG_printf((" writing %d bytes to socket...\n", length)); - in ++; - if (*in == '\0') - break; + if (http->data_encoding == HTTP_ENCODE_CHUNKED) + bytes = http_write_chunk(http, buffer, length); + else + bytes = http_write(http, buffer, length); - *outptr ++ = base64[in[0] & 63]; - } + DEBUG_printf((" wrote %d bytes...\n", bytes)); + } - *outptr ++ = '='; - *outptr = '\0'; + if (http->data_encoding == HTTP_ENCODE_LENGTH) + http->data_remaining -= bytes; + } + else + bytes = 0; /* - * Return the encoded string... + * Handle end-of-request processing... */ - return (out); + if ((http->data_encoding == HTTP_ENCODE_CHUNKED && length == 0) || + (http->data_encoding == HTTP_ENCODE_LENGTH && http->data_remaining == 0)) + { + /* + * Finished with the transfer; unless we are sending POST or PUT + * data, go idle... + */ + + DEBUG_puts("httpWrite: changing states..."); + + if (http->wused) + httpFlushWrite(http); + + if (http->data_encoding == HTTP_ENCODE_CHUNKED) + { + /* + * Send a 0-length chunk at the end of the request... + */ + + http_write(http, "0\r\n\r\n", 5); + + /* + * Reset the data state... + */ + + http->data_encoding = HTTP_ENCODE_LENGTH; + http->data_remaining = 0; + } + + if (http->state == HTTP_POST_RECV) + http->state ++; + else if (http->state == HTTP_PUT_RECV) + http->state = HTTP_STATUS; + else + http->state = HTTP_WAITING; + } + + return (bytes); } +#if defined(HAVE_SSL) && defined(HAVE_CDSASSL) /* - * 'httpGetLength()' - Get the amount of data remaining from the - * content-length or transfer-encoding fields. + * '_httpWriteCDSA()' - Write function for CDSA encryption code. */ -int /* O - Content length */ -httpGetLength(http_t *http) /* I - HTTP data */ +OSStatus /* O - -1 on error, 0 on success */ +_httpWriteCDSA( + SSLConnectionRef connection, /* I - SSL/TLS connection */ + const void *data, /* I - Data buffer */ + size_t *dataLength) /* IO - Number of bytes */ { - DEBUG_printf(("httpGetLength(%p), state = %d\n", http, http->state)); + OSStatus result; /* Return value */ + ssize_t bytes; /* Number of bytes read */ - if (strcasecmp(http->fields[HTTP_FIELD_TRANSFER_ENCODING], "chunked") == 0) - { - DEBUG_puts("httpGetLength: chunked request!"); + do + bytes = write((int)connection, data, *dataLength); + while (bytes == -1 && errno == EINTR); - http->data_encoding = HTTP_ENCODE_CHUNKED; - http->data_remaining = 0; + if (bytes == *dataLength) + result = 0; + else if (bytes >= 0) + { + *dataLength = bytes; + result = errSSLWouldBlock; } else { - http->data_encoding = HTTP_ENCODE_LENGTH; - - /* - * The following is a hack for HTTP servers that don't send a - * content-length or transfer-encoding field... - * - * If there is no content-length then the connection must close - * after the transfer is complete... - */ - - if (http->fields[HTTP_FIELD_CONTENT_LENGTH][0] == '\0') - http->data_remaining = 2147483647; + *dataLength = 0; + + if (errno == EAGAIN) + result = errSSLWouldBlock; + else if (errno == EPIPE) + result = errSSLClosedAbort; else - http->data_remaining = atoi(http->fields[HTTP_FIELD_CONTENT_LENGTH]); - - DEBUG_printf(("httpGetLength: content_length = %d\n", http->data_remaining)); + result = errSSLInternal; } - return (http->data_remaining); + return result; } +#endif /* HAVE_SSL && HAVE_CDSASSL */ /* @@ -1847,8 +2004,61 @@ http_field(const char *name) /* I - String name */ if (strcasecmp(name, http_fields[i]) == 0) return ((http_field_t)i); - return (HTTP_FIELD_UNKNOWN); + return (HTTP_FIELD_UNKNOWN); +} + + +#ifdef HAVE_SSL +/* + * 'http_read_ssl()' - Read from a SSL/TLS connection. + */ + +static int /* O - Bytes read */ +http_read_ssl(http_t *http, /* I - HTTP connection */ + char *buf, /* I - Buffer to store data */ + int len) /* I - Length of buffer */ +{ +# if defined(HAVE_LIBSSL) + return (SSL_read((SSL *)(http->tls), buf, len)); + +# elif defined(HAVE_GNUTLS) + return (gnutls_record_recv(((http_tls_t *)(http->tls))->session, buf, len)); + +# elif defined(HAVE_CDSASSL) + int result; /* Return value */ + OSStatus error; /* Error info */ + size_t processed; /* Number of bytes processed */ + + + error = SSLRead((SSLContextRef)http->tls, buf, len, &processed); + + switch (error) + { + case 0 : + result = (int)processed; + break; + case errSSLClosedGraceful : + result = 0; + break; + case errSSLWouldBlock : + if (processed) + result = (int)processed; + else + { + result = -1; + errno = EINTR; + } + break; + default : + errno = EPIPE; + result = -1; + break; + } + + return (result); +# endif /* HAVE_LIBSSL */ } +#endif /* HAVE_SSL */ /* @@ -1856,7 +2066,7 @@ http_field(const char *name) /* I - String name */ */ static int /* O - 0 on success, non-zero on error */ -http_send(http_t *http, /* I - HTTP data */ +http_send(http_t *http, /* I - HTTP connection */ http_state_t request, /* I - Request code */ const char *uri) /* I - URI */ { @@ -1883,9 +2093,19 @@ http_send(http_t *http, /* I - HTTP data */ /* Hex digits */ + DEBUG_printf(("http_send(http=%p, request=HTTP_%s, uri=\"%s\")\n", + http, codes[request], uri)); + if (http == NULL || uri == NULL) return (-1); + /* + * Set the User-Agent field if it isn't already... + */ + + if (!http->fields[HTTP_FIELD_USER_AGENT][0]) + httpSetField(http, HTTP_FIELD_USER_AGENT, CUPS_MINIMAL); + /* * Encode the URI as needed... */ @@ -1910,7 +2130,8 @@ http_send(http_t *http, /* I - HTTP data */ */ if (http->status == HTTP_ERROR || http->status >= HTTP_BAD_REQUEST) - httpReconnect(http); + if (httpReconnect(http)) + return (-1); /* * Send the request header... @@ -1948,100 +2169,212 @@ http_send(http_t *http, /* I - HTTP data */ } } + if (http->cookie) + if (httpPrintf(http, "Cookie: $Version=0; %s\r\n", http->cookie) < 1) + { + http->status = HTTP_ERROR; + return (-1); + } + + if (http->expect == HTTP_CONTINUE && + (http->state == HTTP_POST_RECV || http->state == HTTP_PUT_RECV)) + if (httpPrintf(http, "Expect: 100-continue\r\n") < 1) + { + http->status = HTTP_ERROR; + return (-1); + } + if (httpPrintf(http, "\r\n") < 1) { http->status = HTTP_ERROR; return (-1); } + httpGetLength2(http); httpClearFields(http); return (0); } +#ifdef HAVE_SSL /* - * 'http_wait()' - Wait for data available on a connection. + * 'http_setup_ssl()' - Set up SSL/TLS support on a connection. */ -static int /* O - 1 if data is available, 0 otherwise */ -http_wait(http_t *http, /* I - HTTP data */ - int msec) /* I - Milliseconds to wait */ +static int /* O - Status of connection */ +http_setup_ssl(http_t *http) /* I - HTTP connection */ { -#ifndef WIN32 - struct rlimit limit; /* Runtime limit */ -#endif /* !WIN32 */ - struct timeval timeout; /* Timeout */ - int nfds; /* Result from select() */ +# ifdef HAVE_LIBSSL + SSL_CTX *context; /* Context for encryption */ + SSL *conn; /* Connection for encryption */ +# elif defined(HAVE_GNUTLS) + http_tls_t *conn; /* TLS session object */ + gnutls_certificate_client_credentials *credentials; + /* TLS credentials */ +# elif defined(HAVE_CDSASSL) + SSLContextRef conn; /* Context for encryption */ + OSStatus error; /* Error info */ +# endif /* HAVE_LIBSSL */ - /* - * Check the SSL/TLS buffers for data first... - */ + DEBUG_printf(("http_setup_ssl(http=%p)\n", http)); -#ifdef HAVE_SSL - if (http->tls) - { # ifdef HAVE_LIBSSL - if (SSL_pending((SSL *)(http->tls))) - return (1); -# elif defined(HAVE_GNUTLS) - if (gnutls_record_check_pending(((http_tls_t *)(http->tls))->session)) - return (1); -# elif defined(HAVE_CDSASSL) - size_t bytes; /* Bytes that are available */ + context = SSL_CTX_new(SSLv23_client_method()); - if (!SSLGetBufferedReadSize((SSLContextRef)http->tls, &bytes) && bytes > 0) - return; -# endif /* HAVE_LIBSSL */ + SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */ + + conn = SSL_new(context); + + SSL_set_fd(conn, http->fd); + if (SSL_connect(conn) != 1) + { +# ifdef DEBUG + unsigned long error; /* Error code */ + + while ((error = ERR_get_error()) != 0) + printf("http_setup_ssl: %s\n", ERR_error_string(error, NULL)); +# endif /* DEBUG */ + + SSL_CTX_free(context); + SSL_free(conn); + +# ifdef WIN32 + http->error = WSAGetLastError(); +# else + http->error = errno; +# endif /* WIN32 */ + http->status = HTTP_ERROR; + + return (HTTP_ERROR); } -#endif /* HAVE_SSL */ - /* - * Then try doing a select() to poll the socket... - */ +# elif defined(HAVE_GNUTLS) + conn = (http_tls_t *)malloc(sizeof(http_tls_t)); - if (!http->input_set) + if (conn == NULL) { -#ifdef WIN32 - /* - * Windows has a fixed-size select() structure, different (surprise, - * surprise!) from all UNIX implementations. Just allocate this - * fixed structure... - */ + http->error = errno; + http->status = HTTP_ERROR; - http->input_set = calloc(1, sizeof(fd_set)); -#else - /* - * Allocate the select() input set based upon the max number of file - * descriptors available for this process... - */ + return (-1); + } - getrlimit(RLIMIT_NOFILE, &limit); + credentials = (gnutls_certificate_client_credentials *) + malloc(sizeof(gnutls_certificate_client_credentials)); + if (credentials == NULL) + { + free(conn); - http->input_set = calloc(1, (limit.rlim_cur + 7) / 8); -#endif /* WIN32 */ + http->error = errno; + http->status = HTTP_ERROR; - if (!http->input_set) - return (0); + return (-1); } - FD_SET(http->fd, http->input_set); + gnutls_certificate_allocate_credentials(credentials); + + gnutls_init(&(conn->session), GNUTLS_CLIENT); + gnutls_set_default_priority(conn->session); + gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, *credentials); + gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr)http->fd); - if (msec >= 0) + if ((gnutls_handshake(conn->session)) != GNUTLS_E_SUCCESS) { - timeout.tv_sec = msec / 1000; - timeout.tv_usec = (msec % 1000) * 1000; + http->error = errno; + http->status = HTTP_ERROR; - nfds = select(http->fd + 1, http->input_set, NULL, NULL, &timeout); + return (-1); } - else - nfds = select(http->fd + 1, http->input_set, NULL, NULL, NULL); - FD_CLR(http->fd, http->input_set); + conn->credentials = credentials; - return (nfds > 0); +# elif defined(HAVE_CDSASSL) + error = SSLNewContext(false, &conn); + + if (!error) + error = SSLSetIOFuncs(conn, _httpReadCDSA, _httpWriteCDSA); + + if (!error) + error = SSLSetConnection(conn, (SSLConnectionRef)http->fd); + + if (!error) + error = SSLSetAllowsExpiredCerts(conn, true); + + if (!error) + error = SSLSetAllowsAnyRoot(conn, true); + + if (!error) + { + while ((error = SSLHandshake(conn)) == errSSLWouldBlock) + usleep(1000); + } + + if (error != 0) + { + http->error = error; + http->status = HTTP_ERROR; + + SSLDisposeContext(conn); + + close(http->fd); + + return (-1); + } +# endif /* HAVE_CDSASSL */ + + http->tls = conn; + return (0); +} +#endif /* HAVE_SSL */ + + +#ifdef HAVE_SSL +/* + * 'http_shutdown_ssl()' - Shut down SSL/TLS on a connection. + */ + +static void +http_shutdown_ssl(http_t *http) /* I - HTTP connection */ +{ +# ifdef HAVE_LIBSSL + SSL_CTX *context; /* Context for encryption */ + SSL *conn; /* Connection for encryption */ + + + conn = (SSL *)(http->tls); + context = SSL_get_SSL_CTX(conn); + + SSL_shutdown(conn); + SSL_CTX_free(context); + SSL_free(conn); + +# elif defined(HAVE_GNUTLS) + http_tls_t *conn; /* Encryption session */ + gnutls_certificate_client_credentials *credentials; + /* TLS credentials */ + + + conn = (http_tls_t *)(http->tls); + credentials = (gnutls_certificate_client_credentials *)(conn->credentials); + + gnutls_bye(conn->session, GNUTLS_SHUT_RDWR); + gnutls_deinit(conn->session); + gnutls_certificate_free_credentials(*credentials); + free(credentials); + free(conn); + +# elif defined(HAVE_CDSASSL) + while (SSLClose((SSLContextRef)http->tls) == errSSLWouldBlock) + usleep(1000); + + SSLDisposeContext((SSLContextRef)http->tls); +# endif /* HAVE_LIBSSL */ + + http->tls = NULL; } +#endif /* HAVE_SSL */ #ifdef HAVE_SSL @@ -2050,7 +2383,7 @@ http_wait(http_t *http, /* I - HTTP data */ */ static int /* O - Status of connection */ -http_upgrade(http_t *http) /* I - HTTP data */ +http_upgrade(http_t *http) /* I - HTTP connection */ { int ret; /* Return value */ http_t myhttp; /* Local copy of HTTP data */ @@ -2133,207 +2466,248 @@ http_upgrade(http_t *http) /* I - HTTP data */ else return (ret); } +#endif /* HAVE_SSL */ /* - * 'http_setup_ssl()' - Set up SSL/TLS support on a connection. - */ - -static int /* O - Status of connection */ -http_setup_ssl(http_t *http) /* I - HTTP data */ -{ -# ifdef HAVE_LIBSSL - SSL_CTX *context; /* Context for encryption */ - SSL *conn; /* Connection for encryption */ -# elif defined(HAVE_GNUTLS) - http_tls_t *conn; /* TLS session object */ - gnutls_certificate_client_credentials *credentials; - /* TLS credentials */ -# elif defined(HAVE_CDSASSL) - SSLContextRef conn; /* Context for encryption */ - OSStatus error; /* Error info */ -# endif /* HAVE_LIBSSL */ - - -# ifdef HAVE_LIBSSL - context = SSL_CTX_new(SSLv23_client_method()); - conn = SSL_new(context); - - SSL_set_fd(conn, http->fd); - if (SSL_connect(conn) != 1) - { - SSL_CTX_free(context); - SSL_free(conn); - -# ifdef WIN32 - http->error = WSAGetLastError(); -# else - http->error = errno; -# endif /* WIN32 */ - http->status = HTTP_ERROR; - - return (HTTP_ERROR); - } - -# elif defined(HAVE_GNUTLS) - conn = (http_tls_t *)malloc(sizeof(http_tls_t)); - - if (conn == NULL) - { - http->error = errno; - http->status = HTTP_ERROR; - - return (-1); - } + * 'http_wait()' - Wait for data available on a connection. + */ - credentials = (gnutls_certificate_client_credentials *) - malloc(sizeof(gnutls_certificate_client_credentials)); - if (credentials == NULL) - { - free(conn); +static int /* O - 1 if data is available, 0 otherwise */ +http_wait(http_t *http, /* I - HTTP connection */ + int msec) /* I - Milliseconds to wait */ +{ +#ifndef WIN32 + struct rlimit limit; /* Runtime limit */ + int set_size; /* Size of select set */ +#endif /* !WIN32 */ + struct timeval timeout; /* Timeout */ + int nfds; /* Result from select() */ - http->error = errno; - http->status = HTTP_ERROR; - return (-1); - } + DEBUG_printf(("http_wait(http=%p, msec=%d)\n", http, msec)); - gnutls_certificate_allocate_credentials(credentials); + if (http->fd < 0) + return (0); - gnutls_init(&(conn->session), GNUTLS_CLIENT); - gnutls_set_default_priority(conn->session); - gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, *credentials); - gnutls_transport_set_ptr(conn->session, http->fd); + /* + * Check the SSL/TLS buffers for data first... + */ - if ((gnutls_handshake(conn->session)) != GNUTLS_E_SUCCESS) +#ifdef HAVE_SSL + if (http->tls) { - http->error = errno; - http->status = HTTP_ERROR; +# ifdef HAVE_LIBSSL + if (SSL_pending((SSL *)(http->tls))) + return (1); +# elif defined(HAVE_GNUTLS) + if (gnutls_record_check_pending(((http_tls_t *)(http->tls))->session)) + return (1); +# elif defined(HAVE_CDSASSL) + size_t bytes; /* Bytes that are available */ - return (-1); + if (!SSLGetBufferedReadSize((SSLContextRef)http->tls, &bytes) && bytes > 0) + return (1); +# endif /* HAVE_LIBSSL */ } +#endif /* HAVE_SSL */ - conn->credentials = credentials; + /* + * Then try doing a select() to poll the socket... + */ -# elif defined(HAVE_CDSASSL) - error = SSLNewContext(false, &conn); + if (!http->input_set) + { +#ifdef WIN32 + /* + * Windows has a fixed-size select() structure, different (surprise, + * surprise!) from all UNIX implementations. Just allocate this + * fixed structure... + */ - if (!error) - error = SSLSetIOFuncs(conn, CDSAReadFunc, CDSAWriteFunc); + http->input_set = calloc(1, sizeof(fd_set)); +#else + /* + * Allocate the select() input set based upon the max number of file + * descriptors available for this process... + */ - if (!error) - error = SSLSetConnection(conn, (SSLConnectionRef)http->fd); + getrlimit(RLIMIT_NOFILE, &limit); - if (!error) - error = SSLSetAllowsExpiredCerts(conn, true); + set_size = (limit.rlim_cur + 31) / 8 + 4; + if (set_size < sizeof(fd_set)) + set_size = sizeof(fd_set); - if (!error) - error = SSLSetAllowsAnyRoot(conn, true); + http->input_set = calloc(1, set_size); +#endif /* WIN32 */ - if (!error) - error = SSLHandshake(conn); + if (!http->input_set) + return (0); + } - if (error != 0) + do { - http->error = error; - http->status = HTTP_ERROR; - - SSLDisposeContext(conn); + FD_SET(http->fd, http->input_set); - close(http->fd); + if (msec >= 0) + { + timeout.tv_sec = msec / 1000; + timeout.tv_usec = (msec % 1000) * 1000; - return (-1); + nfds = select(http->fd + 1, http->input_set, NULL, NULL, &timeout); + } + else + nfds = select(http->fd + 1, http->input_set, NULL, NULL, NULL); } -# endif /* HAVE_CDSASSL */ +#ifdef WIN32 + while (nfds < 0 && WSAGetLastError() == WSAEINTR); +#else + while (nfds < 0 && errno == EINTR); +#endif /* WIN32 */ - http->tls = conn; - return (0); + FD_CLR(http->fd, http->input_set); + + return (nfds > 0); } /* - * 'http_shutdown_ssl()' - Shut down SSL/TLS on a connection. + * 'http_write()' - Write a buffer to a HTTP connection. */ - -static void -http_shutdown_ssl(http_t *http) /* I - HTTP data */ + +static int /* O - Number of bytes written */ +http_write(http_t *http, /* I - HTTP connection */ + const char *buffer, /* I - Buffer for data */ + int length) /* I - Number of bytes to write */ { -# ifdef HAVE_LIBSSL - SSL_CTX *context; /* Context for encryption */ - SSL *conn; /* Connection for encryption */ + int tbytes, /* Total bytes sent */ + bytes; /* Bytes sent */ - conn = (SSL *)(http->tls); - context = SSL_get_SSL_CTX(conn); + tbytes = 0; - SSL_shutdown(conn); - SSL_CTX_free(context); - SSL_free(conn); + while (length > 0) + { +#ifdef HAVE_SSL + if (http->tls) + bytes = http_write_ssl(http, buffer, length); + else +#endif /* HAVE_SSL */ + bytes = send(http->fd, buffer, length, 0); -# elif defined(HAVE_GNUTLS) - http_tls_t *conn; /* Encryption session */ - gnutls_certificate_client_credentials *credentials; - /* TLS credentials */ + if (bytes < 0) + { +#ifdef WIN32 + if (WSAGetLastError() != http->error) + { + http->error = WSAGetLastError(); + continue; + } +#else + if (errno == EINTR) + continue; + else if (errno != http->error && errno != ECONNRESET) + { + http->error = errno; + continue; + } +#endif /* WIN32 */ + DEBUG_puts("http_write: error writing data...\n"); - conn = (http_tls_t *)(http->tls); - credentials = (gnutls_certificate_client_credentials *)(conn->credentials); + return (-1); + } - gnutls_bye(conn->session, GNUTLS_SHUT_RDWR); - gnutls_deinit(conn->session); - gnutls_certificate_free_credentials(*credentials); - free(credentials); - free(conn); + buffer += bytes; + tbytes += bytes; + length -= bytes; + } -# elif defined(HAVE_CDSASSL) - SSLClose((SSLContextRef)http->tls); - SSLDisposeContext((SSLContextRef)http->tls); -# endif /* HAVE_LIBSSL */ +#ifdef DEBUG + { + int i, j, ch; + printf("http_write: wrote %d bytes: \n", tbytes); + for (i = 0, buffer -= tbytes; i < tbytes; i += 16) + { + printf(" "); - http->tls = NULL; + for (j = 0; j < 16 && (i + j) < tbytes; j ++) + printf(" %02X", buffer[i + j] & 255); + + while (j < 16) + { + printf(" "); + j ++; + } + + printf(" "); + for (j = 0; j < 16 && (i + j) < tbytes; j ++) + { + ch = buffer[i + j] & 255; + + if (ch < ' ' || ch == 127) + ch = '.'; + + putchar(ch); + } + putchar('\n'); + } + } +#endif /* DEBUG */ + + return (tbytes); } /* - * 'http_read_ssl()' - Read from a SSL/TLS connection. + * 'http_write_chunk()' - Write a chunked buffer. */ -static int /* O - Bytes read */ -http_read_ssl(http_t *http, /* I - HTTP data */ - char *buf, /* I - Buffer to store data */ - int len) /* I - Length of buffer */ +static int /* O - Number bytes written */ +http_write_chunk(http_t *http, /* I - HTTP connection */ + const char *buffer, /* I - Buffer to write */ + int length) /* I - Length of buffer */ { -# if defined(HAVE_LIBSSL) - return (SSL_read((SSL *)(http->tls), buf, len)); + char header[255]; /* Chunk header */ + int bytes; /* Bytes written */ -# elif defined(HAVE_GNUTLS) - return (gnutls_record_recv(((http_tls_t *)(http->tls))->session, buf, len)); - -# elif defined(HAVE_CDSASSL) - OSStatus error; /* Error info */ - size_t processed; /* Number of bytes processed */ + DEBUG_printf(("http_write_chunk(http=%p, buffer=%p, length=%d)\n", + http, buffer, length)); + /* + * Write the chunk header, data, and trailer. + */ - error = SSLRead((SSLContextRef)http->tls, buf, len, &processed); + sprintf(header, "%x\r\n", length); + if (http_write(http, header, strlen(header)) < 0) + { + DEBUG_puts(" http_write of length failed!"); + return (-1); + } - if (error == 0) - return (processed); - else + if ((bytes = http_write(http, buffer, length)) < 0) { - http->error = error; + DEBUG_puts(" http_write of buffer failed!"); + return (-1); + } + if (http_write(http, "\r\n", 2) < 0) + { + DEBUG_puts(" http_write of CR LF failed!"); return (-1); } -# endif /* HAVE_LIBSSL */ + + return (bytes); } +#ifdef HAVE_SSL /* * 'http_write_ssl()' - Write to a SSL/TLS connection. */ static int /* O - Bytes written */ -http_write_ssl(http_t *http, /* I - HTTP data */ +http_write_ssl(http_t *http, /* I - HTTP connection */ const char *buf, /* I - Buffer holding data */ int len) /* I - Length of buffer */ { @@ -2343,72 +2717,42 @@ http_write_ssl(http_t *http, /* I - HTTP data */ # elif defined(HAVE_GNUTLS) return (gnutls_record_send(((http_tls_t *)(http->tls))->session, buf, len)); # elif defined(HAVE_CDSASSL) + int result; /* Return value */ OSStatus error; /* Error info */ size_t processed; /* Number of bytes processed */ error = SSLWrite((SSLContextRef)http->tls, buf, len, &processed); - if (error == 0) - return (processed); - else - { - http->error = error; - return (-1); - } -# endif /* HAVE_LIBSSL */ -} - - -# if defined(HAVE_CDSASSL) -/* - * 'CDSAReadFunc()' - Read function for CDSA decryption code. - */ - -static OSStatus /* O - -1 on error, 0 on success */ -CDSAReadFunc(SSLConnectionRef connection, /* I - SSL/TLS connection */ - void *data, /* I - Data buffer */ - size_t *dataLength) /* IO - Number of bytes */ -{ - ssize_t bytes; /* Number of bytes read */ - - - bytes = recv((int)connection, data, *dataLength, 0); - if (bytes >= 0) + switch (error) { - *dataLength = bytes; - return (0); + case 0 : + result = (int)processed; + break; + case errSSLClosedGraceful : + result = 0; + break; + case errSSLWouldBlock : + if (processed) + result = (int)processed; + else + { + result = -1; + errno = EINTR; + } + break; + default : + errno = EPIPE; + result = -1; + break; } - else - return (-1); -} - - -/* - * 'CDSAWriteFunc()' - Write function for CDSA encryption code. - */ - -static OSStatus /* O - -1 on error, 0 on success */ -CDSAWriteFunc(SSLConnectionRef connection, /* I - SSL/TLS connection */ - const void *data, /* I - Data buffer */ - size_t *dataLength) /* IO - Number of bytes */ -{ - ssize_t bytes; - - bytes = write((int)connection, data, *dataLength); - if (bytes >= 0) - { - *dataLength = bytes; - return (0); - } - else - return (-1); + return (result); +# endif /* HAVE_LIBSSL */ } -# endif /* HAVE_CDSASSL */ #endif /* HAVE_SSL */ /* - * End of "$Id: http.c,v 1.122 2003/08/04 18:45:51 swdev Exp $". + * End of "$Id$". */