#include "curl_setup.h"
#include "strtoofft.h"
#include "strequal.h"
#include "rawstr.h"
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.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
#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 "vtls/vtls.h"
#include "http_digest.h"
#include "curl_ntlm.h"
#include "http_negotiate.h"
#include "share.h"
#include "curl_memory.h"
#include "select.h"
#include "multiif.h"
#include "connect.h"
#include "non-ascii.h"
#define _MPRINTF_REPLACE
#include <curl/mprintf.h>
#include "memdebug.h"
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->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)) {
const struct HTTP *http = data->req.protop;
if(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) {
if(conn->handler->flags & PROTOPT_NONETWORK) {
failf(data, "Read callback asked for PAUSE when not supported!");
return CURLE_READ_ERROR;
}
else {
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;
if(
#ifdef CURL_DO_LINEEND_CONV
(data->set.prefer_ascii) ||
#endif
(data->set.crlf)) {
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)
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;
}
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->handler->protocol&(CURLPROTO_SCP|CURLPROTO_SFTP) ||
#if defined(USE_NGHTTP2)
Curl_ssl_data_pending(conn, FIRSTSOCKET) ||
((conn->handler->protocol&PROTO_FAMILY_HTTP) && conn->httpversion == 20 &&
conn->proto.httpc.closed);
#else
Curl_ssl_data_pending(conn, FIRSTSOCKET);
#endif
}
static void read_rewind(struct connectdata *conn,
size_t thismuch)
{
DEBUGASSERT(conn->read_pos >= 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 = %zu): [%s]\n",
conn->read_pos, buf));
}
#endif
}
bool Curl_meets_timecondition(struct SessionHandle *data, time_t timeofdoc)
{
if((timeofdoc == 0) || (data->set.timevalue == 0))
return TRUE;
switch(data->set.timecondition) {
case CURL_TIMECOND_IFMODSINCE:
default:
if(timeofdoc <= data->set.timevalue) {
infof(data,
"The requested document is not new enough\n");
data->info.timecond = TRUE;
return FALSE;
}
break;
case CURL_TIMECOND_IFUNMODSINCE:
if(timeofdoc >= data->set.timevalue) {
infof(data,
"The requested document is not old enough\n");
data->info.timecond = TRUE;
return FALSE;
}
break;
}
return TRUE;
}
static CURLcode readwrite_data(struct SessionHandle *data,
struct connectdata *conn,
struct SingleRequest *k,
int *didwhat, bool *done)
{
CURLcode result = CURLE_OK;
ssize_t nread;
size_t excess = 0;
bool is_empty_data = FALSE;
bool readmore = FALSE;
*done = FALSE;
do {
size_t buffersize = data->set.buffer_size?
data->set.buffer_size : BUFSIZE;
size_t bytestoread = buffersize;
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) {
result = Curl_read(conn, conn->sockfd, k->buf, bytestoread, &nread);
if(CURLE_AGAIN == result)
break;
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 = ((nread == 0) && (k->bodywrites == 0)) ? TRUE : FALSE;
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;
if(conn->handler->readwrite) {
result = conn->handler->readwrite(data, conn, &nread, &readmore);
if(result)
return result;
if(readmore)
break;
}
#ifndef CURL_DISABLE_HTTP
if(k->header) {
bool stop_reading = FALSE;
result = Curl_http_readwrite_headers(data, conn, &nread, &stop_reading);
if(result)
return result;
if(conn->handler->readwrite &&
(k->maxdownload <= 0 && nread > 0)) {
result = conn->handler->readwrite(data, conn, &nread, &readmore);
if(result)
return result;
if(readmore)
break;
}
if(stop_reading) {
if(nread > 0) {
if(Curl_multi_pipeline_enabled(conn->data->multi)) {
infof(data,
"Rewinding stream by : %zd"
" bytes on url %s (zero-length body)\n",
nread, data->state.path);
read_rewind(conn, (size_t)nread);
}
else {
infof(data,
"Excess found in a non pipelined read:"
" excess = %zd"
" url = %s (zero-length body)\n",
nread, data->state.path);
}
}
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->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)) {
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(!Curl_meets_timecondition(data, k->timeofdoc)) {
*done = TRUE;
data->info.httpcode = 304;
infof(data, "Simulate a HTTP 304 response!\n");
connclose(conn, "Simulated 304 handling");
return CURLE_OK;
}
}
}
}
#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, "%s in chunked-encoding", Curl_chunked_strerror(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: %zu bytes\n",
dataleft);
if(Curl_multi_pipeline_enabled(conn->data->multi)) {
infof(conn->data, "Rewinding %zu bytes\n",dataleft);
read_rewind(conn, dataleft);
}
}
}
}
#endif
if(k->badheader && !k->ignorebody) {
DEBUGF(infof(data, "Increasing bytecount by %zu from hbuflen\n",
k->hbuflen));
k->bytecount += k->hbuflen;
}
if((-1 != k->maxdownload) &&
(k->bytecount + nread >= k->maxdownload)) {
excess = (size_t)(k->bytecount + nread - k->maxdownload);
if(excess > 0 && !k->ignorebody) {
if(Curl_multi_pipeline_enabled(conn->data->multi)) {
infof(data,
"Rewinding stream by : %zu"
" bytes on url %s (size = %" CURL_FORMAT_CURL_OFF_T
", maxdownload = %" CURL_FORMAT_CURL_OFF_T
", bytecount = %" CURL_FORMAT_CURL_OFF_T ", nread = %zd)\n",
excess, data->state.path,
k->size, k->maxdownload, k->bytecount, nread);
read_rewind(conn, excess);
}
else {
infof(data,
"Excess found in a non pipelined read:"
" excess = %zu"
", size = %" CURL_FORMAT_CURL_OFF_T
", maxdownload = %" CURL_FORMAT_CURL_OFF_T
", bytecount = %" CURL_FORMAT_CURL_OFF_T "\n",
excess, k->size, k->maxdownload, k->bytecount);
}
}
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) {
if(k->maxdownload == -1 || (curl_off_t)k->hbuflen <= k->maxdownload)
result = Curl_client_write(conn, CLIENTWRITE_BODY,
data->state.headerbuff,
k->hbuflen);
else
result = Curl_client_write(conn, CLIENTWRITE_BODY,
data->state.headerbuff,
(size_t)k->maxdownload);
if(result)
return result;
}
if(k->badheader < HEADER_ALLBAD) {
#ifdef HAVE_LIBZ
switch (conn->data->set.http_ce_skip ?
IDENTITY : k->auto_decoding) {
case IDENTITY:
#endif
if(!k->ignorebody) {
#ifndef CURL_DISABLE_POP3
if(conn->handler->protocol&PROTO_FAMILY_POP3)
result = Curl_pop3_write(conn, k->str, nread);
else
#endif
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(conn->handler->readwrite &&
(excess > 0 && !conn->bits.stream_was_rewound)) {
k->str += nread;
nread = (ssize_t)excess;
result = conn->handler->readwrite(data, conn, &nread, &readmore);
if(result)
return result;
if(readmore)
k->keepon |= KEEP_RECV;
break;
}
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;
}
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;
struct HTTP *http = data->req.protop;
if((k->exp100 == EXP100_SENDING_REQUEST) &&
(http->sending == HTTPSEND_BODY)) {
k->exp100 = EXP100_AWAITING_CONTINUE;
k->keepon &= ~KEEP_SEND;
k->start100 = Curl_tvnow();
*didwhat &= ~KEEP_SEND;
Curl_expire(data, data->set.expect_100_timeout);
break;
}
if(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)) {
if(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;
#ifndef CURL_DISABLE_SMTP
if(conn->handler->protocol & PROTO_FAMILY_SMTP) {
result = Curl_smtp_escape_eob(conn, nread);
if(result)
return result;
}
else
#endif
if((!sending_http_headers) && (
#ifdef CURL_DO_LINEEND_CONV
(data->set.prefer_ascii) ||
#endif
(data->set.crlf))) {
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->state.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);
k->writebytecount += bytes_written;
if(k->writebytecount == data->state.infilesize) {
k->upload_done = TRUE;
infof(data, "We are completely uploaded and fine\n");
}
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;
}
}
Curl_pgrsSetUploadCounter(data, k->writebytecount);
} WHILE_FALSE;
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 >= data->set.expect_100_timeout) {
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(k->keepon) {
if(0 > Curl_timeleft(data, &k->now, FALSE)) {
if(k->size != -1) {
failf(data, "Operation timed out after %ld milliseconds with %"
CURL_FORMAT_CURL_OFF_T " out of %"
CURL_FORMAT_CURL_OFF_T " bytes received",
Curl_tvdiff(k->now, data->progress.t_startsingle), k->bytecount,
k->size);
}
else {
failf(data, "Operation timed out after %ld milliseconds with %"
CURL_FORMAT_CURL_OFF_T " bytes received",
Curl_tvdiff(k->now, data->progress.t_startsingle), k->bytecount);
}
return CURLE_OPERATION_TIMEDOUT;
}
}
else {
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 %" CURL_FORMAT_CURL_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 = (0 == (k->keepon&(KEEP_RECV|KEEP_SEND|
KEEP_RECV_PAUSE|KEEP_SEND_PAUSE))) ? TRUE : FALSE;
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;
}
long Curl_sleep_time(curl_off_t rate_bps, curl_off_t cur_rate_bps,
int pkt_size)
{
curl_off_t min_sleep = 0;
curl_off_t rv = 0;
if(rate_bps == 0)
return 0;
if(cur_rate_bps > (rate_bps + (rate_bps >> 10))) {
rate_bps -= rate_bps >> 6;
min_sleep = 1;
}
else if(cur_rate_bps < (rate_bps - (rate_bps >> 10))) {
rate_bps += rate_bps >> 6;
}
rv = ((curl_off_t)((pkt_size * 8) * 1000) / rate_bps);
if(rv < min_sleep)
rv = min_sleep;
if(rv > 0x7fffffff)
rv = 0x7fffffff;
return (long)rv;
}
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.max_ssl_sessions);
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.ssl_connect_retry = FALSE;
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);
if(data->change.resolve)
res = Curl_loadhostpairs(data);
if(!res) {
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);
if(data->set.timeout)
Curl_expire(data, data->set.timeout);
if(data->set.connecttimeout)
Curl_expire(data, data->set.connecttimeout);
data->state.authhost.picked &= data->state.authhost.want;
data->state.authproxy.picked &= data->state.authproxy.want;
}
return res;
}
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
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 (2 == sscanf(url, "%15[^?&/:]://%c", prot, &letter)) ? TRUE : FALSE;
}
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 {
if((relurl[0] == '/') && (relurl[1] == '/')) {
*protsep=0;
useurl = &relurl[2];
}
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 (%ld) 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) {
Curl_safefree(data->change.referer);
data->change.referer_alloc = FALSE;
}
data->change.referer = strdup(data->change.url);
if(!data->change.referer)
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) {
Curl_safefree(data->change.url);
data->change.url_alloc = FALSE;
}
data->change.url = newurl;
data->change.url_alloc = TRUE;
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.keep_post & CURL_REDIR_POST_301)) {
infof(data, "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.keep_post & CURL_REDIR_POST_302)) {
infof(data, "Switch from POST to GET\n");
data->set.httpreq = HTTPREQ_GET;
}
break;
case 303:
if(data->set.httpreq != HTTPREQ_GET
&& !(data->set.keep_post & CURL_REDIR_POST_303)) {
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_pgrsResetTimesSizes(data);
return CURLE_OK;
#endif
}
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");
connclose(conn, "Reconnect dead connection");
result = Curl_done(&conn, result, FALSE);
*connp = NULL;
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_resolver_wait_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->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)))
return CURLE_OK;
if( data->state.ssl_connect_retry ||
((data->req.bytecount +
data->req.headerbytecount == 0) &&
conn->bits.reuse &&
!data->set.opt_no_body &&
data->set.rtspreq != RTSPREQ_RECEIVE)) {
infof(conn->data, "Connection died, retrying a fresh connect\n");
*url = strdup(conn->data->change.url);
if(!*url)
return CURLE_OUT_OF_MEMORY;
connclose(conn, "retry");
conn->bits.retry = TRUE;
if(conn->handler->protocol&PROTO_FAMILY_HTTP) {
struct HTTP *http = data->req.protop;
if(http->writebytecount)
return Curl_readrewind(conn);
}
}
return CURLE_OK;
}
void
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) {
struct HTTP *http = data->req.protop;
if((data->state.expect100header) &&
(conn->handler->protocol&PROTO_FAMILY_HTTP) &&
(http->sending == HTTPSEND_BODY)) {
k->exp100 = EXP100_AWAITING_CONTINUE;
k->start100 = Curl_tvnow();
Curl_expire(data, data->set.expect_100_timeout);
}
else {
if(data->state.expect100header)
k->exp100 = EXP100_SENDING_REQUEST;
k->keepon |= KEEP_SEND;
}
}
}
}