diff options
author | Andy Cedilnik <andy.cedilnik@kitware.com> | 2003-01-07 02:13:39 (GMT) |
---|---|---|
committer | Andy Cedilnik <andy.cedilnik@kitware.com> | 2003-01-07 02:13:39 (GMT) |
commit | baeba76200afb19cf8ea4e67025c1916ac210b13 (patch) | |
tree | f9fb8746b534b90c89e1d54e7a16a0b14873f894 /Source/CTest/Curl/http.c | |
parent | 0f14e027b5263a78b2bd1d41a319f16dacaa16af (diff) | |
download | CMake-baeba76200afb19cf8ea4e67025c1916ac210b13.zip CMake-baeba76200afb19cf8ea4e67025c1916ac210b13.tar.gz CMake-baeba76200afb19cf8ea4e67025c1916ac210b13.tar.bz2 |
Initial import
Diffstat (limited to 'Source/CTest/Curl/http.c')
-rw-r--r-- | Source/CTest/Curl/http.c | 985 |
1 files changed, 985 insertions, 0 deletions
diff --git a/Source/CTest/Curl/http.c b/Source/CTest/Curl/http.c new file mode 100644 index 0000000..1413618 --- /dev/null +++ b/Source/CTest/Curl/http.c @@ -0,0 +1,985 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +/* -- WIN32 approved -- */ +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <winsock.h> +#include <time.h> +#include <io.h> +#else +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#include <sys/time.h> + +#ifdef HAVE_TIME_H +#ifdef TIME_WITH_SYS_TIME +#include <time.h> +#endif +#endif + +#include <sys/resource.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <netdb.h> +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_NET_IF_H +#include <net/if.h> +#endif +#include <sys/ioctl.h> +#include <signal.h> + +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + + +#endif + +#include "urldata.h" +#include <curl/curl.h> +#include "transfer.h" +#include "sendf.h" +#include "formdata.h" +#include "progress.h" +#include "base64.h" +#include "cookie.h" +#include "strequal.h" +#include "ssluse.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +/* ------------------------------------------------------------------------- */ +/* + * The add_buffer series of functions are used to build one large memory chunk + * from repeated function invokes. Used so that the entire HTTP request can + * be sent in one go. + */ +static CURLcode + add_buffer(send_buffer *in, const void *inptr, size_t size); + +/* + * add_buffer_init() returns a fine buffer struct + */ +static +send_buffer *add_buffer_init(void) +{ + send_buffer *blonk; + blonk=(send_buffer *)malloc(sizeof(send_buffer)); + if(blonk) { + memset(blonk, 0, sizeof(send_buffer)); + return blonk; + } + return NULL; /* failed, go home */ +} + +/* + * add_buffer_send() sends a buffer and frees all associated memory. + */ +static +CURLcode add_buffer_send(int sockfd, struct connectdata *conn, send_buffer *in, + long *bytes_written) +{ + ssize_t amount; + CURLcode res; + char *ptr; + int size; + + if(conn->data->set.verbose) { + fputs("> ", conn->data->set.err); + /* this data _may_ contain binary stuff */ + fwrite(in->buffer, in->size_used, 1, conn->data->set.err); + } + + /* The looping below is required since we use non-blocking sockets, but due + to the circumstances we will just loop and try again and again etc */ + + ptr = in->buffer; + size = in->size_used; + do { + res = Curl_write(conn, sockfd, ptr, size, &amount); + + if(CURLE_OK != res) + break; + + if(amount != size) { + size -= amount; + ptr += amount; + } + else + break; + + } while(1); + + if(in->buffer) + free(in->buffer); + free(in); + + *bytes_written = amount; + + return res; +} + + +/* + * add_bufferf() builds a buffer from the formatted input + */ +static +CURLcode add_bufferf(send_buffer *in, const char *fmt, ...) +{ + CURLcode result = CURLE_OUT_OF_MEMORY; + char *s; + va_list ap; + va_start(ap, fmt); + s = vaprintf(fmt, ap); /* this allocs a new string to append */ + va_end(ap); + + if(s) { + result = add_buffer(in, s, strlen(s)); + free(s); + } + return result; +} + +/* + * add_buffer() appends a memory chunk to the existing one + */ +static +CURLcode add_buffer(send_buffer *in, const void *inptr, size_t size) +{ + char *new_rb; + int new_size; + + if(!in->buffer || + ((in->size_used + size) > (in->size_max - 1))) { + new_size = (in->size_used+size)*2; + if(in->buffer) + /* we have a buffer, enlarge the existing one */ + new_rb = (char *)realloc(in->buffer, new_size); + else + /* create a new buffer */ + new_rb = (char *)malloc(new_size); + + if(!new_rb) + return CURLE_OUT_OF_MEMORY; + + in->buffer = new_rb; + in->size_max = new_size; + } + memcpy(&in->buffer[in->size_used], inptr, size); + + in->size_used += size; + + return CURLE_OK; +} + +/* end of the add_buffer functions */ +/* ------------------------------------------------------------------------- */ + +/* + * This function checks the linked list of custom HTTP headers for a particular + * header (prefix). + */ +static bool checkheaders(struct SessionHandle *data, const char *thisheader) +{ + struct curl_slist *head; + size_t thislen = strlen(thisheader); + + for(head = data->set.headers; head; head=head->next) { + if(strnequal(head->data, thisheader, thislen)) { + return TRUE; + } + } + return FALSE; +} + +/* + * ConnectHTTPProxyTunnel() requires that we're connected to a HTTP proxy. This + * function will issue the necessary commands to get a seamless tunnel through + * this proxy. After that, the socket can be used just as a normal socket. + */ + +CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn, + int tunnelsocket, + char *hostname, int remote_port) +{ + int httperror=0; + int subversion=0; + struct SessionHandle *data=conn->data; + CURLcode result; + int res; + + int nread; /* total size read */ + int perline; /* count bytes per line */ + bool keepon=TRUE; + ssize_t gotbytes; + char *ptr; + int timeout = 3600; /* default timeout in seconds */ + struct timeval interval; + fd_set rkeepfd; + fd_set readfd; + char *line_start; + +#define SELECT_OK 0 +#define SELECT_ERROR 1 +#define SELECT_TIMEOUT 2 + int error = SELECT_OK; + + infof(data, "Establish HTTP proxy tunnel to %s:%d\n", hostname, remote_port); + + /* OK, now send the connect request to the proxy */ + result = + Curl_sendf(tunnelsocket, conn, + "CONNECT %s:%d HTTP/1.0\015\012" + "%s" + "%s" + "\r\n", + hostname, remote_port, + (conn->bits.proxy_user_passwd)?conn->allocptr.proxyuserpwd:"", + (data->set.useragent?conn->allocptr.uagent:"") + ); + if(result) { + failf(data, "Failed sending CONNECT to proxy"); + return result; + } + + /* Now, read the full reply we get from the proxy */ + + + if(data->set.timeout) { + /* if timeout is requested, find out how much remaining time we have */ + timeout = data->set.timeout - /* timeout time */ + Curl_tvdiff(Curl_tvnow(), conn->now)/1000; /* spent time */ + if(timeout <=0 ) { + failf(data, "Transfer aborted due to timeout"); + return -SELECT_TIMEOUT; /* already too little time */ + } + } + + FD_ZERO (&readfd); /* clear it */ + FD_SET (tunnelsocket, &readfd); /* read socket */ + + /* get this in a backup variable to be able to restore it on each lap in the + select() loop */ + rkeepfd = readfd; + + ptr=data->state.buffer; + line_start = ptr; + + nread=0; + perline=0; + keepon=TRUE; + + while((nread<BUFSIZE) && (keepon && !error)) { + readfd = rkeepfd; /* set every lap */ + interval.tv_sec = timeout; + interval.tv_usec = 0; + + switch (select (tunnelsocket+1, &readfd, NULL, NULL, &interval)) { + case -1: /* select() error, stop reading */ + error = SELECT_ERROR; + failf(data, "Transfer aborted due to select() error"); + break; + case 0: /* timeout */ + error = SELECT_TIMEOUT; + failf(data, "Transfer aborted due to timeout"); + break; + default: + /* + * This code previously didn't use the kerberos sec_read() code + * to read, but when we use Curl_read() it may do so. Do confirm + * that this is still ok and then remove this comment! + */ + res= Curl_read(conn, tunnelsocket, ptr, BUFSIZE-nread, + &gotbytes); + if(res< 0) + /* EWOULDBLOCK */ + continue; /* go loop yourself */ + else if(res) + keepon = FALSE; + else if(gotbytes <= 0) { + keepon = FALSE; + error = SELECT_ERROR; + failf(data, "Connection aborted"); + } + else { + /* we got a whole chunk of data, which can be anything from one + * byte to a set of lines and possibly just a piece of the last + * line */ + int i; + + nread += gotbytes; + for(i = 0; i < gotbytes; ptr++, i++) { + perline++; /* amount of bytes in this line so far */ + if(*ptr=='\n') { + /* a newline is CRLF in ftp-talk, so the CR is ignored as + the line isn't really terminated until the LF comes */ + + /* output debug output if that is requested */ + if(data->set.verbose) { + fputs("< ", data->set.err); + fwrite(line_start, perline, 1, data->set.err); + /* no need to output LF here, it is part of the data */ + } + + if('\r' == line_start[0]) { + /* end of headers */ + keepon=FALSE; + break; /* breaks out of loop, not switch */ + } + + if(2 == sscanf(line_start, "HTTP/1.%d %d", + &subversion, + &httperror)) { + ; + } + + perline=0; /* line starts over here */ + line_start = ptr+1; + } + } + } + break; + } /* switch */ + } /* while there's buffer left and loop is requested */ + + if(error) + return CURLE_READ_ERROR; + + if(200 != httperror) { + if(407 == httperror) + /* Added Nov 6 1998 */ + failf(data, "Proxy requires authorization!"); + else + failf(data, "Received error code %d from proxy", httperror); + return CURLE_READ_ERROR; + } + + infof (data, "Proxy replied to CONNECT request\n"); + return CURLE_OK; +} + +/* + * HTTP stuff to do at connect-time. + */ +CURLcode Curl_http_connect(struct connectdata *conn) +{ + struct SessionHandle *data; + CURLcode result; + + data=conn->data; + + /* If we are not using a proxy and we want a secure connection, + * perform SSL initialization & connection now. + * If using a proxy with https, then we must tell the proxy to CONNECT + * us to the host we want to talk to. Only after the connect + * has occured, can we start talking SSL + */ + + if(data->change.proxy && + ((conn->protocol & PROT_HTTPS) || data->set.tunnel_thru_httpproxy)) { + + /* either HTTPS over proxy, OR explicitly asked for a tunnel */ + result = Curl_ConnectHTTPProxyTunnel(conn, conn->firstsocket, + conn->hostname, conn->remote_port); + if(CURLE_OK != result) + return result; + } + + if(conn->protocol & PROT_HTTPS) { + /* now, perform the SSL initialization for this socket */ + result = Curl_SSLConnect(conn); + if(result) + return result; + } + + if(conn->bits.user_passwd && !data->state.this_is_a_follow) { + /* Authorization: is requested, this is not a followed location, get the + original host name */ + data->state.auth_host = strdup(conn->hostname); + } + + return CURLE_OK; +} + +CURLcode Curl_http_done(struct connectdata *conn) +{ + struct SessionHandle *data; + long *bytecount = &conn->bytecount; + struct HTTP *http; + + data=conn->data; + http=conn->proto.http; + + if(HTTPREQ_POST_FORM == data->set.httpreq) { + *bytecount = http->readbytecount + http->writebytecount; + + Curl_formclean(http->sendit); /* Now free that whole lot */ + + data->set.fread = http->storefread; /* restore */ + data->set.in = http->in; /* restore */ + } + else if(HTTPREQ_PUT == data->set.httpreq) { + *bytecount = http->readbytecount + http->writebytecount; + } + + if(0 == (http->readbytecount + conn->headerbytecount)) { + /* nothing was read from the HTTP server, this can't be right + so we return an error here */ + failf(data, "Empty reply from server"); + return CURLE_GOT_NOTHING; + } + + return CURLE_OK; +} + + +CURLcode Curl_http(struct connectdata *conn) +{ + struct SessionHandle *data=conn->data; + char *buf = data->state.buffer; /* this is a short cut to the buffer */ + CURLcode result=CURLE_OK; + struct HTTP *http; + struct Cookie *co=NULL; /* no cookies from start */ + char *ppath = conn->ppath; /* three previous function arguments */ + char *host = conn->name; + long *bytecount = &conn->bytecount; + + if(!conn->proto.http) { + /* Only allocate this struct if we don't already have it! */ + + http = (struct HTTP *)malloc(sizeof(struct HTTP)); + if(!http) + return CURLE_OUT_OF_MEMORY; + memset(http, 0, sizeof(struct HTTP)); + conn->proto.http = http; + } + else + http = conn->proto.http; + + /* We default to persistant connections */ + conn->bits.close = FALSE; + + if ( (conn->protocol&(PROT_HTTP|PROT_FTP)) && + data->set.upload) { + data->set.httpreq = HTTPREQ_PUT; + } + + /* The User-Agent string has been built in url.c already, because it might + have been used in the proxy connect, but if we have got a header with + the user-agent string specified, we erase the previously made string + here. */ + if(checkheaders(data, "User-Agent:") && conn->allocptr.uagent) { + free(conn->allocptr.uagent); + conn->allocptr.uagent=NULL; + } + + if((conn->bits.user_passwd) && !checkheaders(data, "Authorization:")) { + char *authorization; + + /* To prevent the user+password to get sent to other than the original + host due to a location-follow, we do some weirdo checks here */ + if(!data->state.this_is_a_follow || + !data->state.auth_host || + strequal(data->state.auth_host, conn->hostname)) { + sprintf(data->state.buffer, "%s:%s", + data->state.user, data->state.passwd); + if(Curl_base64_encode(data->state.buffer, strlen(data->state.buffer), + &authorization) >= 0) { + if(conn->allocptr.userpwd) + free(conn->allocptr.userpwd); + conn->allocptr.userpwd = aprintf( "Authorization: Basic %s\015\012", + authorization); + free(authorization); + } + } + } + if((data->change.referer) && !checkheaders(data, "Referer:")) { + if(conn->allocptr.ref) + free(conn->allocptr.ref); + conn->allocptr.ref = aprintf("Referer: %s\015\012", data->change.referer); + } + if(data->set.cookie && !checkheaders(data, "Cookie:")) { + if(conn->allocptr.cookie) + free(conn->allocptr.cookie); + conn->allocptr.cookie = aprintf("Cookie: %s\015\012", data->set.cookie); + } + + if(data->cookies) { + co = Curl_cookie_getlist(data->cookies, + host, ppath, + conn->protocol&PROT_HTTPS?TRUE:FALSE); + } + if (data->change.proxy && + !data->set.tunnel_thru_httpproxy && + !(conn->protocol&PROT_HTTPS)) { + /* The path sent to the proxy is in fact the entire URL */ + ppath = data->change.url; + } + if(HTTPREQ_POST_FORM == data->set.httpreq) { + /* we must build the whole darned post sequence first, so that we have + a size of the whole shebang before we start to send it */ + http->sendit = Curl_getFormData(data->set.httppost, &http->postsize); + } + + if(!checkheaders(data, "Host:")) { + /* if ptr_host is already set, it is almost OK since we only re-use + connections to the very same host and port, but when we use a HTTP + proxy we have a persistant connect and yet we must change the Host: + header! */ + + if(conn->allocptr.host) + free(conn->allocptr.host); + + if(((conn->protocol&PROT_HTTPS) && (conn->remote_port == PORT_HTTPS)) || + (!(conn->protocol&PROT_HTTPS) && (conn->remote_port == PORT_HTTP)) ) + /* If (HTTPS on port 443) OR (non-HTTPS on port 80) then don't include + the port number in the host string */ + conn->allocptr.host = aprintf("Host: %s\r\n", host); + else + conn->allocptr.host = aprintf("Host: %s:%d\r\n", host, + conn->remote_port); + } + + if(!checkheaders(data, "Pragma:")) + http->p_pragma = "Pragma: no-cache\r\n"; + + if(!checkheaders(data, "Accept:")) + http->p_accept = "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*\r\n"; + + if(( (HTTPREQ_POST == data->set.httpreq) || + (HTTPREQ_POST_FORM == data->set.httpreq) || + (HTTPREQ_PUT == data->set.httpreq) ) && + conn->resume_from) { + /********************************************************************** + * Resuming upload in HTTP means that we PUT or POST and that we have + * got a resume_from value set. The resume value has already created + * a Range: header that will be passed along. We need to "fast forward" + * the file the given number of bytes and decrease the assume upload + * file size before we continue this venture in the dark lands of HTTP. + *********************************************************************/ + + if(conn->resume_from < 0 ) { + /* + * This is meant to get the size of the present remote-file by itself. + * We don't support this now. Bail out! + */ + conn->resume_from = 0; + } + + if(conn->resume_from) { + /* do we still game? */ + int passed=0; + + /* Now, let's read off the proper amount of bytes from the + input. If we knew it was a proper file we could've just + fseek()ed but we only have a stream here */ + do { + int readthisamountnow = (conn->resume_from - passed); + int actuallyread; + + if(readthisamountnow > BUFSIZE) + readthisamountnow = BUFSIZE; + + actuallyread = + data->set.fread(data->state.buffer, 1, readthisamountnow, + data->set.in); + + passed += actuallyread; + if(actuallyread != readthisamountnow) { + failf(data, "Could only read %d bytes from the input", + passed); + return CURLE_READ_ERROR; + } + } while(passed != conn->resume_from); /* loop until done */ + + /* now, decrease the size of the read */ + if(data->set.infilesize>0) { + data->set.infilesize -= conn->resume_from; + + if(data->set.infilesize <= 0) { + failf(data, "File already completely uploaded"); + return CURLE_PARTIAL_FILE; + } + } + /* we've passed, proceed as normal */ + } + } + if(conn->bits.use_range) { + /* + * A range is selected. We use different headers whether we're downloading + * or uploading and we always let customized headers override our internal + * ones if any such are specified. + */ + if((data->set.httpreq == HTTPREQ_GET) && + !checkheaders(data, "Range:")) { + conn->allocptr.rangeline = aprintf("Range: bytes=%s\r\n", conn->range); + } + else if((data->set.httpreq != HTTPREQ_GET) && + !checkheaders(data, "Content-Range:")) { + + if(conn->resume_from) { + /* This is because "resume" was selected */ + long total_expected_size= conn->resume_from + data->set.infilesize; + conn->allocptr.rangeline = aprintf("Content-Range: bytes %s%ld/%ld\r\n", + conn->range, total_expected_size-1, + total_expected_size); + } + else { + /* Range was selected and then we just pass the incoming range and + append total size */ + conn->allocptr.rangeline = aprintf("Content-Range: bytes %s/%d\r\n", + conn->range, data->set.infilesize); + } + } + } + + do { + /* Use 1.1 unless the use specificly asked for 1.0 */ + const char *httpstring= + data->set.httpversion==CURL_HTTP_VERSION_1_0?"1.0":"1.1"; + + send_buffer *req_buffer; + struct curl_slist *headers=data->set.headers; + + /* initialize a dynamic send-buffer */ + req_buffer = add_buffer_init(); + + /* add the main request stuff */ + add_bufferf(req_buffer, + "%s " /* GET/HEAD/POST/PUT */ + "%s HTTP/%s\r\n" /* path */ + "%s" /* proxyuserpwd */ + "%s" /* userpwd */ + "%s" /* range */ + "%s" /* user agent */ + "%s" /* cookie */ + "%s" /* host */ + "%s" /* pragma */ + "%s" /* accept */ + "%s", /* referer */ + + data->set.customrequest?data->set.customrequest: + (data->set.no_body?"HEAD": + ((HTTPREQ_POST == data->set.httpreq) || + (HTTPREQ_POST_FORM == data->set.httpreq))?"POST": + (HTTPREQ_PUT == data->set.httpreq)?"PUT":"GET"), + ppath, httpstring, + (conn->bits.proxy_user_passwd && + conn->allocptr.proxyuserpwd)?conn->allocptr.proxyuserpwd:"", + (conn->bits.user_passwd && conn->allocptr.userpwd)? + conn->allocptr.userpwd:"", + (conn->bits.use_range && conn->allocptr.rangeline)? + conn->allocptr.rangeline:"", + (data->set.useragent && *data->set.useragent && conn->allocptr.uagent)? + conn->allocptr.uagent:"", + (conn->allocptr.cookie?conn->allocptr.cookie:""), /* Cookie: <data> */ + (conn->allocptr.host?conn->allocptr.host:""), /* Host: host */ + http->p_pragma?http->p_pragma:"", + http->p_accept?http->p_accept:"", + (data->change.referer && conn->allocptr.ref)?conn->allocptr.ref:"" /* Referer: <data> <CRLF> */ + ); + + if(co) { + int count=0; + struct Cookie *store=co; + /* now loop through all cookies that matched */ + while(co) { + if(co->value && strlen(co->value)) { + if(0 == count) { + add_bufferf(req_buffer, "Cookie: "); + } + add_bufferf(req_buffer, + "%s%s=%s", count?"; ":"", co->name, co->value); + count++; + } + co = co->next; /* next cookie please */ + } + if(count) { + add_buffer(req_buffer, "\r\n", 2); + } + Curl_cookie_freelist(store); /* free the cookie list */ + co=NULL; + } + + if(data->set.timecondition) { + struct tm *thistime; + + /* Phil Karn (Fri, 13 Apr 2001) pointed out that the If-Modified-Since + * header family should have their times set in GMT as RFC2616 defines: + * "All HTTP date/time stamps MUST be represented in Greenwich Mean Time + * (GMT), without exception. For the purposes of HTTP, GMT is exactly + * equal to UTC (Coordinated Universal Time)." (see page 20 of RFC2616). + */ + +#ifdef HAVE_GMTIME_R + /* thread-safe version */ + struct tm keeptime; + thistime = (struct tm *)gmtime_r(&data->set.timevalue, &keeptime); +#else + thistime = gmtime(&data->set.timevalue); +#endif + if(NULL == thistime) { + failf(data, "localtime() failed!"); + return CURLE_OUT_OF_MEMORY; + } + +#ifdef HAVE_STRFTIME + /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ + strftime(buf, BUFSIZE-1, "%a, %d %b %Y %H:%M:%S GMT", thistime); +#else + /* TODO: Right, we *could* write a replacement here */ + strcpy(buf, "no strftime() support"); +#endif + switch(data->set.timecondition) { + case TIMECOND_IFMODSINCE: + default: + add_bufferf(req_buffer, + "If-Modified-Since: %s\r\n", buf); + break; + case TIMECOND_IFUNMODSINCE: + add_bufferf(req_buffer, + "If-Unmodified-Since: %s\r\n", buf); + break; + case TIMECOND_LASTMOD: + add_bufferf(req_buffer, + "Last-Modified: %s\r\n", buf); + break; + } + } + + while(headers) { + char *ptr = strchr(headers->data, ':'); + if(ptr) { + /* we require a colon for this to be a true header */ + + ptr++; /* pass the colon */ + while(*ptr && isspace((int)*ptr)) + ptr++; + + if(*ptr) { + /* only send this if the contents was non-blank */ + + add_bufferf(req_buffer, "%s\r\n", headers->data); + } + } + headers = headers->next; + } + + if(HTTPREQ_POST_FORM == data->set.httpreq) { + if(Curl_FormInit(&http->form, http->sendit)) { + failf(data, "Internal HTTP POST error!"); + return CURLE_HTTP_POST_ERROR; + } + + http->storefread = data->set.fread; /* backup */ + http->in = data->set.in; /* backup */ + + data->set.fread = (curl_read_callback) + Curl_FormReader; /* set the read function to read from the + generated form data */ + data->set.in = (FILE *)&http->form; + + add_bufferf(req_buffer, + "Content-Length: %d\r\n", http->postsize); + + if(!checkheaders(data, "Expect:")) { + /* if not disabled explicitly we add a Expect: 100-continue + to the headers which actually speeds up post operations (as + there is one packet coming back from the web server) */ + add_bufferf(req_buffer, + "Expect: 100-continue\r\n"); + data->set.expect100header = TRUE; + } + + if(!checkheaders(data, "Content-Type:")) { + /* Get Content-Type: line from Curl_FormReadOneLine, which happens + to always be the first line. We can know this for sure since + we always build the formpost linked list the same way! + + The Content-Type header line also contains the MIME boundary + string etc why disabling this header is likely to not make things + work, but we support it anyway. + */ + char contentType[256]; + int linelength=0; + linelength = Curl_FormReadOneLine (contentType, + sizeof(contentType), + 1, + (FILE *)&http->form); + if(linelength == -1) { + failf(data, "Could not get Content-Type header line!"); + return CURLE_HTTP_POST_ERROR; + } + add_buffer(req_buffer, contentType, linelength); + } + + /* make the request end in a true CRLF */ + add_buffer(req_buffer, "\r\n", 2); + + /* set upload size to the progress meter */ + Curl_pgrsSetUploadSize(data, http->postsize); + + /* fire away the whole request to the server */ + result = add_buffer_send(conn->firstsocket, conn, req_buffer, + &data->info.request_size); + if(result) + failf(data, "Failed sending POST request"); + else + /* setup variables for the upcoming transfer */ + result = Curl_Transfer(conn, conn->firstsocket, -1, TRUE, + &http->readbytecount, + conn->firstsocket, + &http->writebytecount); + if(result) { + Curl_formclean(http->sendit); /* free that whole lot */ + return result; + } + } + else if(HTTPREQ_PUT == data->set.httpreq) { + /* Let's PUT the data to the server! */ + + if(data->set.infilesize>0) { + add_bufferf(req_buffer, + "Content-Length: %d\r\n\r\n", /* file size */ + data->set.infilesize ); + } + else + add_bufferf(req_buffer, "\015\012"); + + /* set the upload size to the progress meter */ + Curl_pgrsSetUploadSize(data, data->set.infilesize); + + /* this sends the buffer and frees all the buffer resources */ + result = add_buffer_send(conn->firstsocket, conn, req_buffer, + &data->info.request_size); + if(result) + failf(data, "Faied sending POST request"); + else + /* prepare for transfer */ + result = Curl_Transfer(conn, conn->firstsocket, -1, TRUE, + &http->readbytecount, + conn->firstsocket, + &http->writebytecount); + if(result) + return result; + + } + else { + if(HTTPREQ_POST == data->set.httpreq) { + /* this is the simple POST, using x-www-form-urlencoded style */ + + if(!data->set.postfields) { + /* + * This is an attempt to do a POST without having anything to + * actually send. Let's make a NULL pointer equal "" here. Good/bad + * ? + */ + data->set.postfields = (char *)""; + data->set.postfieldsize = 0; /* it might been set to something illegal, + anything > 0 would be! */ + } + + if(!checkheaders(data, "Content-Length:")) + /* we allow replacing this header, although it isn't very wise to + actually set your own */ + add_bufferf(req_buffer, + "Content-Length: %d\r\n", + (data->set.postfieldsize?data->set.postfieldsize: + strlen(data->set.postfields)) ); + + if(!checkheaders(data, "Content-Type:")) + add_bufferf(req_buffer, + "Content-Type: application/x-www-form-urlencoded\r\n"); + + /* and here comes the actual data */ + if(data->set.postfieldsize) { + add_buffer(req_buffer, "\r\n", 2); + add_buffer(req_buffer, data->set.postfields, + data->set.postfieldsize); + } + else { + add_bufferf(req_buffer, + "\r\n" + "%s", + data->set.postfields ); + } + } + else + add_buffer(req_buffer, "\r\n", 2); + + /* issue the request */ + result = add_buffer_send(conn->firstsocket, conn, req_buffer, + &data->info.request_size); + + if(result) + failf(data, "Failed sending HTTP request"); + else + /* HTTP GET/HEAD download: */ + result = Curl_Transfer(conn, conn->firstsocket, -1, TRUE, bytecount, + -1, NULL); /* nothing to upload */ + } + if(result) + return result; + } while (0); /* this is just a left-over from the multiple document download + attempts */ + + return CURLE_OK; +} + + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ |