#include "setup.h"
#ifndef CURL_DISABLE_HTTP
#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
#include <curl/mprintf.h>
#ifdef MALLOCDEBUG
#include "memdebug.h"
#endif
static CURLcode
add_buffer(send_buffer *in, const void *inptr, size_t size);
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;
}
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;
ptr = in->buffer;
size = in->size_used;
do {
res = Curl_write(conn, sockfd, ptr, size, &amount);
if(CURLE_OK != res)
break;
if(conn->data->set.verbose)
Curl_debug(conn->data, CURLINFO_HEADER_OUT, ptr, amount);
if(amount != size) {
size -= amount;
ptr += amount;
}
else
break;
} while(1);
if(in->buffer)
free(in->buffer);
free(in);
*bytes_written += amount;
return res;
}
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);
va_end(ap);
if(s) {
result = add_buffer(in, s, strlen(s));
free(s);
}
return result;
}
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)
new_rb = (char *)realloc(in->buffer, new_size);
else
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;
}
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;
}
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;
int perline;
bool keepon=TRUE;
ssize_t gotbytes;
char *ptr;
int timeout = 3600;
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);
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;
}
if(data->set.timeout) {
timeout = data->set.timeout -
Curl_tvdiff(Curl_tvnow(), conn->now)/1000;
if(timeout <=0 ) {
failf(data, "Transfer aborted due to timeout");
return -SELECT_TIMEOUT;
}
}
FD_ZERO (&readfd);
FD_SET (tunnelsocket, &readfd);
rkeepfd = readfd;
ptr=data->state.buffer;
line_start = ptr;
nread=0;
perline=0;
keepon=TRUE;
while((nread<BUFSIZE) && (keepon && !error)) {
readfd = rkeepfd;
interval.tv_sec = timeout;
interval.tv_usec = 0;
switch (select (tunnelsocket+1, &readfd, NULL, NULL, &interval)) {
case -1:
error = SELECT_ERROR;
failf(data, "Transfer aborted due to select() error");
break;
case 0:
error = SELECT_TIMEOUT;
failf(data, "Transfer aborted due to timeout");
break;
default:
res= Curl_read(conn, tunnelsocket, ptr, BUFSIZE-nread,
&gotbytes);
if(res< 0)
continue;
else if(res)
keepon = FALSE;
else if(gotbytes <= 0) {
keepon = FALSE;
error = SELECT_ERROR;
failf(data, "Connection aborted");
}
else {
int i;
nread += gotbytes;
for(i = 0; i < gotbytes; ptr++, i++) {
perline++;
if(*ptr=='\n') {
if(data->set.verbose)
Curl_debug(data, CURLINFO_DATA_IN, line_start, perline);
if('\r' == line_start[0]) {
keepon=FALSE;
break;
}
if(2 == sscanf(line_start, "HTTP/1.%d %d",
&subversion,
&httperror)) {
;
}
perline=0;
line_start = ptr+1;
}
}
}
break;
}
}
if(error)
return CURLE_RECV_ERROR;
if(200 != httperror) {
if(407 == httperror)
failf(data, "Proxy requires authorization!");
else
failf(data, "Received error code %d from proxy", httperror);
return CURLE_RECV_ERROR;
}
infof (data, "Proxy replied to CONNECT request\n");
return CURLE_OK;
}
CURLcode Curl_http_connect(struct connectdata *conn)
{
struct SessionHandle *data;
CURLcode result;
data=conn->data;
if(conn->bits.httpproxy &&
((conn->protocol & PROT_HTTPS) || data->set.tunnel_thru_httpproxy)) {
result = Curl_ConnectHTTPProxyTunnel(conn, conn->firstsocket,
conn->hostname, conn->remote_port);
if(CURLE_OK != result)
return result;
}
if(conn->protocol & PROT_HTTPS) {
result = Curl_SSLConnect(conn);
if(result)
return result;
}
if(conn->bits.user_passwd && !data->state.this_is_a_follow) {
data->state.auth_host = strdup(conn->hostname);
}
return CURLE_OK;
}
CURLcode Curl_http_done(struct connectdata *conn)
{
struct SessionHandle *data;
struct HTTP *http;
data=conn->data;
http=conn->proto.http;
if(HTTPREQ_POST_FORM == data->set.httpreq) {
conn->bytecount = http->readbytecount + http->writebytecount;
Curl_formclean(http->sendit);
data->set.fread = http->storefread;
data->set.in = http->in;
}
else if(HTTPREQ_PUT == data->set.httpreq)
conn->bytecount = http->readbytecount + http->writebytecount;
if(0 == (http->readbytecount + conn->headerbytecount)) {
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;
CURLcode result=CURLE_OK;
struct HTTP *http;
struct Cookie *co=NULL;
char *ppath = conn->ppath;
char *host = conn->name;
const char *te = "";
if(!conn->proto.http) {
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;
conn->bits.close = FALSE;
if ( (conn->protocol&(PROT_HTTP|PROT_FTP)) &&
data->set.upload) {
data->set.httpreq = HTTPREQ_PUT;
}
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;
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(conn->bits.upload_chunky) {
if(!checkheaders(data, "Transfer-Encoding:")) {
te = "Transfer-Encoding: chunked\r\n";
}
}
if(data->cookies) {
co = Curl_cookie_getlist(data->cookies,
host, ppath,
(conn->protocol&PROT_HTTPS?TRUE:FALSE));
}
if (data->change.proxy && *data->change.proxy &&
!data->set.tunnel_thru_httpproxy &&
!(conn->protocol&PROT_HTTPS)) {
ppath = data->change.url;
}
if(HTTPREQ_POST_FORM == data->set.httpreq) {
result = Curl_getFormData(&http->sendit, data->set.httppost,
&http->postsize);
if(CURLE_OK != result) {
failf(data, "failed creating formpost data");
return result;
}
}
if(!checkheaders(data, "Host:")) {
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)) )
conn->allocptr.host = aprintf("Host: %s%s%s\r\n",
conn->bits.ipv6_ip?"[":"",
host,
conn->bits.ipv6_ip?"]":"");
else
conn->allocptr.host = aprintf("Host: %s%s%s:%d\r\n",
conn->bits.ipv6_ip?"[":"",
host,
conn->bits.ipv6_ip?"]":"",
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) {
if(conn->resume_from < 0 ) {
conn->resume_from = 0;
}
if(conn->resume_from) {
int passed=0;
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);
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;
}
}
}
}
if(conn->bits.use_range) {
if((data->set.httpreq == HTTPREQ_GET) &&
!checkheaders(data, "Range:")) {
if(conn->allocptr.rangeline)
free(conn->allocptr.rangeline);
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) {
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 {
conn->allocptr.rangeline = aprintf("Content-Range: bytes %s/%d\r\n",
conn->range, data->set.infilesize);
}
}
}
do {
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;
req_buffer = add_buffer_init();
add_bufferf(req_buffer,
"%s "
"%s HTTP/%s\r\n"
"%s"
"%s"
"%s"
"%s"
"%s"
"%s"
"%s"
"%s"
"%s"
"%s"
"%s",
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:""),
(conn->allocptr.host?conn->allocptr.host:""),
http->p_pragma?http->p_pragma:"",
http->p_accept?http->p_accept:"",
(data->set.encoding && *data->set.encoding && conn->allocptr.accept_encoding)?
conn->allocptr.accept_encoding:"",
(data->change.referer && conn->allocptr.ref)?conn->allocptr.ref:"" ,
te
);
if(co) {
int count=0;
struct Cookie *store=co;
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;
}
if(count) {
add_buffer(req_buffer, "\r\n", 2);
}
Curl_cookie_freelist(store);
co=NULL;
}
if(data->set.timecondition) {
struct tm *thistime;
#ifdef HAVE_GMTIME_R
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
strftime(buf, BUFSIZE-1, "%a, %d %b %Y %H:%M:%S GMT", thistime);
#else
strcpy(buf, "no strftime() support");
#endif
switch(data->set.timecondition) {
case CURL_TIMECOND_IFMODSINCE:
default:
add_bufferf(req_buffer,
"If-Modified-Since: %s\r\n", buf);
break;
case CURL_TIMECOND_IFUNMODSINCE:
add_bufferf(req_buffer,
"If-Unmodified-Since: %s\r\n", buf);
break;
case CURL_TIMECOND_LASTMOD:
add_bufferf(req_buffer,
"Last-Modified: %s\r\n", buf);
break;
}
}
while(headers) {
char *ptr = strchr(headers->data, ':');
if(ptr) {
ptr++;
while(*ptr && isspace((int)*ptr))
ptr++;
if(*ptr) {
add_bufferf(req_buffer, "%s\r\n", headers->data);
}
}
headers = headers->next;
}
switch(data->set.httpreq) {
case HTTPREQ_POST_FORM:
if(Curl_FormInit(&http->form, http->sendit)) {
failf(data, "Internal HTTP POST error!");
return CURLE_HTTP_POST_ERROR;
}
http->storefread = data->set.fread;
http->in = data->set.in;
data->set.fread = (curl_read_callback)
Curl_FormReader;
data->set.in = (FILE *)&http->form;
add_bufferf(req_buffer,
"Content-Length: %d\r\n", http->postsize);
if(!checkheaders(data, "Expect:")) {
add_bufferf(req_buffer,
"Expect: 100-continue\r\n");
data->set.expect100header = TRUE;
}
if(!checkheaders(data, "Content-Type:")) {
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);
}
add_buffer(req_buffer, "\r\n", 2);
Curl_pgrsSetUploadSize(data, http->postsize);
result = add_buffer_send(conn->firstsocket, conn, req_buffer,
&data->info.request_size);
if(result)
failf(data, "Failed sending POST request");
else
result = Curl_Transfer(conn, conn->firstsocket, -1, TRUE,
&http->readbytecount,
conn->firstsocket,
&http->writebytecount);
if(result) {
Curl_formclean(http->sendit);
return result;
}
break;
case HTTPREQ_PUT:
if(data->set.infilesize>0) {
add_bufferf(req_buffer,
"Content-Length: %d\r\n\r\n",
data->set.infilesize );
}
else
add_bufferf(req_buffer, "\015\012");
Curl_pgrsSetUploadSize(data, data->set.infilesize);
result = add_buffer_send(conn->firstsocket, conn, req_buffer,
&data->info.request_size);
if(result)
failf(data, "Faied sending POST request");
else
result = Curl_Transfer(conn, conn->firstsocket, -1, TRUE,
&http->readbytecount,
conn->firstsocket,
&http->writebytecount);
if(result)
return result;
break;
case HTTPREQ_POST:
if(!checkheaders(data, "Content-Length:"))
add_bufferf(req_buffer,
"Content-Length: %d\r\n",
data->set.postfieldsize?
data->set.postfieldsize:
(data->set.postfields?strlen(data->set.postfields):0) );
if(!checkheaders(data, "Content-Type:"))
add_bufferf(req_buffer,
"Content-Type: application/x-www-form-urlencoded\r\n");
add_buffer(req_buffer, "\r\n", 2);
if(data->set.postfieldsize && data->set.postfields) {
add_buffer(req_buffer, data->set.postfields,
data->set.postfieldsize);
}
else if(data->set.postfields)
add_bufferf(req_buffer,
"%s",
data->set.postfields );
result = add_buffer_send(conn->firstsocket, conn, req_buffer,
&data->info.request_size);
if(result)
failf(data, "Failed sending HTTP POST request");
else
result =
Curl_Transfer(conn, conn->firstsocket, -1, TRUE,
&http->readbytecount,
data->set.postfields?-1:conn->firstsocket,
data->set.postfields?NULL:&http->writebytecount);
break;
default:
add_buffer(req_buffer, "\r\n", 2);
result = add_buffer_send(conn->firstsocket, conn, req_buffer,
&data->info.request_size);
if(result)
failf(data, "Failed sending HTTP request");
else
result = Curl_Transfer(conn, conn->firstsocket, -1, TRUE,
&http->readbytecount,
-1, NULL);
}
if(result)
return result;
} while (0);
return CURLE_OK;
}
#endif