* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
#include "transfer.h"
#include "sendf.h"
#include "formdata.h"
+#include "mime.h"
#include "progress.h"
#include "curl_base64.h"
#include "cookie.h"
#include "http_proxy.h"
#include "warnless.h"
#include "non-ascii.h"
-#include "conncache.h"
#include "pipeline.h"
#include "http2.h"
#include "connect.h"
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
PORT_HTTP, /* defport */
CURLPROTO_HTTP, /* protocol */
PROTOPT_CREDSPERREQUEST /* flags */
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
PORT_HTTPS, /* defport */
CURLPROTO_HTTPS, /* protocol */
PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN_NPN /* flags */
if(!http)
return CURLE_OUT_OF_MEMORY;
+ Curl_mime_initpart(&http->form, conn->data);
conn->data->req.protop = http;
Curl_http2_setup_conn(conn);
return CURLE_OK;
}
-/*
- * checkheaders() checks the linked list of custom HTTP headers for a
- * particular header (prefix).
- *
- * Returns a pointer to the first matching header or NULL if none matched.
- */
-char *Curl_checkheaders(const struct connectdata *conn,
- const char *thisheader)
-{
- struct curl_slist *head;
- size_t thislen = strlen(thisheader);
- struct Curl_easy *data = conn->data;
-
- for(head = data->set.headers;head; head=head->next) {
- if(strncasecompare(head->data, thisheader, thislen))
- return head->data;
- }
-
- return NULL;
-}
/*
* checkProxyHeaders() checks the linked list of custom proxy headers
* if proxy headers are not available, then it will lookup into http header
* link list
*
- * It takes a connectdata struct as input instead of the Curl_easy simply
- * to know if this is a proxy request or not, as it then might check a
- * different header list.
+ * It takes a connectdata struct as input instead of the Curl_easy simply to
+ * know if this is a proxy request or not, as it then might check a different
+ * header list. Provide the header prefix without colon!.
*/
char *Curl_checkProxyheaders(const struct connectdata *conn,
const char *thisheader)
for(head = (conn->bits.proxy && data->set.sep_headers) ?
data->set.proxyheaders : data->set.headers;
- head; head=head->next) {
- if(strncasecompare(head->data, thisheader, thislen))
+ head; head = head->next) {
+ if(strncasecompare(head->data, thisheader, thislen) &&
+ Curl_headersep(head->data[thislen]))
return head->data;
}
const char *user;
const char *pwd;
CURLcode result;
+ char *out;
if(proxy) {
userp = &conn->allocptr.proxyuserpwd;
pwd = conn->passwd;
}
- snprintf(data->state.buffer, CURL_BUFSIZE(data->set.buffer_size),
- "%s:%s", user, pwd);
+ out = aprintf("%s:%s", user, pwd);
+ if(!out)
+ return CURLE_OUT_OF_MEMORY;
- result = Curl_base64_encode(data,
- data->state.buffer, strlen(data->state.buffer),
- &authorization, &size);
+ result = Curl_base64_encode(data, out, strlen(out), &authorization, &size);
if(result)
- return result;
+ goto fail;
- if(!authorization)
- return CURLE_REMOTE_ACCESS_DENIED;
+ if(!authorization) {
+ result = CURLE_REMOTE_ACCESS_DENIED;
+ goto fail;
+ }
free(*userp);
*userp = aprintf("%sAuthorization: Basic %s\r\n",
proxy ? "Proxy-" : "",
authorization);
free(authorization);
- if(!*userp)
- return CURLE_OUT_OF_MEMORY;
+ if(!*userp) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
- return CURLE_OK;
+ fail:
+ free(out);
+ return result;
}
/* pickoneauth() selects the most favourable authentication method from the
case HTTPREQ_POST:
if(data->state.infilesize != -1)
expectsend = data->state.infilesize;
- else if(data->set.postfields)
- expectsend = (curl_off_t)strlen(data->set.postfields);
break;
case HTTPREQ_PUT:
if(data->state.infilesize != -1)
expectsend = data->state.infilesize;
break;
case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
expectsend = http->postsize;
break;
default:
#endif
#if defined(USE_NTLM) && defined(NTLM_WB_ENABLED)
if(authstatus->picked == CURLAUTH_NTLM_WB) {
- auth="NTLM_WB";
+ auth = "NTLM_WB";
result = Curl_output_ntlm_wb(conn, proxy);
if(result)
return result;
if(authstatus->picked == CURLAUTH_BASIC) {
/* Basic */
if((proxy && conn->bits.proxy_user_passwd &&
- !Curl_checkProxyheaders(conn, "Proxy-authorization:")) ||
+ !Curl_checkProxyheaders(conn, "Proxy-authorization")) ||
(!proxy && conn->bits.user_passwd &&
- !Curl_checkheaders(conn, "Authorization:"))) {
+ !Curl_checkheaders(conn, "Authorization"))) {
auth = "Basic";
result = http_output_basic(conn, proxy);
if(result)
proxy ? "Proxy" : "Server", auth,
proxy ? (conn->http_proxy.user ? conn->http_proxy.user : "") :
(conn->user ? conn->user : ""));
- authstatus->multi = (!authstatus->done) ? TRUE : FALSE;
+ authstatus->multipass = (!authstatus->done) ? TRUE : FALSE;
}
else
- authstatus->multi = FALSE;
+ authstatus->multipass = FALSE;
return CURLE_OK;
}
if(!data->state.this_is_a_follow ||
conn->bits.netrc ||
!data->state.first_host ||
- data->set.http_disable_hostname_check_before_authentication ||
+ data->set.allow_auth_to_other_hosts ||
strcasecompare(data->state.first_host, conn->host.name)) {
result = output_auth_headers(conn, authhost, request, path, FALSE);
}
http->sending++; /* move one step up */
- http->backup.postsize=0;
+ http->backup.postsize = 0;
}
else
http->postsize = 0;
buffer is using this size.
*/
- sendsize = (size > CURL_MAX_WRITE_SIZE) ? CURL_MAX_WRITE_SIZE : size;
+ sendsize = CURLMIN(size, CURL_MAX_WRITE_SIZE);
/* OpenSSL is very picky and we must send the SAME buffer pointer to the
library when we attempt to re-send this buffer. Sending the same data
/* there was body data sent beyond the initial header part, pass that
on to the debug callback too */
Curl_debug(conn->data, CURLINFO_DATA_OUT,
- ptr+headlen, bodylen, conn);
+ ptr + headlen, bodylen, conn);
}
}
This needs FIXing.
*/
return CURLE_SEND_ERROR;
- else
- Curl_pipeline_leave_write(conn);
+ Curl_pipeline_leave_write(conn);
}
}
Curl_add_buffer_free(in);
(~(size * 2) < (in->size_used * 2)))
new_size = (size_t)-1;
else
- new_size = (in->size_used+size) * 2;
+ new_size = (in->size_used + size) * 2;
if(in->buffer)
/* we have a buffer, enlarge the existing one */
clen = strlen(content); /* length of the word to find */
/* find the content string in the rest of the line */
- for(;len>=clen;len--, start++) {
+ for(; len >= clen; len--, start++) {
if(strncasecompare(start, content, clen))
return TRUE; /* match! */
}
if(result)
return result;
+ if(conn->bits.proxy_connect_closed)
+ /* this is not an error, just part of the connection negotiation */
+ return CURLE_OK;
+
if(CONNECT_FIRSTSOCKET_PROXY_SSL())
return CURLE_OK; /* wait for HTTPS proxy SSL initialization to complete */
- if(conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
+ if(Curl_connect_ongoing(conn))
/* nothing else to do except wait right now - we're not done here. */
return CURLE_OK;
- if(conn->given->flags & PROTOPT_SSL) {
+ if(conn->given->protocol & CURLPROTO_HTTPS) {
/* perform SSL initialization */
result = https_connecting(conn, done);
if(result)
struct Curl_easy *data = conn->data;
struct HTTP *http = data->req.protop;
+ /* Clear multipass flag. If authentication isn't done yet, then it will get
+ * a chance to be set back to true when we output the next auth header */
+ data->state.authhost.multipass = FALSE;
+ data->state.authproxy.multipass = FALSE;
+
Curl_unencode_cleanup(conn);
#ifdef USE_SPNEGO
Curl_http2_done(conn, premature);
- if(HTTPREQ_POST_FORM == data->set.httpreq) {
- data->req.bytecount = http->readbytecount + http->writebytecount;
+ Curl_mime_cleanpart(&http->form);
- Curl_formclean(&http->sendit); /* Now free that whole lot */
- if(http->form.fp) {
- /* a file being uploaded was left opened, close it! */
- fclose(http->form.fp);
- http->form.fp = NULL;
- }
- }
- else if(HTTPREQ_PUT == data->set.httpreq)
+ switch(data->set.httpreq) {
+ case HTTPREQ_PUT:
+ case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
data->req.bytecount = http->readbytecount + http->writebytecount;
+ break;
+ default:
+ break;
+ }
if(status)
return status;
/* if not doing HTTP 1.0 or version 2, or disabled explicitly, we add an
Expect: 100-continue to the headers which actually speeds up post
operations (as there is one packet coming back from the web server) */
- ptr = Curl_checkheaders(conn, "Expect:");
+ ptr = Curl_checkheaders(conn, "Expect");
if(ptr) {
data->state.expect100header =
Curl_compareheader(ptr, "Expect:", "100-continue");
char *ptr;
struct curl_slist *h[2];
struct curl_slist *headers;
- int numlists=1; /* by default */
+ int numlists = 1; /* by default */
struct Curl_easy *data = conn->data;
int i;
}
/* loop through one or two lists */
- for(i=0; i < numlists; i++) {
+ for(i = 0; i < numlists; i++) {
headers = h[i];
while(headers) {
+ char *semicolonp = NULL;
ptr = strchr(headers->data, ':');
+ if(!ptr) {
+ char *optr;
+ /* no colon, semicolon? */
+ ptr = strchr(headers->data, ';');
+ if(ptr) {
+ optr = ptr;
+ ptr++; /* pass the semicolon */
+ while(*ptr && ISSPACE(*ptr))
+ ptr++;
+
+ if(*ptr) {
+ /* this may be used for something else in the future */
+ optr = NULL;
+ }
+ else {
+ if(*(--ptr) == ';') {
+ /* send no-value custom header if terminated by semicolon */
+ *ptr = ':';
+ semicolonp = ptr;
+ }
+ }
+ ptr = optr;
+ }
+ }
if(ptr) {
/* we require a colon for this to be a true header */
while(*ptr && ISSPACE(*ptr))
ptr++;
- if(*ptr) {
- /* only send this if the contents was non-blank */
+ if(*ptr || semicolonp) {
+ /* only send this if the contents was non-blank or done special */
+ CURLcode result = CURLE_OK;
if(conn->allocptr.host &&
/* a Host: header was sent already, don't pass on any custom Host:
/* this header (extended by formdata.c) is sent later */
checkprefix("Content-Type:", headers->data))
;
+ else if(data->set.httpreq == HTTPREQ_POST_MIME &&
+ /* this header is sent later */
+ checkprefix("Content-Type:", headers->data))
+ ;
else if(conn->bits.authneg &&
/* while doing auth neg, don't allow the custom length since
we will force length zero then */
- checkprefix("Content-Length", headers->data))
+ checkprefix("Content-Length:", headers->data))
;
else if(conn->allocptr.te &&
/* when asking for Transfer-Encoding, don't pass on a custom
Connection: */
- checkprefix("Connection", headers->data))
+ checkprefix("Connection:", headers->data))
;
else if((conn->httpversion == 20) &&
checkprefix("Transfer-Encoding:", headers->data))
/* HTTP/2 doesn't support chunked requests */
;
+ else if(checkprefix("Authorization:", headers->data) &&
+ /* be careful of sending this potentially sensitive header to
+ other hosts */
+ (data->state.this_is_a_follow &&
+ data->state.first_host &&
+ !data->set.allow_auth_to_other_hosts &&
+ !strcasecompare(data->state.first_host, conn->host.name)))
+ ;
else {
- CURLcode result = Curl_add_bufferf(req_buffer, "%s\r\n",
- headers->data);
- if(result)
- return result;
- }
- }
- }
- else {
- ptr = strchr(headers->data, ';');
- if(ptr) {
-
- ptr++; /* pass the semicolon */
- while(*ptr && ISSPACE(*ptr))
- ptr++;
-
- if(*ptr) {
- /* this may be used for something else in the future */
- }
- else {
- if(*(--ptr) == ';') {
- CURLcode result;
-
- /* send no-value custom header if terminated by semicolon */
- *ptr = ':';
- result = Curl_add_bufferf(req_buffer, "%s\r\n",
- headers->data);
- if(result)
- return result;
- }
+ result = Curl_add_bufferf(req_buffer, "%s\r\n", headers->data);
}
+ if(semicolonp)
+ *semicolonp = ';'; /* put back the semicolon */
+ if(result)
+ return result;
}
}
headers = headers->next;
Curl_send_buffer *req_buffer)
{
const struct tm *tm;
- char *buf = data->state.buffer;
struct tm keeptime;
CURLcode result;
+ char datestr[80];
+ const char *condp;
if(data->set.timecondition == CURL_TIMECOND_NONE)
/* no condition was asked for */
}
tm = &keeptime;
+ switch(data->set.timecondition) {
+ default:
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ case CURL_TIMECOND_IFMODSINCE:
+ condp = "If-Modified-Since";
+ break;
+ case CURL_TIMECOND_IFUNMODSINCE:
+ condp = "If-Unmodified-Since";
+ break;
+ case CURL_TIMECOND_LASTMOD:
+ condp = "Last-Modified";
+ break;
+ }
+
/* The If-Modified-Since header family should have their times set in
* GMT as RFC2616 defines: "All HTTP date/time stamps MUST be
* represented in Greenwich Mean Time (GMT), without exception. For the
*/
/* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
- snprintf(buf, BUFSIZE-1,
- "%s, %02d %s %4d %02d:%02d:%02d GMT",
+ snprintf(datestr, sizeof(datestr),
+ "%s: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
+ condp,
Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
tm->tm_mday,
Curl_month[tm->tm_mon],
tm->tm_min,
tm->tm_sec);
- switch(data->set.timecondition) {
- default:
- break;
- case CURL_TIMECOND_IFMODSINCE:
- result = Curl_add_bufferf(req_buffer,
- "If-Modified-Since: %s\r\n", buf);
- break;
- case CURL_TIMECOND_IFUNMODSINCE:
- result = Curl_add_bufferf(req_buffer,
- "If-Unmodified-Since: %s\r\n", buf);
- break;
- case CURL_TIMECOND_LASTMOD:
- result = Curl_add_bufferf(req_buffer,
- "Last-Modified: %s\r\n", buf);
- break;
- }
+ result = Curl_add_buffer(req_buffer, datestr, strlen(datestr));
return result;
}
const char *httpstring;
Curl_send_buffer *req_buffer;
curl_off_t postsize = 0; /* curl_off_t to handle large file sizes */
- int seekerr = CURL_SEEKFUNC_OK;
+ int seekerr = CURL_SEEKFUNC_CANTSEEK;
/* Always consider the DO phase done after this function call, even if there
may be parts of the request that is not yet sent, since we can deal with
switch(httpreq) {
case HTTPREQ_POST:
case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
request = "POST";
break;
case HTTPREQ_PUT:
request = "PUT";
break;
+ case HTTPREQ_OPTIONS:
+ request = "OPTIONS";
+ break;
default: /* this should never happen */
case HTTPREQ_GET:
request = "GET";
it might have been used in the proxy connect, but if we have got a header
with the user-agent string specified, we erase the previously made string
here. */
- if(Curl_checkheaders(conn, "User-Agent:")) {
+ if(Curl_checkheaders(conn, "User-Agent")) {
free(conn->allocptr.uagent);
- conn->allocptr.uagent=NULL;
+ conn->allocptr.uagent = NULL;
}
/* setup the authentication headers */
if(result)
return result;
- if((data->state.authhost.multi || data->state.authproxy.multi) &&
+ if((data->state.authhost.multipass || data->state.authproxy.multipass) &&
(httpreq != HTTPREQ_GET) &&
(httpreq != HTTPREQ_HEAD)) {
/* Auth is required and we are not authenticated yet. Make a PUT or POST
conn->bits.authneg = FALSE;
Curl_safefree(conn->allocptr.ref);
- if(data->change.referer && !Curl_checkheaders(conn, "Referer:")) {
+ if(data->change.referer && !Curl_checkheaders(conn, "Referer")) {
conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer);
if(!conn->allocptr.ref)
return CURLE_OUT_OF_MEMORY;
conn->allocptr.ref = NULL;
#if !defined(CURL_DISABLE_COOKIES)
- if(data->set.str[STRING_COOKIE] && !Curl_checkheaders(conn, "Cookie:"))
+ if(data->set.str[STRING_COOKIE] && !Curl_checkheaders(conn, "Cookie"))
addcookies = data->set.str[STRING_COOKIE];
#endif
- if(!Curl_checkheaders(conn, "Accept-Encoding:") &&
+ if(!Curl_checkheaders(conn, "Accept-Encoding") &&
data->set.str[STRING_ENCODING]) {
Curl_safefree(conn->allocptr.accept_encoding);
conn->allocptr.accept_encoding =
#ifdef HAVE_LIBZ
/* we only consider transfer-encoding magic if libz support is built-in */
- if(!Curl_checkheaders(conn, "TE:") &&
+ if(!Curl_checkheaders(conn, "TE") &&
data->set.http_transfer_encoding) {
/* When we are to insert a TE: header in the request, we must also insert
TE in a Connection: header, so we need to merge the custom provided
Connection: header and prevent the original to get sent. Note that if
the user has inserted his/hers own TE: header we don't do this magic
but then assume that the user will handle it all! */
- char *cptr = Curl_checkheaders(conn, "Connection:");
+ char *cptr = Curl_checkheaders(conn, "Connection");
#define TE_HEADER "TE: gzip\r\n"
Curl_safefree(conn->allocptr.te);
+ if(cptr) {
+ cptr = Curl_copy_header_value(cptr);
+ if(!cptr)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
/* Create the (updated) Connection: header */
- conn->allocptr.te = cptr? aprintf("%s, TE\r\n" TE_HEADER, cptr):
- strdup("Connection: TE\r\n" TE_HEADER);
+ conn->allocptr.te = aprintf("Connection: %s%sTE\r\n" TE_HEADER,
+ cptr ? cptr : "", (cptr && *cptr) ? ", ":"");
+ free(cptr);
if(!conn->allocptr.te)
return CURLE_OUT_OF_MEMORY;
}
#endif
- ptr = Curl_checkheaders(conn, "Transfer-Encoding:");
+ switch(httpreq) {
+ case HTTPREQ_POST_MIME:
+ http->sendit = &data->set.mimepost;
+ break;
+ case HTTPREQ_POST_FORM:
+ /* Convert the form structure into a mime structure. */
+ Curl_mime_cleanpart(&http->form);
+ result = Curl_getformdata(data, &http->form, data->set.httppost,
+ data->state.fread_func);
+ if(result)
+ return result;
+ http->sendit = &http->form;
+ break;
+ default:
+ http->sendit = NULL;
+ }
+
+ if(http->sendit) {
+ const char *cthdr = Curl_checkheaders(conn, "Content-Type");
+
+ /* Read and seek body only. */
+ http->sendit->flags |= MIME_BODY_ONLY;
+
+ /* Prepare the mime structure headers & set content type. */
+
+ if(cthdr)
+ for(cthdr += 13; *cthdr == ' '; cthdr++)
+ ;
+ else if(http->sendit->kind == MIMEKIND_MULTIPART)
+ cthdr = "multipart/form-data";
+
+ curl_mime_headers(http->sendit, data->set.headers, 0);
+ result = Curl_mime_prepare_headers(http->sendit, cthdr,
+ NULL, MIMESTRATEGY_FORM);
+ curl_mime_headers(http->sendit, NULL, 0);
+ if(!result)
+ result = Curl_mime_rewind(http->sendit);
+ if(result)
+ return result;
+ http->postsize = Curl_mime_size(http->sendit);
+ }
+
+ ptr = Curl_checkheaders(conn, "Transfer-Encoding");
if(ptr) {
/* Some kind of TE is requested, check if 'chunked' is chosen */
data->req.upload_chunky =
Curl_compareheader(ptr, "Transfer-Encoding:", "chunked");
}
else {
- if((conn->handler->protocol&PROTO_FAMILY_HTTP) &&
- data->set.upload &&
- (data->state.infilesize == -1)) {
+ if((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
+ (((httpreq == HTTPREQ_POST_MIME || httpreq == HTTPREQ_POST_FORM) &&
+ http->postsize < 0) ||
+ (data->set.upload && data->state.infilesize == -1))) {
if(conn->bits.authneg)
/* don't enable chunked during auth neg */
;
Curl_safefree(conn->allocptr.host);
- ptr = Curl_checkheaders(conn, "Host:");
+ ptr = Curl_checkheaders(conn, "Host");
if(ptr && (!data->state.this_is_a_follow ||
strcasecompare(data->state.first_host, conn->host.name))) {
#if !defined(CURL_DISABLE_COOKIES)
#endif
if(strcmp("Host:", ptr)) {
- conn->allocptr.host = aprintf("%s\r\n", ptr);
+ conn->allocptr.host = aprintf("Host:%s\r\n", &ptr[5]);
if(!conn->allocptr.host)
return CURLE_OUT_OF_MEMORY;
}
}
#ifndef CURL_DISABLE_PROXY
- if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
+ if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
/* Using a proxy but does not tunnel through it */
/* The path sent to the proxy is in fact the entire URL. But if the remote
}
#endif /* CURL_DISABLE_PROXY */
- if(HTTPREQ_POST_FORM == httpreq) {
- /* we must build the whole post sequence first, so that we have a size of
- the whole transfer before we start to send it */
- result = Curl_getformdata(data, &http->sendit, data->set.httppost,
- Curl_checkheaders(conn, "Content-Type:"),
- &http->postsize);
- if(result)
- return result;
- }
-
- http->p_accept = Curl_checkheaders(conn, "Accept:")?NULL:"Accept: */*\r\n";
+ http->p_accept = Curl_checkheaders(conn, "Accept")?NULL:"Accept: */*\r\n";
- if(( (HTTPREQ_POST == httpreq) ||
- (HTTPREQ_POST_FORM == httpreq) ||
- (HTTPREQ_PUT == httpreq) ) &&
+ if((HTTPREQ_POST == httpreq || HTTPREQ_PUT == httpreq) &&
data->state.resume_from) {
/**********************************************************************
* Resuming upload in HTTP means that we PUT or POST and that we have
* a Range: header that will be passed along. We need to "fast forward"
* the file the given number of bytes and decrease the assume upload
* file size before we continue this venture in the dark lands of HTTP.
+ * Resuming mime/form posting at an offset > 0 has no sense and is ignored.
*********************************************************************/
if(data->state.resume_from < 0) {
/* Now, let's read off the proper amount of bytes from the
input. */
if(conn->seek_func) {
+ Curl_set_in_callback(data, true);
seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
SEEK_SET);
+ Curl_set_in_callback(data, false);
}
if(seekerr != CURL_SEEKFUNC_OK) {
+ curl_off_t passed = 0;
+
if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
failf(data, "Could not seek stream");
return CURLE_READ_ERROR;
}
/* when seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
- else {
- curl_off_t passed=0;
- do {
- size_t readthisamountnow =
- (data->state.resume_from - passed > CURL_OFF_T_C(BUFSIZE)) ?
- BUFSIZE : curlx_sotouz(data->state.resume_from - passed);
-
- size_t actuallyread =
- data->state.fread_func(data->state.buffer, 1, readthisamountnow,
- data->state.in);
-
- passed += actuallyread;
- if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
- /* this checks for greater-than only to make sure that the
- CURL_READFUNC_ABORT return code still aborts */
- failf(data, "Could only read %" CURL_FORMAT_CURL_OFF_T
- " bytes from the input", passed);
- return CURLE_READ_ERROR;
- }
- } while(passed < data->state.resume_from);
- }
+ do {
+ size_t readthisamountnow =
+ (data->state.resume_from - passed > data->set.buffer_size) ?
+ (size_t)data->set.buffer_size :
+ curlx_sotouz(data->state.resume_from - passed);
+
+ size_t actuallyread =
+ data->state.fread_func(data->state.buffer, 1, readthisamountnow,
+ data->state.in);
+
+ passed += actuallyread;
+ if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
+ /* this checks for greater-than only to make sure that the
+ CURL_READFUNC_ABORT return code still aborts */
+ failf(data, "Could only read %" CURL_FORMAT_CURL_OFF_T
+ " bytes from the input", passed);
+ return CURLE_READ_ERROR;
+ }
+ } while(passed < data->state.resume_from);
}
/* now, decrease the size of the read */
* ones if any such are specified.
*/
if(((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) &&
- !Curl_checkheaders(conn, "Range:")) {
+ !Curl_checkheaders(conn, "Range")) {
/* if a line like this was already allocated, free the previous one */
free(conn->allocptr.rangeline);
conn->allocptr.rangeline = aprintf("Range: bytes=%s\r\n",
data->state.range);
}
- else if((httpreq != HTTPREQ_GET) &&
- !Curl_checkheaders(conn, "Content-Range:")) {
+ else if((httpreq == HTTPREQ_POST || httpreq == HTTPREQ_PUT) &&
+ !Curl_checkheaders(conn, "Content-Range")) {
/* if a line like this was already allocated, free the previous one */
free(conn->allocptr.rangeline);
}
else if(data->state.resume_from) {
/* This is because "resume" was selected */
- curl_off_t total_expected_size=
+ curl_off_t total_expected_size =
data->state.resume_from + data->state.infilesize;
conn->allocptr.rangeline =
aprintf("Content-Range: bytes %s%" CURL_FORMAT_CURL_OFF_T
if(result)
return result;
+ if(data->set.str[STRING_TARGET])
+ ppath = data->set.str[STRING_TARGET];
+
/* url */
if(paste_ftp_userpwd)
result = Curl_add_bufferf(req_buffer, "ftp://%s:%s@%s",
conn->allocptr.ref:"" /* Referer: <data> */,
(conn->bits.httpproxy &&
!conn->bits.tunnel_proxy &&
- !Curl_checkProxyheaders(conn, "Proxy-Connection:"))?
+ !Curl_checkProxyheaders(conn, "Proxy-Connection"))?
"Proxy-Connection: Keep-Alive\r\n":"",
te
);
- /* clear userpwd to avoid re-using credentials from re-used connections */
+ /* clear userpwd and proxyuserpwd to avoid re-using old credentials
+ * from re-used connections */
Curl_safefree(conn->allocptr.userpwd);
-
- /*
- * Free proxyuserpwd for Negotiate/NTLM. Cannot reuse as it is associated
- * with the connection and shouldn't be repeated over it either.
- */
- switch(data->state.authproxy.picked) {
- case CURLAUTH_NEGOTIATE:
- case CURLAUTH_NTLM:
- case CURLAUTH_NTLM_WB:
- Curl_safefree(conn->allocptr.proxyuserpwd);
- break;
- }
+ Curl_safefree(conn->allocptr.proxyuserpwd);
if(result)
return result;
#if !defined(CURL_DISABLE_COOKIES)
if(data->cookies || addcookies) {
- struct Cookie *co=NULL; /* no cookies from start */
- int count=0;
+ struct Cookie *co = NULL; /* no cookies from start */
+ int count = 0;
if(data->cookies) {
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
}
if(co) {
- struct Cookie *store=co;
+ struct Cookie *store = co;
/* now loop through all cookies that matched */
while(co) {
if(co->value) {
switch(httpreq) {
- case HTTPREQ_POST_FORM:
- if(!http->sendit || conn->bits.authneg) {
- /* nothing to post! */
- result = Curl_add_bufferf(req_buffer, "Content-Length: 0\r\n\r\n");
- if(result)
- return result;
-
- result = Curl_add_buffer_send(req_buffer, conn,
- &data->info.request_size, 0, FIRSTSOCKET);
- if(result)
- failf(data, "Failed sending POST request");
- else
- /* setup variables for the upcoming transfer */
- Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount,
- -1, NULL);
- break;
- }
-
- if(Curl_FormInit(&http->form, http->sendit)) {
- failf(data, "Internal HTTP POST error!");
- return CURLE_HTTP_POST_ERROR;
- }
-
- /* Get the currently set callback function pointer and store that in the
- form struct since we might want the actual user-provided callback later
- on. The data->set.fread_func pointer itself will be changed for the
- multipart case to the function that returns a multipart formatted
- stream. */
- http->form.fread_func = data->state.fread_func;
-
- /* Set the read function to read from the generated form data */
- data->state.fread_func = (curl_read_callback)Curl_FormReader;
- data->state.in = &http->form;
+ case HTTPREQ_PUT: /* Let's PUT the data to the server! */
- http->sending = HTTPSEND_BODY;
+ if(conn->bits.authneg)
+ postsize = 0;
+ else
+ postsize = data->state.infilesize;
- if(!data->req.upload_chunky &&
- !Curl_checkheaders(conn, "Content-Length:")) {
+ if((postsize != -1) && !data->req.upload_chunky &&
+ (conn->bits.authneg || !Curl_checkheaders(conn, "Content-Length"))) {
/* only add Content-Length if not uploading chunked */
result = Curl_add_bufferf(req_buffer,
"Content-Length: %" CURL_FORMAT_CURL_OFF_T
- "\r\n", http->postsize);
+ "\r\n", postsize);
if(result)
return result;
}
- result = expect100(data, conn, req_buffer);
- if(result)
- return result;
-
- {
-
- /* Get Content-Type: line from Curl_formpostheader.
- */
- char *contentType;
- size_t linelength=0;
- contentType = Curl_formpostheader((void *)&http->form,
- &linelength);
- if(!contentType) {
- failf(data, "Could not get Content-Type header line!");
- return CURLE_HTTP_POST_ERROR;
- }
-
- result = Curl_add_buffer(req_buffer, contentType, linelength);
+ if(postsize != 0) {
+ result = expect100(data, conn, req_buffer);
if(result)
return result;
}
- /* make the request end in a true CRLF */
- result = Curl_add_buffer(req_buffer, "\r\n", 2);
+ result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers */
if(result)
return result;
- /* set upload size to the progress meter */
- Curl_pgrsSetUploadSize(data, http->postsize);
+ /* set the upload size to the progress meter */
+ Curl_pgrsSetUploadSize(data, postsize);
- /* fire away the whole request to the server */
+ /* this sends the buffer and frees all the buffer resources */
result = Curl_add_buffer_send(req_buffer, conn,
&data->info.request_size, 0, FIRSTSOCKET);
if(result)
- failf(data, "Failed sending POST request");
+ failf(data, "Failed sending PUT request");
else
- /* setup variables for the upcoming transfer */
+ /* prepare for transfer */
Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
- &http->readbytecount, FIRSTSOCKET,
- &http->writebytecount);
-
- if(result) {
- Curl_formclean(&http->sendit); /* free that whole lot */
- return result;
- }
-
- /* convert the form data */
- result = Curl_convert_form(data, http->sendit);
- if(result) {
- Curl_formclean(&http->sendit); /* free that whole lot */
+ &http->readbytecount, postsize?FIRSTSOCKET:-1,
+ postsize?&http->writebytecount:NULL);
+ if(result)
return result;
- }
-
break;
- case HTTPREQ_PUT: /* Let's PUT the data to the server! */
+ case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
+ /* This is form posting using mime data. */
+ if(conn->bits.authneg) {
+ /* nothing to post! */
+ result = Curl_add_bufferf(req_buffer, "Content-Length: 0\r\n\r\n");
+ if(result)
+ return result;
- if(conn->bits.authneg)
- postsize = 0;
- else
- postsize = data->state.infilesize;
+ result = Curl_add_buffer_send(req_buffer, conn,
+ &data->info.request_size, 0, FIRSTSOCKET);
+ if(result)
+ failf(data, "Failed sending POST request");
+ else
+ /* setup variables for the upcoming transfer */
+ Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount,
+ -1, NULL);
+ break;
+ }
- if((postsize != -1) && !data->req.upload_chunky &&
- (conn->bits.authneg || !Curl_checkheaders(conn, "Content-Length:"))) {
- /* only add Content-Length if not uploading chunked */
+ postsize = http->postsize;
+
+ /* We only set Content-Length and allow a custom Content-Length if
+ we don't upload data chunked, as RFC2616 forbids us to set both
+ kinds of headers (Transfer-Encoding: chunked and Content-Length) */
+ if(postsize != -1 && !data->req.upload_chunky &&
+ (conn->bits.authneg || !Curl_checkheaders(conn, "Content-Length"))) {
+ /* we allow replacing this header if not during auth negotiation,
+ although it isn't very wise to actually set your own */
result = Curl_add_bufferf(req_buffer,
"Content-Length: %" CURL_FORMAT_CURL_OFF_T
"\r\n", postsize);
return result;
}
- if(postsize != 0) {
+ /* Output mime-generated headers. */
+ {
+ struct curl_slist *hdr;
+
+ for(hdr = http->sendit->curlheaders; hdr; hdr = hdr->next) {
+ result = Curl_add_bufferf(req_buffer, "%s\r\n", hdr->data);
+ if(result)
+ return result;
+ }
+ }
+
+ /* For really small posts we don't use Expect: headers at all, and for
+ the somewhat bigger ones we allow the app to disable it. Just make
+ sure that the expect100header is always set to the preferred value
+ here. */
+ ptr = Curl_checkheaders(conn, "Expect");
+ if(ptr) {
+ data->state.expect100header =
+ Curl_compareheader(ptr, "Expect:", "100-continue");
+ }
+ else if(postsize > EXPECT_100_THRESHOLD || postsize < 0) {
result = expect100(data, conn, req_buffer);
if(result)
return result;
}
+ else
+ data->state.expect100header = FALSE;
- result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers */
+ /* make the request end in a true CRLF */
+ result = Curl_add_buffer(req_buffer, "\r\n", 2);
if(result)
return result;
/* set the upload size to the progress meter */
Curl_pgrsSetUploadSize(data, postsize);
+ /* Read from mime structure. */
+ data->state.fread_func = (curl_read_callback) Curl_mime_read;
+ data->state.in = (void *) http->sendit;
+ http->sending = HTTPSEND_BODY;
+
/* this sends the buffer and frees all the buffer resources */
result = Curl_add_buffer_send(req_buffer, conn,
&data->info.request_size, 0, FIRSTSOCKET);
if(result)
- failf(data, "Failed sending PUT request");
+ failf(data, "Failed sending POST request");
else
/* prepare for transfer */
Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
postsize?&http->writebytecount:NULL);
if(result)
return result;
+
break;
case HTTPREQ_POST:
if(conn->bits.authneg)
postsize = 0;
- else {
- /* figure out the size of the postfields */
- postsize = (data->state.infilesize != -1)?
- data->state.infilesize:
- (data->set.postfields? (curl_off_t)strlen(data->set.postfields):-1);
- }
+ else
+ /* the size of the post body */
+ postsize = data->state.infilesize;
/* We only set Content-Length and allow a custom Content-Length if
we don't upload data chunked, as RFC2616 forbids us to set both
kinds of headers (Transfer-Encoding: chunked and Content-Length) */
if((postsize != -1) && !data->req.upload_chunky &&
- (conn->bits.authneg || !Curl_checkheaders(conn, "Content-Length:"))) {
+ (conn->bits.authneg || !Curl_checkheaders(conn, "Content-Length"))) {
/* we allow replacing this header if not during auth negotiation,
although it isn't very wise to actually set your own */
result = Curl_add_bufferf(req_buffer,
return result;
}
- if(!Curl_checkheaders(conn, "Content-Type:")) {
+ if(!Curl_checkheaders(conn, "Content-Type")) {
result = Curl_add_bufferf(req_buffer,
"Content-Type: application/"
"x-www-form-urlencoded\r\n");
the somewhat bigger ones we allow the app to disable it. Just make
sure that the expect100header is always set to the preferred value
here. */
- ptr = Curl_checkheaders(conn, "Expect:");
+ ptr = Curl_checkheaders(conn, "Expect");
if(ptr) {
data->state.expect100header =
Curl_compareheader(ptr, "Expect:", "100-continue");
}
- else if(postsize > TINY_INITIAL_POST_SIZE || postsize < 0) {
+ else if(postsize > EXPECT_100_THRESHOLD || postsize < 0) {
result = expect100(data, conn, req_buffer);
if(result)
return result;
its size. */
if(conn->httpversion != 20 &&
!data->state.expect100header &&
- (postsize < MAX_INITIAL_POST_SIZE)) {
+ (postsize < MAX_INITIAL_POST_SIZE)) {
/* if we don't use expect: 100 AND
postsize is less than MAX_INITIAL_POST_SIZE
data->req.upload_done = TRUE;
data->req.keepon &= ~KEEP_SEND; /* we're done writing */
data->req.exp100 = EXP100_SEND_DATA; /* already sent */
+ Curl_expire_done(data, EXPIRE_100_TIMEOUT);
}
}
failf(data, "Failed to allocate memory for conversion!");
return FALSE; /* can't return CURLE_OUT_OF_MEMORY so return FALSE */
}
- if(CURLE_OK != Curl_convert_from_network(data, scratch, strlen(s)+1)) {
+ if(CURLE_OK != Curl_convert_from_network(data, scratch, strlen(s) + 1)) {
/* Curl_convert_from_network calls failf if unsuccessful */
free(scratch);
return FALSE; /* can't return CURLE_foobar so return FALSE */
checkrtspprefix(struct Curl_easy *data,
const char *s)
{
+ bool result = FALSE;
#ifdef CURL_DOES_CONVERSIONS
/* convert from the network encoding using a scratch area */
failf(data, "Failed to allocate memory for conversion!");
return FALSE; /* can't return CURLE_OUT_OF_MEMORY so return FALSE */
}
- if(CURLE_OK != Curl_convert_from_network(data, scratch, strlen(s)+1)) {
+ if(CURLE_OK != Curl_convert_from_network(data, scratch, strlen(s) + 1)) {
/* Curl_convert_from_network calls failf if unsuccessful */
- free(scratch);
- return FALSE; /* can't return CURLE_foobar so return FALSE */
+ result = FALSE; /* can't return CURLE_foobar so return FALSE */
}
- s = scratch;
+ else
+ result = checkprefix("RTSP/", scratch)? TRUE: FALSE;
+ free(scratch);
#else
(void)data; /* unused */
+ result = checkprefix("RTSP/", s)? TRUE: FALSE;
#endif /* CURL_DOES_CONVERSIONS */
- if(checkprefix("RTSP/", s))
- return TRUE;
- else
- return FALSE;
+
+ return result;
}
#endif /* CURL_DISABLE_RTSP */
struct SingleRequest *k,
size_t length)
{
- if(k->hbuflen + length >= data->state.headersize) {
+ size_t newsize = k->hbuflen + length;
+ if(newsize > CURL_MAX_HTTP_HEADER) {
+ /* The reason to have a max limit for this is to avoid the risk of a bad
+ server feeding libcurl with a never-ending header that will cause
+ reallocs infinitely */
+ failf(data, "Rejected %zd bytes header (max is %d)!", newsize,
+ CURL_MAX_HTTP_HEADER);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ if(newsize >= data->state.headersize) {
/* We enlarge the header buffer as it is too small */
char *newbuff;
size_t hbufp_index;
- size_t newsize;
-
- if(k->hbuflen + length > CURL_MAX_HTTP_HEADER) {
- /* The reason to have a max limit for this is to avoid the risk of a bad
- server feeding libcurl with a never-ending header that will cause
- reallocs infinitely */
- 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);
+ 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.headersize = newsize;
data->state.headerbuff = newbuff;
k->hbufp = data->state.headerbuff + hbufp_index;
}
}
/* decrease the size of the remaining (supposed) header line */
- rest_length = (k->end_ptr - k->str)+1;
+ rest_length = (k->end_ptr - k->str) + 1;
*nread -= (ssize_t)rest_length;
k->str = k->end_ptr + 1; /* move past new line */
if(k->exp100 > EXP100_SEND_DATA) {
k->exp100 = EXP100_SEND_DATA;
k->keepon |= KEEP_SEND;
+ Curl_expire_done(data, EXPIRE_100_TIMEOUT);
}
break;
case 101:
!(conn->handler->protocol & CURLPROTO_RTSP) &&
data->set.httpreq != HTTPREQ_HEAD) {
/* On HTTP 1.1, when connection is not to get closed, but no
- Content-Length nor Content-Encoding chunked have been
+ Content-Length nor Transfer-Encoding chunked have been
received, according to RFC2616 section 4.4 point 5, we
assume that the server will close the connection to
signal the end of the document. */
case HTTPREQ_PUT:
case HTTPREQ_POST:
case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
/* We got an error response. If this happened before the whole
* request body has been sent we stop sending and mark the
* connection for closure after we've read the entire response.
*/
+ Curl_expire_done(data, EXPIRE_100_TIMEOUT);
if(!k->upload_done) {
if(data->set.http_keep_sending_on_error) {
infof(data, "HTTP error before end of send, keep sending\n");
k->maxdownload = k->size;
}
- /* If max download size is *zero* (nothing) we already
- have nothing and can safely return ok now! */
- if(0 == k->maxdownload)
+ /* If max download size is *zero* (nothing) we already have
+ nothing and can safely return ok now! But for HTTP/2, we'd
+ like to call http2_handle_stream_close to properly close a
+ stream. In order to do this, we keep reading until we
+ close the stream. */
+ if(0 == k->maxdownload
+#if defined(USE_NGHTTP2)
+ && !((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
+ conn->httpversion == 20)
+#endif
+ )
*stop_reading = TRUE;
if(*stop_reading) {
#define HEADER1 scratch
#define SCRATCHSIZE 21
CURLcode res;
- char scratch[SCRATCHSIZE+1]; /* "HTTP/major.minor 123" */
+ char scratch[SCRATCHSIZE + 1]; /* "HTTP/major.minor 123" */
/* We can't really convert this yet because we
don't know if it's the 1st header line or the body.
So we do a partial conversion into a scratch area,
/*
* https://tools.ietf.org/html/rfc7230#section-3.1.2
*
- * The reponse code is always a three-digit number in HTTP as the spec
+ * The response code is always a three-digit number in HTTP as the spec
* says. We try to allow any number here, but we cannot make
* guarantees on future behaviors since it isn't within the protocol.
*/
+ char separator;
nc = sscanf(HEADER1,
- " HTTP/%d.%d %d",
+ " HTTP/%1d.%1d%c%3d",
&httpversion_major,
&conn->httpversion,
+ &separator,
&k->httpcode);
if(nc == 1 && httpversion_major == 2 &&
1 == sscanf(HEADER1, " HTTP/2 %d", &k->httpcode)) {
conn->httpversion = 0;
- nc = 3;
+ nc = 4;
+ separator = ' ';
}
- if(nc==3) {
+ if((nc == 4) && (' ' == separator)) {
conn->httpversion += 10 * httpversion_major;
if(k->upgr101 == UPGR101_RECEIVED) {
infof(data, "Lying server, not serving HTTP/2\n");
}
}
- else {
+ else if(!nc) {
/* this is the real world, not a Nirvana
NCSA 1.5.x returns this crap when asked for HTTP/1.1
*/
- nc=sscanf(HEADER1, " HTTP %3d", &k->httpcode);
+ nc = sscanf(HEADER1, " HTTP %3d", &k->httpcode);
conn->httpversion = 10;
/* If user has set option HTTP200ALIASES,
}
}
}
+ else {
+ failf(data, "Unsupported HTTP version in response\n");
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
}
else if(conn->handler->protocol & CURLPROTO_RTSP) {
+ char separator;
nc = sscanf(HEADER1,
- " RTSP/%d.%d %3d",
+ " RTSP/%1d.%1d%c%3d",
&rtspversion_major,
&conn->rtspversion,
+ &separator,
&k->httpcode);
- if(nc==3) {
+ if((nc == 4) && (' ' == separator)) {
conn->rtspversion += 10 * rtspversion_major;
conn->httpversion = 11; /* For us, RTSP acts like HTTP 1.1 */
}
((k->httpcode != 407) || !conn->bits.proxy_user_passwd) ) {
if(data->state.resume_from &&
- (data->set.httpreq==HTTPREQ_GET) &&
+ (data->set.httpreq == HTTPREQ_GET) &&
(k->httpcode == 416)) {
/* "Requested Range Not Satisfiable", just proceed and
pretend this is no error */
* fields. */
if(data->set.timecondition)
data->info.timecond = TRUE;
- k->size=0;
- k->maxdownload=0;
+ k->size = 0;
+ k->maxdownload = 0;
k->ignorecl = TRUE; /* ignore Content-Length headers */
break;
default:
/* Check for Content-Length: header lines to get size */
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) {
+ curl_off_t contentlength;
+ CURLofft offt = curlx_strtoofft(k->p + 15, NULL, 10, &contentlength);
+
+ if(offt == CURL_OFFT_OK) {
+ if(data->set.max_filesize &&
+ contentlength > data->set.max_filesize) {
+ failf(data, "Maximum file size exceeded");
+ return CURLE_FILESIZE_EXCEEDED;
+ }
k->size = contentlength;
k->maxdownload = k->size;
/* we set the progress download size already at this point
info as soon as possible */
Curl_pgrsSetDownloadSize(data, k->size);
}
+ else if(offt == CURL_OFFT_FLOW) {
+ /* out of range */
+ if(data->set.max_filesize) {
+ failf(data, "Maximum file size exceeded");
+ return CURLE_FILESIZE_EXCEEDED;
+ }
+ streamclose(conn, "overflow content-length");
+ infof(data, "Overflow Content-Length: value!\n");
+ }
else {
- /* Negative Content-Length is really odd, and we know it
- happens for example when older Apache servers send large
- files */
- streamclose(conn, "negative content-length");
- infof(data, "Negative content-length: %" CURL_FORMAT_CURL_OFF_T
- ", closing after transfer\n", contentlength);
+ /* negative or just rubbish - bad HTTP */
+ failf(data, "Invalid Content-Length: value");
+ return CURLE_WEIRD_SERVER_REPLY;
}
}
/* check for Content-Type: header lines to get the MIME-type */
* of chunks, and a chunk-data set to zero signals the
* end-of-chunks. */
- char *start;
-
- /* Find the first non-space letter */
- start = k->p + 18;
-
- for(;;) {
- /* skip whitespaces and commas */
- while(*start && (ISSPACE(*start) || (*start == ',')))
- start++;
-
- if(checkprefix("chunked", start)) {
- k->chunk = TRUE; /* chunks coming our way */
-
- /* init our chunky engine */
- Curl_httpchunk_init(conn);
-
- start += 7;
- }
-
- if(k->auto_decoding)
- /* TODO: we only support the first mentioned compression for now */
- break;
-
- if(checkprefix("identity", start)) {
- k->auto_decoding = IDENTITY;
- start += 8;
- }
- else if(checkprefix("deflate", start)) {
- k->auto_decoding = DEFLATE;
- start += 7;
- }
- else if(checkprefix("gzip", start)) {
- k->auto_decoding = GZIP;
- start += 4;
- }
- else if(checkprefix("x-gzip", start)) {
- k->auto_decoding = GZIP;
- start += 6;
- }
- else
- /* unknown! */
- break;
-
- }
-
+ result = Curl_build_unencoding_stack(conn, k->p + 18, TRUE);
+ if(result)
+ return result;
}
else if(checkprefix("Content-Encoding:", k->p) &&
data->set.str[STRING_ENCODING]) {
* 2616). zlib cannot handle compress. However, errors are
* handled further down when the response body is processed
*/
- char *start;
-
- /* Find the first non-space letter */
- start = k->p + 17;
- while(*start && ISSPACE(*start))
- start++;
-
- /* Record the content-encoding for later use */
- if(checkprefix("identity", start))
- k->auto_decoding = IDENTITY;
- else if(checkprefix("deflate", start))
- k->auto_decoding = DEFLATE;
- else if(checkprefix("gzip", start)
- || checkprefix("x-gzip", start))
- k->auto_decoding = GZIP;
+ result = Curl_build_unencoding_stack(conn, k->p + 17, FALSE);
+ if(result)
+ return result;
}
else if(checkprefix("Content-Range:", k->p)) {
/* Content-Range: bytes [num]-
/* if it truly stopped on a digit */
if(ISDIGIT(*ptr)) {
- k->offset = curlx_strtoofft(ptr, NULL, 10);
-
- if(data->state.resume_from == k->offset)
- /* we asked for a resume and we got it */
- k->content_range = TRUE;
+ if(!curlx_strtoofft(ptr, NULL, 10, &k->offset)) {
+ if(data->state.resume_from == k->offset)
+ /* we asked for a resume and we got it */
+ k->content_range = TRUE;
+ }
}
else
data->state.resume_from = 0; /* get everything */
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE,
CURL_LOCK_ACCESS_SINGLE);
Curl_cookie_add(data,
- data->cookies, TRUE, k->p+11,
+ data->cookies, TRUE, k->p + 11,
/* If there is a custom-set Host: name, use it
here, or else use real peer host name. */
conn->allocptr.cookiehost?
#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:"),
+ 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;
+ data->info.filetime = k->timeofdoc;
}
else if((checkprefix("WWW-Authenticate:", k->p) &&
(401 == k->httpcode)) ||