#include "setup.h"
#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>
#include "strequal.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
#include <netinet/in.h>
#include <sys/time.h>
#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
#ifndef HAVE_SELECT
#error "We can't compile without select() support!"
#endif
#ifndef HAVE_SOCKET
#error "We can't compile without socket() support!"
#endif
#endif
#include "urldata.h"
#include <curl/curl.h>
#include <curl/types.h>
#include "netrc.h"
#include "content_encoding.h"
#include "hostip.h"
#include "transfer.h"
#include "sendf.h"
#include "speedcheck.h"
#include "getpass.h"
#include "progress.h"
#include "getdate.h"
#include "http.h"
#include "url.h"
#include "getinfo.h"
#include "ssluse.h"
#define _MPRINTF_REPLACE
#include <curl/mprintf.h>
#ifdef MALLOCDEBUG
#include "memdebug.h"
#endif
#ifndef min
#define min(a, b) ((a) < (b) ? (a) : (b))
#endif
enum {
KEEP_NONE,
KEEP_READ,
KEEP_WRITE
};
static bool
compareheader(char *headerline,
const char *header,
const char *content)
{
size_t hlen = strlen(header);
size_t clen;
size_t len;
char *start;
char *end;
if(!strnequal(headerline, header, hlen))
return FALSE;
start = &headerline[hlen];
while(*start && isspace((int)*start))
start++;
end = strchr(start, '\r');
if(!end) {
end = strchr(start, '\n');
if(!end)
return FALSE;
}
len = end-start;
clen = strlen(content);
for(;len>=clen;len--, start++) {
if(strnequal(start, content, clen))
return TRUE;
}
return FALSE;
}
static struct timeval notimeout={0,0};
CURLcode Curl_readwrite(struct connectdata *conn,
bool *done)
{
struct Curl_transfer_keeper *k = &conn->keep;
struct SessionHandle *data = conn->data;
int result;
ssize_t nread;
int didwhat=0;
fd_set extrareadfd;
fd_set extrawritefd;
fd_set *readfdp = k->readfdp;
fd_set *writefdp = k->writefdp;
if((k->keepon & KEEP_READ) && !readfdp) {
FD_ZERO(&extrareadfd);
FD_SET(conn->sockfd, &extrareadfd);
readfdp = &extrareadfd;
select(conn->sockfd+1, readfdp, NULL, NULL, ¬imeout);
}
if((k->keepon & KEEP_WRITE) && !writefdp) {
FD_ZERO(&extrawritefd);
FD_SET(conn->writesockfd, &extrawritefd);
writefdp = &extrawritefd;
select(conn->writesockfd+1, NULL, writefdp, NULL, ¬imeout);
}
do {
if((k->keepon & KEEP_READ) &&
(FD_ISSET(conn->sockfd, readfdp))) {
result = Curl_read(conn, conn->sockfd, k->buf,
data->set.buffer_size?
data->set.buffer_size:BUFSIZE -1,
&nread);
if(0>result)
break;
if(result>0)
return result;
if ((k->bytecount == 0) && (k->writebytecount == 0))
Curl_pgrsTime(data, TIMER_STARTTRANSFER);
didwhat |= KEEP_READ;
if (0 < nread)
k->buf[nread] = 0;
else if (0 >= nread) {
k->keepon &= ~KEEP_READ;
FD_ZERO(&k->rkeepfd);
break;
}
k->str = k->buf;
if (k->header) {
bool stop_reading = FALSE;
do {
int hbufp_index;
k->str_start = k->str;
k->end_ptr = strchr (k->str_start, '\n');
if (!k->end_ptr) {
if (k->hbuflen + nread >= data->state.headersize) {
char *newbuff;
long newsize=MAX((k->hbuflen+nread)*3/2,
data->state.headersize*2);
hbufp_index = k->hbufp - data->state.headerbuff;
newbuff = (char *)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, nread);
k->hbufp += nread;
k->hbuflen += nread;
if (!k->headerline && (k->hbuflen>5)) {
if(!checkprefix("HTTP/", data->state.headerbuff)) {
k->header = FALSE;
k->badheader = HEADER_ALLBAD;
break;
}
}
break;
}
nread -= (k->end_ptr - k->str)+1;
k->str = k->end_ptr + 1;
if (k->hbuflen + (k->str - k->str_start) >=
data->state.headersize) {
char *newbuff;
long newsize=MAX((k->hbuflen+
(k->str-k->str_start))*3/2,
data->state.headersize*2);
hbufp_index = k->hbufp - data->state.headerbuff;
newbuff = (char *)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;
}
strncpy (k->hbufp, k->str_start, k->str - k->str_start);
k->hbufp += k->str - k->str_start;
k->hbuflen += k->str - k->str_start;
*k->hbufp = 0;
k->p = data->state.headerbuff;
if(!k->headerline) {
if((k->hbuflen>5) &&
!checkprefix("HTTP/", data->state.headerbuff)) {
k->header = FALSE;
k->badheader = HEADER_PARTHEADER;
break;
}
}
if (('\n' == *k->p) || ('\r' == *k->p)) {
int headerlen;
if ('\r' == *k->p)
k->p++;
if ('\n' == *k->p)
k->p++;
if(100 == k->httpcode) {
k->header = TRUE;
k->headerline = 0;
if (k->write_after_100_header) {
k->write_after_100_header = FALSE;
FD_SET (conn->writesockfd, &k->writefd);
k->keepon |= KEEP_WRITE;
k->wkeepfd = k->writefd;
}
}
else
k->header = FALSE;
if (417 == k->httpcode) {
k->write_after_100_header = FALSE;
k->keepon &= ~KEEP_WRITE;
FD_ZERO(&k->wkeepfd);
}
k->writetype = CLIENTWRITE_HEADER;
if (data->set.http_include_header)
k->writetype |= CLIENTWRITE_BODY;
headerlen = k->p - data->state.headerbuff;
result = Curl_client_write(data, k->writetype,
data->state.headerbuff,
headerlen);
if(result)
return result;
data->info.header_size += headerlen;
conn->headerbytecount += headerlen;
if(!k->header) {
if(data->set.no_body)
stop_reading = TRUE;
else if(!conn->bits.close) {
if(-1 != conn->size)
conn->maxdownload = conn->size;
}
if(0 == conn->maxdownload)
stop_reading = TRUE;
if(stop_reading) {
k->keepon &= ~KEEP_READ;
FD_ZERO(&k->rkeepfd);
}
break;
}
k->hbufp = data->state.headerbuff;
k->hbuflen = 0;
continue;
}
if (!k->headerline++) {
int httpversion_major;
int nc=sscanf (k->p, " HTTP/%d.%d %3d",
&httpversion_major,
&k->httpversion,
&k->httpcode);
if (nc==3) {
k->httpversion += 10 * httpversion_major;
}
else {
nc=sscanf (k->p, " HTTP %3d", &k->httpcode);
k->httpversion = 10;
}
if (nc) {
data->info.httpcode = k->httpcode;
data->info.httpversion = k->httpversion;
if (data->set.http_fail_on_error &&
(k->httpcode >= 400)) {
failf (data, "The requested file was not found");
return CURLE_HTTP_NOT_FOUND;
}
if(k->httpversion == 10)
conn->bits.close = TRUE;
switch(k->httpcode) {
case 204:
case 304:
conn->size=0;
conn->maxdownload=0;
break;
default:
break;
}
}
else {
k->header = FALSE;
break;
}
}
if (checkprefix("Content-Length:", k->p) &&
sscanf (k->p+15, " %ld", &k->contentlength)) {
conn->size = k->contentlength;
Curl_pgrsSetDownloadSize(data, k->contentlength);
}
else if (checkprefix("Content-Type:", k->p)) {
char *start;
char *end;
int len;
for(start=k->p+14;
*start && isspace((int)*start);
start++);
for(end=start, len=0;
*end && !isspace((int)*end);
end++, len++);
data->info.contenttype = malloc(len + 1);
if (NULL == data->info.contenttype)
return CURLE_OUT_OF_MEMORY;
memcpy(data->info.contenttype, start, len);
data->info.contenttype[len] = 0;
}
else if((k->httpversion == 10) &&
conn->bits.httpproxy &&
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((k->httpversion == 10) &&
compareheader(k->p, "Connection:", "keep-alive")) {
conn->bits.close = FALSE;
infof(data, "HTTP/1.0 connection set to keep alive!\n");
}
else if (compareheader(k->p, "Connection:", "close")) {
conn->bits.close = TRUE;
}
else if (compareheader(k->p, "Transfer-Encoding:", "chunked")) {
conn->bits.chunk = TRUE;
Curl_httpchunk_init(conn);
}
else if (checkprefix("Content-Encoding:", k->p) &&
data->set.encoding) {
char *start;
for(start=k->p+17;
*start && isspace((int)*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)) {
if (sscanf (k->p+14, " bytes %d-", &k->offset) ||
sscanf (k->p+14, " bytes: %d-", &k->offset)) {
if (conn->resume_from == k->offset) {
k->content_range = TRUE;
}
}
}
else if(data->cookies &&
checkprefix("Set-Cookie:", k->p)) {
Curl_cookie_add(data->cookies, TRUE, k->p+11, conn->name);
}
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 = k->timeofdoc;
}
else if ((k->httpcode >= 300 && k->httpcode < 400) &&
(data->set.http_follow_location) &&
checkprefix("Location:", k->p)) {
char *ptr;
char *start=k->p;
char backup;
start += 9;
while(*start && isspace((int)*start ))
start++;
ptr = start;
while(*ptr && !isspace((int)*ptr))
ptr++;
backup = *ptr;
if(ptr != start) {
*ptr = '\0';
conn->newurl = strdup(start);
*ptr = backup;
}
}
k->writetype = CLIENTWRITE_HEADER;
if (data->set.http_include_header)
k->writetype |= CLIENTWRITE_BODY;
if(data->set.verbose)
Curl_debug(data, CURLINFO_HEADER_IN,
k->p, k->hbuflen);
result = Curl_client_write(data, k->writetype, k->p,
k->hbuflen);
if(result)
return result;
data->info.header_size += k->hbuflen;
conn->headerbytecount += k->hbuflen;
k->hbufp = data->state.headerbuff;
k->hbuflen = 0;
}
while (!stop_reading && *k->str);
if(stop_reading)
break;
}
if (k->str && !k->header && (nread > 0)) {
if(0 == k->bodywrites) {
if(conn->protocol&PROT_HTTP) {
if (conn->newurl) {
infof (data, "Follow to new URL: %s\n", conn->newurl);
k->keepon &= ~KEEP_READ;
FD_ZERO(&k->rkeepfd);
*done = TRUE;
return CURLE_OK;
}
else if (conn->resume_from &&
!k->content_range &&
(data->set.httpreq==HTTPREQ_GET)) {
failf (data, "HTTP server doesn't seem to support "
"byte ranges. Cannot resume.");
return CURLE_HTTP_RANGE_ERROR;
}
else if(data->set.timecondition && !conn->range) {
if((k->timeofdoc > 0) && (data->set.timevalue > 0)) {
switch(data->set.timecondition) {
case TIMECOND_IFMODSINCE:
default:
if(k->timeofdoc < data->set.timevalue) {
infof(data,
"The requested document is not new enough\n");
*done = TRUE;
return CURLE_OK;
}
break;
case TIMECOND_IFUNMODSINCE:
if(k->timeofdoc > data->set.timevalue) {
infof(data,
"The requested document is not old enough\n");
*done = TRUE;
return CURLE_OK;
}
break;
}
}
}
}
}
k->bodywrites++;
if(data->set.verbose) {
if(k->badheader) {
Curl_debug(data, CURLINFO_DATA_IN, data->state.headerbuff,
k->hbuflen);
if(k->badheader == HEADER_PARTHEADER)
Curl_debug(data, CURLINFO_DATA_IN, k->str, nread);
}
else
Curl_debug(data, CURLINFO_DATA_IN, k->str, nread);
}
if(conn->bits.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 in the chunky parser");
return CURLE_RECV_ERROR;
}
else if(CHUNKE_STOP == res) {
k->keepon &= ~KEEP_READ;
FD_ZERO(&k->rkeepfd);
}
}
if((-1 != conn->maxdownload) &&
(k->bytecount + nread >= conn->maxdownload)) {
nread = conn->maxdownload - k->bytecount;
if(nread < 0 )
nread = 0;
k->keepon &= ~KEEP_READ;
FD_ZERO(&k->rkeepfd);
}
k->bytecount += nread;
Curl_pgrsSetDownloadCounter(data, (double)k->bytecount);
if(!conn->bits.chunk && (nread || k->badheader)) {
if(k->badheader) {
result = Curl_client_write(data, CLIENTWRITE_BODY,
data->state.headerbuff,
k->hbuflen);
}
if(k->badheader < HEADER_ALLBAD) {
#ifdef HAVE_LIBZ
switch (k->content_encoding) {
case IDENTITY:
#endif
result = Curl_client_write(data, CLIENTWRITE_BODY, k->str,
nread);
#ifdef HAVE_LIBZ
break;
case DEFLATE:
result = Curl_unencode_deflate_write(data, k, nread);
break;
case GZIP:
case COMPRESS:
default:
failf (data, "Unrecognized content encoding type. "
"libcurl understands `identity' and `deflate' "
"content encodings.");
result = CURLE_BAD_CONTENT_ENCODING;
break;
}
#endif
}
k->badheader = HEADER_NORMAL;
if(result)
return result;
}
}
}
if((k->keepon & KEEP_WRITE) &&
(FD_ISSET(conn->writesockfd, writefdp)) ) {
int i, si;
ssize_t bytes_written;
if ((k->bytecount == 0) && (k->writebytecount == 0))
Curl_pgrsTime(data, TIMER_STARTTRANSFER);
didwhat |= KEEP_WRITE;
if(0 == conn->upload_present) {
size_t buffersize = BUFSIZE;
conn->upload_fromhere = k->uploadbuf;
if(!k->upload_done) {
if(conn->bits.upload_chunky) {
buffersize -= (8 + 2 + 2);
conn->upload_fromhere += 10;
}
nread = data->set.fread(conn->upload_fromhere, 1,
buffersize, data->set.in);
if(conn->bits.upload_chunky) {
char hexbuffer[9];
int hexlen = snprintf(hexbuffer, sizeof(hexbuffer),
"%x\r\n", nread);
conn->upload_fromhere -= hexlen;
nread += hexlen;
memcpy(conn->upload_fromhere, hexbuffer, hexlen);
if(nread>0) {
memcpy(conn->upload_fromhere +
nread, "\r\n", 2);
nread+=2;
}
else {
k->upload_done = TRUE;
}
}
}
else
nread = 0;
if (nread<=0) {
k->keepon &= ~KEEP_WRITE;
FD_ZERO(&k->wkeepfd);
break;
}
conn->upload_present = nread;
if (data->set.crlf) {
for(i = 0, si = 0; i < nread; i++, si++) {
if (conn->upload_fromhere[i] == 0x0a) {
data->state.scratch[si++] = 0x0d;
data->state.scratch[si] = 0x0a;
}
else
data->state.scratch[si] = conn->upload_fromhere[i];
}
if(si != nread) {
nread = si;
conn->upload_fromhere = data->state.scratch;
conn->upload_present = nread;
}
}
}
else {
}
result = Curl_write(conn,
conn->writesockfd,
conn->upload_fromhere,
conn->upload_present,
&bytes_written);
if(result)
return result;
else if(conn->upload_present != bytes_written) {
conn->upload_present -= bytes_written;
conn->upload_fromhere += bytes_written;
}
else {
conn->upload_fromhere = k->uploadbuf;
conn->upload_present = 0;
if(k->upload_done) {
k->keepon &= ~KEEP_WRITE;
FD_ZERO(&k->wkeepfd);
}
}
if(data->set.verbose)
Curl_debug(data, CURLINFO_DATA_OUT, conn->upload_fromhere,
bytes_written);
k->writebytecount += bytes_written;
Curl_pgrsSetUploadCounter(data, (double)k->writebytecount);
}
} while(0);
if(didwhat) {
if(conn->bytecountp)
*conn->bytecountp = k->bytecount;
if(conn->writebytecountp)
*conn->writebytecountp = k->writebytecount;
}
else {
if (k->write_after_100_header) {
k->write_after_100_header = FALSE;
FD_SET (conn->writesockfd, &k->writefd);
k->keepon |= KEEP_WRITE;
k->wkeepfd = k->writefd;
}
}
k->now = Curl_tvnow();
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)/1000) >= data->set.timeout)) {
failf (data, "Operation timed out with %d out of %d bytes received",
k->bytecount, conn->size);
return CURLE_OPERATION_TIMEOUTED;
}
if(!k->keepon) {
if(!(data->set.no_body) && k->contentlength &&
(k->bytecount != k->contentlength) &&
!conn->newurl) {
failf(data, "transfer closed with %d bytes remaining to read",
k->contentlength-k->bytecount);
return CURLE_PARTIAL_FILE;
}
else if(conn->bits.chunk && conn->proto.http->chunk.datasize) {
failf(data, "transfer closed with at least %d bytes remaining",
conn->proto.http->chunk.datasize);
return CURLE_PARTIAL_FILE;
}
if(Curl_pgrsUpdate(conn))
return CURLE_ABORTED_BY_CALLBACK;
}
*done = !k->keepon;
return CURLE_OK;
}
CURLcode Curl_readwrite_init(struct connectdata *conn)
{
struct SessionHandle *data = conn->data;
struct Curl_transfer_keeper *k = &conn->keep;
memset(k, 0, sizeof(struct Curl_transfer_keeper));
k->start = Curl_tvnow();
k->now = k->start;
k->header = TRUE;
k->httpversion = -1;
data = conn->data;
k->buf = data->state.buffer;
k->uploadbuf = data->state.uploadbuffer;
k->maxfd = (conn->sockfd>conn->writesockfd?
conn->sockfd:conn->writesockfd)+1;
k->hbufp = data->state.headerbuff;
Curl_pgrsTime(data, TIMER_PRETRANSFER);
Curl_speedinit(data);
Curl_pgrsSetUploadCounter(data, 0);
Curl_pgrsSetDownloadCounter(data, 0);
if (!conn->bits.getheader) {
k->header = FALSE;
if(conn->size > 0)
Curl_pgrsSetDownloadSize(data, conn->size);
}
if(conn->bits.getheader || !data->set.no_body) {
FD_ZERO (&k->readfd);
if(conn->sockfd != -1) {
FD_SET (conn->sockfd, &k->readfd);
k->keepon |= KEEP_READ;
}
FD_ZERO (&k->writefd);
if(conn->writesockfd != -1) {
if (data->set.expect100header)
k->write_after_100_header = TRUE;
else {
FD_SET (conn->writesockfd, &k->writefd);
k->keepon |= KEEP_WRITE;
}
}
k->rkeepfd = k->readfd;
k->wkeepfd = k->writefd;
}
return CURLE_OK;
}
void Curl_single_fdset(struct connectdata *conn,
fd_set *read_fd_set,
fd_set *write_fd_set,
fd_set *exc_fd_set,
int *max_fd)
{
*max_fd = -1;
if(conn->keep.keepon & KEEP_READ) {
FD_SET(conn->sockfd, read_fd_set);
*max_fd = conn->sockfd;
conn->keep.readfdp = read_fd_set;
}
if(conn->keep.keepon & KEEP_WRITE) {
FD_SET(conn->writesockfd, write_fd_set);
if(conn->writesockfd > *max_fd)
*max_fd = conn->writesockfd;
conn->keep.writefdp = write_fd_set;
}
*exc_fd_set = *exc_fd_set;
}
static CURLcode
Transfer(struct connectdata *conn)
{
struct SessionHandle *data = conn->data;
CURLcode result;
struct Curl_transfer_keeper *k = &conn->keep;
bool done=FALSE;
Curl_readwrite_init(conn);
if((conn->sockfd == -1) && (conn->writesockfd == -1))
return CURLE_OK;
if(!conn->bits.getheader && data->set.no_body)
return CURLE_OK;
k->writefdp = &k->writefd;
k->readfdp = &k->readfd;
while (!done) {
struct timeval interval;
k->readfd = k->rkeepfd;
k->writefd = k->wkeepfd;
interval.tv_sec = 1;
interval.tv_usec = 0;
switch (select (k->maxfd, k->readfdp, k->writefdp, NULL, &interval)) {
case -1:
#ifdef EINTR
if(errno == EINTR)
;
else
#endif
done = TRUE;
continue;
case 0:
result = Curl_readwrite(conn, &done);
break;
default:
result = Curl_readwrite(conn, &done);
break;
}
if(result)
return result;
}
return CURLE_OK;
}
CURLcode Curl_pretransfer(struct SessionHandle *data)
{
if(!data->change.url)
return CURLE_URL_MALFORMAT;
#ifdef USE_SSLEAY
Curl_SSL_InitSessions(data, data->set.ssl.numsessions);
#endif
data->set.followlocation=0;
data->state.this_is_a_follow = FALSE;
data->state.errorbuf = FALSE;
if(data->change.cookielist) {
struct curl_slist *list = data->change.cookielist;
while(list) {
data->cookies = Curl_cookie_init(list->data,
data->cookies,
data->set.cookiesession);
list = list->next;
}
curl_slist_free_all(data->change.cookielist);
data->change.cookielist = NULL;
}
data->state.allow_port = TRUE;
#if defined(HAVE_SIGNAL) && defined(SIGPIPE)
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)
if(!data->set.no_signal)
signal(SIGPIPE, data->state.prev_signal);
#endif
return CURLE_OK;
}
CURLcode Curl_follow(struct SessionHandle *data,
char *newurl)
{
char prot[16];
char letter;
if (data->set.maxredirs &&
(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);
data->change.referer_alloc = TRUE;
}
if(2 != sscanf(newurl, "%15[^?&/:]://%c", prot, &letter)) {
char *protsep;
char *pathsep;
char *newest;
char *useurl = newurl;
char *url_clone=strdup(data->change.url);
if(!url_clone)
return CURLE_OUT_OF_MEMORY;
protsep=strstr(url_clone, "//");
if(!protsep)
protsep=url_clone;
else
protsep+=2;
if('/' != newurl[0]) {
int level=0;
pathsep = strrchr(protsep, '?');
if(pathsep)
*pathsep=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)
*pathsep=0;
}
newest=(char *)malloc( strlen(url_clone) +
1 +
strlen(useurl) + 1);
if(!newest)
return CURLE_OUT_OF_MEMORY;
sprintf(newest, "%s%s%s", url_clone,
(('/' == useurl[0]) || (protsep && !*protsep))?"":"/",
useurl);
free(newurl);
free(url_clone);
newurl = newest;
}
else
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, "Follows Location: to new URL: '%s'\n", data->change.url);
switch(data->info.httpcode) {
case 300:
case 306:
case 307:
default:
break;
case 301:
if( data->set.httpreq == HTTPREQ_POST
|| data->set.httpreq == HTTPREQ_POST_FORM) {
infof(data,
"Violate RFC 2616/10.3.2 and switch from POST to GET\n");
data->set.httpreq = HTTPREQ_GET;
}
break;
case 302:
case 303:
if(data->set.httpreq != HTTPREQ_GET) {
data->set.httpreq = HTTPREQ_GET;
infof(data, "Disables POST, goes with %s\n",
data->set.no_body?"HEAD":"GET");
}
break;
case 304:
break;
case 305:
break;
}
Curl_pgrsTime(data, TIMER_REDIRECT);
Curl_pgrsResetTimes(data);
return CURLE_OK;
}
CURLcode Curl_perform(struct SessionHandle *data)
{
CURLcode res;
CURLcode res2;
struct connectdata *conn=NULL;
char *newurl = NULL;
data->state.used_interface = Curl_if_easy;
res = Curl_pretransfer(data);
if(res)
return res;
do {
Curl_pgrsTime(data, TIMER_STARTSINGLE);
res = Curl_connect(data, &conn);
if(res == CURLE_OK) {
res = Curl_do(&conn);
if(res == CURLE_OK) {
CURLcode res2;
if(conn->protocol&PROT_FTPS)
conn->ssl.use = FALSE;
res = Transfer(conn);
if(conn->protocol&PROT_FTPS)
conn->ssl.use = TRUE;
if(res == CURLE_OK)
newurl = conn->newurl?strdup(conn->newurl):NULL;
else {
conn->bits.close = TRUE;
if(-1 !=conn->secondarysocket) {
sclose(conn->secondarysocket);
conn->secondarysocket=-1;
}
}
res2 = Curl_done(conn);
if(CURLE_OK == res)
res = res2;
}
if((res == CURLE_OK) && newurl) {
res = Curl_follow(data, newurl);
if(CURLE_OK == res) {
newurl = NULL;
continue;
}
}
}
break;
} while(1);
if(newurl)
free(newurl);
res2 = Curl_posttransfer(data);
if(!res && res2)
res = res2;
return res;
}
CURLcode
Curl_Transfer(struct connectdata *c_conn,
int sockfd,
int size,
bool getheader,
long *bytecountp,
int writesockfd,
long *writebytecountp
)
{
struct connectdata *conn = (struct connectdata *)c_conn;
if(!conn)
return CURLE_BAD_FUNCTION_ARGUMENT;
conn->sockfd = sockfd;
conn->size = size;
conn->bits.getheader = getheader;
conn->bytecountp = bytecountp;
conn->writesockfd = writesockfd;
conn->writebytecountp = writebytecountp;
return CURLE_OK;
}