12264Sjacobs /*
22264Sjacobs * "$Id: http.c 148 2006-04-25 16:54:17Z njacobs $"
32264Sjacobs *
42264Sjacobs * HTTP routines for the Common UNIX Printing System (CUPS).
52264Sjacobs *
62264Sjacobs * Copyright 1997-2005 by Easy Software Products, all rights reserved.
72264Sjacobs *
82264Sjacobs * These coded instructions, statements, and computer programs are the
92264Sjacobs * property of Easy Software Products and are protected by Federal
102264Sjacobs * copyright law. Distribution and use rights are outlined in the file
112264Sjacobs * "LICENSE.txt" which should have been included with this file. If this
122264Sjacobs * file is missing or damaged please contact Easy Software Products
132264Sjacobs * at:
142264Sjacobs *
152264Sjacobs * Attn: CUPS Licensing Information
162264Sjacobs * Easy Software Products
172264Sjacobs * 44141 Airport View Drive, Suite 204
182264Sjacobs * Hollywood, Maryland 20636 USA
192264Sjacobs *
202264Sjacobs * Voice: (301) 373-9600
212264Sjacobs * EMail: cups-info@cups.org
222264Sjacobs * WWW: http://www.cups.org
232264Sjacobs *
242264Sjacobs * This file is subject to the Apple OS-Developed Software exception.
252264Sjacobs *
262264Sjacobs * Contents:
272264Sjacobs *
282264Sjacobs * httpInitialize() - Initialize the HTTP interface library and set the
292264Sjacobs * default HTTP proxy (if any).
302264Sjacobs * httpCheck() - Check to see if there is a pending response from
312264Sjacobs * the server.
322264Sjacobs * httpClearCookie() - Clear the cookie value(s).
332264Sjacobs * httpClose() - Close an HTTP connection...
342264Sjacobs * httpConnect() - Connect to a HTTP server.
352264Sjacobs * httpConnectEncrypt() - Connect to a HTTP server using encryption.
362264Sjacobs * httpEncryption() - Set the required encryption on the link.
372264Sjacobs * httpReconnect() - Reconnect to a HTTP server...
382264Sjacobs * httpGetSubField() - Get a sub-field value.
392264Sjacobs * httpSetField() - Set the value of an HTTP header.
402264Sjacobs * httpDelete() - Send a DELETE request to the server.
412264Sjacobs * httpGet() - Send a GET request to the server.
422264Sjacobs * httpHead() - Send a HEAD request to the server.
432264Sjacobs * httpOptions() - Send an OPTIONS request to the server.
442264Sjacobs * httpPost() - Send a POST request to the server.
452264Sjacobs * httpPut() - Send a PUT request to the server.
462264Sjacobs * httpTrace() - Send an TRACE request to the server.
472264Sjacobs * httpFlush() - Flush data from a HTTP connection.
482264Sjacobs * httpRead() - Read data from a HTTP connection.
492264Sjacobs * httpSetCookie() - Set the cookie value(s)...
502264Sjacobs * httpWait() - Wait for data available on a connection.
512264Sjacobs * httpWrite() - Write data to a HTTP connection.
522264Sjacobs * httpGets() - Get a line of text from a HTTP connection.
532264Sjacobs * httpPrintf() - Print a formatted string to a HTTP connection.
542264Sjacobs * httpGetDateString() - Get a formatted date/time string from a time value.
552264Sjacobs * httpGetDateTime() - Get a time value from a formatted date/time string.
562264Sjacobs * httpUpdate() - Update the current HTTP state for incoming data.
572264Sjacobs * httpDecode64() - Base64-decode a string.
582264Sjacobs * httpDecode64_2() - Base64-decode a string.
592264Sjacobs * httpEncode64() - Base64-encode a string.
602264Sjacobs * httpEncode64_2() - Base64-encode a string.
612264Sjacobs * httpGetLength() - Get the amount of data remaining from the
622264Sjacobs * content-length or transfer-encoding fields.
632264Sjacobs * http_field() - Return the field index for a field name.
642264Sjacobs * http_send() - Send a request with all fields and the trailing
652264Sjacobs * blank line.
662264Sjacobs * http_wait() - Wait for data available on a connection.
672264Sjacobs * http_upgrade() - Force upgrade to TLS encryption.
682264Sjacobs * http_setup_ssl() - Set up SSL/TLS on a connection.
692264Sjacobs * http_shutdown_ssl() - Shut down SSL/TLS on a connection.
702264Sjacobs * http_read_ssl() - Read from a SSL/TLS connection.
712264Sjacobs * http_write_ssl() - Write to a SSL/TLS connection.
722264Sjacobs * CDSAReadFunc() - Read function for CDSA decryption code.
732264Sjacobs * CDSAWriteFunc() - Write function for CDSA encryption code.
742264Sjacobs */
752264Sjacobs
762264Sjacobs #pragma ident "%Z%%M% %I% %E% SMI"
772264Sjacobs
782264Sjacobs /*
792264Sjacobs * Include necessary headers...
802264Sjacobs */
812264Sjacobs
822264Sjacobs #include "http-private.h"
832264Sjacobs
842264Sjacobs #include <stdio.h>
852264Sjacobs #include <stdlib.h>
862264Sjacobs #include <stdarg.h>
872264Sjacobs #include <ctype.h>
882264Sjacobs #include "string.h"
892264Sjacobs #include <fcntl.h>
902264Sjacobs #include <errno.h>
912264Sjacobs
922264Sjacobs #include "http.h"
932264Sjacobs #include "debug.h"
942264Sjacobs
952264Sjacobs #ifndef WIN32
962264Sjacobs # include <signal.h>
972264Sjacobs # include <sys/time.h>
982264Sjacobs # include <sys/resource.h>
992264Sjacobs #endif /* !WIN32 */
1002264Sjacobs
1012264Sjacobs
1022264Sjacobs /*
1032264Sjacobs * Some operating systems have done away with the Fxxxx constants for
1042264Sjacobs * the fcntl() call; this works around that "feature"...
1052264Sjacobs */
1062264Sjacobs
1072264Sjacobs #ifndef FNONBLK
1082264Sjacobs # define FNONBLK O_NONBLOCK
1092264Sjacobs #endif /* !FNONBLK */
1102264Sjacobs
1112264Sjacobs
1122264Sjacobs /*
1132264Sjacobs * Local functions...
1142264Sjacobs */
1152264Sjacobs
1162264Sjacobs static http_field_t http_field(const char *name);
1172264Sjacobs static int http_send(http_t *http, http_state_t request,
1182264Sjacobs const char *uri);
1192264Sjacobs static int http_wait(http_t *http, int msec);
1202264Sjacobs #ifdef HAVE_SSL
1212264Sjacobs static int http_upgrade(http_t *http);
1222264Sjacobs static int http_setup_ssl(http_t *http);
1232264Sjacobs static void http_shutdown_ssl(http_t *http);
1242264Sjacobs static int http_read_ssl(http_t *http, char *buf, int len);
1252264Sjacobs static int http_write_ssl(http_t *http, const char *buf, int len);
1262264Sjacobs # ifdef HAVE_CDSASSL
1272264Sjacobs static OSStatus CDSAReadFunc(SSLConnectionRef connection, void *data, size_t *dataLength);
1282264Sjacobs static OSStatus CDSAWriteFunc(SSLConnectionRef connection, const void *data, size_t *dataLength);
1292264Sjacobs # endif /* HAVE_CDSASSL */
1302264Sjacobs #endif /* HAVE_SSL */
1312264Sjacobs
1322264Sjacobs
1332264Sjacobs /*
1342264Sjacobs * Local globals...
1352264Sjacobs */
1362264Sjacobs
1372264Sjacobs static const char * const http_fields[] =
1382264Sjacobs {
1392264Sjacobs "Accept-Language",
1402264Sjacobs "Accept-Ranges",
1412264Sjacobs "Authorization",
1422264Sjacobs "Connection",
1432264Sjacobs "Content-Encoding",
1442264Sjacobs "Content-Language",
1452264Sjacobs "Content-Length",
1462264Sjacobs "Content-Location",
1472264Sjacobs "Content-MD5",
1482264Sjacobs "Content-Range",
1492264Sjacobs "Content-Type",
1502264Sjacobs "Content-Version",
1512264Sjacobs "Date",
1522264Sjacobs "Host",
1532264Sjacobs "If-Modified-Since",
1542264Sjacobs "If-Unmodified-since",
1552264Sjacobs "Keep-Alive",
1562264Sjacobs "Last-Modified",
1572264Sjacobs "Link",
1582264Sjacobs "Location",
1592264Sjacobs "Range",
1602264Sjacobs "Referer",
1612264Sjacobs "Retry-After",
1622264Sjacobs "Transfer-Encoding",
1632264Sjacobs "Upgrade",
1642264Sjacobs "User-Agent",
1652264Sjacobs "WWW-Authenticate"
1662264Sjacobs };
1672264Sjacobs static const char * const days[7] =
1682264Sjacobs {
1692264Sjacobs "Sun",
1702264Sjacobs "Mon",
1712264Sjacobs "Tue",
1722264Sjacobs "Wed",
1732264Sjacobs "Thu",
1742264Sjacobs "Fri",
1752264Sjacobs "Sat"
1762264Sjacobs };
1772264Sjacobs static const char * const months[12] =
1782264Sjacobs {
1792264Sjacobs "Jan",
1802264Sjacobs "Feb",
1812264Sjacobs "Mar",
1822264Sjacobs "Apr",
1832264Sjacobs "May",
1842264Sjacobs "Jun",
1852264Sjacobs "Jul",
1862264Sjacobs "Aug",
1872264Sjacobs "Sep",
1882264Sjacobs "Oct",
1892264Sjacobs "Nov",
1902264Sjacobs "Dec"
1912264Sjacobs };
1922264Sjacobs
1932264Sjacobs void
httpDumpData(FILE * fp,const char * tag,const char * buffer,int bytes)194*3125Sjacobs httpDumpData(FILE *fp, const char *tag, const char *buffer, int bytes)
1952264Sjacobs {
1962264Sjacobs int i, j, ch;
1972264Sjacobs
1982264Sjacobs fprintf(fp, "%s %d(0x%x) bytes...\n", tag, bytes, bytes);
1992264Sjacobs for (i = 0; i < bytes; i += 16) {
2002264Sjacobs fprintf(fp, "%s ", (tag ? tag : ""));
2012264Sjacobs
2022264Sjacobs for (j = 0; j < 16 && (i + j) < bytes; j ++)
2032264Sjacobs fprintf(fp, " %02X", buffer[i + j] & 255);
2042264Sjacobs
2052264Sjacobs while (j < 16) {
2062264Sjacobs fprintf(fp, " ");
2072264Sjacobs j++;
2082264Sjacobs }
2092264Sjacobs
2102264Sjacobs fprintf(fp, " ");
2112264Sjacobs for (j = 0; j < 16 && (i + j) < bytes; j ++) {
2122264Sjacobs ch = buffer[i + j] & 255;
2132264Sjacobs if (ch < ' ' || ch == 127)
2142264Sjacobs ch = '.';
2152264Sjacobs putc(ch, fp);
2162264Sjacobs }
2172264Sjacobs putc('\n', fp);
2182264Sjacobs }
2192264Sjacobs }
2202264Sjacobs
2212264Sjacobs
2222264Sjacobs /*
2232264Sjacobs * 'httpInitialize()' - Initialize the HTTP interface library and set the
2242264Sjacobs * default HTTP proxy (if any).
2252264Sjacobs */
2262264Sjacobs
2272264Sjacobs void
httpInitialize(void)2282264Sjacobs httpInitialize(void)
2292264Sjacobs {
2302264Sjacobs #ifdef HAVE_LIBSSL
2312264Sjacobs # ifndef WIN32
2322264Sjacobs struct timeval curtime; /* Current time in microseconds */
2332264Sjacobs # endif /* !WIN32 */
2342264Sjacobs int i; /* Looping var */
2352264Sjacobs unsigned char data[1024]; /* Seed data */
2362264Sjacobs #endif /* HAVE_LIBSSL */
2372264Sjacobs
2382264Sjacobs #ifdef WIN32
2392264Sjacobs WSADATA winsockdata; /* WinSock data */
2402264Sjacobs static int initialized = 0; /* Has WinSock been initialized? */
2412264Sjacobs
2422264Sjacobs
2432264Sjacobs if (!initialized)
2442264Sjacobs WSAStartup(MAKEWORD(1,1), &winsockdata);
2452264Sjacobs #elif defined(HAVE_SIGSET)
2462264Sjacobs sigset(SIGPIPE, SIG_IGN);
2472264Sjacobs #elif defined(HAVE_SIGACTION)
2482264Sjacobs struct sigaction action; /* POSIX sigaction data */
2492264Sjacobs
2502264Sjacobs
2512264Sjacobs /*
2522264Sjacobs * Ignore SIGPIPE signals...
2532264Sjacobs */
2542264Sjacobs
2552264Sjacobs memset(&action, 0, sizeof(action));
2562264Sjacobs action.sa_handler = SIG_IGN;
2572264Sjacobs sigaction(SIGPIPE, &action, NULL);
2582264Sjacobs #else
2592264Sjacobs signal(SIGPIPE, SIG_IGN);
2602264Sjacobs #endif /* WIN32 */
2612264Sjacobs
2622264Sjacobs #ifdef HAVE_GNUTLS
2632264Sjacobs gnutls_global_init();
2642264Sjacobs #endif /* HAVE_GNUTLS */
2652264Sjacobs
2662264Sjacobs #ifdef HAVE_LIBSSL
2672264Sjacobs SSL_load_error_strings();
2682264Sjacobs SSL_library_init();
2692264Sjacobs
2702264Sjacobs /*
2712264Sjacobs * Using the current time is a dubious random seed, but on some systems
2722264Sjacobs * it is the best we can do (on others, this seed isn't even used...)
2732264Sjacobs */
2742264Sjacobs
2752264Sjacobs #ifdef WIN32
2762264Sjacobs #else
2772264Sjacobs gettimeofday(&curtime, NULL);
2782264Sjacobs srand(curtime.tv_sec + curtime.tv_usec);
2792264Sjacobs #endif /* WIN32 */
2802264Sjacobs
2812264Sjacobs for (i = 0; i < sizeof(data); i ++)
2822264Sjacobs data[i] = rand(); /* Yes, this is a poor source of random data... */
2832264Sjacobs
2842264Sjacobs RAND_seed(&data, sizeof(data));
2852264Sjacobs #endif /* HAVE_LIBSSL */
2862264Sjacobs }
2872264Sjacobs
2882264Sjacobs
2892264Sjacobs /*
2902264Sjacobs * 'httpCheck()' - Check to see if there is a pending response from the server.
2912264Sjacobs */
2922264Sjacobs
2932264Sjacobs int /* O - 0 = no data, 1 = data available */
httpCheck(http_t * http)2942264Sjacobs httpCheck(http_t *http) /* I - HTTP connection */
2952264Sjacobs {
2962264Sjacobs return (httpWait(http, 0));
2972264Sjacobs }
2982264Sjacobs
2992264Sjacobs
3002264Sjacobs /*
3012264Sjacobs * 'httpClearCookie()' - Clear the cookie value(s).
3022264Sjacobs */
3032264Sjacobs
3042264Sjacobs void
httpClearCookie(http_t * http)3052264Sjacobs httpClearCookie(http_t *http) /* I - Connection */
3062264Sjacobs {
3072264Sjacobs if (!http)
3082264Sjacobs return;
3092264Sjacobs
3102264Sjacobs if (http->cookie)
3112264Sjacobs {
3122264Sjacobs free(http->cookie);
3132264Sjacobs http->cookie = NULL;
3142264Sjacobs }
3152264Sjacobs }
3162264Sjacobs
3172264Sjacobs
3182264Sjacobs /*
3192264Sjacobs * 'httpClose()' - Close an HTTP connection...
3202264Sjacobs */
3212264Sjacobs
3222264Sjacobs void
httpClose(http_t * http)3232264Sjacobs httpClose(http_t *http) /* I - Connection to close */
3242264Sjacobs {
3252264Sjacobs DEBUG_printf(("httpClose(http=%p)\n", http));
3262264Sjacobs
3272264Sjacobs if (!http)
3282264Sjacobs return;
3292264Sjacobs
3302264Sjacobs if (http->input_set)
3312264Sjacobs free(http->input_set);
3322264Sjacobs
3332264Sjacobs if (http->cookie)
3342264Sjacobs free(http->cookie);
3352264Sjacobs
3362264Sjacobs #ifdef HAVE_SSL
3372264Sjacobs if (http->tls)
3382264Sjacobs http_shutdown_ssl(http);
3392264Sjacobs #endif /* HAVE_SSL */
3402264Sjacobs
3412264Sjacobs #ifdef WIN32
3422264Sjacobs closesocket(http->fd);
3432264Sjacobs #else
3442264Sjacobs close(http->fd);
3452264Sjacobs #endif /* WIN32 */
3462264Sjacobs
3472264Sjacobs free(http);
3482264Sjacobs }
3492264Sjacobs
3502264Sjacobs
3512264Sjacobs /*
3522264Sjacobs * 'httpConnect()' - Connect to a HTTP server.
3532264Sjacobs */
3542264Sjacobs
3552264Sjacobs http_t * /* O - New HTTP connection */
httpConnect(const char * host,int port)3562264Sjacobs httpConnect(const char *host, /* I - Host to connect to */
3572264Sjacobs int port) /* I - Port number */
3582264Sjacobs {
3592264Sjacobs http_encryption_t encrypt; /* Type of encryption to use */
3602264Sjacobs
3612264Sjacobs
3622264Sjacobs /*
3632264Sjacobs * Set the default encryption status...
3642264Sjacobs */
3652264Sjacobs
3662264Sjacobs if (port == 443)
3672264Sjacobs encrypt = HTTP_ENCRYPT_ALWAYS;
3682264Sjacobs else
3692264Sjacobs encrypt = HTTP_ENCRYPT_IF_REQUESTED;
3702264Sjacobs
3712264Sjacobs return (httpConnectEncrypt(host, port, encrypt));
3722264Sjacobs }
3732264Sjacobs
3742264Sjacobs
3752264Sjacobs /*
3762264Sjacobs * 'httpConnectEncrypt()' - Connect to a HTTP server using encryption.
3772264Sjacobs */
3782264Sjacobs
3792264Sjacobs http_t * /* O - New HTTP connection */
httpConnectEncrypt(const char * host,int port,http_encryption_t encrypt)3802264Sjacobs httpConnectEncrypt(const char *host, /* I - Host to connect to */
3812264Sjacobs int port, /* I - Port number */
3822264Sjacobs http_encryption_t encrypt)
3832264Sjacobs /* I - Type of encryption to use */
3842264Sjacobs {
3852264Sjacobs int i; /* Looping var */
3862264Sjacobs http_t *http; /* New HTTP connection */
3872264Sjacobs struct hostent *hostaddr; /* Host address data */
3882264Sjacobs
3892264Sjacobs
3902264Sjacobs DEBUG_printf(("httpConnectEncrypt(host=\"%s\", port=%d, encrypt=%d)\n",
3912264Sjacobs host ? host : "(null)", port, encrypt));
3922264Sjacobs
3932264Sjacobs if (!host)
3942264Sjacobs return (NULL);
3952264Sjacobs
3962264Sjacobs httpInitialize();
3972264Sjacobs
3982264Sjacobs /*
3992264Sjacobs * Lookup the host...
4002264Sjacobs */
4012264Sjacobs
4022264Sjacobs if ((hostaddr = httpGetHostByName(host)) == NULL)
4032264Sjacobs {
4042264Sjacobs /*
4052264Sjacobs * This hack to make users that don't have a localhost entry in
4062264Sjacobs * their hosts file or DNS happy...
4072264Sjacobs */
4082264Sjacobs
4092264Sjacobs if (strcasecmp(host, "localhost") != 0)
4102264Sjacobs return (NULL);
4112264Sjacobs else if ((hostaddr = httpGetHostByName("127.0.0.1")) == NULL)
4122264Sjacobs return (NULL);
4132264Sjacobs }
4142264Sjacobs
4152264Sjacobs /*
4162264Sjacobs * Verify that it is an IPv4, IPv6, or domain address...
4172264Sjacobs */
4182264Sjacobs
4192264Sjacobs if ((hostaddr->h_addrtype != AF_INET || hostaddr->h_length != 4)
4202264Sjacobs #ifdef AF_INET6
4212264Sjacobs && (hostaddr->h_addrtype != AF_INET6 || hostaddr->h_length != 16)
4222264Sjacobs #endif /* AF_INET6 */
4232264Sjacobs #ifdef AF_LOCAL
4242264Sjacobs && (hostaddr->h_addrtype != AF_LOCAL)
4252264Sjacobs #endif /* AF_LOCAL */
4262264Sjacobs )
4272264Sjacobs return (NULL);
4282264Sjacobs
4292264Sjacobs /*
4302264Sjacobs * Allocate memory for the structure...
4312264Sjacobs */
4322264Sjacobs
4332264Sjacobs http = calloc(sizeof(http_t), 1);
4342264Sjacobs if (http == NULL)
4352264Sjacobs return (NULL);
4362264Sjacobs
4372264Sjacobs http->version = HTTP_1_1;
4382264Sjacobs http->blocking = 1;
4392264Sjacobs http->activity = time(NULL);
4402264Sjacobs http->fd = -1;
4412264Sjacobs
4422264Sjacobs /*
4432264Sjacobs * Set the encryption status...
4442264Sjacobs */
4452264Sjacobs
4462264Sjacobs if (port == 443) /* Always use encryption for https */
4472264Sjacobs http->encryption = HTTP_ENCRYPT_ALWAYS;
4482264Sjacobs else
4492264Sjacobs http->encryption = encrypt;
4502264Sjacobs
4512264Sjacobs /*
4522264Sjacobs * Loop through the addresses we have until one of them connects...
4532264Sjacobs */
4542264Sjacobs
4552264Sjacobs strlcpy(http->hostname, host, sizeof(http->hostname));
4562264Sjacobs
4572264Sjacobs for (i = 0; hostaddr->h_addr_list[i]; i ++)
4582264Sjacobs {
4592264Sjacobs /*
4602264Sjacobs * Load the address...
4612264Sjacobs */
4622264Sjacobs
4632264Sjacobs httpAddrLoad(hostaddr, port, i, &(http->hostaddr));
4642264Sjacobs
4652264Sjacobs /*
4662264Sjacobs * Connect to the remote system...
4672264Sjacobs */
4682264Sjacobs
4692264Sjacobs if (!httpReconnect(http))
4702264Sjacobs return (http);
4712264Sjacobs }
4722264Sjacobs
4732264Sjacobs /*
4742264Sjacobs * Could not connect to any known address - bail out!
4752264Sjacobs */
4762264Sjacobs
4772264Sjacobs free(http);
4782264Sjacobs return (NULL);
4792264Sjacobs }
4802264Sjacobs
4812264Sjacobs
4822264Sjacobs /*
4832264Sjacobs * 'httpEncryption()' - Set the required encryption on the link.
4842264Sjacobs */
4852264Sjacobs
4862264Sjacobs int /* O - -1 on error, 0 on success */
httpEncryption(http_t * http,http_encryption_t e)4872264Sjacobs httpEncryption(http_t *http, /* I - HTTP data */
4882264Sjacobs http_encryption_t e) /* I - New encryption preference */
4892264Sjacobs {
4902264Sjacobs DEBUG_printf(("httpEncryption(http=%p, e=%d)\n", http, e));
4912264Sjacobs
4922264Sjacobs #ifdef HAVE_SSL
4932264Sjacobs if (!http)
4942264Sjacobs return (0);
4952264Sjacobs
4962264Sjacobs http->encryption = e;
4972264Sjacobs
4982264Sjacobs if ((http->encryption == HTTP_ENCRYPT_ALWAYS && !http->tls) ||
4992264Sjacobs (http->encryption == HTTP_ENCRYPT_NEVER && http->tls))
5002264Sjacobs return (httpReconnect(http));
5012264Sjacobs else if (http->encryption == HTTP_ENCRYPT_REQUIRED && !http->tls)
5022264Sjacobs return (http_upgrade(http));
5032264Sjacobs else
5042264Sjacobs return (0);
5052264Sjacobs #else
5062264Sjacobs if (e == HTTP_ENCRYPT_ALWAYS || e == HTTP_ENCRYPT_REQUIRED)
5072264Sjacobs return (-1);
5082264Sjacobs else
5092264Sjacobs return (0);
5102264Sjacobs #endif /* HAVE_SSL */
5112264Sjacobs }
5122264Sjacobs
5132264Sjacobs
5142264Sjacobs /*
5152264Sjacobs * 'httpReconnect()' - Reconnect to a HTTP server...
5162264Sjacobs */
5172264Sjacobs
5182264Sjacobs int /* O - 0 on success, non-zero on failure */
httpReconnect(http_t * http)5192264Sjacobs httpReconnect(http_t *http) /* I - HTTP data */
5202264Sjacobs {
5212264Sjacobs int val; /* Socket option value */
5222264Sjacobs int status; /* Connect status */
5232264Sjacobs
5242264Sjacobs
5252264Sjacobs DEBUG_printf(("httpReconnect(http=%p)\n", http));
5262264Sjacobs
5272264Sjacobs if (!http)
5282264Sjacobs return (-1);
5292264Sjacobs
5302264Sjacobs #ifdef HAVE_SSL
5312264Sjacobs if (http->tls)
5322264Sjacobs http_shutdown_ssl(http);
5332264Sjacobs #endif /* HAVE_SSL */
5342264Sjacobs
5352264Sjacobs /*
5362264Sjacobs * Close any previously open socket...
5372264Sjacobs */
5382264Sjacobs
5392264Sjacobs if (http->fd >= 0)
5402264Sjacobs #ifdef WIN32
5412264Sjacobs closesocket(http->fd);
5422264Sjacobs #else
5432264Sjacobs close(http->fd);
5442264Sjacobs #endif /* WIN32 */
5452264Sjacobs
5462264Sjacobs /*
5472264Sjacobs * Create the socket and set options to allow reuse.
5482264Sjacobs */
5492264Sjacobs
5502264Sjacobs if ((http->fd = socket(http->hostaddr.addr.sa_family, SOCK_STREAM, 0)) < 0)
5512264Sjacobs {
5522264Sjacobs #ifdef WIN32
5532264Sjacobs http->error = WSAGetLastError();
5542264Sjacobs #else
5552264Sjacobs http->error = errno;
5562264Sjacobs #endif /* WIN32 */
5572264Sjacobs http->status = HTTP_ERROR;
5582264Sjacobs return (-1);
5592264Sjacobs }
5602264Sjacobs
5612264Sjacobs #ifdef FD_CLOEXEC
5622264Sjacobs fcntl(http->fd, F_SETFD, FD_CLOEXEC); /* Close this socket when starting *
5632264Sjacobs * other processes... */
5642264Sjacobs #endif /* FD_CLOEXEC */
5652264Sjacobs
5662264Sjacobs val = 1;
5672264Sjacobs setsockopt(http->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val));
5682264Sjacobs
5692264Sjacobs #ifdef SO_REUSEPORT
5702264Sjacobs val = 1;
5712264Sjacobs setsockopt(http->fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
5722264Sjacobs #endif /* SO_REUSEPORT */
5732264Sjacobs
5742264Sjacobs /*
5752264Sjacobs * Using TCP_NODELAY improves responsiveness, especially on systems
5762264Sjacobs * with a slow loopback interface... Since we write large buffers
5772264Sjacobs * when sending print files and requests, there shouldn't be any
5782264Sjacobs * performance penalty for this...
5792264Sjacobs */
5802264Sjacobs
5812264Sjacobs val = 1;
5822264Sjacobs #ifdef WIN32
5832264Sjacobs setsockopt(http->fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
5842264Sjacobs #else
5852264Sjacobs setsockopt(http->fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
5862264Sjacobs #endif /* WIN32 */
5872264Sjacobs
5882264Sjacobs /*
5892264Sjacobs * Connect to the server...
5902264Sjacobs */
5912264Sjacobs
5922264Sjacobs #ifdef AF_INET6
5932264Sjacobs if (http->hostaddr.addr.sa_family == AF_INET6)
5942264Sjacobs status = connect(http->fd, (struct sockaddr *)&(http->hostaddr),
5952264Sjacobs sizeof(http->hostaddr.ipv6));
5962264Sjacobs else
5972264Sjacobs #endif /* AF_INET6 */
5982264Sjacobs #ifdef AF_LOCAL
5992264Sjacobs if (http->hostaddr.addr.sa_family == AF_LOCAL)
6002264Sjacobs status = connect(http->fd, (struct sockaddr *)&(http->hostaddr),
6012264Sjacobs SUN_LEN(&(http->hostaddr.un)));
6022264Sjacobs else
6032264Sjacobs #endif /* AF_LOCAL */
6042264Sjacobs status = connect(http->fd, (struct sockaddr *)&(http->hostaddr),
6052264Sjacobs sizeof(http->hostaddr.ipv4));
6062264Sjacobs
6072264Sjacobs if (status < 0)
6082264Sjacobs {
6092264Sjacobs #ifdef WIN32
6102264Sjacobs http->error = WSAGetLastError();
6112264Sjacobs #else
6122264Sjacobs http->error = errno;
6132264Sjacobs #endif /* WIN32 */
6142264Sjacobs http->status = HTTP_ERROR;
6152264Sjacobs
6162264Sjacobs #ifdef WIN32
6172264Sjacobs closesocket(http->fd);
6182264Sjacobs #else
6192264Sjacobs close(http->fd);
6202264Sjacobs #endif
6212264Sjacobs
6222264Sjacobs http->fd = -1;
6232264Sjacobs
6242264Sjacobs return (-1);
6252264Sjacobs }
6262264Sjacobs
6272264Sjacobs http->error = 0;
6282264Sjacobs http->status = HTTP_CONTINUE;
6292264Sjacobs
6302264Sjacobs #ifdef HAVE_SSL
6312264Sjacobs if (http->encryption == HTTP_ENCRYPT_ALWAYS)
6322264Sjacobs {
6332264Sjacobs /*
6342264Sjacobs * Always do encryption via SSL.
6352264Sjacobs */
6362264Sjacobs
6372264Sjacobs if (http_setup_ssl(http) != 0)
6382264Sjacobs {
6392264Sjacobs #ifdef WIN32
6402264Sjacobs closesocket(http->fd);
6412264Sjacobs #else
6422264Sjacobs close(http->fd);
6432264Sjacobs #endif /* WIN32 */
6442264Sjacobs
6452264Sjacobs return (-1);
6462264Sjacobs }
6472264Sjacobs }
6482264Sjacobs else if (http->encryption == HTTP_ENCRYPT_REQUIRED)
6492264Sjacobs return (http_upgrade(http));
6502264Sjacobs #endif /* HAVE_SSL */
6512264Sjacobs
6522264Sjacobs return (0);
6532264Sjacobs }
6542264Sjacobs
6552264Sjacobs
6562264Sjacobs /*
6572264Sjacobs * 'httpGetSubField()' - Get a sub-field value.
6582264Sjacobs */
6592264Sjacobs
6602264Sjacobs char * /* O - Value or NULL */
httpGetSubField(http_t * http,http_field_t field,const char * name,char * value)6612264Sjacobs httpGetSubField(http_t *http, /* I - HTTP data */
6622264Sjacobs http_field_t field, /* I - Field index */
6632264Sjacobs const char *name, /* I - Name of sub-field */
6642264Sjacobs char *value) /* O - Value string */
6652264Sjacobs {
6662264Sjacobs const char *fptr; /* Pointer into field */
6672264Sjacobs char temp[HTTP_MAX_VALUE], /* Temporary buffer for name */
6682264Sjacobs *ptr; /* Pointer into string buffer */
6692264Sjacobs
6702264Sjacobs
6712264Sjacobs DEBUG_printf(("httpGetSubField(http=%p, field=%d, name=\"%s\", value=%p)\n",
6722264Sjacobs http, field, name, value));
6732264Sjacobs
6742264Sjacobs if (http == NULL ||
6752264Sjacobs field < HTTP_FIELD_ACCEPT_LANGUAGE ||
6762264Sjacobs field > HTTP_FIELD_WWW_AUTHENTICATE ||
6772264Sjacobs name == NULL || value == NULL)
6782264Sjacobs return (NULL);
6792264Sjacobs
6802264Sjacobs for (fptr = http->fields[field]; *fptr;)
6812264Sjacobs {
6822264Sjacobs /*
6832264Sjacobs * Skip leading whitespace...
6842264Sjacobs */
6852264Sjacobs
6862264Sjacobs while (isspace(*fptr & 255))
6872264Sjacobs fptr ++;
6882264Sjacobs
6892264Sjacobs if (*fptr == ',')
6902264Sjacobs {
6912264Sjacobs fptr ++;
6922264Sjacobs continue;
6932264Sjacobs }
6942264Sjacobs
6952264Sjacobs /*
6962264Sjacobs * Get the sub-field name...
6972264Sjacobs */
6982264Sjacobs
6992264Sjacobs for (ptr = temp;
7002264Sjacobs *fptr && *fptr != '=' && !isspace(*fptr & 255) && ptr < (temp + sizeof(temp) - 1);
7012264Sjacobs *ptr++ = *fptr++);
7022264Sjacobs
7032264Sjacobs *ptr = '\0';
7042264Sjacobs
7052264Sjacobs DEBUG_printf(("httpGetSubField: name=\"%s\"\n", temp));
7062264Sjacobs
7072264Sjacobs /*
7082264Sjacobs * Skip trailing chars up to the '='...
7092264Sjacobs */
7102264Sjacobs
7112264Sjacobs while (isspace(*fptr & 255))
7122264Sjacobs fptr ++;
7132264Sjacobs
7142264Sjacobs if (!*fptr)
7152264Sjacobs break;
7162264Sjacobs
7172264Sjacobs if (*fptr != '=')
7182264Sjacobs continue;
7192264Sjacobs
7202264Sjacobs /*
7212264Sjacobs * Skip = and leading whitespace...
7222264Sjacobs */
7232264Sjacobs
7242264Sjacobs fptr ++;
7252264Sjacobs
7262264Sjacobs while (isspace(*fptr & 255))
7272264Sjacobs fptr ++;
7282264Sjacobs
7292264Sjacobs if (*fptr == '\"')
7302264Sjacobs {
7312264Sjacobs /*
7322264Sjacobs * Read quoted string...
7332264Sjacobs */
7342264Sjacobs
7352264Sjacobs for (ptr = value, fptr ++;
7362264Sjacobs *fptr && *fptr != '\"' && ptr < (value + HTTP_MAX_VALUE - 1);
7372264Sjacobs *ptr++ = *fptr++);
7382264Sjacobs
7392264Sjacobs *ptr = '\0';
7402264Sjacobs
7412264Sjacobs while (*fptr && *fptr != '\"')
7422264Sjacobs fptr ++;
7432264Sjacobs
7442264Sjacobs if (*fptr)
7452264Sjacobs fptr ++;
7462264Sjacobs }
7472264Sjacobs else
7482264Sjacobs {
7492264Sjacobs /*
7502264Sjacobs * Read unquoted string...
7512264Sjacobs */
7522264Sjacobs
7532264Sjacobs for (ptr = value;
7542264Sjacobs *fptr && !isspace(*fptr & 255) && *fptr != ',' && ptr < (value + HTTP_MAX_VALUE - 1);
7552264Sjacobs *ptr++ = *fptr++);
7562264Sjacobs
7572264Sjacobs *ptr = '\0';
7582264Sjacobs
7592264Sjacobs while (*fptr && !isspace(*fptr & 255) && *fptr != ',')
7602264Sjacobs fptr ++;
7612264Sjacobs }
7622264Sjacobs
7632264Sjacobs DEBUG_printf(("httpGetSubField: value=\"%s\"\n", value));
7642264Sjacobs
7652264Sjacobs /*
7662264Sjacobs * See if this is the one...
7672264Sjacobs */
7682264Sjacobs
7692264Sjacobs if (strcmp(name, temp) == 0)
7702264Sjacobs return (value);
7712264Sjacobs }
7722264Sjacobs
7732264Sjacobs value[0] = '\0';
7742264Sjacobs
7752264Sjacobs return (NULL);
7762264Sjacobs }
7772264Sjacobs
7782264Sjacobs
7792264Sjacobs /*
7802264Sjacobs * 'httpSetField()' - Set the value of an HTTP header.
7812264Sjacobs */
7822264Sjacobs
7832264Sjacobs void
httpSetField(http_t * http,http_field_t field,const char * value)7842264Sjacobs httpSetField(http_t *http, /* I - HTTP data */
7852264Sjacobs http_field_t field, /* I - Field index */
7862264Sjacobs const char *value) /* I - Value */
7872264Sjacobs {
7882264Sjacobs if (http == NULL ||
7892264Sjacobs field < HTTP_FIELD_ACCEPT_LANGUAGE ||
7902264Sjacobs field > HTTP_FIELD_WWW_AUTHENTICATE ||
7912264Sjacobs value == NULL)
7922264Sjacobs return;
7932264Sjacobs
7942264Sjacobs strlcpy(http->fields[field], value, HTTP_MAX_VALUE);
7952264Sjacobs }
7962264Sjacobs
7972264Sjacobs
7982264Sjacobs /*
7992264Sjacobs * 'httpDelete()' - Send a DELETE request to the server.
8002264Sjacobs */
8012264Sjacobs
8022264Sjacobs int /* O - Status of call (0 = success) */
httpDelete(http_t * http,const char * uri)8032264Sjacobs httpDelete(http_t *http, /* I - HTTP data */
8042264Sjacobs const char *uri) /* I - URI to delete */
8052264Sjacobs {
8062264Sjacobs return (http_send(http, HTTP_DELETE, uri));
8072264Sjacobs }
8082264Sjacobs
8092264Sjacobs
8102264Sjacobs /*
8112264Sjacobs * 'httpGet()' - Send a GET request to the server.
8122264Sjacobs */
8132264Sjacobs
8142264Sjacobs int /* O - Status of call (0 = success) */
httpGet(http_t * http,const char * uri)8152264Sjacobs httpGet(http_t *http, /* I - HTTP data */
8162264Sjacobs const char *uri) /* I - URI to get */
8172264Sjacobs {
8182264Sjacobs return (http_send(http, HTTP_GET, uri));
8192264Sjacobs }
8202264Sjacobs
8212264Sjacobs
8222264Sjacobs /*
8232264Sjacobs * 'httpHead()' - Send a HEAD request to the server.
8242264Sjacobs */
8252264Sjacobs
8262264Sjacobs int /* O - Status of call (0 = success) */
httpHead(http_t * http,const char * uri)8272264Sjacobs httpHead(http_t *http, /* I - HTTP data */
8282264Sjacobs const char *uri) /* I - URI for head */
8292264Sjacobs {
8302264Sjacobs return (http_send(http, HTTP_HEAD, uri));
8312264Sjacobs }
8322264Sjacobs
8332264Sjacobs
8342264Sjacobs /*
8352264Sjacobs * 'httpOptions()' - Send an OPTIONS request to the server.
8362264Sjacobs */
8372264Sjacobs
8382264Sjacobs int /* O - Status of call (0 = success) */
httpOptions(http_t * http,const char * uri)8392264Sjacobs httpOptions(http_t *http, /* I - HTTP data */
8402264Sjacobs const char *uri) /* I - URI for options */
8412264Sjacobs {
8422264Sjacobs return (http_send(http, HTTP_OPTIONS, uri));
8432264Sjacobs }
8442264Sjacobs
8452264Sjacobs
8462264Sjacobs /*
8472264Sjacobs * 'httpPost()' - Send a POST request to the server.
8482264Sjacobs */
8492264Sjacobs
8502264Sjacobs int /* O - Status of call (0 = success) */
httpPost(http_t * http,const char * uri)8512264Sjacobs httpPost(http_t *http, /* I - HTTP data */
8522264Sjacobs const char *uri) /* I - URI for post */
8532264Sjacobs {
8542264Sjacobs httpGetLength(http);
8552264Sjacobs
8562264Sjacobs return (http_send(http, HTTP_POST, uri));
8572264Sjacobs }
8582264Sjacobs
8592264Sjacobs
8602264Sjacobs /*
8612264Sjacobs * 'httpPut()' - Send a PUT request to the server.
8622264Sjacobs */
8632264Sjacobs
8642264Sjacobs int /* O - Status of call (0 = success) */
httpPut(http_t * http,const char * uri)8652264Sjacobs httpPut(http_t *http, /* I - HTTP data */
8662264Sjacobs const char *uri) /* I - URI to put */
8672264Sjacobs {
8682264Sjacobs httpGetLength(http);
8692264Sjacobs
8702264Sjacobs return (http_send(http, HTTP_PUT, uri));
8712264Sjacobs }
8722264Sjacobs
8732264Sjacobs
8742264Sjacobs /*
8752264Sjacobs * 'httpTrace()' - Send an TRACE request to the server.
8762264Sjacobs */
8772264Sjacobs
8782264Sjacobs int /* O - Status of call (0 = success) */
httpTrace(http_t * http,const char * uri)8792264Sjacobs httpTrace(http_t *http, /* I - HTTP data */
8802264Sjacobs const char *uri) /* I - URI for trace */
8812264Sjacobs {
8822264Sjacobs return (http_send(http, HTTP_TRACE, uri));
8832264Sjacobs }
8842264Sjacobs
8852264Sjacobs
8862264Sjacobs /*
8872264Sjacobs * 'httpFlush()' - Flush data from a HTTP connection.
8882264Sjacobs */
8892264Sjacobs
8902264Sjacobs void
httpFlush(http_t * http)8912264Sjacobs httpFlush(http_t *http) /* I - HTTP data */
8922264Sjacobs {
8932264Sjacobs char buffer[8192]; /* Junk buffer */
8942264Sjacobs
8952264Sjacobs
8962264Sjacobs DEBUG_printf(("httpFlush(http=%p), state=%d\n", http, http->state));
8972264Sjacobs
8982264Sjacobs while (httpRead(http, buffer, sizeof(buffer)) > 0);
8992264Sjacobs }
9002264Sjacobs
9012264Sjacobs
9022264Sjacobs /*
9032264Sjacobs * 'httpRead()' - Read data from a HTTP connection.
9042264Sjacobs */
9052264Sjacobs
9062264Sjacobs int /* O - Number of bytes read */
httpRead(http_t * http,char * buffer,int length)9072264Sjacobs httpRead(http_t *http, /* I - HTTP data */
9082264Sjacobs char *buffer, /* I - Buffer for data */
9092264Sjacobs int length) /* I - Maximum number of bytes */
9102264Sjacobs {
9112264Sjacobs int bytes; /* Bytes read */
9122264Sjacobs char len[32]; /* Length string */
9132264Sjacobs
9142264Sjacobs
9152264Sjacobs DEBUG_printf(("httpRead(http=%p, buffer=%p, length=%d)\n",
9162264Sjacobs http, buffer, length));
9172264Sjacobs
9182264Sjacobs if (http == NULL || buffer == NULL)
9192264Sjacobs return (-1);
9202264Sjacobs
9212264Sjacobs http->activity = time(NULL);
9222264Sjacobs
9232264Sjacobs if (length <= 0)
9242264Sjacobs return (0);
9252264Sjacobs
9262264Sjacobs if (http->data_encoding == HTTP_ENCODE_CHUNKED &&
9272264Sjacobs http->data_remaining <= 0)
9282264Sjacobs {
9292264Sjacobs DEBUG_puts("httpRead: Getting chunk length...");
9302264Sjacobs
9312264Sjacobs if (httpGets(len, sizeof(len), http) == NULL)
9322264Sjacobs {
9332264Sjacobs DEBUG_puts("httpRead: Could not get length!");
9342264Sjacobs return (0);
9352264Sjacobs }
9362264Sjacobs
9372264Sjacobs http->data_remaining = strtol(len, NULL, 16);
9382264Sjacobs if (http->data_remaining < 0)
9392264Sjacobs {
9402264Sjacobs DEBUG_puts("httpRead: Negative chunk length!");
9412264Sjacobs return (0);
9422264Sjacobs }
9432264Sjacobs }
9442264Sjacobs
9452264Sjacobs DEBUG_printf(("httpRead: data_remaining=%d\n", http->data_remaining));
9462264Sjacobs
9472264Sjacobs if (http->data_remaining <= 0)
9482264Sjacobs {
9492264Sjacobs /*
9502264Sjacobs * A zero-length chunk ends a transfer; unless we are reading POST
9512264Sjacobs * data, go idle...
9522264Sjacobs */
9532264Sjacobs
9542264Sjacobs if (http->data_encoding == HTTP_ENCODE_CHUNKED)
9552264Sjacobs httpGets(len, sizeof(len), http);
9562264Sjacobs
9572264Sjacobs if (http->state == HTTP_POST_RECV)
9582264Sjacobs http->state ++;
9592264Sjacobs else
9602264Sjacobs http->state = HTTP_WAITING;
9612264Sjacobs
9622264Sjacobs /*
9632264Sjacobs * Prevent future reads for this request...
9642264Sjacobs */
9652264Sjacobs
9662264Sjacobs http->data_encoding = HTTP_ENCODE_LENGTH;
9672264Sjacobs
9682264Sjacobs return (0);
9692264Sjacobs }
9702264Sjacobs else if (length > http->data_remaining)
9712264Sjacobs length = http->data_remaining;
9722264Sjacobs
9732264Sjacobs if (http->used == 0 && length <= 256)
9742264Sjacobs {
9752264Sjacobs /*
9762264Sjacobs * Buffer small reads for better performance...
9772264Sjacobs */
9782264Sjacobs
9792264Sjacobs if (!http->blocking && !httpWait(http, 1000))
9802264Sjacobs return (0);
9812264Sjacobs
9822264Sjacobs if (http->data_remaining > sizeof(http->buffer))
9832264Sjacobs bytes = sizeof(http->buffer);
9842264Sjacobs else
9852264Sjacobs bytes = http->data_remaining;
9862264Sjacobs
9872264Sjacobs #ifdef HAVE_SSL
9882264Sjacobs if (http->tls)
9892264Sjacobs bytes = http_read_ssl(http, http->buffer, bytes);
9902264Sjacobs else
9912264Sjacobs #endif /* HAVE_SSL */
9922264Sjacobs {
9932264Sjacobs DEBUG_printf(("httpRead: reading %d bytes from socket into buffer...\n",
9942264Sjacobs bytes));
9952264Sjacobs
9962264Sjacobs bytes = recv(http->fd, http->buffer, bytes, 0);
9972264Sjacobs
9982264Sjacobs DEBUG_printf(("httpRead: read %d bytes from socket into buffer...\n",
9992264Sjacobs bytes));
10002264Sjacobs #ifdef DEBUG_HTTP
10012264Sjacobs httpDumpData(stdout, "httpRead:", http->buffer, bytes);
10022264Sjacobs #endif
10032264Sjacobs }
10042264Sjacobs
10052264Sjacobs if (bytes > 0)
10062264Sjacobs http->used = bytes;
10072264Sjacobs else if (bytes < 0)
10082264Sjacobs {
10092264Sjacobs #ifdef WIN32
10102264Sjacobs http->error = WSAGetLastError();
10112264Sjacobs return (-1);
10122264Sjacobs #else
10132264Sjacobs if (errno != EINTR)
10142264Sjacobs {
10152264Sjacobs http->error = errno;
10162264Sjacobs return (-1);
10172264Sjacobs }
10182264Sjacobs #endif /* WIN32 */
10192264Sjacobs }
10202264Sjacobs else
10212264Sjacobs {
10222264Sjacobs http->error = EPIPE;
10232264Sjacobs return (0);
10242264Sjacobs }
10252264Sjacobs }
10262264Sjacobs
10272264Sjacobs if (http->used > 0)
10282264Sjacobs {
10292264Sjacobs if (length > http->used)
10302264Sjacobs length = http->used;
10312264Sjacobs
10322264Sjacobs bytes = length;
10332264Sjacobs
10342264Sjacobs DEBUG_printf(("httpRead: grabbing %d bytes from input buffer...\n", bytes));
10352264Sjacobs
10362264Sjacobs memcpy(buffer, http->buffer, length);
10372264Sjacobs http->used -= length;
10382264Sjacobs
10392264Sjacobs if (http->used > 0)
10402264Sjacobs memmove(http->buffer, http->buffer + length, http->used);
10412264Sjacobs }
10422264Sjacobs #ifdef HAVE_SSL
10432264Sjacobs else if (http->tls)
10442264Sjacobs {
10452264Sjacobs if (!http->blocking && !httpWait(http, 1000))
10462264Sjacobs return (0);
10472264Sjacobs
10482264Sjacobs bytes = http_read_ssl(http, buffer, length);
10492264Sjacobs }
10502264Sjacobs #endif /* HAVE_SSL */
10512264Sjacobs else
10522264Sjacobs {
10532264Sjacobs if (!http->blocking && !httpWait(http, 1000))
10542264Sjacobs return (0);
10552264Sjacobs
10562264Sjacobs DEBUG_printf(("httpRead: reading %d bytes from socket...\n", length));
10572264Sjacobs
10582264Sjacobs while ((bytes = recv(http->fd, buffer, length, 0)) < 0)
10592264Sjacobs if (errno != EINTR)
10602264Sjacobs break;
10612264Sjacobs DEBUG_printf(("httpRead: read %d bytes from socket...\n", bytes));
10622264Sjacobs }
10632264Sjacobs #ifdef DEBUG_HTTP
10642264Sjacobs httpDumpData(stdout, "httpRead:", buffer, bytes);
10652264Sjacobs #endif
10662264Sjacobs
10672264Sjacobs if (bytes > 0)
10682264Sjacobs http->data_remaining -= bytes;
10692264Sjacobs else if (bytes < 0)
10702264Sjacobs {
10712264Sjacobs #ifdef WIN32
10722264Sjacobs http->error = WSAGetLastError();
10732264Sjacobs #else
10742264Sjacobs if (errno == EINTR)
10752264Sjacobs bytes = 0;
10762264Sjacobs else
10772264Sjacobs http->error = errno;
10782264Sjacobs #endif /* WIN32 */
10792264Sjacobs }
10802264Sjacobs else
10812264Sjacobs {
10822264Sjacobs http->error = EPIPE;
10832264Sjacobs return (0);
10842264Sjacobs }
10852264Sjacobs
10862264Sjacobs if (http->data_remaining == 0)
10872264Sjacobs {
10882264Sjacobs if (http->data_encoding == HTTP_ENCODE_CHUNKED)
10892264Sjacobs httpGets(len, sizeof(len), http);
10902264Sjacobs
10912264Sjacobs if (http->data_encoding != HTTP_ENCODE_CHUNKED)
10922264Sjacobs {
10932264Sjacobs if (http->state == HTTP_POST_RECV)
10942264Sjacobs http->state ++;
10952264Sjacobs else
10962264Sjacobs http->state = HTTP_WAITING;
10972264Sjacobs }
10982264Sjacobs }
10992264Sjacobs
11002264Sjacobs return (bytes);
11012264Sjacobs }
11022264Sjacobs
11032264Sjacobs
11042264Sjacobs /*
11052264Sjacobs * 'httpSetCookie()' - Set the cookie value(s)...
11062264Sjacobs */
11072264Sjacobs
11082264Sjacobs void
httpSetCookie(http_t * http,const char * cookie)11092264Sjacobs httpSetCookie(http_t *http, /* I - Connection */
11102264Sjacobs const char *cookie) /* I - Cookie string */
11112264Sjacobs {
11122264Sjacobs if (!http)
11132264Sjacobs return;
11142264Sjacobs
11152264Sjacobs if (http->cookie)
11162264Sjacobs free(http->cookie);
11172264Sjacobs
11182264Sjacobs if (cookie)
11192264Sjacobs http->cookie = strdup(cookie);
11202264Sjacobs else
11212264Sjacobs http->cookie = NULL;
11222264Sjacobs }
11232264Sjacobs
11242264Sjacobs
11252264Sjacobs /*
11262264Sjacobs * 'httpWait()' - Wait for data available on a connection.
11272264Sjacobs */
11282264Sjacobs
11292264Sjacobs int /* O - 1 if data is available, 0 otherwise */
httpWait(http_t * http,int msec)11302264Sjacobs httpWait(http_t *http, /* I - HTTP data */
11312264Sjacobs int msec) /* I - Milliseconds to wait */
11322264Sjacobs {
11332264Sjacobs /*
11342264Sjacobs * First see if there is data in the buffer...
11352264Sjacobs */
11362264Sjacobs
11372264Sjacobs if (http == NULL)
11382264Sjacobs return (0);
11392264Sjacobs
11402264Sjacobs if (http->used)
11412264Sjacobs return (1);
11422264Sjacobs
11432264Sjacobs /*
11442264Sjacobs * If not, check the SSL/TLS buffers and do a select() on the connection...
11452264Sjacobs */
11462264Sjacobs
11472264Sjacobs return (http_wait(http, msec));
11482264Sjacobs }
11492264Sjacobs
11502264Sjacobs
11512264Sjacobs /*
11522264Sjacobs * 'httpWrite()' - Write data to a HTTP connection.
11532264Sjacobs */
11542264Sjacobs
11552264Sjacobs int /* O - Number of bytes written */
httpWrite(http_t * http,const char * buffer,int length)11562264Sjacobs httpWrite(http_t *http, /* I - HTTP data */
11572264Sjacobs const char *buffer, /* I - Buffer for data */
11582264Sjacobs int length) /* I - Number of bytes to write */
11592264Sjacobs {
11602264Sjacobs int tbytes, /* Total bytes sent */
11612264Sjacobs bytes; /* Bytes sent */
11622264Sjacobs
11632264Sjacobs
11642264Sjacobs if (http == NULL || buffer == NULL)
11652264Sjacobs return (-1);
11662264Sjacobs
11672264Sjacobs http->activity = time(NULL);
11682264Sjacobs
11692264Sjacobs if (http->data_encoding == HTTP_ENCODE_CHUNKED)
11702264Sjacobs {
11712264Sjacobs if (httpPrintf(http, "%x\r\n", length) < 0)
11722264Sjacobs return (-1);
11732264Sjacobs
11742264Sjacobs if (length == 0)
11752264Sjacobs {
11762264Sjacobs /*
11772264Sjacobs * A zero-length chunk ends a transfer; unless we are sending POST
11782264Sjacobs * or PUT data, go idle...
11792264Sjacobs */
11802264Sjacobs
11812264Sjacobs DEBUG_printf(("httpWrite: changing states from %d", http->state));
11822264Sjacobs
11832264Sjacobs if (http->state == HTTP_POST_RECV)
11842264Sjacobs http->state ++;
11852264Sjacobs else if (http->state == HTTP_PUT_RECV)
11862264Sjacobs http->state = HTTP_STATUS;
11872264Sjacobs else
11882264Sjacobs http->state = HTTP_WAITING;
11892264Sjacobs DEBUG_printf((" to %d\n", http->state));
11902264Sjacobs
11912264Sjacobs if (httpPrintf(http, "\r\n") < 0)
11922264Sjacobs return (-1);
11932264Sjacobs
11942264Sjacobs return (0);
11952264Sjacobs }
11962264Sjacobs }
11972264Sjacobs
11982264Sjacobs tbytes = 0;
11992264Sjacobs
12002264Sjacobs while (length > 0)
12012264Sjacobs {
12022264Sjacobs #ifdef HAVE_SSL
12032264Sjacobs if (http->tls)
12042264Sjacobs bytes = http_write_ssl(http, buffer, length);
12052264Sjacobs else
12062264Sjacobs #endif /* HAVE_SSL */
12072264Sjacobs bytes = send(http->fd, buffer, length, 0);
12082264Sjacobs
12092264Sjacobs #ifdef DEBUG_HTTP
12102264Sjacobs if (bytes >= 0)
12112264Sjacobs httpDumpData(stdout, "httpWrite:", buffer, bytes);
12122264Sjacobs #endif /* DEBUG */
12132264Sjacobs
12142264Sjacobs
12152264Sjacobs if (bytes < 0)
12162264Sjacobs {
12172264Sjacobs #ifdef WIN32
12182264Sjacobs if (WSAGetLastError() != http->error)
12192264Sjacobs {
12202264Sjacobs http->error = WSAGetLastError();
12212264Sjacobs continue;
12222264Sjacobs }
12232264Sjacobs #else
12242264Sjacobs if (errno == EINTR)
12252264Sjacobs continue;
12262264Sjacobs else if (errno != http->error && errno != ECONNRESET)
12272264Sjacobs {
12282264Sjacobs http->error = errno;
12292264Sjacobs continue;
12302264Sjacobs }
12312264Sjacobs #endif /* WIN32 */
12322264Sjacobs
12332264Sjacobs DEBUG_puts("httpWrite: error writing data...\n");
12342264Sjacobs
12352264Sjacobs return (-1);
12362264Sjacobs }
12372264Sjacobs
12382264Sjacobs buffer += bytes;
12392264Sjacobs tbytes += bytes;
12402264Sjacobs length -= bytes;
12412264Sjacobs if (http->data_encoding == HTTP_ENCODE_LENGTH)
12422264Sjacobs http->data_remaining -= bytes;
12432264Sjacobs }
12442264Sjacobs
12452264Sjacobs if (http->data_encoding == HTTP_ENCODE_CHUNKED)
12462264Sjacobs if (httpPrintf(http, "\r\n") < 0)
12472264Sjacobs return (-1);
12482264Sjacobs
12492264Sjacobs if (http->data_remaining == 0 && http->data_encoding == HTTP_ENCODE_LENGTH)
12502264Sjacobs {
12512264Sjacobs /*
12522264Sjacobs * Finished with the transfer; unless we are sending POST or PUT
12532264Sjacobs * data, go idle...
12542264Sjacobs */
12552264Sjacobs
12562264Sjacobs DEBUG_printf(("httpWrite: changing states from %d", http->state));
12572264Sjacobs
12582264Sjacobs if (http->state == HTTP_POST_RECV)
12592264Sjacobs http->state ++;
12602264Sjacobs else if (http->state == HTTP_PUT_RECV)
12612264Sjacobs http->state = HTTP_STATUS;
12622264Sjacobs else
12632264Sjacobs http->state = HTTP_WAITING;
12642264Sjacobs
12652264Sjacobs DEBUG_printf((" to %d\n", http->state));
12662264Sjacobs }
12672264Sjacobs
12682264Sjacobs return (tbytes);
12692264Sjacobs }
12702264Sjacobs
12712264Sjacobs
12722264Sjacobs /*
12732264Sjacobs * 'httpGets()' - Get a line of text from a HTTP connection.
12742264Sjacobs */
12752264Sjacobs
12762264Sjacobs char * /* O - Line or NULL */
httpGets(char * line,int length,http_t * http)12772264Sjacobs httpGets(char *line, /* I - Line to read into */
12782264Sjacobs int length, /* I - Max length of buffer */
12792264Sjacobs http_t *http) /* I - HTTP data */
12802264Sjacobs {
12812264Sjacobs char *lineptr, /* Pointer into line */
12822264Sjacobs *bufptr, /* Pointer into input buffer */
12832264Sjacobs *bufend; /* Pointer to end of buffer */
12842264Sjacobs int bytes; /* Number of bytes read */
12852264Sjacobs
12862264Sjacobs
12872264Sjacobs DEBUG_printf(("httpGets(line=%p, length=%d, http=%p)\n", line, length, http));
12882264Sjacobs
12892264Sjacobs if (http == NULL || line == NULL)
12902264Sjacobs return (NULL);
12912264Sjacobs
12922264Sjacobs /*
12932264Sjacobs * Pre-scan the buffer and see if there is a newline in there...
12942264Sjacobs */
12952264Sjacobs
12962264Sjacobs #ifdef WIN32
12972264Sjacobs WSASetLastError(0);
12982264Sjacobs #else
12992264Sjacobs errno = 0;
13002264Sjacobs #endif /* WIN32 */
13012264Sjacobs
13022264Sjacobs do
13032264Sjacobs {
13042264Sjacobs bufptr = http->buffer;
13052264Sjacobs bufend = http->buffer + http->used;
13062264Sjacobs
13072264Sjacobs while (bufptr < bufend)
13082264Sjacobs if (*bufptr == 0x0a)
13092264Sjacobs break;
13102264Sjacobs else
13112264Sjacobs bufptr ++;
13122264Sjacobs
13132264Sjacobs if (bufptr >= bufend && http->used < HTTP_MAX_BUFFER)
13142264Sjacobs {
13152264Sjacobs /*
13162264Sjacobs * No newline; see if there is more data to be read...
13172264Sjacobs */
13182264Sjacobs
13192264Sjacobs if (!http->blocking && !http_wait(http, 1000))
13202264Sjacobs return (NULL);
13212264Sjacobs
13222264Sjacobs #ifdef HAVE_SSL
13232264Sjacobs if (http->tls)
13242264Sjacobs bytes = http_read_ssl(http, bufend, HTTP_MAX_BUFFER - http->used);
13252264Sjacobs else
13262264Sjacobs #endif /* HAVE_SSL */
13272264Sjacobs bytes = recv(http->fd, bufend, HTTP_MAX_BUFFER - http->used, 0);
13282264Sjacobs
13292264Sjacobs DEBUG_printf(("httpGets: read %d bytes...\n", bytes));
13302264Sjacobs #ifdef DEBUG_HTTP
13312264Sjacobs httpDumpData(stdout, "httpGets:", bufend, bytes);
13322264Sjacobs #endif
13332264Sjacobs
13342264Sjacobs if (bytes < 0)
13352264Sjacobs {
13362264Sjacobs /*
13372264Sjacobs * Nope, can't get a line this time...
13382264Sjacobs */
13392264Sjacobs
13402264Sjacobs #ifdef WIN32
13412264Sjacobs if (WSAGetLastError() != http->error)
13422264Sjacobs {
13432264Sjacobs http->error = WSAGetLastError();
13442264Sjacobs continue;
13452264Sjacobs }
13462264Sjacobs
13472264Sjacobs DEBUG_printf(("httpGets: recv() error %d!\n", WSAGetLastError()));
13482264Sjacobs #else
13492264Sjacobs DEBUG_printf(("httpGets: recv() error %d!\n", errno));
13502264Sjacobs
13512264Sjacobs if (errno == EINTR)
13522264Sjacobs continue;
13532264Sjacobs else if (errno != http->error)
13542264Sjacobs {
13552264Sjacobs http->error = errno;
13562264Sjacobs continue;
13572264Sjacobs }
13582264Sjacobs #endif /* WIN32 */
13592264Sjacobs
13602264Sjacobs return (NULL);
13612264Sjacobs }
13622264Sjacobs else if (bytes == 0)
13632264Sjacobs {
13642264Sjacobs http->error = EPIPE;
13652264Sjacobs
13662264Sjacobs return (NULL);
13672264Sjacobs }
13682264Sjacobs
13692264Sjacobs /*
13702264Sjacobs * Yup, update the amount used and the end pointer...
13712264Sjacobs */
13722264Sjacobs
13732264Sjacobs http->used += bytes;
13742264Sjacobs bufend += bytes;
13752264Sjacobs bufptr = bufend;
13762264Sjacobs }
13772264Sjacobs }
13782264Sjacobs while (bufptr >= bufend && http->used < HTTP_MAX_BUFFER);
13792264Sjacobs
13802264Sjacobs http->activity = time(NULL);
13812264Sjacobs
13822264Sjacobs /*
13832264Sjacobs * Read a line from the buffer...
13842264Sjacobs */
13852264Sjacobs
13862264Sjacobs lineptr = line;
13872264Sjacobs bufptr = http->buffer;
13882264Sjacobs bytes = 0;
13892264Sjacobs length --;
13902264Sjacobs
13912264Sjacobs while (bufptr < bufend && bytes < length)
13922264Sjacobs {
13932264Sjacobs bytes ++;
13942264Sjacobs
13952264Sjacobs if (*bufptr == 0x0a)
13962264Sjacobs {
13972264Sjacobs bufptr ++;
13982264Sjacobs break;
13992264Sjacobs }
14002264Sjacobs else if (*bufptr == 0x0d)
14012264Sjacobs bufptr ++;
14022264Sjacobs else
14032264Sjacobs *lineptr++ = *bufptr++;
14042264Sjacobs }
14052264Sjacobs
14062264Sjacobs if (bytes > 0)
14072264Sjacobs {
14082264Sjacobs *lineptr = '\0';
14092264Sjacobs
14102264Sjacobs http->used -= bytes;
14112264Sjacobs if (http->used > 0)
14122264Sjacobs memmove(http->buffer, bufptr, http->used);
14132264Sjacobs
14142264Sjacobs DEBUG_printf(("httpGets: Returning \"%s\"\n", line));
14152264Sjacobs return (line);
14162264Sjacobs }
14172264Sjacobs
14182264Sjacobs DEBUG_puts("httpGets: No new line available!");
14192264Sjacobs
14202264Sjacobs return (NULL);
14212264Sjacobs }
14222264Sjacobs
14232264Sjacobs
14242264Sjacobs /*
14252264Sjacobs * 'httpPrintf()' - Print a formatted string to a HTTP connection.
14262264Sjacobs */
14272264Sjacobs
14282264Sjacobs int /* O - Number of bytes written */
httpPrintf(http_t * http,const char * format,...)14292264Sjacobs httpPrintf(http_t *http, /* I - HTTP data */
14302264Sjacobs const char *format, /* I - printf-style format string */
14312264Sjacobs ...) /* I - Additional args as needed */
14322264Sjacobs {
14332264Sjacobs int bytes, /* Number of bytes to write */
14342264Sjacobs nbytes, /* Number of bytes written */
14352264Sjacobs tbytes; /* Number of bytes all together */
14362264Sjacobs char buf[HTTP_MAX_BUFFER], /* Buffer for formatted string */
14372264Sjacobs *bufptr; /* Pointer into buffer */
14382264Sjacobs va_list ap; /* Variable argument pointer */
14392264Sjacobs
14402264Sjacobs
14412264Sjacobs DEBUG_printf(("httpPrintf: httpPrintf(http=%p, format=\"%s\", ...)\n", http, format));
14422264Sjacobs
14432264Sjacobs va_start(ap, format);
14442264Sjacobs bytes = vsnprintf(buf, sizeof(buf), format, ap);
14452264Sjacobs va_end(ap);
14462264Sjacobs
14472264Sjacobs DEBUG_printf(("httpPrintf: %s", buf));
14482264Sjacobs
14492264Sjacobs for (tbytes = 0, bufptr = buf; tbytes < bytes; tbytes += nbytes, bufptr += nbytes)
14502264Sjacobs {
14512264Sjacobs #ifdef HAVE_SSL
14522264Sjacobs if (http->tls)
14532264Sjacobs nbytes = http_write_ssl(http, bufptr, bytes - tbytes);
14542264Sjacobs else
14552264Sjacobs #endif /* HAVE_SSL */
14562264Sjacobs nbytes = send(http->fd, bufptr, bytes - tbytes, 0);
14572264Sjacobs
14582264Sjacobs #ifdef DEBUG_HTTP
14592264Sjacobs if (nbytes >= 0)
14602264Sjacobs httpDumpData(stdout, "httpPrintf:", bufptr, nbytes);
14612264Sjacobs #endif
14622264Sjacobs
14632264Sjacobs if (nbytes < 0)
14642264Sjacobs {
14652264Sjacobs nbytes = 0;
14662264Sjacobs
14672264Sjacobs #ifdef WIN32
14682264Sjacobs if (WSAGetLastError() != http->error)
14692264Sjacobs {
14702264Sjacobs http->error = WSAGetLastError();
14712264Sjacobs continue;
14722264Sjacobs }
14732264Sjacobs #else
14742264Sjacobs if (errno == EINTR)
14752264Sjacobs continue;
14762264Sjacobs else if (errno != http->error)
14772264Sjacobs {
14782264Sjacobs http->error = errno;
14792264Sjacobs continue;
14802264Sjacobs }
14812264Sjacobs #endif /* WIN32 */
14822264Sjacobs
14832264Sjacobs return (-1);
14842264Sjacobs }
14852264Sjacobs }
14862264Sjacobs
14872264Sjacobs return (bytes);
14882264Sjacobs }
14892264Sjacobs
14902264Sjacobs
14912264Sjacobs /*
14922264Sjacobs * 'httpGetDateString()' - Get a formatted date/time string from a time value.
14932264Sjacobs */
14942264Sjacobs
14952264Sjacobs const char * /* O - Date/time string */
httpGetDateString(time_t t)14962264Sjacobs httpGetDateString(time_t t) /* I - UNIX time */
14972264Sjacobs {
14982264Sjacobs struct tm *tdate;
14992264Sjacobs static char datetime[256];
15002264Sjacobs
15012264Sjacobs
15022264Sjacobs tdate = gmtime(&t);
15032264Sjacobs snprintf(datetime, sizeof(datetime), "%s, %02d %s %d %02d:%02d:%02d GMT",
15042264Sjacobs days[tdate->tm_wday], tdate->tm_mday, months[tdate->tm_mon],
15052264Sjacobs tdate->tm_year + 1900, tdate->tm_hour, tdate->tm_min, tdate->tm_sec);
15062264Sjacobs
15072264Sjacobs return (datetime);
15082264Sjacobs }
15092264Sjacobs
15102264Sjacobs
15112264Sjacobs /*
15122264Sjacobs * 'httpGetDateTime()' - Get a time value from a formatted date/time string.
15132264Sjacobs */
15142264Sjacobs
15152264Sjacobs time_t /* O - UNIX time */
httpGetDateTime(const char * s)15162264Sjacobs httpGetDateTime(const char *s) /* I - Date/time string */
15172264Sjacobs {
15182264Sjacobs int i; /* Looping var */
15192264Sjacobs struct tm tdate; /* Time/date structure */
15202264Sjacobs char mon[16]; /* Abbreviated month name */
15212264Sjacobs int day, year; /* Day of month and year */
15222264Sjacobs int hour, min, sec; /* Time */
15232264Sjacobs
15242264Sjacobs
15252264Sjacobs if (sscanf(s, "%*s%d%15s%d%d:%d:%d", &day, mon, &year, &hour, &min, &sec) < 6)
15262264Sjacobs return (0);
15272264Sjacobs
15282264Sjacobs for (i = 0; i < 12; i ++)
15292264Sjacobs if (strcasecmp(mon, months[i]) == 0)
15302264Sjacobs break;
15312264Sjacobs
15322264Sjacobs if (i >= 12)
15332264Sjacobs return (0);
15342264Sjacobs
15352264Sjacobs tdate.tm_mon = i;
15362264Sjacobs tdate.tm_mday = day;
15372264Sjacobs tdate.tm_year = year - 1900;
15382264Sjacobs tdate.tm_hour = hour;
15392264Sjacobs tdate.tm_min = min;
15402264Sjacobs tdate.tm_sec = sec;
15412264Sjacobs tdate.tm_isdst = 0;
15422264Sjacobs
15432264Sjacobs return (mktime(&tdate));
15442264Sjacobs }
15452264Sjacobs
15462264Sjacobs
15472264Sjacobs /*
15482264Sjacobs * 'httpUpdate()' - Update the current HTTP state for incoming data.
15492264Sjacobs */
15502264Sjacobs
15512264Sjacobs http_status_t /* O - HTTP status */
httpUpdate(http_t * http)15522264Sjacobs httpUpdate(http_t *http) /* I - HTTP data */
15532264Sjacobs {
15542264Sjacobs char line[1024], /* Line from connection... */
15552264Sjacobs *value; /* Pointer to value on line */
15562264Sjacobs http_field_t field; /* Field index */
15572264Sjacobs int major, minor, /* HTTP version numbers */
15582264Sjacobs status; /* Request status */
15592264Sjacobs
15602264Sjacobs
15612264Sjacobs DEBUG_printf(("httpUpdate(http=%p), state=%d\n", http, http->state));
15622264Sjacobs
15632264Sjacobs /*
15642264Sjacobs * If we haven't issued any commands, then there is nothing to "update"...
15652264Sjacobs */
15662264Sjacobs
15672264Sjacobs if (http->state == HTTP_WAITING)
15682264Sjacobs return (HTTP_CONTINUE);
15692264Sjacobs
15702264Sjacobs /*
15712264Sjacobs * Grab all of the lines we can from the connection...
15722264Sjacobs */
15732264Sjacobs
15742264Sjacobs line[0] = '\0';
15752264Sjacobs while (httpGets(line, sizeof(line), http) != NULL)
15762264Sjacobs {
15772264Sjacobs DEBUG_printf(("httpUpdate: Got \"%s\"\n", line));
15782264Sjacobs
15792264Sjacobs if (line[0] == '\0')
15802264Sjacobs {
15812264Sjacobs /*
15822264Sjacobs * Blank line means the start of the data section (if any). Return
15832264Sjacobs * the result code, too...
15842264Sjacobs *
15852264Sjacobs * If we get status 100 (HTTP_CONTINUE), then we *don't* change states.
15862264Sjacobs * Instead, we just return HTTP_CONTINUE to the caller and keep on
15872264Sjacobs * tryin'...
15882264Sjacobs */
15892264Sjacobs
15902264Sjacobs if (http->status == HTTP_CONTINUE)
15912264Sjacobs return (http->status);
15922264Sjacobs
15932264Sjacobs if (http->status < HTTP_BAD_REQUEST)
15942264Sjacobs http->digest_tries = 0;
15952264Sjacobs
15962264Sjacobs #ifdef HAVE_SSL
15972264Sjacobs if (http->status == HTTP_SWITCHING_PROTOCOLS && !http->tls)
15982264Sjacobs {
15992264Sjacobs if (http_setup_ssl(http) != 0)
16002264Sjacobs {
16012264Sjacobs # ifdef WIN32
16022264Sjacobs closesocket(http->fd);
16032264Sjacobs # else
16042264Sjacobs close(http->fd);
16052264Sjacobs # endif /* WIN32 */
16062264Sjacobs
16072264Sjacobs return (HTTP_ERROR);
16082264Sjacobs }
16092264Sjacobs
16102264Sjacobs return (HTTP_CONTINUE);
16112264Sjacobs }
16122264Sjacobs #endif /* HAVE_SSL */
16132264Sjacobs
16142264Sjacobs httpGetLength(http);
16152264Sjacobs
16162264Sjacobs switch (http->state)
16172264Sjacobs {
16182264Sjacobs case HTTP_GET :
16192264Sjacobs case HTTP_POST :
16202264Sjacobs case HTTP_POST_RECV :
16212264Sjacobs case HTTP_PUT :
16222264Sjacobs http->state ++;
16232264Sjacobs case HTTP_POST_SEND :
16242264Sjacobs break;
16252264Sjacobs
16262264Sjacobs default :
16272264Sjacobs http->state = HTTP_WAITING;
16282264Sjacobs break;
16292264Sjacobs }
16302264Sjacobs
16312264Sjacobs return (http->status);
16322264Sjacobs }
16332264Sjacobs else if (strncmp(line, "HTTP/", 5) == 0)
16342264Sjacobs {
16352264Sjacobs /*
16362264Sjacobs * Got the beginning of a response...
16372264Sjacobs */
16382264Sjacobs
16392264Sjacobs if (sscanf(line, "HTTP/%d.%d%d", &major, &minor, &status) != 3)
16402264Sjacobs return (HTTP_ERROR);
16412264Sjacobs
16422264Sjacobs http->version = (http_version_t)(major * 100 + minor);
16432264Sjacobs http->status = (http_status_t)status;
16442264Sjacobs }
16452264Sjacobs else if ((value = strchr(line, ':')) != NULL)
16462264Sjacobs {
16472264Sjacobs /*
16482264Sjacobs * Got a value...
16492264Sjacobs */
16502264Sjacobs
16512264Sjacobs *value++ = '\0';
16522264Sjacobs while (isspace(*value & 255))
16532264Sjacobs value ++;
16542264Sjacobs
16552264Sjacobs /*
16562264Sjacobs * Be tolerants of servers that send unknown attribute fields...
16572264Sjacobs */
16582264Sjacobs
16592264Sjacobs if (!strcasecmp(line, "expect"))
16602264Sjacobs {
16612264Sjacobs /*
16622264Sjacobs * "Expect: 100-continue" or similar...
16632264Sjacobs */
16642264Sjacobs
16652264Sjacobs http->expect = (http_status_t)atoi(value);
16662264Sjacobs }
16672264Sjacobs else if (!strcasecmp(line, "cookie"))
16682264Sjacobs {
16692264Sjacobs /*
16702264Sjacobs * "Cookie: name=value[; name=value ...]" - replaces previous cookies...
16712264Sjacobs */
16722264Sjacobs
16732264Sjacobs httpSetCookie(http, value);
16742264Sjacobs }
16752264Sjacobs else if ((field = http_field(line)) == HTTP_FIELD_UNKNOWN)
16762264Sjacobs {
16772264Sjacobs DEBUG_printf(("httpUpdate: unknown field %s seen!\n", line));
16782264Sjacobs continue;
16792264Sjacobs }
16802264Sjacobs else
16812264Sjacobs httpSetField(http, field, value);
16822264Sjacobs }
16832264Sjacobs else
16842264Sjacobs {
16852264Sjacobs http->status = HTTP_ERROR;
16862264Sjacobs return (HTTP_ERROR);
16872264Sjacobs }
16882264Sjacobs }
16892264Sjacobs
16902264Sjacobs /*
16912264Sjacobs * See if there was an error...
16922264Sjacobs */
16932264Sjacobs
16942264Sjacobs if (http->error == EPIPE && http->status > HTTP_CONTINUE)
16952264Sjacobs return (http->status);
16962264Sjacobs
16972264Sjacobs if (http->error)
16982264Sjacobs {
16992264Sjacobs DEBUG_printf(("httpUpdate: socket error %d - %s\n", http->error,
17002264Sjacobs strerror(http->error)));
17012264Sjacobs http->status = HTTP_ERROR;
17022264Sjacobs return (HTTP_ERROR);
17032264Sjacobs }
17042264Sjacobs
17052264Sjacobs /*
17062264Sjacobs * If we haven't already returned, then there is nothing new...
17072264Sjacobs */
17082264Sjacobs
17092264Sjacobs return (HTTP_CONTINUE);
17102264Sjacobs }
17112264Sjacobs
17122264Sjacobs
17132264Sjacobs /*
17142264Sjacobs * 'httpDecode64()' - Base64-decode a string.
17152264Sjacobs */
17162264Sjacobs
17172264Sjacobs char * /* O - Decoded string */
httpDecode64(char * out,const char * in)17182264Sjacobs httpDecode64(char *out, /* I - String to write to */
17192264Sjacobs const char *in) /* I - String to read from */
17202264Sjacobs {
17212264Sjacobs int outlen; /* Output buffer length */
17222264Sjacobs
17232264Sjacobs
17242264Sjacobs /*
17252264Sjacobs * Use the old maximum buffer size for binary compatibility...
17262264Sjacobs */
17272264Sjacobs
17282264Sjacobs outlen = 512;
17292264Sjacobs
17302264Sjacobs return (httpDecode64_2(out, &outlen, in));
17312264Sjacobs }
17322264Sjacobs
17332264Sjacobs
17342264Sjacobs /*
17352264Sjacobs * 'httpDecode64_2()' - Base64-decode a string.
17362264Sjacobs */
17372264Sjacobs
17382264Sjacobs char * /* O - Decoded string */
httpDecode64_2(char * out,int * outlen,const char * in)17392264Sjacobs httpDecode64_2(char *out, /* I - String to write to */
17402264Sjacobs int *outlen, /* IO - Size of output string */
17412264Sjacobs const char *in) /* I - String to read from */
17422264Sjacobs {
17432264Sjacobs int pos, /* Bit position */
17442264Sjacobs base64; /* Value of this character */
17452264Sjacobs char *outptr, /* Output pointer */
17462264Sjacobs *outend; /* End of output buffer */
17472264Sjacobs
17482264Sjacobs
17492264Sjacobs /*
17502264Sjacobs * Range check input...
17512264Sjacobs */
17522264Sjacobs
17532264Sjacobs if (!out || !outlen || *outlen < 1 || !in || !*in)
17542264Sjacobs return (NULL);
17552264Sjacobs
17562264Sjacobs /*
17572264Sjacobs * Convert from base-64 to bytes...
17582264Sjacobs */
17592264Sjacobs
17602264Sjacobs for (outptr = out, outend = out + *outlen - 1, pos = 0; *in != '\0'; in ++)
17612264Sjacobs {
17622264Sjacobs /*
17632264Sjacobs * Decode this character into a number from 0 to 63...
17642264Sjacobs */
17652264Sjacobs
17662264Sjacobs if (*in >= 'A' && *in <= 'Z')
17672264Sjacobs base64 = *in - 'A';
17682264Sjacobs else if (*in >= 'a' && *in <= 'z')
17692264Sjacobs base64 = *in - 'a' + 26;
17702264Sjacobs else if (*in >= '0' && *in <= '9')
17712264Sjacobs base64 = *in - '0' + 52;
17722264Sjacobs else if (*in == '+')
17732264Sjacobs base64 = 62;
17742264Sjacobs else if (*in == '/')
17752264Sjacobs base64 = 63;
17762264Sjacobs else if (*in == '=')
17772264Sjacobs break;
17782264Sjacobs else
17792264Sjacobs continue;
17802264Sjacobs
17812264Sjacobs /*
17822264Sjacobs * Store the result in the appropriate chars...
17832264Sjacobs */
17842264Sjacobs
17852264Sjacobs switch (pos)
17862264Sjacobs {
17872264Sjacobs case 0 :
17882264Sjacobs if (outptr < outend)
17892264Sjacobs *outptr = base64 << 2;
17902264Sjacobs pos ++;
17912264Sjacobs break;
17922264Sjacobs case 1 :
17932264Sjacobs if (outptr < outend)
17942264Sjacobs *outptr++ |= (base64 >> 4) & 3;
17952264Sjacobs if (outptr < outend)
17962264Sjacobs *outptr = (base64 << 4) & 255;
17972264Sjacobs pos ++;
17982264Sjacobs break;
17992264Sjacobs case 2 :
18002264Sjacobs if (outptr < outend)
18012264Sjacobs *outptr++ |= (base64 >> 2) & 15;
18022264Sjacobs if (outptr < outend)
18032264Sjacobs *outptr = (base64 << 6) & 255;
18042264Sjacobs pos ++;
18052264Sjacobs break;
18062264Sjacobs case 3 :
18072264Sjacobs if (outptr < outend)
18082264Sjacobs *outptr++ |= base64;
18092264Sjacobs pos = 0;
18102264Sjacobs break;
18112264Sjacobs }
18122264Sjacobs }
18132264Sjacobs
18142264Sjacobs *outptr = '\0';
18152264Sjacobs
18162264Sjacobs /*
18172264Sjacobs * Return the decoded string and size...
18182264Sjacobs */
18192264Sjacobs
18202264Sjacobs *outlen = (int)(outptr - out);
18212264Sjacobs
18222264Sjacobs return (out);
18232264Sjacobs }
18242264Sjacobs
18252264Sjacobs
18262264Sjacobs /*
18272264Sjacobs * 'httpEncode64()' - Base64-encode a string.
18282264Sjacobs */
18292264Sjacobs
18302264Sjacobs char * /* O - Encoded string */
httpEncode64(char * out,const char * in)18312264Sjacobs httpEncode64(char *out, /* I - String to write to */
18322264Sjacobs const char *in) /* I - String to read from */
18332264Sjacobs {
18342264Sjacobs return (httpEncode64_2(out, 512, in, strlen(in)));
18352264Sjacobs }
18362264Sjacobs
18372264Sjacobs
18382264Sjacobs /*
18392264Sjacobs * 'httpEncode64_2()' - Base64-encode a string.
18402264Sjacobs */
18412264Sjacobs
18422264Sjacobs char * /* O - Encoded string */
httpEncode64_2(char * out,int outlen,const char * in,int inlen)18432264Sjacobs httpEncode64_2(char *out, /* I - String to write to */
18442264Sjacobs int outlen, /* I - Size of output string */
18452264Sjacobs const char *in, /* I - String to read from */
18462264Sjacobs int inlen) /* I - Size of input string */
18472264Sjacobs {
18482264Sjacobs char *outptr, /* Output pointer */
18492264Sjacobs *outend; /* End of output buffer */
18502264Sjacobs static const char base64[] = /* Base64 characters... */
18512264Sjacobs {
18522264Sjacobs "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
18532264Sjacobs "abcdefghijklmnopqrstuvwxyz"
18542264Sjacobs "0123456789"
18552264Sjacobs "+/"
18562264Sjacobs };
18572264Sjacobs
18582264Sjacobs
18592264Sjacobs /*
18602264Sjacobs * Range check input...
18612264Sjacobs */
18622264Sjacobs
18632264Sjacobs if (!out || outlen < 1 || !in || inlen < 1)
18642264Sjacobs return (NULL);
18652264Sjacobs
18662264Sjacobs /*
18672264Sjacobs * Convert bytes to base-64...
18682264Sjacobs */
18692264Sjacobs
18702264Sjacobs for (outptr = out, outend = out + outlen - 1; inlen > 0; in ++, inlen --)
18712264Sjacobs {
18722264Sjacobs /*
18732264Sjacobs * Encode the up to 3 characters as 4 Base64 numbers...
18742264Sjacobs */
18752264Sjacobs
18762264Sjacobs if (outptr < outend)
18772264Sjacobs *outptr ++ = base64[(in[0] & 255) >> 2];
18782264Sjacobs if (outptr < outend)
18792264Sjacobs *outptr ++ = base64[(((in[0] & 255) << 4) | ((in[1] & 255) >> 4)) & 63];
18802264Sjacobs
18812264Sjacobs in ++;
18822264Sjacobs inlen --;
18832264Sjacobs if (inlen <= 0)
18842264Sjacobs {
18852264Sjacobs if (outptr < outend)
18862264Sjacobs *outptr ++ = '=';
18872264Sjacobs if (outptr < outend)
18882264Sjacobs *outptr ++ = '=';
18892264Sjacobs break;
18902264Sjacobs }
18912264Sjacobs
18922264Sjacobs if (outptr < outend)
18932264Sjacobs *outptr ++ = base64[(((in[0] & 255) << 2) | ((in[1] & 255) >> 6)) & 63];
18942264Sjacobs
18952264Sjacobs in ++;
18962264Sjacobs inlen --;
18972264Sjacobs if (inlen <= 0)
18982264Sjacobs {
18992264Sjacobs if (outptr < outend)
19002264Sjacobs *outptr ++ = '=';
19012264Sjacobs break;
19022264Sjacobs }
19032264Sjacobs
19042264Sjacobs if (outptr < outend)
19052264Sjacobs *outptr ++ = base64[in[0] & 63];
19062264Sjacobs }
19072264Sjacobs
19082264Sjacobs *outptr = '\0';
19092264Sjacobs
19102264Sjacobs /*
19112264Sjacobs * Return the encoded string...
19122264Sjacobs */
19132264Sjacobs
19142264Sjacobs return (out);
19152264Sjacobs }
19162264Sjacobs
19172264Sjacobs
19182264Sjacobs /*
19192264Sjacobs * 'httpGetLength()' - Get the amount of data remaining from the
19202264Sjacobs * content-length or transfer-encoding fields.
19212264Sjacobs */
19222264Sjacobs
19232264Sjacobs int /* O - Content length */
httpGetLength(http_t * http)19242264Sjacobs httpGetLength(http_t *http) /* I - HTTP data */
19252264Sjacobs {
19262264Sjacobs DEBUG_printf(("httpGetLength(http=%p), state=%d\n", http, http->state));
19272264Sjacobs
19282264Sjacobs if (strcasecmp(http->fields[HTTP_FIELD_TRANSFER_ENCODING], "chunked") == 0)
19292264Sjacobs {
19302264Sjacobs DEBUG_puts("httpGetLength: chunked request!");
19312264Sjacobs
19322264Sjacobs http->data_encoding = HTTP_ENCODE_CHUNKED;
19332264Sjacobs http->data_remaining = 0;
19342264Sjacobs }
19352264Sjacobs else
19362264Sjacobs {
19372264Sjacobs http->data_encoding = HTTP_ENCODE_LENGTH;
19382264Sjacobs
19392264Sjacobs /*
19402264Sjacobs * The following is a hack for HTTP servers that don't send a
19412264Sjacobs * content-length or transfer-encoding field...
19422264Sjacobs *
19432264Sjacobs * If there is no content-length then the connection must close
19442264Sjacobs * after the transfer is complete...
19452264Sjacobs */
19462264Sjacobs
19472264Sjacobs if (http->fields[HTTP_FIELD_CONTENT_LENGTH][0] == '\0')
19482264Sjacobs http->data_remaining = 2147483647;
19492264Sjacobs else
19502264Sjacobs http->data_remaining = atoi(http->fields[HTTP_FIELD_CONTENT_LENGTH]);
19512264Sjacobs
19522264Sjacobs DEBUG_printf(("httpGetLength: content_length=%d\n", http->data_remaining));
19532264Sjacobs }
19542264Sjacobs
19552264Sjacobs return (http->data_remaining);
19562264Sjacobs }
19572264Sjacobs
19582264Sjacobs
19592264Sjacobs /*
19602264Sjacobs * 'http_field()' - Return the field index for a field name.
19612264Sjacobs */
19622264Sjacobs
19632264Sjacobs static http_field_t /* O - Field index */
http_field(const char * name)19642264Sjacobs http_field(const char *name) /* I - String name */
19652264Sjacobs {
19662264Sjacobs int i; /* Looping var */
19672264Sjacobs
19682264Sjacobs
19692264Sjacobs for (i = 0; i < HTTP_FIELD_MAX; i ++)
19702264Sjacobs if (strcasecmp(name, http_fields[i]) == 0)
19712264Sjacobs return ((http_field_t)i);
19722264Sjacobs
19732264Sjacobs return (HTTP_FIELD_UNKNOWN);
19742264Sjacobs }
19752264Sjacobs
19762264Sjacobs
19772264Sjacobs /*
19782264Sjacobs * 'http_send()' - Send a request with all fields and the trailing blank line.
19792264Sjacobs */
19802264Sjacobs
19812264Sjacobs static int /* O - 0 on success, non-zero on error */
http_send(http_t * http,http_state_t request,const char * uri)19822264Sjacobs http_send(http_t *http, /* I - HTTP data */
19832264Sjacobs http_state_t request, /* I - Request code */
19842264Sjacobs const char *uri) /* I - URI */
19852264Sjacobs {
19862264Sjacobs int i; /* Looping var */
19872264Sjacobs char *ptr, /* Pointer in buffer */
19882264Sjacobs buf[1024]; /* Encoded URI buffer */
19892264Sjacobs static const char * const codes[] =
19902264Sjacobs { /* Request code strings */
19912264Sjacobs NULL,
19922264Sjacobs "OPTIONS",
19932264Sjacobs "GET",
19942264Sjacobs NULL,
19952264Sjacobs "HEAD",
19962264Sjacobs "POST",
19972264Sjacobs NULL,
19982264Sjacobs NULL,
19992264Sjacobs "PUT",
20002264Sjacobs NULL,
20012264Sjacobs "DELETE",
20022264Sjacobs "TRACE",
20032264Sjacobs "CLOSE"
20042264Sjacobs };
20052264Sjacobs static const char hex[] = "0123456789ABCDEF";
20062264Sjacobs /* Hex digits */
20072264Sjacobs
20082264Sjacobs
20092264Sjacobs DEBUG_printf(("http_send(http=%p, request=HTTP_%s, uri=\"%s\")\n",
20102264Sjacobs http, codes[request], uri));
20112264Sjacobs
20122264Sjacobs if (http == NULL || uri == NULL)
20132264Sjacobs return (-1);
20142264Sjacobs
20152264Sjacobs /*
20162264Sjacobs * Encode the URI as needed...
20172264Sjacobs */
20182264Sjacobs
20192264Sjacobs for (ptr = buf; *uri != '\0' && ptr < (buf + sizeof(buf) - 1); uri ++)
20202264Sjacobs if (*uri <= ' ' || *uri >= 127)
20212264Sjacobs {
20222264Sjacobs if (ptr < (buf + sizeof(buf) - 1))
20232264Sjacobs *ptr ++ = '%';
20242264Sjacobs if (ptr < (buf + sizeof(buf) - 1))
20252264Sjacobs *ptr ++ = hex[(*uri >> 4) & 15];
20262264Sjacobs if (ptr < (buf + sizeof(buf) - 1))
20272264Sjacobs *ptr ++ = hex[*uri & 15];
20282264Sjacobs }
20292264Sjacobs else
20302264Sjacobs *ptr ++ = *uri;
20312264Sjacobs
20322264Sjacobs *ptr = '\0';
20332264Sjacobs
20342264Sjacobs /*
20352264Sjacobs * See if we had an error the last time around; if so, reconnect...
20362264Sjacobs */
20372264Sjacobs
20382264Sjacobs if (http->status == HTTP_ERROR || http->status >= HTTP_BAD_REQUEST)
20392264Sjacobs httpReconnect(http);
20402264Sjacobs
20412264Sjacobs /*
20422264Sjacobs * Send the request header...
20432264Sjacobs */
20442264Sjacobs
20452264Sjacobs http->state = request;
20462264Sjacobs if (request == HTTP_POST || request == HTTP_PUT)
20472264Sjacobs http->state ++;
20482264Sjacobs
20492264Sjacobs http->status = HTTP_CONTINUE;
20502264Sjacobs
20512264Sjacobs #ifdef HAVE_SSL
20522264Sjacobs if (http->encryption == HTTP_ENCRYPT_REQUIRED && !http->tls)
20532264Sjacobs {
20542264Sjacobs httpSetField(http, HTTP_FIELD_CONNECTION, "Upgrade");
20552264Sjacobs httpSetField(http, HTTP_FIELD_UPGRADE, "TLS/1.0,SSL/2.0,SSL/3.0");
20562264Sjacobs }
20572264Sjacobs #endif /* HAVE_SSL */
20582264Sjacobs
20592264Sjacobs if (httpPrintf(http, "%s %s HTTP/1.1\r\n", codes[request], buf) < 1)
20602264Sjacobs {
20612264Sjacobs http->status = HTTP_ERROR;
20622264Sjacobs return (-1);
20632264Sjacobs }
20642264Sjacobs
20652264Sjacobs for (i = 0; i < HTTP_FIELD_MAX; i ++)
20662264Sjacobs if (http->fields[i][0] != '\0')
20672264Sjacobs {
20682264Sjacobs DEBUG_printf(("%s: %s\n", http_fields[i], http->fields[i]));
20692264Sjacobs
20702264Sjacobs if (httpPrintf(http, "%s: %s\r\n", http_fields[i], http->fields[i]) < 1)
20712264Sjacobs {
20722264Sjacobs http->status = HTTP_ERROR;
20732264Sjacobs return (-1);
20742264Sjacobs }
20752264Sjacobs }
20762264Sjacobs
20772264Sjacobs if (httpPrintf(http, "\r\n") < 1)
20782264Sjacobs {
20792264Sjacobs http->status = HTTP_ERROR;
20802264Sjacobs return (-1);
20812264Sjacobs }
20822264Sjacobs
20832264Sjacobs httpClearFields(http);
20842264Sjacobs
20852264Sjacobs return (0);
20862264Sjacobs }
20872264Sjacobs
20882264Sjacobs
20892264Sjacobs /*
20902264Sjacobs * 'http_wait()' - Wait for data available on a connection.
20912264Sjacobs */
20922264Sjacobs
20932264Sjacobs static int /* O - 1 if data is available, 0 otherwise */
http_wait(http_t * http,int msec)20942264Sjacobs http_wait(http_t *http, /* I - HTTP data */
20952264Sjacobs int msec) /* I - Milliseconds to wait */
20962264Sjacobs {
20972264Sjacobs #ifndef WIN32
20982264Sjacobs struct rlimit limit; /* Runtime limit */
20992264Sjacobs #endif /* !WIN32 */
21002264Sjacobs struct timeval timeout; /* Timeout */
21012264Sjacobs int nfds; /* Result from select() */
21022264Sjacobs int set_size; /* Size of select set */
21032264Sjacobs
21042264Sjacobs
21052264Sjacobs DEBUG_printf(("http_wait(http=%p, msec=%d)\n", http, msec));
21062264Sjacobs
21072264Sjacobs /*
21082264Sjacobs * Check the SSL/TLS buffers for data first...
21092264Sjacobs */
21102264Sjacobs
21112264Sjacobs #ifdef HAVE_SSL
21122264Sjacobs if (http->tls)
21132264Sjacobs {
21142264Sjacobs # ifdef HAVE_LIBSSL
21152264Sjacobs if (SSL_pending((SSL *)(http->tls)))
21162264Sjacobs return (1);
21172264Sjacobs # elif defined(HAVE_GNUTLS)
21182264Sjacobs if (gnutls_record_check_pending(((http_tls_t *)(http->tls))->session))
21192264Sjacobs return (1);
21202264Sjacobs # elif defined(HAVE_CDSASSL)
21212264Sjacobs size_t bytes; /* Bytes that are available */
21222264Sjacobs
21232264Sjacobs if (!SSLGetBufferedReadSize((SSLContextRef)http->tls, &bytes) && bytes > 0)
21242264Sjacobs return;
21252264Sjacobs # endif /* HAVE_LIBSSL */
21262264Sjacobs }
21272264Sjacobs #endif /* HAVE_SSL */
21282264Sjacobs
21292264Sjacobs /*
21302264Sjacobs * Then try doing a select() to poll the socket...
21312264Sjacobs */
21322264Sjacobs
21332264Sjacobs if (!http->input_set)
21342264Sjacobs {
21352264Sjacobs #ifdef WIN32
21362264Sjacobs /*
21372264Sjacobs * Windows has a fixed-size select() structure, different (surprise,
21382264Sjacobs * surprise!) from all UNIX implementations. Just allocate this
21392264Sjacobs * fixed structure...
21402264Sjacobs */
21412264Sjacobs
21422264Sjacobs http->input_set = calloc(1, sizeof(fd_set));
21432264Sjacobs #else
21442264Sjacobs /*
21452264Sjacobs * Allocate the select() input set based upon the max number of file
21462264Sjacobs * descriptors available for this process...
21472264Sjacobs */
21482264Sjacobs
21492264Sjacobs getrlimit(RLIMIT_NOFILE, &limit);
21502264Sjacobs
21512264Sjacobs set_size = (limit.rlim_cur + 31) / 8 + 4;
21522264Sjacobs if (set_size < sizeof(fd_set))
21532264Sjacobs set_size = sizeof(fd_set);
21542264Sjacobs
21552264Sjacobs http->input_set = calloc(1, set_size);
21562264Sjacobs #endif /* WIN32 */
21572264Sjacobs
21582264Sjacobs if (!http->input_set)
21592264Sjacobs return (0);
21602264Sjacobs }
21612264Sjacobs
21622264Sjacobs do
21632264Sjacobs {
21642264Sjacobs FD_SET(http->fd, http->input_set);
21652264Sjacobs
21662264Sjacobs if (msec >= 0)
21672264Sjacobs {
21682264Sjacobs timeout.tv_sec = msec / 1000;
21692264Sjacobs timeout.tv_usec = (msec % 1000) * 1000;
21702264Sjacobs
21712264Sjacobs nfds = select(http->fd + 1, http->input_set, NULL, NULL, &timeout);
21722264Sjacobs }
21732264Sjacobs else
21742264Sjacobs nfds = select(http->fd + 1, http->input_set, NULL, NULL, NULL);
21752264Sjacobs }
21762264Sjacobs #ifdef WIN32
21772264Sjacobs while (nfds < 0 && WSAGetLastError() == WSAEINTR);
21782264Sjacobs #else
21792264Sjacobs while (nfds < 0 && errno == EINTR);
21802264Sjacobs #endif /* WIN32 */
21812264Sjacobs
21822264Sjacobs FD_CLR(http->fd, http->input_set);
21832264Sjacobs
21842264Sjacobs return (nfds > 0);
21852264Sjacobs }
21862264Sjacobs
21872264Sjacobs
21882264Sjacobs #ifdef HAVE_SSL
21892264Sjacobs /*
21902264Sjacobs * 'http_upgrade()' - Force upgrade to TLS encryption.
21912264Sjacobs */
21922264Sjacobs
21932264Sjacobs static int /* O - Status of connection */
http_upgrade(http_t * http)21942264Sjacobs http_upgrade(http_t *http) /* I - HTTP data */
21952264Sjacobs {
21962264Sjacobs int ret; /* Return value */
21972264Sjacobs http_t myhttp; /* Local copy of HTTP data */
21982264Sjacobs
21992264Sjacobs
22002264Sjacobs DEBUG_printf(("http_upgrade(%p)\n", http));
22012264Sjacobs
22022264Sjacobs /*
22032264Sjacobs * Copy the HTTP data to a local variable so we can do the OPTIONS
22042264Sjacobs * request without interfering with the existing request data...
22052264Sjacobs */
22062264Sjacobs
22072264Sjacobs memcpy(&myhttp, http, sizeof(myhttp));
22082264Sjacobs
22092264Sjacobs /*
22102264Sjacobs * Send an OPTIONS request to the server, requiring SSL or TLS
22112264Sjacobs * encryption on the link...
22122264Sjacobs */
22132264Sjacobs
22142264Sjacobs httpClearFields(&myhttp);
22152264Sjacobs httpSetField(&myhttp, HTTP_FIELD_CONNECTION, "upgrade");
22162264Sjacobs httpSetField(&myhttp, HTTP_FIELD_UPGRADE, "TLS/1.0, SSL/2.0, SSL/3.0");
22172264Sjacobs
22182264Sjacobs if ((ret = httpOptions(&myhttp, "*")) == 0)
22192264Sjacobs {
22202264Sjacobs /*
22212264Sjacobs * Wait for the secure connection...
22222264Sjacobs */
22232264Sjacobs
22242264Sjacobs while (httpUpdate(&myhttp) == HTTP_CONTINUE);
22252264Sjacobs }
22262264Sjacobs
22272264Sjacobs httpFlush(&myhttp);
22282264Sjacobs
22292264Sjacobs /*
22302264Sjacobs * Copy the HTTP data back over, if any...
22312264Sjacobs */
22322264Sjacobs
22332264Sjacobs http->fd = myhttp.fd;
22342264Sjacobs http->error = myhttp.error;
22352264Sjacobs http->activity = myhttp.activity;
22362264Sjacobs http->status = myhttp.status;
22372264Sjacobs http->version = myhttp.version;
22382264Sjacobs http->keep_alive = myhttp.keep_alive;
22392264Sjacobs http->used = myhttp.used;
22402264Sjacobs
22412264Sjacobs if (http->used)
22422264Sjacobs memcpy(http->buffer, myhttp.buffer, http->used);
22432264Sjacobs
22442264Sjacobs http->auth_type = myhttp.auth_type;
22452264Sjacobs http->nonce_count = myhttp.nonce_count;
22462264Sjacobs
22472264Sjacobs memcpy(http->nonce, myhttp.nonce, sizeof(http->nonce));
22482264Sjacobs
22492264Sjacobs http->tls = myhttp.tls;
22502264Sjacobs http->encryption = myhttp.encryption;
22512264Sjacobs
22522264Sjacobs /*
22532264Sjacobs * See if we actually went secure...
22542264Sjacobs */
22552264Sjacobs
22562264Sjacobs if (!http->tls)
22572264Sjacobs {
22582264Sjacobs /*
22592264Sjacobs * Server does not support HTTP upgrade...
22602264Sjacobs */
22612264Sjacobs
22622264Sjacobs DEBUG_puts("Server does not support HTTP upgrade!");
22632264Sjacobs
22642264Sjacobs # ifdef WIN32
22652264Sjacobs closesocket(http->fd);
22662264Sjacobs # else
22672264Sjacobs close(http->fd);
22682264Sjacobs # endif
22692264Sjacobs
22702264Sjacobs http->fd = -1;
22712264Sjacobs
22722264Sjacobs return (-1);
22732264Sjacobs }
22742264Sjacobs else
22752264Sjacobs return (ret);
22762264Sjacobs }
22772264Sjacobs
22782264Sjacobs
22792264Sjacobs /*
22802264Sjacobs * 'http_setup_ssl()' - Set up SSL/TLS support on a connection.
22812264Sjacobs */
22822264Sjacobs
22832264Sjacobs static int /* O - Status of connection */
http_setup_ssl(http_t * http)22842264Sjacobs http_setup_ssl(http_t *http) /* I - HTTP data */
22852264Sjacobs {
22862264Sjacobs # ifdef HAVE_LIBSSL
22872264Sjacobs SSL_CTX *context; /* Context for encryption */
22882264Sjacobs SSL *conn; /* Connection for encryption */
22892264Sjacobs # elif defined(HAVE_GNUTLS)
22902264Sjacobs http_tls_t *conn; /* TLS session object */
22912264Sjacobs gnutls_certificate_client_credentials *credentials;
22922264Sjacobs /* TLS credentials */
22932264Sjacobs # elif defined(HAVE_CDSASSL)
22942264Sjacobs SSLContextRef conn; /* Context for encryption */
22952264Sjacobs OSStatus error; /* Error info */
22962264Sjacobs # endif /* HAVE_LIBSSL */
22972264Sjacobs
22982264Sjacobs
22992264Sjacobs DEBUG_printf(("http_setup_ssl(http=%p)\n", http));
23002264Sjacobs
23012264Sjacobs # ifdef HAVE_LIBSSL
23022264Sjacobs context = SSL_CTX_new(SSLv23_client_method());
23032264Sjacobs
23042264Sjacobs SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */
23052264Sjacobs
23062264Sjacobs conn = SSL_new(context);
23072264Sjacobs
23082264Sjacobs SSL_set_fd(conn, http->fd);
23092264Sjacobs if (SSL_connect(conn) != 1)
23102264Sjacobs {
23112264Sjacobs # ifdef DEBUG
23122264Sjacobs unsigned long error; /* Error code */
23132264Sjacobs
23142264Sjacobs while ((error = ERR_get_error()) != 0)
23152264Sjacobs printf("http_setup_ssl: %s\n", ERR_error_string(error, NULL));
23162264Sjacobs # endif /* DEBUG */
23172264Sjacobs
23182264Sjacobs SSL_CTX_free(context);
23192264Sjacobs SSL_free(conn);
23202264Sjacobs
23212264Sjacobs # ifdef WIN32
23222264Sjacobs http->error = WSAGetLastError();
23232264Sjacobs # else
23242264Sjacobs http->error = errno;
23252264Sjacobs # endif /* WIN32 */
23262264Sjacobs http->status = HTTP_ERROR;
23272264Sjacobs
23282264Sjacobs return (HTTP_ERROR);
23292264Sjacobs }
23302264Sjacobs
23312264Sjacobs # elif defined(HAVE_GNUTLS)
23322264Sjacobs conn = (http_tls_t *)malloc(sizeof(http_tls_t));
23332264Sjacobs
23342264Sjacobs if (conn == NULL)
23352264Sjacobs {
23362264Sjacobs http->error = errno;
23372264Sjacobs http->status = HTTP_ERROR;
23382264Sjacobs
23392264Sjacobs return (-1);
23402264Sjacobs }
23412264Sjacobs
23422264Sjacobs credentials = (gnutls_certificate_client_credentials *)
23432264Sjacobs malloc(sizeof(gnutls_certificate_client_credentials));
23442264Sjacobs if (credentials == NULL)
23452264Sjacobs {
23462264Sjacobs free(conn);
23472264Sjacobs
23482264Sjacobs http->error = errno;
23492264Sjacobs http->status = HTTP_ERROR;
23502264Sjacobs
23512264Sjacobs return (-1);
23522264Sjacobs }
23532264Sjacobs
23542264Sjacobs gnutls_certificate_allocate_credentials(credentials);
23552264Sjacobs
23562264Sjacobs gnutls_init(&(conn->session), GNUTLS_CLIENT);
23572264Sjacobs gnutls_set_default_priority(conn->session);
23582264Sjacobs gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, *credentials);
23592264Sjacobs gnutls_transport_set_ptr(conn->session, http->fd);
23602264Sjacobs
23612264Sjacobs if ((gnutls_handshake(conn->session)) != GNUTLS_E_SUCCESS)
23622264Sjacobs {
23632264Sjacobs http->error = errno;
23642264Sjacobs http->status = HTTP_ERROR;
23652264Sjacobs
23662264Sjacobs return (-1);
23672264Sjacobs }
23682264Sjacobs
23692264Sjacobs conn->credentials = credentials;
23702264Sjacobs
23712264Sjacobs # elif defined(HAVE_CDSASSL)
23722264Sjacobs error = SSLNewContext(false, &conn);
23732264Sjacobs
23742264Sjacobs if (!error)
23752264Sjacobs error = SSLSetIOFuncs(conn, CDSAReadFunc, CDSAWriteFunc);
23762264Sjacobs
23772264Sjacobs if (!error)
23782264Sjacobs error = SSLSetConnection(conn, (SSLConnectionRef)http->fd);
23792264Sjacobs
23802264Sjacobs if (!error)
23812264Sjacobs error = SSLSetAllowsExpiredCerts(conn, true);
23822264Sjacobs
23832264Sjacobs if (!error)
23842264Sjacobs error = SSLSetAllowsAnyRoot(conn, true);
23852264Sjacobs
23862264Sjacobs if (!error)
23872264Sjacobs error = SSLHandshake(conn);
23882264Sjacobs
23892264Sjacobs if (error != 0)
23902264Sjacobs {
23912264Sjacobs http->error = error;
23922264Sjacobs http->status = HTTP_ERROR;
23932264Sjacobs
23942264Sjacobs SSLDisposeContext(conn);
23952264Sjacobs
23962264Sjacobs close(http->fd);
23972264Sjacobs
23982264Sjacobs return (-1);
23992264Sjacobs }
24002264Sjacobs # endif /* HAVE_CDSASSL */
24012264Sjacobs
24022264Sjacobs http->tls = conn;
24032264Sjacobs return (0);
24042264Sjacobs }
24052264Sjacobs
24062264Sjacobs
24072264Sjacobs /*
24082264Sjacobs * 'http_shutdown_ssl()' - Shut down SSL/TLS on a connection.
24092264Sjacobs */
24102264Sjacobs
24112264Sjacobs static void
http_shutdown_ssl(http_t * http)24122264Sjacobs http_shutdown_ssl(http_t *http) /* I - HTTP data */
24132264Sjacobs {
24142264Sjacobs # ifdef HAVE_LIBSSL
24152264Sjacobs SSL_CTX *context; /* Context for encryption */
24162264Sjacobs SSL *conn; /* Connection for encryption */
24172264Sjacobs
24182264Sjacobs
24192264Sjacobs conn = (SSL *)(http->tls);
24202264Sjacobs context = SSL_get_SSL_CTX(conn);
24212264Sjacobs
24222264Sjacobs SSL_shutdown(conn);
24232264Sjacobs SSL_CTX_free(context);
24242264Sjacobs SSL_free(conn);
24252264Sjacobs
24262264Sjacobs # elif defined(HAVE_GNUTLS)
24272264Sjacobs http_tls_t *conn; /* Encryption session */
24282264Sjacobs gnutls_certificate_client_credentials *credentials;
24292264Sjacobs /* TLS credentials */
24302264Sjacobs
24312264Sjacobs
24322264Sjacobs conn = (http_tls_t *)(http->tls);
24332264Sjacobs credentials = (gnutls_certificate_client_credentials *)(conn->credentials);
24342264Sjacobs
24352264Sjacobs gnutls_bye(conn->session, GNUTLS_SHUT_RDWR);
24362264Sjacobs gnutls_deinit(conn->session);
24372264Sjacobs gnutls_certificate_free_credentials(*credentials);
24382264Sjacobs free(credentials);
24392264Sjacobs free(conn);
24402264Sjacobs
24412264Sjacobs # elif defined(HAVE_CDSASSL)
24422264Sjacobs SSLClose((SSLContextRef)http->tls);
24432264Sjacobs SSLDisposeContext((SSLContextRef)http->tls);
24442264Sjacobs # endif /* HAVE_LIBSSL */
24452264Sjacobs
24462264Sjacobs http->tls = NULL;
24472264Sjacobs }
24482264Sjacobs
24492264Sjacobs
24502264Sjacobs /*
24512264Sjacobs * 'http_read_ssl()' - Read from a SSL/TLS connection.
24522264Sjacobs */
24532264Sjacobs
24542264Sjacobs static int /* O - Bytes read */
http_read_ssl(http_t * http,char * buf,int len)24552264Sjacobs http_read_ssl(http_t *http, /* I - HTTP data */
24562264Sjacobs char *buf, /* I - Buffer to store data */
24572264Sjacobs int len) /* I - Length of buffer */
24582264Sjacobs {
24592264Sjacobs # if defined(HAVE_LIBSSL)
24602264Sjacobs return (SSL_read((SSL *)(http->tls), buf, len));
24612264Sjacobs
24622264Sjacobs # elif defined(HAVE_GNUTLS)
24632264Sjacobs return (gnutls_record_recv(((http_tls_t *)(http->tls))->session, buf, len));
24642264Sjacobs
24652264Sjacobs # elif defined(HAVE_CDSASSL)
24662264Sjacobs OSStatus error; /* Error info */
24672264Sjacobs size_t processed; /* Number of bytes processed */
24682264Sjacobs
24692264Sjacobs
24702264Sjacobs error = SSLRead((SSLContextRef)http->tls, buf, len, &processed);
24712264Sjacobs
24722264Sjacobs if (error == 0)
24732264Sjacobs return (processed);
24742264Sjacobs else
24752264Sjacobs {
24762264Sjacobs http->error = error;
24772264Sjacobs
24782264Sjacobs return (-1);
24792264Sjacobs }
24802264Sjacobs # endif /* HAVE_LIBSSL */
24812264Sjacobs }
24822264Sjacobs
24832264Sjacobs
24842264Sjacobs /*
24852264Sjacobs * 'http_write_ssl()' - Write to a SSL/TLS connection.
24862264Sjacobs */
24872264Sjacobs
24882264Sjacobs static int /* O - Bytes written */
http_write_ssl(http_t * http,const char * buf,int len)24892264Sjacobs http_write_ssl(http_t *http, /* I - HTTP data */
24902264Sjacobs const char *buf, /* I - Buffer holding data */
24912264Sjacobs int len) /* I - Length of buffer */
24922264Sjacobs {
24932264Sjacobs # if defined(HAVE_LIBSSL)
24942264Sjacobs return (SSL_write((SSL *)(http->tls), buf, len));
24952264Sjacobs
24962264Sjacobs # elif defined(HAVE_GNUTLS)
24972264Sjacobs return (gnutls_record_send(((http_tls_t *)(http->tls))->session, buf, len));
24982264Sjacobs # elif defined(HAVE_CDSASSL)
24992264Sjacobs OSStatus error; /* Error info */
25002264Sjacobs size_t processed; /* Number of bytes processed */
25012264Sjacobs
25022264Sjacobs
25032264Sjacobs error = SSLWrite((SSLContextRef)http->tls, buf, len, &processed);
25042264Sjacobs
25052264Sjacobs if (error == 0)
25062264Sjacobs return (processed);
25072264Sjacobs else
25082264Sjacobs {
25092264Sjacobs http->error = error;
25102264Sjacobs return (-1);
25112264Sjacobs }
25122264Sjacobs # endif /* HAVE_LIBSSL */
25132264Sjacobs }
25142264Sjacobs
25152264Sjacobs
25162264Sjacobs # if defined(HAVE_CDSASSL)
25172264Sjacobs /*
25182264Sjacobs * 'CDSAReadFunc()' - Read function for CDSA decryption code.
25192264Sjacobs */
25202264Sjacobs
25212264Sjacobs static OSStatus /* O - -1 on error, 0 on success */
CDSAReadFunc(SSLConnectionRef connection,void * data,size_t * dataLength)25222264Sjacobs CDSAReadFunc(SSLConnectionRef connection, /* I - SSL/TLS connection */
25232264Sjacobs void *data, /* I - Data buffer */
25242264Sjacobs size_t *dataLength) /* IO - Number of bytes */
25252264Sjacobs {
25262264Sjacobs ssize_t bytes; /* Number of bytes read */
25272264Sjacobs
25282264Sjacobs #ifdef DEBUG_HTTP
25292264Sjacobs httpDumpData(stdout, "CDSAReadFunc:", data, *dataLength);
25302264Sjacobs #endif
25312264Sjacobs bytes = recv((int)connection, data, *dataLength, 0);
25322264Sjacobs if (bytes >= 0)
25332264Sjacobs {
25342264Sjacobs *dataLength = bytes;
25352264Sjacobs return (0);
25362264Sjacobs }
25372264Sjacobs else
25382264Sjacobs return (-1);
25392264Sjacobs }
25402264Sjacobs
25412264Sjacobs
25422264Sjacobs /*
25432264Sjacobs * 'CDSAWriteFunc()' - Write function for CDSA encryption code.
25442264Sjacobs */
25452264Sjacobs
25462264Sjacobs static OSStatus /* O - -1 on error, 0 on success */
CDSAWriteFunc(SSLConnectionRef connection,const void * data,size_t * dataLength)25472264Sjacobs CDSAWriteFunc(SSLConnectionRef connection, /* I - SSL/TLS connection */
25482264Sjacobs const void *data, /* I - Data buffer */
25492264Sjacobs size_t *dataLength) /* IO - Number of bytes */
25502264Sjacobs {
25512264Sjacobs ssize_t bytes;
25522264Sjacobs
25532264Sjacobs
25542264Sjacobs bytes = write((int)connection, data, *dataLength);
25552264Sjacobs if (bytes >= 0)
25562264Sjacobs {
25572264Sjacobs *dataLength = bytes;
25582264Sjacobs return (0);
25592264Sjacobs }
25602264Sjacobs else
25612264Sjacobs return (-1);
25622264Sjacobs }
25632264Sjacobs # endif /* HAVE_CDSASSL */
25642264Sjacobs #endif /* HAVE_SSL */
25652264Sjacobs
25662264Sjacobs
25672264Sjacobs /*
25682264Sjacobs * End of "$Id: http.c 148 2006-04-25 16:54:17Z njacobs $"
25692264Sjacobs */
2570