#include "setup.h"
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include "strtoofft.h"
#include "strequal.h"
#include "rawstr.h"
#ifdef WIN32
#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
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#ifndef HAVE_SOCKET
#error "We can't compile without socket() support!"
#endif
#endif
#include "urldata.h"
#include <curl/curl.h>
#include "netrc.h"
#include "content_encoding.h"
#include "hostip.h"
#include "transfer.h"
#include "sendf.h"
#include "speedcheck.h"
#include "progress.h"
#include "http.h"
#include "url.h"
#include "getinfo.h"
#include "sslgen.h"
#include "http_digest.h"
#include "http_ntlm.h"
#include "http_negotiate.h"
#include "share.h"
#include "curl_memory.h"
#include "select.h"
#include "multiif.h"
#include "easyif.h"
#define _MPRINTF_REPLACE
#include <curl/mprintf.h>
#include "memdebug.h"
#define CURL_TIMEOUT_EXPECT_100 1000
#ifndef CURL_DISABLE_HTTP
static CURLcode readwrite_http_headers(struct SessionHandle *data,
struct connectdata *conn,
struct SingleRequest *k,
ssize_t *nread,
bool *stop_reading);
#endif
CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp)
{
struct SessionHandle *data = conn->data;
size_t buffersize = (size_t)bytes;
int nread;
#ifdef CURL_DOES_CONVERSIONS
bool sending_http_headers = FALSE;
if((conn->protocol&PROT_HTTP) &&
(data->state.proto.http->sending == HTTPSEND_REQUEST)) {
sending_http_headers = TRUE;
}
#endif
if(data->req.upload_chunky) {
buffersize -= (8 + 2 + 2);
data->req.upload_fromhere += (8 + 2);
}
nread = (int)conn->fread_func(data->req.upload_fromhere, 1,
buffersize, conn->fread_in);
if(nread == CURL_READFUNC_ABORT) {
failf(data, "operation aborted by callback");
*nreadp = 0;
return CURLE_ABORTED_BY_CALLBACK;
}
else if(nread == CURL_READFUNC_PAUSE) {
struct SingleRequest *k = &data->req;
k->keepon |= KEEP_SEND_PAUSE;
if(data->req.upload_chunky) {
data->req.upload_fromhere -= (8 + 2);
}
*nreadp = 0;
return CURLE_OK;
}
else if((size_t)nread > buffersize) {
*nreadp = 0;
failf(data, "read function returned funny value");
return CURLE_READ_ERROR;
}
if(!data->req.forbidchunk && data->req.upload_chunky) {
char hexbuffer[11];
const char *endofline_native;
const char *endofline_network;
int hexlen;
#ifdef CURL_DO_LINEEND_CONV
if((data->set.crlf) || (data->set.prefer_ascii)) {
#else
if(data->set.crlf) {
#endif
endofline_native = "\n";
endofline_network = "\x0a";
} else {
endofline_native = "\r\n";
endofline_network = "\x0d\x0a";
}
hexlen = snprintf(hexbuffer, sizeof(hexbuffer),
"%x%s", nread, endofline_native);
data->req.upload_fromhere -= hexlen;
nread += hexlen;
memcpy(data->req.upload_fromhere, hexbuffer, hexlen);
memcpy(data->req.upload_fromhere + nread,
endofline_network,
strlen(endofline_network));
#ifdef CURL_DOES_CONVERSIONS
CURLcode res;
int length;
if(data->set.prefer_ascii) {
length = nread;
} else {
length = strlen(hexbuffer);
}
res = Curl_convert_to_network(data, data->req.upload_fromhere, length);
if(res != CURLE_OK) {
return(res);
}
#endif
if((nread - hexlen) == 0) {
data->req.upload_done = TRUE;
}
nread+=(int)strlen(endofline_native);
}
#ifdef CURL_DOES_CONVERSIONS
else if((data->set.prefer_ascii) && (!sending_http_headers)) {
CURLcode res;
res = Curl_convert_to_network(data, data->req.upload_fromhere, nread);
if(res != CURLE_OK)
return(res);
}
#endif
*nreadp = nread;
return CURLE_OK;
}
#ifndef CURL_DISABLE_HTTP
static bool
checkhttpprefix(struct SessionHandle *data,
const char *s)
{
struct curl_slist *head = data->set.http200aliases;
bool rc = FALSE;
#ifdef CURL_DOES_CONVERSIONS
char *scratch = calloc(1, strlen(s)+1);
if(NULL == scratch) {
failf (data, "Failed to calloc memory for conversion!");
return FALSE;
}
strcpy(scratch, s);
if(CURLE_OK != Curl_convert_from_network(data, scratch, strlen(s)+1)) {
free(scratch);
return FALSE;
}
s = scratch;
#endif
while(head) {
if(checkprefix(head->data, s)) {
rc = TRUE;
break;
}
head = head->next;
}
if((rc != TRUE) && (checkprefix("HTTP/", s))) {
rc = TRUE;
}
#ifdef CURL_DOES_CONVERSIONS
free(scratch);
#endif
return rc;
}
#endif
CURLcode Curl_readrewind(struct connectdata *conn)
{
struct SessionHandle *data = conn->data;
conn->bits.rewindaftersend = FALSE;
data->req.keepon &= ~KEEP_SEND;
if(data->set.postfields ||
(data->set.httpreq == HTTPREQ_POST_FORM))
;
else {
if(data->set.seek_func) {
int err;
err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET);
if(err) {
failf(data, "seek callback returned error %d", (int)err);
return CURLE_SEND_FAIL_REWIND;
}
}
else if(data->set.ioctl_func) {
curlioerr err;
err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD,
data->set.ioctl_client);
infof(data, "the ioctl callback returned %d\n", (int)err);
if(err) {
failf(data, "ioctl callback returned error %d", (int)err);
return CURLE_SEND_FAIL_REWIND;
}
}
else {
if(data->set.fread_func == (curl_read_callback)fread) {
if(-1 != fseek(data->set.in, 0, SEEK_SET))
return CURLE_OK;
}
failf(data, "necessary data rewind wasn't possible");
return CURLE_SEND_FAIL_REWIND;
}
}
return CURLE_OK;
}
static int data_pending(const struct connectdata *conn)
{
return conn->protocol&(PROT_SCP|PROT_SFTP) ||
Curl_ssl_data_pending(conn, FIRSTSOCKET);
}
static void read_rewind(struct connectdata *conn,
size_t thismuch)
{
conn->read_pos -= thismuch;
conn->bits.stream_was_rewound = TRUE;
#ifdef DEBUGBUILD
{
char buf[512 + 1];
size_t show;
show = CURLMIN(conn->buf_len - conn->read_pos, sizeof(buf)-1);
if(conn->master_buffer) {
memcpy(buf, conn->master_buffer + conn->read_pos, show);
buf[show] = '\0';
}
else {
buf[0] = '\0';
}
DEBUGF(infof(conn->data,
"Buffer after stream rewind (read_pos = %d): [%s]",
conn->read_pos, buf));
}
#endif
}
static CURLcode readwrite_data(struct SessionHandle *data,
struct connectdata *conn,
struct SingleRequest *k,
int *didwhat, bool *done)
{
CURLcode result = CURLE_OK;
ssize_t nread;
bool is_empty_data = FALSE;
*done = FALSE;
do {
size_t buffersize = data->set.buffer_size?
data->set.buffer_size : BUFSIZE;
size_t bytestoread = buffersize;
int readrc;
if(k->size != -1 && !k->header) {
curl_off_t totalleft = k->size - k->bytecount;
if(totalleft < (curl_off_t)bytestoread)
bytestoread = (size_t)totalleft;
}
if(bytestoread) {
readrc = Curl_read(conn, conn->sockfd, k->buf, bytestoread, &nread);
if(0 > readrc)
break;
result = (CURLcode)readrc;
if(result>0)
return result;
}
else {
nread = 0;
}
if((k->bytecount == 0) && (k->writebytecount == 0)) {
Curl_pgrsTime(data, TIMER_STARTTRANSFER);
if(k->exp100 > EXP100_SEND_DATA)
k->start100 = Curl_tvnow();
}
*didwhat |= KEEP_RECV;
is_empty_data = (bool)((nread == 0) && (k->bodywrites == 0));
if(0 < nread || is_empty_data) {
k->buf[nread] = 0;
}
else if(0 >= nread) {
DEBUGF(infof(data, "nread <= 0, server closed connection, bailing\n"));
k->keepon &= ~KEEP_RECV;
break;
}
k->str = k->buf;
#ifndef CURL_DISABLE_HTTP
if(k->header) {
bool stop_reading = FALSE;
result = readwrite_http_headers(data, conn, k, &nread, &stop_reading);
if(result)
return result;
if(stop_reading)
break;
}
#endif
if(k->str && !k->header && (nread > 0 || is_empty_data)) {
#ifndef CURL_DISABLE_HTTP
if(0 == k->bodywrites && !is_empty_data) {
if(conn->protocol&PROT_HTTP) {
if(data->req.newurl) {
if(conn->bits.close) {
k->keepon &= ~KEEP_RECV;
*done = TRUE;
return CURLE_OK;
}
k->ignorebody = TRUE;
infof(data, "Ignoring the response-body\n");
}
if(data->state.resume_from && !k->content_range &&
(data->set.httpreq==HTTPREQ_GET) &&
!k->ignorebody) {
failf(data, "HTTP server doesn't seem to support "
"byte ranges. Cannot resume.");
return CURLE_RANGE_ERROR;
}
if(data->set.timecondition && !data->state.range) {
if((k->timeofdoc > 0) && (data->set.timevalue > 0)) {
switch(data->set.timecondition) {
case CURL_TIMECOND_IFMODSINCE:
default:
if(k->timeofdoc < data->set.timevalue) {
infof(data,
"The requested document is not new enough\n");
*done = TRUE;
data->info.timecond = TRUE;
return CURLE_OK;
}
break;
case CURL_TIMECOND_IFUNMODSINCE:
if(k->timeofdoc > data->set.timevalue) {
infof(data,
"The requested document is not old enough\n");
*done = TRUE;
data->info.timecond = TRUE;
return CURLE_OK;
}
break;
}
}
}
}
}
#endif
k->bodywrites++;
if(data->set.verbose) {
if(k->badheader) {
Curl_debug(data, CURLINFO_DATA_IN, data->state.headerbuff,
(size_t)k->hbuflen, conn);
if(k->badheader == HEADER_PARTHEADER)
Curl_debug(data, CURLINFO_DATA_IN,
k->str, (size_t)nread, conn);
}
else
Curl_debug(data, CURLINFO_DATA_IN,
k->str, (size_t)nread, conn);
}
#ifndef CURL_DISABLE_HTTP
if(k->chunk) {
CHUNKcode res =
Curl_httpchunk_read(conn, k->str, nread, &nread);
if(CHUNKE_OK < res) {
if(CHUNKE_WRITE_ERROR == res) {
failf(data, "Failed writing data");
return CURLE_WRITE_ERROR;
}
failf(data, "Received problem %d in the chunky parser", res);
return CURLE_RECV_ERROR;
}
else if(CHUNKE_STOP == res) {
size_t dataleft;
k->keepon &= ~KEEP_RECV;
dataleft = conn->chunk.dataleft;
if(dataleft != 0) {
infof(conn->data, "Leftovers after chunking. "
" Rewinding %d bytes\n",dataleft);
read_rewind(conn, dataleft);
}
}
}
#endif
if((-1 != k->maxdownload) &&
(k->bytecount + nread >= k->maxdownload)) {
size_t excess = (size_t)(k->bytecount + nread - k->maxdownload);
if(excess > 0 && !k->ignorebody) {
infof(data,
"Rewinding stream by : %d"
" bytes on url %s (size = %" FORMAT_OFF_T
", maxdownload = %" FORMAT_OFF_T
", bytecount = %" FORMAT_OFF_T ", nread = %d)\n",
excess, data->state.path,
k->size, k->maxdownload, k->bytecount, nread);
read_rewind(conn, excess);
}
nread = (ssize_t) (k->maxdownload - k->bytecount);
if(nread < 0 )
nread = 0;
k->keepon &= ~KEEP_RECV;
}
k->bytecount += nread;
Curl_pgrsSetDownloadCounter(data, k->bytecount);
if(!k->chunk && (nread || k->badheader || is_empty_data)) {
if(k->badheader && !k->ignorebody) {
result = Curl_client_write(conn, CLIENTWRITE_BODY,
data->state.headerbuff,
k->hbuflen);
if(result)
return result;
}
if(k->badheader < HEADER_ALLBAD) {
#ifdef HAVE_LIBZ
switch (conn->data->set.http_ce_skip ?
IDENTITY : k->content_encoding) {
case IDENTITY:
#endif
if(!k->ignorebody)
result = Curl_client_write(conn, CLIENTWRITE_BODY, k->str,
nread);
#ifdef HAVE_LIBZ
break;
case DEFLATE:
if(!k->ignorebody)
result = Curl_unencode_deflate_write(conn, k, nread);
break;
case GZIP:
if(!k->ignorebody)
result = Curl_unencode_gzip_write(conn, k, nread);
break;
case COMPRESS:
default:
failf (data, "Unrecognized content encoding type. "
"libcurl understands `identity', `deflate' and `gzip' "
"content encodings.");
result = CURLE_BAD_CONTENT_ENCODING;
break;
}
#endif
}
k->badheader = HEADER_NORMAL;
if(result)
return result;
}
}
if(is_empty_data) {
k->keepon &= ~KEEP_RECV;
}
} while(data_pending(conn));
if(((k->keepon & (KEEP_RECV|KEEP_SEND)) == KEEP_SEND) &&
conn->bits.close ) {
infof(data, "we are done reading and this is set to close, stop send\n");
k->keepon &= ~KEEP_SEND;
}
return CURLE_OK;
}
#ifndef CURL_DISABLE_HTTP
static CURLcode header_append(struct SessionHandle *data,
struct SingleRequest *k,
size_t length)
{
if(k->hbuflen + length >= data->state.headersize) {
char *newbuff;
size_t hbufp_index;
size_t newsize;
if(k->hbuflen + length > CURL_MAX_HTTP_HEADER) {
failf (data, "Avoided giant realloc for header (max is %d)!",
CURL_MAX_HTTP_HEADER);
return CURLE_OUT_OF_MEMORY;
}
newsize=CURLMAX((k->hbuflen+ length)*3/2, data->state.headersize*2);
hbufp_index = k->hbufp - data->state.headerbuff;
newbuff = realloc(data->state.headerbuff, newsize);
if(!newbuff) {
failf (data, "Failed to alloc memory for big header!");
return CURLE_OUT_OF_MEMORY;
}
data->state.headersize=newsize;
data->state.headerbuff = newbuff;
k->hbufp = data->state.headerbuff + hbufp_index;
}
memcpy(k->hbufp, k->str_start, length);
k->hbufp += length;
k->hbuflen += length;
*k->hbufp = 0;
return CURLE_OK;
}
static CURLcode readwrite_http_headers(struct SessionHandle *data,
struct connectdata *conn,
struct SingleRequest *k,
ssize_t *nread,
bool *stop_reading)
{
CURLcode result;
do {
size_t rest_length;
size_t full_length;
int writetype;
k->str_start = k->str;
k->end_ptr = memchr(k->str_start, 0x0a, *nread);
if(!k->end_ptr) {
result = header_append(data, k, *nread);
if(result)
return result;
if(!k->headerline && (k->hbuflen>5)) {
if(!checkhttpprefix(data, data->state.headerbuff)) {
k->header = FALSE;
k->badheader = HEADER_ALLBAD;
break;
}
}
break;
}
rest_length = (k->end_ptr - k->str)+1;
*nread -= (ssize_t)rest_length;
k->str = k->end_ptr + 1;
full_length = k->str - k->str_start;
result = header_append(data, k, full_length);
if(result)
return result;
k->end_ptr = k->hbufp;
k->p = data->state.headerbuff;
if(!k->headerline) {
if((k->hbuflen>5) &&
!checkhttpprefix(data, data->state.headerbuff)) {
k->header = FALSE;
if(*nread)
k->badheader = HEADER_PARTHEADER;
else {
k->badheader = HEADER_ALLBAD;
*nread = (ssize_t)rest_length;
}
break;
}
}
if((0x0a == *k->p) || (0x0d == *k->p)) {
size_t headerlen;
#ifdef CURL_DOES_CONVERSIONS
if(0x0d == *k->p) {
*k->p = '\r';
k->p++;
}
if(0x0a == *k->p) {
*k->p = '\n';
k->p++;
}
#else
if('\r' == *k->p)
k->p++;
if('\n' == *k->p)
k->p++;
#endif
if(100 <= k->httpcode && 199 >= k->httpcode) {
k->header = TRUE;
k->headerline = 0;
if(k->exp100) {
k->exp100 = EXP100_SEND_DATA;
k->keepon |= KEEP_SEND;
}
}
else {
k->header = FALSE;
if((k->size == -1) && !k->chunk && !conn->bits.close &&
(data->set.httpreq != HTTPREQ_HEAD) &&
(conn->httpversion >= 11) ) {
infof(data, "no chunk, no close, no size. Assume close to "
"signal end\n");
conn->bits.close = TRUE;
}
}
if(417 == k->httpcode) {
k->exp100 = EXP100_FAILED;
k->keepon &= ~KEEP_SEND;
}
if(Curl_http_should_fail(conn)) {
failf (data, "The requested URL returned error: %d",
k->httpcode);
return CURLE_HTTP_RETURNED_ERROR;
}
writetype = CLIENTWRITE_HEADER;
if(data->set.include_header)
writetype |= CLIENTWRITE_BODY;
headerlen = k->p - data->state.headerbuff;
result = Curl_client_write(conn, writetype,
data->state.headerbuff,
headerlen);
if(result)
return result;
data->info.header_size += (long)headerlen;
data->req.headerbytecount += (long)headerlen;
data->req.deductheadercount =
(100 <= k->httpcode && 199 >= k->httpcode)?data->req.headerbytecount:0;
if(data->state.resume_from &&
(data->set.httpreq==HTTPREQ_GET) &&
(k->httpcode == 416)) {
*stop_reading = TRUE;
}
if(!*stop_reading) {
result = Curl_http_auth_act(conn);
if(result)
return result;
if(conn->bits.rewindaftersend) {
infof(data, "Keep sending data to get tossed away!\n");
k->keepon |= KEEP_SEND;
}
}
if(!k->header) {
if(data->set.opt_no_body)
*stop_reading = TRUE;
else {
if(k->chunk)
k->size=-1;
}
if(-1 != k->size) {
Curl_pgrsSetDownloadSize(data, k->size);
k->maxdownload = k->size;
}
if(0 == k->maxdownload)
*stop_reading = TRUE;
if(*stop_reading) {
k->keepon &= ~KEEP_RECV;
}
if(data->set.verbose)
Curl_debug(data, CURLINFO_HEADER_IN,
k->str_start, headerlen, conn);
break;
}
k->hbufp = data->state.headerbuff;
k->hbuflen = 0;
continue;
}
if(!k->headerline++) {
int httpversion_major;
int nc;
#ifdef CURL_DOES_CONVERSIONS
#define HEADER1 scratch
#define SCRATCHSIZE 21
CURLcode res;
char scratch[SCRATCHSIZE+1];
strncpy(&scratch[0], k->p, SCRATCHSIZE);
scratch[SCRATCHSIZE] = 0;
res = Curl_convert_from_network(data,
&scratch[0],
SCRATCHSIZE);
if(CURLE_OK != res) {
return res;
}
#else
#define HEADER1 k->p
#endif
nc = sscanf(HEADER1,
" HTTP/%d.%d %3d",
&httpversion_major,
&conn->httpversion,
&k->httpcode);
if(nc==3) {
conn->httpversion += 10 * httpversion_major;
}
else {
nc=sscanf(HEADER1, " HTTP %3d", &k->httpcode);
conn->httpversion = 10;
if(!nc) {
if(checkhttpprefix(data, k->p)) {
nc = 1;
k->httpcode = 200;
conn->httpversion = 10;
}
}
}
if(nc) {
data->info.httpcode = k->httpcode;
data->info.httpversion = conn->httpversion;
if (!data->state.httpversion ||
data->state.httpversion > conn->httpversion)
data->state.httpversion = conn->httpversion;
if(data->set.http_fail_on_error && (k->httpcode >= 400) &&
((k->httpcode != 401) || !conn->bits.user_passwd) &&
((k->httpcode != 407) || !conn->bits.proxy_user_passwd) ) {
if(data->state.resume_from &&
(data->set.httpreq==HTTPREQ_GET) &&
(k->httpcode == 416)) {
}
else {
failf (data, "The requested URL returned error: %d",
k->httpcode);
return CURLE_HTTP_RETURNED_ERROR;
}
}
if(conn->httpversion == 10) {
infof(data, "HTTP 1.0, assume close after body\n");
conn->bits.close = TRUE;
}
else if(conn->httpversion >= 11 &&
!conn->bits.close) {
DEBUGF(infof(data,
"HTTP 1.1 or later with persistent connection, "
"pipelining supported\n"));
conn->server_supports_pipelining = TRUE;
}
switch(k->httpcode) {
case 204:
case 416:
case 304:
if(data->set.timecondition)
data->info.timecond = TRUE;
k->size=0;
k->maxdownload=0;
k->ignorecl = TRUE;
break;
default:
break;
}
}
else {
k->header = FALSE;
break;
}
}
#ifdef CURL_DOES_CONVERSIONS
result = Curl_convert_from_network(data, k->p, strlen(k->p));
if(CURLE_OK != result) {
return(result);
}
#endif
if(!k->ignorecl && !data->set.ignorecl &&
checkprefix("Content-Length:", k->p)) {
curl_off_t contentlength = curlx_strtoofft(k->p+15, NULL, 10);
if(data->set.max_filesize &&
contentlength > data->set.max_filesize) {
failf(data, "Maximum file size exceeded");
return CURLE_FILESIZE_EXCEEDED;
}
if(contentlength >= 0) {
k->size = contentlength;
k->maxdownload = k->size;
Curl_pgrsSetDownloadSize(data, k->size);
}
else {
conn->bits.close = TRUE;
infof(data, "Negative content-length: %" FORMAT_OFF_T
", closing after transfer\n", contentlength);
}
}
else if(checkprefix("Content-Type:", k->p)) {
char *contenttype = Curl_copy_header_value(k->p);
if (!contenttype)
return CURLE_OUT_OF_MEMORY;
if (!*contenttype)
free(contenttype);
else {
Curl_safefree(data->info.contenttype);
data->info.contenttype = contenttype;
}
}
else if((conn->httpversion == 10) &&
conn->bits.httpproxy &&
Curl_compareheader(k->p,
"Proxy-Connection:", "keep-alive")) {
conn->bits.close = FALSE;
infof(data, "HTTP/1.0 proxy connection set to keep alive!\n");
}
else if((conn->httpversion == 11) &&
conn->bits.httpproxy &&
Curl_compareheader(k->p,
"Proxy-Connection:", "close")) {
conn->bits.close = TRUE;
infof(data, "HTTP/1.1 proxy connection set close!\n");
}
else if((conn->httpversion == 10) &&
Curl_compareheader(k->p, "Connection:", "keep-alive")) {
conn->bits.close = FALSE;
infof(data, "HTTP/1.0 connection set to keep alive!\n");
}
else if(Curl_compareheader(k->p, "Connection:", "close")) {
conn->bits.close = TRUE;
}
else if(Curl_compareheader(k->p,
"Transfer-Encoding:", "chunked")) {
k->chunk = TRUE;
Curl_httpchunk_init(conn);
}
else if(checkprefix("Trailer:", k->p) ||
checkprefix("Trailers:", k->p)) {
k->trailerhdrpresent = TRUE;
}
else if(checkprefix("Content-Encoding:", k->p) &&
data->set.str[STRING_ENCODING]) {
char *start;
start = k->p + 17;
while(*start && ISSPACE(*start))
start++;
if(checkprefix("identity", start))
k->content_encoding = IDENTITY;
else if(checkprefix("deflate", start))
k->content_encoding = DEFLATE;
else if(checkprefix("gzip", start)
|| checkprefix("x-gzip", start))
k->content_encoding = GZIP;
else if(checkprefix("compress", start)
|| checkprefix("x-compress", start))
k->content_encoding = COMPRESS;
}
else if(checkprefix("Content-Range:", k->p)) {
char *ptr = k->p + 14;
while(*ptr && !ISDIGIT(*ptr))
ptr++;
k->offset = curlx_strtoofft(ptr, NULL, 10);
if(data->state.resume_from == k->offset)
k->content_range = TRUE;
}
#if !defined(CURL_DISABLE_COOKIES)
else if(data->cookies &&
checkprefix("Set-Cookie:", k->p)) {
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE,
CURL_LOCK_ACCESS_SINGLE);
Curl_cookie_add(data,
data->cookies, TRUE, k->p+11,
conn->allocptr.cookiehost?
conn->allocptr.cookiehost:conn->host.name,
data->state.path);
Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
}
#endif
else if(checkprefix("Last-Modified:", k->p) &&
(data->set.timecondition || data->set.get_filetime) ) {
time_t secs=time(NULL);
k->timeofdoc = curl_getdate(k->p+strlen("Last-Modified:"),
&secs);
if(data->set.get_filetime)
data->info.filetime = (long)k->timeofdoc;
}
else if((checkprefix("WWW-Authenticate:", k->p) &&
(401 == k->httpcode)) ||
(checkprefix("Proxy-authenticate:", k->p) &&
(407 == k->httpcode))) {
result = Curl_http_input_auth(conn, k->httpcode, k->p);
if(result)
return result;
}
else if((k->httpcode >= 300 && k->httpcode < 400) &&
checkprefix("Location:", k->p)) {
char *location = Curl_copy_header_value(k->p);
if (!location)
return CURLE_OUT_OF_MEMORY;
if (!*location)
free(location);
else {
DEBUGASSERT(!data->req.location);
data->req.location = location;
if(data->set.http_follow_location) {
DEBUGASSERT(!data->req.newurl);
data->req.newurl = strdup(data->req.location);
if(!data->req.newurl)
return CURLE_OUT_OF_MEMORY;
result = Curl_http_perhapsrewind(conn);
if(result)
return result;
}
}
}
writetype = CLIENTWRITE_HEADER;
if(data->set.include_header)
writetype |= CLIENTWRITE_BODY;
if(data->set.verbose)
Curl_debug(data, CURLINFO_HEADER_IN,
k->p, (size_t)k->hbuflen, conn);
result = Curl_client_write(conn, writetype, k->p, k->hbuflen);
if(result)
return result;
data->info.header_size += (long)k->hbuflen;
data->req.headerbytecount += (long)k->hbuflen;
k->hbufp = data->state.headerbuff;
k->hbuflen = 0;
}
while(!*stop_reading && *k->str);
return CURLE_OK;
}
#endif
static CURLcode readwrite_upload(struct SessionHandle *data,
struct connectdata *conn,
struct SingleRequest *k,
int *didwhat)
{
ssize_t i, si;
ssize_t bytes_written;
CURLcode result;
ssize_t nread;
bool sending_http_headers = FALSE;
if((k->bytecount == 0) && (k->writebytecount == 0))
Curl_pgrsTime(data, TIMER_STARTTRANSFER);
*didwhat |= KEEP_SEND;
do {
if(0 == data->req.upload_present) {
data->req.upload_fromhere = k->uploadbuf;
if(!k->upload_done) {
int fillcount;
if((k->exp100 == EXP100_SENDING_REQUEST) &&
(data->state.proto.http->sending == HTTPSEND_BODY)) {
k->exp100 = EXP100_AWAITING_CONTINUE;
k->keepon &= ~KEEP_SEND;
k->start100 = Curl_tvnow();
*didwhat &= ~KEEP_SEND;
break;
}
if(conn->protocol&PROT_HTTP) {
if(data->state.proto.http->sending == HTTPSEND_REQUEST)
sending_http_headers = TRUE;
else
sending_http_headers = FALSE;
}
result = Curl_fillreadbuffer(conn, BUFSIZE, &fillcount);
if(result)
return result;
nread = (ssize_t)fillcount;
}
else
nread = 0;
if(!nread && (k->keepon & KEEP_SEND_PAUSE)) {
break;
}
else if(nread<=0) {
k->keepon &= ~KEEP_SEND;
if(conn->bits.rewindaftersend) {
result = Curl_readrewind(conn);
if(result)
return result;
}
break;
}
data->req.upload_present = nread;
if((!sending_http_headers) &&
#ifdef CURL_DO_LINEEND_CONV
((data->set.crlf) || (data->set.prefer_ascii))) {
#else
(data->set.crlf)) {
#endif
if(data->state.scratch == NULL)
data->state.scratch = malloc(2*BUFSIZE);
if(data->state.scratch == NULL) {
failf (data, "Failed to alloc scratch buffer!");
return CURLE_OUT_OF_MEMORY;
}
for(i = 0, si = 0; i < nread; i++, si++) {
if(data->req.upload_fromhere[i] == 0x0a) {
data->state.scratch[si++] = 0x0d;
data->state.scratch[si] = 0x0a;
if(!data->set.crlf) {
data->set.infilesize++;
}
}
else
data->state.scratch[si] = data->req.upload_fromhere[i];
}
if(si != nread) {
nread = si;
data->req.upload_fromhere = data->state.scratch;
data->req.upload_present = nread;
}
}
}
else {
}
result = Curl_write(conn,
conn->writesockfd,
data->req.upload_fromhere,
data->req.upload_present,
&bytes_written);
if(result)
return result;
if(data->set.verbose)
Curl_debug(data, CURLINFO_DATA_OUT, data->req.upload_fromhere,
(size_t)bytes_written, conn);
if(data->req.upload_present != bytes_written) {
data->req.upload_present -= bytes_written;
data->req.upload_fromhere += bytes_written;
}
else {
data->req.upload_fromhere = k->uploadbuf;
data->req.upload_present = 0;
if(k->upload_done) {
k->keepon &= ~KEEP_SEND;
}
}
k->writebytecount += bytes_written;
Curl_pgrsSetUploadCounter(data, k->writebytecount);
} while(0);
return CURLE_OK;
}
CURLcode Curl_readwrite(struct connectdata *conn,
bool *done)
{
struct SessionHandle *data = conn->data;
struct SingleRequest *k = &data->req;
CURLcode result;
int didwhat=0;
curl_socket_t fd_read;
curl_socket_t fd_write;
int select_res = conn->cselect_bits;
conn->cselect_bits = 0;
if((k->keepon & KEEP_RECVBITS) == KEEP_RECV) {
fd_read = conn->sockfd;
} else
fd_read = CURL_SOCKET_BAD;
if((k->keepon & KEEP_SENDBITS) == KEEP_SEND)
fd_write = conn->writesockfd;
else
fd_write = CURL_SOCKET_BAD;
if(!select_res) {
select_res = Curl_socket_ready(fd_read, fd_write, 0);
}
if(select_res == CURL_CSELECT_ERR) {
failf(data, "select/poll returned error");
return CURLE_SEND_ERROR;
}
if((k->keepon & KEEP_RECV) &&
((select_res & CURL_CSELECT_IN) || conn->bits.stream_was_rewound)) {
result = readwrite_data(data, conn, k, &didwhat, done);
if(result || *done)
return result;
}
if((k->keepon & KEEP_SEND) && (select_res & CURL_CSELECT_OUT)) {
result = readwrite_upload(data, conn, k, &didwhat);
if(result)
return result;
}
k->now = Curl_tvnow();
if(didwhat) {
if(k->bytecountp)
*k->bytecountp = k->bytecount;
if(k->writebytecountp)
*k->writebytecountp = k->writebytecount;
}
else {
if(k->exp100 == EXP100_AWAITING_CONTINUE) {
long ms = Curl_tvdiff(k->now, k->start100);
if(ms > CURL_TIMEOUT_EXPECT_100) {
k->exp100 = EXP100_SEND_DATA;
k->keepon |= KEEP_SEND;
infof(data, "Done waiting for 100-continue\n");
}
}
}
if(Curl_pgrsUpdate(conn))
result = CURLE_ABORTED_BY_CALLBACK;
else
result = Curl_speedcheck(data, k->now);
if(result)
return result;
if(data->set.timeout &&
(Curl_tvdiff(k->now, k->start) >= data->set.timeout)) {
if(k->size != -1) {
failf(data, "Operation timed out after %ld milliseconds with %"
FORMAT_OFF_T " out of %" FORMAT_OFF_T " bytes received",
data->set.timeout, k->bytecount, k->size);
} else {
failf(data, "Operation timed out after %ld milliseconds with %"
FORMAT_OFF_T " bytes received",
data->set.timeout, k->bytecount);
}
return CURLE_OPERATION_TIMEDOUT;
}
if(!k->keepon) {
if(!(data->set.opt_no_body) && (k->size != -1) &&
(k->bytecount != k->size) &&
#ifdef CURL_DO_LINEEND_CONV
(k->bytecount != (k->size + data->state.crlf_conversions)) &&
#endif
!data->req.newurl) {
failf(data, "transfer closed with %" FORMAT_OFF_T
" bytes remaining to read",
k->size - k->bytecount);
return CURLE_PARTIAL_FILE;
}
else if(!(data->set.opt_no_body) &&
k->chunk &&
(conn->chunk.state != CHUNK_STOP)) {
failf(data, "transfer closed with outstanding read data remaining");
return CURLE_PARTIAL_FILE;
}
if(Curl_pgrsUpdate(conn))
return CURLE_ABORTED_BY_CALLBACK;
}
*done = (bool)(0 == (k->keepon&(KEEP_RECV|KEEP_SEND|
KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)));
return CURLE_OK;
}
int Curl_single_getsock(const struct connectdata *conn,
curl_socket_t *sock,
int numsocks)
{
const struct SessionHandle *data = conn->data;
int bitmap = GETSOCK_BLANK;
unsigned sockindex = 0;
if(conn->handler->perform_getsock)
return conn->handler->perform_getsock(conn, sock, numsocks);
if(numsocks < 2)
return GETSOCK_BLANK;
if((data->req.keepon & KEEP_RECVBITS) == KEEP_RECV) {
DEBUGASSERT(conn->sockfd != CURL_SOCKET_BAD);
bitmap |= GETSOCK_READSOCK(sockindex);
sock[sockindex] = conn->sockfd;
}
if((data->req.keepon & KEEP_SENDBITS) == KEEP_SEND) {
if((conn->sockfd != conn->writesockfd) ||
!(data->req.keepon & KEEP_RECV)) {
if(data->req.keepon & KEEP_RECV)
sockindex++;
DEBUGASSERT(conn->writesockfd != CURL_SOCKET_BAD);
sock[sockindex] = conn->writesockfd;
}
bitmap |= GETSOCK_WRITESOCK(sockindex);
}
return bitmap;
}
static CURLcode
Transfer(struct connectdata *conn)
{
CURLcode result;
struct SessionHandle *data = conn->data;
struct SingleRequest *k = &data->req;
bool done=FALSE;
bool first=TRUE;
int timeout_ms;
if((conn->sockfd == CURL_SOCKET_BAD) &&
(conn->writesockfd == CURL_SOCKET_BAD))
return CURLE_OK;
if(!k->getheader && data->set.opt_no_body)
return CURLE_OK;
while(!done) {
curl_socket_t fd_read = conn->sockfd;
curl_socket_t fd_write = conn->writesockfd;
int keepon = k->keepon;
if(conn->waitfor) {
keepon &= ~ (KEEP_RECV|KEEP_SEND);
keepon |= conn->waitfor & (KEEP_RECV|KEEP_SEND);
}
if((keepon & KEEP_SEND) &&
(!data->set.max_send_speed ||
(data->progress.ulspeed < data->set.max_send_speed) )) {
k->keepon &= ~KEEP_SEND_HOLD;
}
else {
fd_write = CURL_SOCKET_BAD;
if(keepon & KEEP_SEND)
k->keepon |= KEEP_SEND_HOLD;
}
if((keepon & KEEP_RECV) &&
(!data->set.max_recv_speed ||
(data->progress.dlspeed < data->set.max_recv_speed)) ) {
k->keepon &= ~KEEP_RECV_HOLD;
}
else {
fd_read = CURL_SOCKET_BAD;
if(keepon & KEEP_RECV)
k->keepon |= KEEP_RECV_HOLD;
}
if(k->keepon & KEEP_RECV_PAUSE)
fd_read = CURL_SOCKET_BAD;
if(k->keepon & KEEP_SEND_PAUSE)
fd_write = CURL_SOCKET_BAD;
if(first &&
((fd_read != CURL_SOCKET_BAD) || (fd_write != CURL_SOCKET_BAD)))
timeout_ms = 0;
else
timeout_ms = 1000;
switch (Curl_socket_ready(fd_read, fd_write, timeout_ms)) {
case -1:
#ifdef EINTR
if(SOCKERRNO == EINTR)
continue;
#endif
return CURLE_RECV_ERROR;
case 0:
default:
result = Curl_readwrite(conn, &done);
break;
}
if(result)
return result;
first = FALSE;
}
return CURLE_OK;
}
CURLcode Curl_pretransfer(struct SessionHandle *data)
{
CURLcode res;
if(!data->change.url) {
failf(data, "No URL set!");
return CURLE_URL_MALFORMAT;
}
res = Curl_ssl_initsessions(data, data->set.ssl.numsessions);
if(res)
return res;
data->set.followlocation=0;
data->state.this_is_a_follow = FALSE;
data->state.errorbuf = FALSE;
data->state.httpversion = 0;
data->state.authproblem = FALSE;
data->state.authhost.want = data->set.httpauth;
data->state.authproxy.want = data->set.proxyauth;
Curl_safefree(data->info.wouldredirect);
data->info.wouldredirect = NULL;
if(data->change.cookielist) {
Curl_cookie_loadfiles(data);
}
data->state.allow_port = TRUE;
#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(HAVE_MSG_NOSIGNAL)
if(!data->set.no_signal)
data->state.prev_signal = signal(SIGPIPE, SIG_IGN);
#endif
Curl_initinfo(data);
Curl_pgrsStartNow(data);
return CURLE_OK;
}
CURLcode Curl_posttransfer(struct SessionHandle *data)
{
#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(HAVE_MSG_NOSIGNAL)
if(!data->set.no_signal)
signal(SIGPIPE, data->state.prev_signal);
#else
(void)data;
#endif
if(!(data->progress.flags & PGRS_HIDE) &&
!data->progress.callback)
fprintf(data->set.err, "\n");
return CURLE_OK;
}
#ifndef CURL_DISABLE_HTTP
static size_t strlen_url(const char *url)
{
const char *ptr;
size_t newlen=0;
bool left=TRUE;
for(ptr=url; *ptr; ptr++) {
switch(*ptr) {
case '?':
left=FALSE;
default:
newlen++;
break;
case ' ':
if(left)
newlen+=3;
else
newlen++;
break;
}
}
return newlen;
}
static void strcpy_url(char *output, const char *url)
{
bool left=TRUE;
const char *iptr;
char *optr = output;
for(iptr = url;
*iptr;
iptr++) {
switch(*iptr) {
case '?':
left=FALSE;
default:
*optr++=*iptr;
break;
case ' ':
if(left) {
*optr++='%';
*optr++='2';
*optr++='0';
}
else
*optr++='+';
break;
}
}
*optr=0;
}
static bool is_absolute_url(const char *url)
{
char prot[16];
char letter;
return (bool)(2 == sscanf(url, "%15[^?&/:]://%c", prot, &letter));
}
static char *concat_url(const char *base, const char *relurl)
{
char *newest;
char *protsep;
char *pathsep;
size_t newlen;
const char *useurl = relurl;
size_t urllen;
char *url_clone=strdup(base);
if(!url_clone)
return NULL;
protsep=strstr(url_clone, "//");
if(!protsep)
protsep=url_clone;
else
protsep+=2;
if('/' != relurl[0]) {
int level=0;
pathsep = strchr(protsep, '?');
if(pathsep)
*pathsep=0;
if(useurl[0] != '?') {
pathsep = strrchr(protsep, '/');
if(pathsep)
*pathsep=0;
}
pathsep = strchr(protsep, '/');
if(pathsep)
protsep = pathsep+1;
else
protsep = NULL;
if((useurl[0] == '.') && (useurl[1] == '/'))
useurl+=2;
while((useurl[0] == '.') &&
(useurl[1] == '.') &&
(useurl[2] == '/')) {
level++;
useurl+=3;
}
if(protsep) {
while(level--) {
pathsep = strrchr(protsep, '/');
if(pathsep)
*pathsep=0;
else {
*protsep=0;
break;
}
}
}
}
else {
pathsep = strchr(protsep, '/');
if(pathsep) {
char *sep = strchr(protsep, '?');
if(sep && (sep < pathsep))
pathsep = sep;
*pathsep=0;
}
else {
pathsep = strchr(protsep, '?');
if(pathsep)
*pathsep=0;
}
}
newlen = strlen_url(useurl);
urllen = strlen(url_clone);
newest = malloc( urllen + 1 +
newlen + 1 );
if(!newest) {
free(url_clone);
return NULL;
}
memcpy(newest, url_clone, urllen);
if(('/' == useurl[0]) || (protsep && !*protsep) || ('?' == useurl[0]))
;
else
newest[urllen++]='/';
strcpy_url(&newest[urllen], useurl);
free(url_clone);
return newest;
}
#endif
CURLcode Curl_follow(struct SessionHandle *data,
char *newurl,
followtype type)
{
#ifdef CURL_DISABLE_HTTP
(void)data;
(void)newurl;
(void)type;
return CURLE_TOO_MANY_REDIRECTS;
#else
bool disallowport = FALSE;
if(type == FOLLOW_REDIR) {
if((data->set.maxredirs != -1) &&
(data->set.followlocation >= data->set.maxredirs)) {
failf(data,"Maximum (%d) redirects followed", data->set.maxredirs);
return CURLE_TOO_MANY_REDIRECTS;
}
data->state.this_is_a_follow = TRUE;
data->set.followlocation++;
if(data->set.http_auto_referer) {
if(data->change.referer_alloc)
free(data->change.referer);
data->change.referer = strdup(data->change.url);
if (!data->change.referer) {
data->change.referer_alloc = FALSE;
return CURLE_OUT_OF_MEMORY;
}
data->change.referer_alloc = TRUE;
}
}
if(!is_absolute_url(newurl)) {
char *absolute = concat_url(data->change.url, newurl);
if (!absolute)
return CURLE_OUT_OF_MEMORY;
free(newurl);
newurl = absolute;
}
else {
disallowport = TRUE;
if(strchr(newurl, ' ')) {
char *newest;
size_t newlen = strlen_url(newurl);
newest = malloc(newlen+1);
if (!newest)
return CURLE_OUT_OF_MEMORY;
strcpy_url(newest, newurl);
free(newurl);
newurl = newest;
}
}
if(type == FOLLOW_FAKE) {
data->info.wouldredirect = newurl;
return CURLE_OK;
}
if(disallowport)
data->state.allow_port = FALSE;
if(data->change.url_alloc)
free(data->change.url);
else
data->change.url_alloc = TRUE;
data->change.url = newurl;
newurl = NULL;
infof(data, "Issue another request to this URL: '%s'\n", data->change.url);
switch(data->info.httpcode) {
default:
break;
case 301:
if( (data->set.httpreq == HTTPREQ_POST
|| data->set.httpreq == HTTPREQ_POST_FORM)
&& !data->set.post301) {
infof(data,
"Violate RFC 2616/10.3.2 and switch from POST to GET\n");
data->set.httpreq = HTTPREQ_GET;
}
break;
case 302:
if( (data->set.httpreq == HTTPREQ_POST
|| data->set.httpreq == HTTPREQ_POST_FORM)
&& !data->set.post302) {
infof(data,
"Violate RFC 2616/10.3.3 and switch from POST to GET\n");
data->set.httpreq = HTTPREQ_GET;
}
break;
case 303:
if(data->set.httpreq != HTTPREQ_GET) {
data->set.httpreq = HTTPREQ_GET;
infof(data, "Disables POST, goes with %s\n",
data->set.opt_no_body?"HEAD":"GET");
}
break;
case 304:
break;
case 305:
break;
}
Curl_pgrsTime(data, TIMER_REDIRECT);
Curl_pgrsResetTimes(data);
return CURLE_OK;
#endif
}
static CURLcode
connect_host(struct SessionHandle *data,
struct connectdata **conn)
{
CURLcode res = CURLE_OK;
bool async;
bool protocol_done=TRUE;
Curl_pgrsTime(data, TIMER_STARTSINGLE);
res = Curl_connect(data, conn, &async, &protocol_done);
if((CURLE_OK == res) && async) {
res = Curl_wait_for_resolv(*conn, NULL);
if(CURLE_OK == res)
res = Curl_async_resolved(*conn, &protocol_done);
else
(void)Curl_disconnect(*conn);
}
return res;
}
CURLcode
Curl_reconnect_request(struct connectdata **connp)
{
CURLcode result = CURLE_OK;
struct connectdata *conn = *connp;
struct SessionHandle *data = conn->data;
infof(data, "Re-used connection seems dead, get a new one\n");
conn->bits.close = TRUE;
result = Curl_done(&conn, result, FALSE);
if((CURLE_OK == result) || (CURLE_SEND_ERROR == result)) {
bool async;
bool protocol_done = TRUE;
result = Curl_connect(data, connp, &async, &protocol_done);
if(CURLE_OK == result) {
conn = *connp;
if(async) {
result = Curl_wait_for_resolv(conn, NULL);
if(result)
return result;
result = Curl_async_resolved(conn, &protocol_done);
if(result)
return result;
}
}
}
return result;
}
CURLcode Curl_retry_request(struct connectdata *conn,
char **url)
{
struct SessionHandle *data = conn->data;
*url = NULL;
if(data->set.upload && !(conn->protocol&PROT_HTTP))
return CURLE_OK;
if((data->req.bytecount +
data->req.headerbytecount == 0) &&
conn->bits.reuse &&
!data->set.opt_no_body) {
infof(conn->data, "Connection died, retrying a fresh connect\n");
*url = strdup(conn->data->change.url);
if(!*url)
return CURLE_OUT_OF_MEMORY;
conn->bits.close = TRUE;
conn->bits.retry = TRUE;
}
return CURLE_OK;
}
CURLcode Curl_perform(struct SessionHandle *data)
{
CURLcode res;
CURLcode res2;
struct connectdata *conn=NULL;
char *newurl = NULL;
followtype follow = FOLLOW_NONE;
data->state.used_interface = Curl_if_easy;
res = Curl_pretransfer(data);
if(res)
return res;
do {
res = connect_host(data, &conn);
if(res == CURLE_OK) {
bool do_done;
if(data->set.connect_only) {
conn->bits.close = FALSE;
res = Curl_done(&conn, CURLE_OK, FALSE);
break;
}
res = Curl_do(&conn, &do_done);
if(res == CURLE_OK) {
res = Transfer(conn);
if((res == CURLE_OK) || (res == CURLE_RECV_ERROR)) {
bool retry = FALSE;
CURLcode rc = Curl_retry_request(conn, &newurl);
if(rc)
res = rc;
else
retry = newurl?TRUE:FALSE;
if(retry) {
res = CURLE_OK;
follow = FOLLOW_RETRY;
if (!newurl)
res = CURLE_OUT_OF_MEMORY;
}
else if (res == CURLE_OK) {
if(data->req.newurl) {
follow = FOLLOW_REDIR;
newurl = strdup(data->req.newurl);
if (!newurl)
res = CURLE_OUT_OF_MEMORY;
}
else if(data->req.location) {
follow = FOLLOW_FAKE;
newurl = strdup(data->req.location);
if (!newurl)
res = CURLE_OUT_OF_MEMORY;
}
}
}
if(res != CURLE_OK) {
conn->bits.close = TRUE;
if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) {
sclose(conn->sock[SECONDARYSOCKET]);
conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
}
}
res2 = Curl_done(&conn, res, FALSE);
if(CURLE_OK == res)
res = res2;
}
else if(conn)
res2 = Curl_done(&conn, res, FALSE);
if((res == CURLE_OK) && follow) {
res = Curl_follow(data, newurl, follow);
if(CURLE_OK == res) {
newurl = NULL;
if(follow >= FOLLOW_RETRY) {
follow = FOLLOW_NONE;
continue;
}
}
}
}
break;
} while(1);
if(newurl)
free(newurl);
if(res && !data->state.errorbuf) {
const char *str = curl_easy_strerror(res);
if(!str)
failf(data, "unspecified error %d", (int)res);
else
failf(data, "%s", str);
}
res2 = Curl_posttransfer(data);
if(!res && res2)
res = res2;
return res;
}
CURLcode
Curl_setup_transfer(
struct connectdata *conn,
int sockindex,
curl_off_t size,
bool getheader,
curl_off_t *bytecountp,
int writesockindex,
curl_off_t *writecountp
)
{
struct SessionHandle *data;
struct SingleRequest *k;
DEBUGASSERT(conn != NULL);
data = conn->data;
k = &data->req;
DEBUGASSERT((sockindex <= 1) && (sockindex >= -1));
conn->sockfd = sockindex == -1 ?
CURL_SOCKET_BAD : conn->sock[sockindex];
conn->writesockfd = writesockindex == -1 ?
CURL_SOCKET_BAD:conn->sock[writesockindex];
k->getheader = getheader;
k->size = size;
k->bytecountp = bytecountp;
k->writebytecountp = writecountp;
if(!k->getheader) {
k->header = FALSE;
if(size > 0)
Curl_pgrsSetDownloadSize(data, size);
}
if(k->getheader || !data->set.opt_no_body) {
if(conn->sockfd != CURL_SOCKET_BAD) {
k->keepon |= KEEP_RECV;
}
if(conn->writesockfd != CURL_SOCKET_BAD) {
if((data->state.expect100header) &&
(data->state.proto.http->sending == HTTPSEND_BODY)) {
k->exp100 = EXP100_AWAITING_CONTINUE;
k->start100 = k->start;
}
else {
if(data->state.expect100header)
k->exp100 = EXP100_SENDING_REQUEST;
k->keepon |= KEEP_SEND;
}
}
}
return CURLE_OK;
}