summaryrefslogtreecommitdiff
path: root/Utilities/cmcurl/http.c
diff options
context:
space:
mode:
Diffstat (limited to 'Utilities/cmcurl/http.c')
-rw-r--r--Utilities/cmcurl/http.c2422
1 files changed, 0 insertions, 2422 deletions
diff --git a/Utilities/cmcurl/http.c b/Utilities/cmcurl/http.c
deleted file mode 100644
index 5405aace1..000000000
--- a/Utilities/cmcurl/http.c
+++ /dev/null
@@ -1,2422 +0,0 @@
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2007, 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
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * $Id$
- ***************************************************************************/
-
-#include "setup.h"
-
-#ifndef CURL_DISABLE_HTTP
-/* -- WIN32 approved -- */
-#include <stdio.h>
-#include <string.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <ctype.h>
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-
-#ifdef WIN32
-#include <time.h>
-#include <io.h>
-#else
-#ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-
-#ifdef HAVE_TIME_H
-#ifdef TIME_WITH_SYS_TIME
-#include <time.h>
-#endif
-#endif
-
-#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
-
-#endif
-
-#include "urldata.h"
-#include <curl/curl.h>
-#include "transfer.h"
-#include "sendf.h"
-#include "easyif.h" /* for Curl_convert_... prototypes */
-#include "formdata.h"
-#include "progress.h"
-#include "base64.h"
-#include "cookie.h"
-#include "strequal.h"
-#include "sslgen.h"
-#include "http_digest.h"
-#include "http_ntlm.h"
-#include "http_negotiate.h"
-#include "url.h"
-#include "share.h"
-#include "hostip.h"
-#include "http.h"
-#include "memory.h"
-#include "select.h"
-#include "parsedate.h" /* for the week day and month names */
-#include "strtoofft.h"
-#include "multiif.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-/* The last #include file should be: */
-#include "memdebug.h"
-
-/*
- * 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.
- */
-static char *checkheaders(struct SessionHandle *data, const char *thisheader)
-{
- struct curl_slist *head;
- size_t thislen = strlen(thisheader);
-
- for(head = data->set.headers; head; head=head->next) {
- if(strnequal(head->data, thisheader, thislen))
- return head->data;
- }
- return NULL;
-}
-
-/*
- * Curl_output_basic() sets up an Authorization: header (or the proxy version)
- * for HTTP Basic authentication.
- *
- * Returns CURLcode.
- */
-static CURLcode Curl_output_basic(struct connectdata *conn, bool proxy)
-{
- char *authorization;
- struct SessionHandle *data=conn->data;
- char **userp;
- char *user;
- char *pwd;
-
- if(proxy) {
- userp = &conn->allocptr.proxyuserpwd;
- user = conn->proxyuser;
- pwd = conn->proxypasswd;
- }
- else {
- userp = &conn->allocptr.userpwd;
- user = conn->user;
- pwd = conn->passwd;
- }
-
- snprintf(data->state.buffer, sizeof(data->state.buffer), "%s:%s", user, pwd);
- if(Curl_base64_encode(data, data->state.buffer,
- strlen(data->state.buffer),
- &authorization) > 0) {
- if(*userp)
- free(*userp);
- *userp = aprintf( "%sAuthorization: Basic %s\r\n",
- proxy?"Proxy-":"",
- authorization);
- free(authorization);
- }
- else
- return CURLE_OUT_OF_MEMORY;
- return CURLE_OK;
-}
-
-/* pickoneauth() selects the most favourable authentication method from the
- * ones available and the ones we want.
- *
- * return TRUE if one was picked
- */
-static bool pickoneauth(struct auth *pick)
-{
- bool picked;
- /* only deal with authentication we want */
- long avail = pick->avail & pick->want;
- picked = TRUE;
-
- /* The order of these checks is highly relevant, as this will be the order
- of preference in case of the existence of multiple accepted types. */
- if(avail & CURLAUTH_GSSNEGOTIATE)
- pick->picked = CURLAUTH_GSSNEGOTIATE;
- else if(avail & CURLAUTH_DIGEST)
- pick->picked = CURLAUTH_DIGEST;
- else if(avail & CURLAUTH_NTLM)
- pick->picked = CURLAUTH_NTLM;
- else if(avail & CURLAUTH_BASIC)
- pick->picked = CURLAUTH_BASIC;
- else {
- pick->picked = CURLAUTH_PICKNONE; /* we select to use nothing */
- picked = FALSE;
- }
- pick->avail = CURLAUTH_NONE; /* clear it here */
-
- return picked;
-}
-
-/*
- * perhapsrewind()
- *
- * If we are doing POST or PUT {
- * If we have more data to send {
- * If we are doing NTLM {
- * Keep sending since we must not disconnect
- * }
- * else {
- * If there is more than just a little data left to send, close
- * the current connection by force.
- * }
- * }
- * If we have sent any data {
- * If we don't have track of all the data {
- * call app to tell it to rewind
- * }
- * else {
- * rewind internally so that the operation can restart fine
- * }
- * }
- * }
- */
-static CURLcode perhapsrewind(struct connectdata *conn)
-{
- struct SessionHandle *data = conn->data;
- struct HTTP *http = data->reqdata.proto.http;
- struct Curl_transfer_keeper *k = &data->reqdata.keep;
- curl_off_t bytessent;
- curl_off_t expectsend = -1; /* default is unknown */
-
- if(!http)
- /* If this is still NULL, we have not reach very far and we can
- safely skip this rewinding stuff */
- return CURLE_OK;
-
- bytessent = http->writebytecount;
-
- if(conn->bits.authneg)
- /* This is a state where we are known to be negotiating and we don't send
- any data then. */
- expectsend = 0;
- else {
- /* figure out how much data we are expected to send */
- switch(data->set.httpreq) {
- case HTTPREQ_POST:
- if(data->set.postfieldsize != -1)
- expectsend = data->set.postfieldsize;
- break;
- case HTTPREQ_PUT:
- if(data->set.infilesize != -1)
- expectsend = data->set.infilesize;
- break;
- case HTTPREQ_POST_FORM:
- expectsend = http->postsize;
- break;
- default:
- break;
- }
- }
-
- conn->bits.rewindaftersend = FALSE; /* default */
-
- if((expectsend == -1) || (expectsend > bytessent)) {
- /* There is still data left to send */
- if((data->state.authproxy.picked == CURLAUTH_NTLM) ||
- (data->state.authhost.picked == CURLAUTH_NTLM)) {
- if(((expectsend - bytessent) < 2000) ||
- (conn->ntlm.state != NTLMSTATE_NONE)) {
- /* The NTLM-negotiation has started *OR* there is just a little (<2K)
- data left to send, keep on sending. */
-
- /* rewind data when completely done sending! */
- if(!conn->bits.authneg)
- conn->bits.rewindaftersend = TRUE;
-
- return CURLE_OK;
- }
- if(conn->bits.close)
- /* this is already marked to get closed */
- return CURLE_OK;
-
- infof(data, "NTLM send, close instead of sending %" FORMAT_OFF_T
- " bytes\n", (curl_off_t)(expectsend - bytessent));
- }
-
- /* This is not NTLM or NTLM with many bytes left to send: close
- */
- conn->bits.close = TRUE;
- k->size = 0; /* don't download any more than 0 bytes */
- }
-
- if(bytessent)
- return Curl_readrewind(conn);
-
- return CURLE_OK;
-}
-
-/*
- * Curl_http_auth_act() gets called when a all HTTP headers have been received
- * and it checks what authentication methods that are available and decides
- * which one (if any) to use. It will set 'newurl' if an auth metod was
- * picked.
- */
-
-CURLcode Curl_http_auth_act(struct connectdata *conn)
-{
- struct SessionHandle *data = conn->data;
- bool pickhost = FALSE;
- bool pickproxy = FALSE;
- CURLcode code = CURLE_OK;
-
- if(100 == data->reqdata.keep.httpcode)
- /* this is a transient response code, ignore */
- return CURLE_OK;
-
- if(data->state.authproblem)
- return data->set.http_fail_on_error?CURLE_HTTP_RETURNED_ERROR:CURLE_OK;
-
- if(conn->bits.user_passwd &&
- ((data->reqdata.keep.httpcode == 401) ||
- (conn->bits.authneg && data->reqdata.keep.httpcode < 300))) {
- pickhost = pickoneauth(&data->state.authhost);
- if(!pickhost)
- data->state.authproblem = TRUE;
- }
- if(conn->bits.proxy_user_passwd &&
- ((data->reqdata.keep.httpcode == 407) ||
- (conn->bits.authneg && data->reqdata.keep.httpcode < 300))) {
- pickproxy = pickoneauth(&data->state.authproxy);
- if(!pickproxy)
- data->state.authproblem = TRUE;
- }
-
- if(pickhost || pickproxy) {
- data->reqdata.newurl = strdup(data->change.url); /* clone URL */
-
- if((data->set.httpreq != HTTPREQ_GET) &&
- (data->set.httpreq != HTTPREQ_HEAD) &&
- !conn->bits.rewindaftersend) {
- code = perhapsrewind(conn);
- if(code)
- return code;
- }
- }
-
- else if((data->reqdata.keep.httpcode < 300) &&
- (!data->state.authhost.done) &&
- conn->bits.authneg) {
- /* no (known) authentication available,
- authentication is not "done" yet and
- no authentication seems to be required and
- we didn't try HEAD or GET */
- if((data->set.httpreq != HTTPREQ_GET) &&
- (data->set.httpreq != HTTPREQ_HEAD)) {
- data->reqdata.newurl = strdup(data->change.url); /* clone URL */
- data->state.authhost.done = TRUE;
- }
- }
- if (Curl_http_should_fail(conn)) {
- failf (data, "The requested URL returned error: %d",
- data->reqdata.keep.httpcode);
- code = CURLE_HTTP_RETURNED_ERROR;
- }
-
- return code;
-}
-
-/**
- * Curl_http_output_auth() setups the authentication headers for the
- * host/proxy and the correct authentication
- * method. conn->data->state.authdone is set to TRUE when authentication is
- * done.
- *
- * @param conn all information about the current connection
- * @param request pointer to the request keyword
- * @param path pointer to the requested path
- * @param proxytunnel boolean if this is the request setting up a "proxy
- * tunnel"
- *
- * @returns CURLcode
- */
-static CURLcode
-Curl_http_output_auth(struct connectdata *conn,
- char *request,
- char *path,
- bool proxytunnel) /* TRUE if this is the request setting
- up the proxy tunnel */
-{
- CURLcode result = CURLE_OK;
- struct SessionHandle *data = conn->data;
- char *auth=NULL;
- struct auth *authhost;
- struct auth *authproxy;
-
- curlassert(data);
-
- authhost = &data->state.authhost;
- authproxy = &data->state.authproxy;
-
- if((conn->bits.httpproxy && conn->bits.proxy_user_passwd) ||
- conn->bits.user_passwd)
- /* continue please */ ;
- else {
- authhost->done = TRUE;
- authproxy->done = TRUE;
- return CURLE_OK; /* no authentication with no user or password */
- }
-
- if(authhost->want && !authhost->picked)
- /* The app has selected one or more methods, but none has been picked
- so far by a server round-trip. Then we set the picked one to the
- want one, and if this is one single bit it'll be used instantly. */
- authhost->picked = authhost->want;
-
- if(authproxy->want && !authproxy->picked)
- /* The app has selected one or more methods, but none has been picked so
- far by a proxy round-trip. Then we set the picked one to the want one,
- and if this is one single bit it'll be used instantly. */
- authproxy->picked = authproxy->want;
-
- /* Send proxy authentication header if needed */
- if (conn->bits.httpproxy &&
- (conn->bits.tunnel_proxy == proxytunnel)) {
-#ifdef USE_NTLM
- if(authproxy->picked == CURLAUTH_NTLM) {
- auth=(char *)"NTLM";
- result = Curl_output_ntlm(conn, TRUE);
- if(result)
- return result;
- }
- else
-#endif
- if(authproxy->picked == CURLAUTH_BASIC) {
- /* Basic */
- if(conn->bits.proxy_user_passwd &&
- !checkheaders(data, "Proxy-authorization:")) {
- auth=(char *)"Basic";
- result = Curl_output_basic(conn, TRUE);
- if(result)
- return result;
- }
- /* NOTE: Curl_output_basic() should set 'done' TRUE, as the other auth
- functions work that way */
- authproxy->done = TRUE;
- }
-#ifndef CURL_DISABLE_CRYPTO_AUTH
- else if(authproxy->picked == CURLAUTH_DIGEST) {
- auth=(char *)"Digest";
- result = Curl_output_digest(conn,
- TRUE, /* proxy */
- (unsigned char *)request,
- (unsigned char *)path);
- if(result)
- return result;
- }
-#endif
- if(auth) {
- infof(data, "Proxy auth using %s with user '%s'\n",
- auth, conn->proxyuser?conn->proxyuser:"");
- authproxy->multi = (bool)(!authproxy->done);
- }
- else
- authproxy->multi = FALSE;
- }
- else
- /* we have no proxy so let's pretend we're done authenticating
- with it */
- authproxy->done = TRUE;
-
- /* To prevent the user+password to get sent to other than the original
- host due to a location-follow, we do some weirdo checks here */
- if(!data->state.this_is_a_follow ||
- conn->bits.netrc ||
- !data->state.first_host ||
- curl_strequal(data->state.first_host, conn->host.name) ||
- data->set.http_disable_hostname_check_before_authentication) {
-
- /* Send web authentication header if needed */
- {
- auth = NULL;
-#ifdef HAVE_GSSAPI
- if((authhost->picked == CURLAUTH_GSSNEGOTIATE) &&
- data->state.negotiate.context &&
- !GSS_ERROR(data->state.negotiate.status)) {
- auth=(char *)"GSS-Negotiate";
- result = Curl_output_negotiate(conn);
- if (result)
- return result;
- authhost->done = TRUE;
- }
- else
-#endif
-#ifdef USE_NTLM
- if(authhost->picked == CURLAUTH_NTLM) {
- auth=(char *)"NTLM";
- result = Curl_output_ntlm(conn, FALSE);
- if(result)
- return result;
- }
- else
-#endif
- {
-#ifndef CURL_DISABLE_CRYPTO_AUTH
- if(authhost->picked == CURLAUTH_DIGEST) {
- auth=(char *)"Digest";
- result = Curl_output_digest(conn,
- FALSE, /* not a proxy */
- (unsigned char *)request,
- (unsigned char *)path);
- if(result)
- return result;
- } else
-#endif
- if(authhost->picked == CURLAUTH_BASIC) {
- if(conn->bits.user_passwd &&
- !checkheaders(data, "Authorization:")) {
- auth=(char *)"Basic";
- result = Curl_output_basic(conn, FALSE);
- if(result)
- return result;
- }
- /* basic is always ready */
- authhost->done = TRUE;
- }
- }
- if(auth) {
- infof(data, "Server auth using %s with user '%s'\n",
- auth, conn->user);
-
- authhost->multi = (bool)(!authhost->done);
- }
- else
- authhost->multi = FALSE;
- }
- }
- else
- authhost->done = TRUE;
-
- return result;
-}
-
-
-/*
- * Curl_http_input_auth() deals with Proxy-Authenticate: and WWW-Authenticate:
- * headers. They are dealt with both in the transfer.c main loop and in the
- * proxy CONNECT loop.
- */
-
-CURLcode Curl_http_input_auth(struct connectdata *conn,
- int httpcode,
- char *header) /* the first non-space */
-{
- /*
- * This resource requires authentication
- */
- struct SessionHandle *data = conn->data;
-
- long *availp;
- char *start;
- struct auth *authp;
-
- if (httpcode == 407) {
- start = header+strlen("Proxy-authenticate:");
- availp = &data->info.proxyauthavail;
- authp = &data->state.authproxy;
- }
- else {
- start = header+strlen("WWW-Authenticate:");
- availp = &data->info.httpauthavail;
- authp = &data->state.authhost;
- }
-
- /* pass all white spaces */
- while(*start && ISSPACE(*start))
- start++;
-
- /*
- * Here we check if we want the specific single authentication (using ==) and
- * if we do, we initiate usage of it.
- *
- * If the provided authentication is wanted as one out of several accepted
- * types (using &), we OR this authentication type to the authavail
- * variable.
- */
-
-#ifdef HAVE_GSSAPI
- if (checkprefix("GSS-Negotiate", start) ||
- checkprefix("Negotiate", start)) {
- *availp |= CURLAUTH_GSSNEGOTIATE;
- authp->avail |= CURLAUTH_GSSNEGOTIATE;
- if(authp->picked == CURLAUTH_GSSNEGOTIATE) {
- /* if exactly this is wanted, go */
- int neg = Curl_input_negotiate(conn, start);
- if (neg == 0) {
- data->reqdata.newurl = strdup(data->change.url);
- data->state.authproblem = (data->reqdata.newurl == NULL);
- }
- else {
- infof(data, "Authentication problem. Ignoring this.\n");
- data->state.authproblem = TRUE;
- }
- }
- }
- else
-#endif
-#ifdef USE_NTLM
- /* NTLM support requires the SSL crypto libs */
- if(checkprefix("NTLM", start)) {
- *availp |= CURLAUTH_NTLM;
- authp->avail |= CURLAUTH_NTLM;
- if(authp->picked == CURLAUTH_NTLM) {
- /* NTLM authentication is picked and activated */
- CURLntlm ntlm =
- Curl_input_ntlm(conn, (bool)(httpcode == 407), start);
-
- if(CURLNTLM_BAD != ntlm)
- data->state.authproblem = FALSE;
- else {
- infof(data, "Authentication problem. Ignoring this.\n");
- data->state.authproblem = TRUE;
- }
- }
- }
- else
-#endif
-#ifndef CURL_DISABLE_CRYPTO_AUTH
- if(checkprefix("Digest", start)) {
- if((authp->avail & CURLAUTH_DIGEST) != 0) {
- infof(data, "Ignoring duplicate digest auth header.\n");
- }
- else {
- CURLdigest dig;
- *availp |= CURLAUTH_DIGEST;
- authp->avail |= CURLAUTH_DIGEST;
-
- /* We call this function on input Digest headers even if Digest
- * authentication isn't activated yet, as we need to store the
- * incoming data from this header in case we are gonna use Digest. */
- dig = Curl_input_digest(conn, (bool)(httpcode == 407), start);
-
- if(CURLDIGEST_FINE != dig) {
- infof(data, "Authentication problem. Ignoring this.\n");
- data->state.authproblem = TRUE;
- }
- }
- }
- else
-#endif
- if(checkprefix("Basic", start)) {
- *availp |= CURLAUTH_BASIC;
- authp->avail |= CURLAUTH_BASIC;
- if(authp->picked == CURLAUTH_BASIC) {
- /* We asked for Basic authentication but got a 40X back
- anyway, which basicly means our name+password isn't
- valid. */
- authp->avail = CURLAUTH_NONE;
- infof(data, "Authentication problem. Ignoring this.\n");
- data->state.authproblem = TRUE;
- }
- }
-
- return CURLE_OK;
-}
-
-/**
- * Curl_http_should_fail() determines whether an HTTP response has gotten us
- * into an error state or not.
- *
- * @param conn all information about the current connection
- *
- * @retval 0 communications should continue
- *
- * @retval 1 communications should not continue
- */
-int Curl_http_should_fail(struct connectdata *conn)
-{
- struct SessionHandle *data;
- struct Curl_transfer_keeper *k;
-
- curlassert(conn);
- data = conn->data;
- curlassert(data);
-
- /*
- ** For readability
- */
- k = &data->reqdata.keep;
-
- /*
- ** If we haven't been asked to fail on error,
- ** don't fail.
- */
- if (!data->set.http_fail_on_error)
- return 0;
-
- /*
- ** Any code < 400 is never terminal.
- */
- if (k->httpcode < 400)
- return 0;
-
- if (data->reqdata.resume_from &&
- (data->set.httpreq==HTTPREQ_GET) &&
- (k->httpcode == 416)) {
- /* "Requested Range Not Satisfiable", just proceed and
- pretend this is no error */
- return 0;
- }
-
- /*
- ** Any code >= 400 that's not 401 or 407 is always
- ** a terminal error
- */
- if ((k->httpcode != 401) &&
- (k->httpcode != 407))
- return 1;
-
- /*
- ** All we have left to deal with is 401 and 407
- */
- curlassert((k->httpcode == 401) || (k->httpcode == 407));
-
- /*
- ** Examine the current authentication state to see if this
- ** is an error. The idea is for this function to get
- ** called after processing all the headers in a response
- ** message. So, if we've been to asked to authenticate a
- ** particular stage, and we've done it, we're OK. But, if
- ** we're already completely authenticated, it's not OK to
- ** get another 401 or 407.
- **
- ** It is possible for authentication to go stale such that
- ** the client needs to reauthenticate. Once that info is
- ** available, use it here.
- */
-#if 0 /* set to 1 when debugging this functionality */
- infof(data,"%s: authstage = %d\n",__FUNCTION__,data->state.authstage);
- infof(data,"%s: authwant = 0x%08x\n",__FUNCTION__,data->state.authwant);
- infof(data,"%s: authavail = 0x%08x\n",__FUNCTION__,data->state.authavail);
- infof(data,"%s: httpcode = %d\n",__FUNCTION__,k->httpcode);
- infof(data,"%s: authdone = %d\n",__FUNCTION__,data->state.authdone);
- infof(data,"%s: newurl = %s\n",__FUNCTION__,data->reqdata.newurl ? data->reqdata.newurl : "(null)");
- infof(data,"%s: authproblem = %d\n",__FUNCTION__,data->state.authproblem);
-#endif
-
- /*
- ** Either we're not authenticating, or we're supposed to
- ** be authenticating something else. This is an error.
- */
- if((k->httpcode == 401) && !conn->bits.user_passwd)
- return TRUE;
- if((k->httpcode == 407) && !conn->bits.proxy_user_passwd)
- return TRUE;
-
- return data->state.authproblem;
-}
-
-/*
- * readmoredata() is a "fread() emulation" to provide POST and/or request
- * data. It is used when a huge POST is to be made and the entire chunk wasn't
- * sent in the first send(). This function will then be called from the
- * transfer.c loop when more data is to be sent to the peer.
- *
- * Returns the amount of bytes it filled the buffer with.
- */
-static size_t readmoredata(char *buffer,
- size_t size,
- size_t nitems,
- void *userp)
-{
- struct connectdata *conn = (struct connectdata *)userp;
- struct HTTP *http = conn->data->reqdata.proto.http;
- size_t fullsize = size * nitems;
-
- if(0 == http->postsize)
- /* nothing to return */
- return 0;
-
- /* make sure that a HTTP request is never sent away chunked! */
- conn->bits.forbidchunk = (bool)(http->sending == HTTPSEND_REQUEST);
-
- if(http->postsize <= (curl_off_t)fullsize) {
- memcpy(buffer, http->postdata, (size_t)http->postsize);
- fullsize = (size_t)http->postsize;
-
- if(http->backup.postsize) {
- /* move backup data into focus and continue on that */
- http->postdata = http->backup.postdata;
- http->postsize = http->backup.postsize;
- conn->fread = http->backup.fread;
- conn->fread_in = http->backup.fread_in;
-
- http->sending++; /* move one step up */
-
- http->backup.postsize=0;
- }
- else
- http->postsize = 0;
-
- return fullsize;
- }
-
- memcpy(buffer, http->postdata, fullsize);
- http->postdata += fullsize;
- http->postsize -= fullsize;
-
- return fullsize;
-}
-
-/* ------------------------------------------------------------------------- */
-/*
- * The add_buffer series of functions are used to build one large memory chunk
- * from repeated function invokes. Used so that the entire HTTP request can
- * be sent in one go.
- */
-
-struct send_buffer {
- char *buffer;
- size_t size_max;
- size_t size_used;
-};
-typedef struct send_buffer send_buffer;
-
-static CURLcode add_custom_headers(struct connectdata *conn,
- send_buffer *req_buffer);
-static CURLcode
- add_buffer(send_buffer *in, const void *inptr, size_t size);
-
-/*
- * add_buffer_init() sets up and returns a fine buffer struct
- */
-static
-send_buffer *add_buffer_init(void)
-{
- send_buffer *blonk;
- blonk=(send_buffer *)malloc(sizeof(send_buffer));
- if(blonk) {
- memset(blonk, 0, sizeof(send_buffer));
- return blonk;
- }
- return NULL; /* failed, go home */
-}
-
-/*
- * add_buffer_send() sends a header buffer and frees all associated memory.
- * Body data may be appended to the header data if desired.
- *
- * Returns CURLcode
- */
-static
-CURLcode add_buffer_send(send_buffer *in,
- struct connectdata *conn,
- long *bytes_written, /* add the number of sent
- bytes to this counter */
- size_t included_body_bytes, /* how much of the buffer
- contains body data (for log tracing) */
- int socketindex)
-
-{
- ssize_t amount;
- CURLcode res;
- char *ptr;
- size_t size;
- struct HTTP *http = conn->data->reqdata.proto.http;
- size_t sendsize;
- curl_socket_t sockfd;
-
- curlassert(socketindex <= SECONDARYSOCKET);
-
- sockfd = conn->sock[socketindex];
-
- /* The looping below is required since we use non-blocking sockets, but due
- to the circumstances we will just loop and try again and again etc */
-
- ptr = in->buffer;
- size = in->size_used;
-
-#ifdef CURL_DOES_CONVERSIONS
- if(size - included_body_bytes > 0) {
- res = Curl_convert_to_network(conn->data, ptr, size - included_body_bytes);
- /* Curl_convert_to_network calls failf if unsuccessful */
- if(res != CURLE_OK) {
- /* conversion failed, free memory and return to the caller */
- if(in->buffer)
- free(in->buffer);
- free(in);
- return res;
- }
- }
-#endif /* CURL_DOES_CONVERSIONS */
-
- if(conn->protocol & PROT_HTTPS) {
- /* We never send more than CURL_MAX_WRITE_SIZE bytes in one single chunk
- when we speak HTTPS, as if only a fraction of it is sent now, this data
- needs to fit into the normal read-callback buffer later on and that
- buffer is using this size.
- */
-
- sendsize= (size > CURL_MAX_WRITE_SIZE)?CURL_MAX_WRITE_SIZE: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
- is not enough, we must use the exact same address. For this reason, we
- must copy the data to the uploadbuffer first, since that is the buffer
- we will be using if this send is retried later.
- */
- memcpy(conn->data->state.uploadbuffer, ptr, sendsize);
- ptr = conn->data->state.uploadbuffer;
- }
- else
- sendsize = size;
-
- res = Curl_write(conn, sockfd, ptr, sendsize, &amount);
-
- if(CURLE_OK == res) {
-
- if(conn->data->set.verbose) {
- /* this data _may_ contain binary stuff */
- Curl_debug(conn->data, CURLINFO_HEADER_OUT, ptr,
- (size_t)(amount-included_body_bytes), conn);
- if (included_body_bytes)
- Curl_debug(conn->data, CURLINFO_DATA_OUT,
- ptr+amount-included_body_bytes,
- (size_t)included_body_bytes, conn);
- }
-
- *bytes_written += amount;
-
- if(http) {
- if((size_t)amount != size) {
- /* The whole request could not be sent in one system call. We must
- queue it up and send it later when we get the chance. We must not
- loop here and wait until it might work again. */
-
- size -= amount;
-
- ptr = in->buffer + amount;
-
- /* backup the currently set pointers */
- http->backup.fread = conn->fread;
- http->backup.fread_in = conn->fread_in;
- http->backup.postdata = http->postdata;
- http->backup.postsize = http->postsize;
-
- /* set the new pointers for the request-sending */
- conn->fread = (curl_read_callback)readmoredata;
- conn->fread_in = (void *)conn;
- http->postdata = ptr;
- http->postsize = (curl_off_t)size;
-
- http->send_buffer = in;
- http->sending = HTTPSEND_REQUEST;
-
- return CURLE_OK;
- }
- http->sending = HTTPSEND_BODY;
- /* the full buffer was sent, clean up and return */
- }
- else {
- if((size_t)amount != size)
- /* We have no continue-send mechanism now, fail. This can only happen
- when this function is used from the CONNECT sending function. We
- currently (stupidly) assume that the whole request is always sent
- away in the first single chunk.
-
- This needs FIXing.
- */
- return CURLE_SEND_ERROR;
- else
- conn->writechannel_inuse = FALSE;
- }
- }
- if(in->buffer)
- free(in->buffer);
- free(in);
-
- return res;
-}
-
-
-/*
- * add_bufferf() add the formatted input to the buffer.
- */
-static
-CURLcode add_bufferf(send_buffer *in, const char *fmt, ...)
-{
- char *s;
- va_list ap;
- va_start(ap, fmt);
- s = vaprintf(fmt, ap); /* this allocs a new string to append */
- va_end(ap);
-
- if(s) {
- CURLcode result = add_buffer(in, s, strlen(s));
- free(s);
- if(CURLE_OK == result)
- return CURLE_OK;
- }
- /* If we failed, we cleanup the whole buffer and return error */
- if(in->buffer)
- free(in->buffer);
- free(in);
- return CURLE_OUT_OF_MEMORY;
-}
-
-/*
- * add_buffer() appends a memory chunk to the existing buffer
- */
-static
-CURLcode add_buffer(send_buffer *in, const void *inptr, size_t size)
-{
- char *new_rb;
- size_t new_size;
-
- if(!in->buffer ||
- ((in->size_used + size) > (in->size_max - 1))) {
- new_size = (in->size_used+size)*2;
- if(in->buffer)
- /* we have a buffer, enlarge the existing one */
- new_rb = (char *)realloc(in->buffer, new_size);
- else
- /* create a new buffer */
- new_rb = (char *)malloc(new_size);
-
- if(!new_rb)
- return CURLE_OUT_OF_MEMORY;
-
- in->buffer = new_rb;
- in->size_max = new_size;
- }
- memcpy(&in->buffer[in->size_used], inptr, size);
-
- in->size_used += size;
-
- return CURLE_OK;
-}
-
-/* end of the add_buffer functions */
-/* ------------------------------------------------------------------------- */
-
-/*
- * Curl_compareheader()
- *
- * Returns TRUE if 'headerline' contains the 'header' with given 'content'.
- * Pass headers WITH the colon.
- */
-bool
-Curl_compareheader(char *headerline, /* line to check */
- const char *header, /* header keyword _with_ colon */
- const char *content) /* content string to find */
-{
- /* RFC2616, section 4.2 says: "Each header field consists of a name followed
- * by a colon (":") and the field value. Field names are case-insensitive.
- * The field value MAY be preceded by any amount of LWS, though a single SP
- * is preferred." */
-
- size_t hlen = strlen(header);
- size_t clen;
- size_t len;
- char *start;
- char *end;
-
- if(!strnequal(headerline, header, hlen))
- return FALSE; /* doesn't start with header */
-
- /* pass the header */
- start = &headerline[hlen];
-
- /* pass all white spaces */
- while(*start && ISSPACE(*start))
- start++;
-
- /* find the end of the header line */
- end = strchr(start, '\r'); /* lines end with CRLF */
- if(!end) {
- /* in case there's a non-standard compliant line here */
- end = strchr(start, '\n');
-
- if(!end)
- /* hm, there's no line ending here, use the zero byte! */
- end = strchr(start, '\0');
- }
-
- len = end-start; /* length of the content part of the input line */
- clen = strlen(content); /* length of the word to find */
-
- /* find the content string in the rest of the line */
- for(;len>=clen;len--, start++) {
- if(strnequal(start, content, clen))
- return TRUE; /* match! */
- }
-
- return FALSE; /* no match */
-}
-
-/*
- * Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This
- * function will issue the necessary commands to get a seamless tunnel through
- * this proxy. After that, the socket can be used just as a normal socket.
- *
- * This badly needs to be rewritten. CONNECT should be sent and dealt with
- * like any ordinary HTTP request, and not specially crafted like this. This
- * function only remains here like this for now since the rewrite is a bit too
- * much work to do at the moment.
- *
- * This function is BLOCKING which is nasty for all multi interface using apps.
- */
-
-CURLcode Curl_proxyCONNECT(struct connectdata *conn,
- int sockindex,
- char *hostname,
- int remote_port)
-{
- int subversion=0;
- struct SessionHandle *data=conn->data;
- struct Curl_transfer_keeper *k = &data->reqdata.keep;
- CURLcode result;
- int res;
- size_t nread; /* total size read */
- int perline; /* count bytes per line */
- int keepon=TRUE;
- ssize_t gotbytes;
- char *ptr;
- long timeout =
- data->set.timeout?data->set.timeout:3600; /* in seconds */
- char *line_start;
- char *host_port;
- curl_socket_t tunnelsocket = conn->sock[sockindex];
- send_buffer *req_buffer;
- curl_off_t cl=0;
- bool closeConnection = FALSE;
-
-#define SELECT_OK 0
-#define SELECT_ERROR 1
-#define SELECT_TIMEOUT 2
- int error = SELECT_OK;
-
- infof(data, "Establish HTTP proxy tunnel to %s:%d\n", hostname, remote_port);
- conn->bits.proxy_connect_closed = FALSE;
-
- do {
- if(data->reqdata.newurl) {
- /* This only happens if we've looped here due to authentication reasons,
- and we don't really use the newly cloned URL here then. Just free()
- it. */
- free(data->reqdata.newurl);
- data->reqdata.newurl = NULL;
- }
-
- /* initialize a dynamic send-buffer */
- req_buffer = add_buffer_init();
-
- if(!req_buffer)
- return CURLE_OUT_OF_MEMORY;
-
- host_port = aprintf("%s:%d", hostname, remote_port);
- if(!host_port)
- return CURLE_OUT_OF_MEMORY;
-
- /* Setup the proxy-authorization header, if any */
- result = Curl_http_output_auth(conn, (char *)"CONNECT", host_port, TRUE);
-
- if(CURLE_OK == result) {
- char *host=(char *)"";
- const char *proxyconn="";
- const char *useragent="";
-
- if(!checkheaders(data, "Host:")) {
- host = aprintf("Host: %s\r\n", host_port);
- if(!host)
- result = CURLE_OUT_OF_MEMORY;
- }
- if(!checkheaders(data, "Proxy-Connection:"))
- proxyconn = "Proxy-Connection: Keep-Alive\r\n";
-
- if(!checkheaders(data, "User-Agent:") && data->set.useragent)
- useragent = conn->allocptr.uagent;
-
- if(CURLE_OK == result) {
- /* Send the connect request to the proxy */
- /* BLOCKING */
- result =
- add_bufferf(req_buffer,
- "CONNECT %s:%d HTTP/1.0\r\n"
- "%s" /* Host: */
- "%s" /* Proxy-Authorization */
- "%s" /* User-Agent */
- "%s", /* Proxy-Connection */
- hostname, remote_port,
- host,
- conn->allocptr.proxyuserpwd?
- conn->allocptr.proxyuserpwd:"",
- useragent,
- proxyconn);
-
- if(CURLE_OK == result)
- result = add_custom_headers(conn, req_buffer);
-
- if(host && *host)
- free(host);
-
- if(CURLE_OK == result)
- /* CRLF terminate the request */
- result = add_bufferf(req_buffer, "\r\n");
-
- if(CURLE_OK == result)
- /* Now send off the request */
- result = add_buffer_send(req_buffer, conn,
- &data->info.request_size, 0, sockindex);
- }
- if(result)
- failf(data, "Failed sending CONNECT to proxy");
- }
- free(host_port);
- if(result)
- return result;
-
- ptr=data->state.buffer;
- line_start = ptr;
-
- nread=0;
- perline=0;
- keepon=TRUE;
-
- while((nread<BUFSIZE) && (keepon && !error)) {
-
- /* if timeout is requested, find out how much remaining time we have */
- long check = timeout - /* timeout time */
- Curl_tvdiff(Curl_tvnow(), conn->now)/1000; /* spent time */
- if(check <=0 ) {
- failf(data, "Proxy CONNECT aborted due to timeout");
- error = SELECT_TIMEOUT; /* already too little time */
- break;
- }
-
- /* timeout each second and check the timeout */
- switch (Curl_select(tunnelsocket, CURL_SOCKET_BAD, 1000)) {
- case -1: /* select() error, stop reading */
- error = SELECT_ERROR;
- failf(data, "Proxy CONNECT aborted due to select() error");
- break;
- case 0: /* timeout */
- break;
- default:
- res = Curl_read(conn, tunnelsocket, ptr, BUFSIZE-nread, &gotbytes);
- if(res< 0)
- /* EWOULDBLOCK */
- continue; /* go loop yourself */
- else if(res)
- keepon = FALSE;
- else if(gotbytes <= 0) {
- keepon = FALSE;
- error = SELECT_ERROR;
- failf(data, "Proxy CONNECT aborted");
- }
- else {
- /*
- * We got a whole chunk of data, which can be anything from one byte
- * to a set of lines and possibly just a piece of the last line.
- */
- int i;
-
- nread += gotbytes;
-
- if(keepon > TRUE) {
- /* This means we are currently ignoring a response-body, so we
- simply count down our counter and make sure to break out of the
- loop when we're done! */
- cl -= gotbytes;
- if(cl<=0) {
- keepon = FALSE;
- break;
- }
- }
- else
- for(i = 0; i < gotbytes; ptr++, i++) {
- perline++; /* amount of bytes in this line so far */
- if(*ptr=='\n') {
- char letter;
- int writetype;
-
- /* output debug if that is requested */
- if(data->set.verbose)
- Curl_debug(data, CURLINFO_HEADER_IN,
- line_start, (size_t)perline, conn);
-
- /* send the header to the callback */
- writetype = CLIENTWRITE_HEADER;
- if(data->set.include_header)
- writetype |= CLIENTWRITE_BODY;
-
- result = Curl_client_write(conn, writetype, line_start, perline);
- if(result)
- return result;
-
- /* Newlines are CRLF, so the CR is ignored as the line isn't
- really terminated until the LF comes. Treat a following CR
- as end-of-headers as well.*/
-
- if(('\r' == line_start[0]) ||
- ('\n' == line_start[0])) {
- /* end of response-headers from the proxy */
- if(cl && (407 == k->httpcode) && !data->state.authproblem) {
- /* If we get a 407 response code with content length when we
- * have no auth problem, we must ignore the whole
- * response-body */
- keepon = 2;
- infof(data, "Ignore %" FORMAT_OFF_T
- " bytes of response-body\n", cl);
- cl -= (gotbytes - i);/* remove the remaining chunk of what
- we already read */
- if(cl<=0)
- /* if the whole thing was already read, we are done! */
- keepon=FALSE;
- }
- else
- keepon = FALSE;
- break; /* breaks out of for-loop, not switch() */
- }
-
- /* keep a backup of the position we are about to blank */
- letter = line_start[perline];
- line_start[perline]=0; /* zero terminate the buffer */
- if((checkprefix("WWW-Authenticate:", line_start) &&
- (401 == k->httpcode)) ||
- (checkprefix("Proxy-authenticate:", line_start) &&
- (407 == k->httpcode))) {
- result = Curl_http_input_auth(conn, k->httpcode, line_start);
- if(result)
- return result;
- }
- else if(checkprefix("Content-Length:", line_start)) {
- cl = curlx_strtoofft(line_start + strlen("Content-Length:"),
- NULL, 10);
- }
- else if(Curl_compareheader(line_start,
- "Connection:", "close"))
- closeConnection = TRUE;
- else if(2 == sscanf(line_start, "HTTP/1.%d %d",
- &subversion,
- &k->httpcode)) {
- /* store the HTTP code from the proxy */
- data->info.httpproxycode = k->httpcode;
- }
- /* put back the letter we blanked out before */
- line_start[perline]= letter;
-
- perline=0; /* line starts over here */
- line_start = ptr+1; /* this skips the zero byte we wrote */
- }
- }
- }
- break;
- } /* switch */
- } /* while there's buffer left and loop is requested */
-
- if(error)
- return CURLE_RECV_ERROR;
-
- if(data->info.httpproxycode != 200)
- /* Deal with the possibly already received authenticate
- headers. 'newurl' is set to a new URL if we must loop. */
- Curl_http_auth_act(conn);
-
- if (closeConnection && data->reqdata.newurl) {
- /* Connection closed by server. Don't use it anymore */
- sclose(conn->sock[sockindex]);
- conn->sock[sockindex] = CURL_SOCKET_BAD;
- break;
- }
- } while(data->reqdata.newurl);
-
- if(200 != k->httpcode) {
- failf(data, "Received HTTP code %d from proxy after CONNECT",
- k->httpcode);
-
- if (closeConnection && data->reqdata.newurl)
- conn->bits.proxy_connect_closed = TRUE;
-
- return CURLE_RECV_ERROR;
- }
-
- /* If a proxy-authorization header was used for the proxy, then we should
- make sure that it isn't accidentally used for the document request
- after we've connected. So let's free and clear it here. */
- Curl_safefree(conn->allocptr.proxyuserpwd);
- conn->allocptr.proxyuserpwd = NULL;
-
- data->state.authproxy.done = TRUE;
-
- infof (data, "Proxy replied OK to CONNECT request\n");
- return CURLE_OK;
-}
-
-/*
- * Curl_http_connect() performs HTTP stuff to do at connect-time, called from
- * the generic Curl_connect().
- */
-CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
-{
- struct SessionHandle *data;
- CURLcode result;
-
- data=conn->data;
-
- /* If we are not using a proxy and we want a secure connection, perform SSL
- * initialization & connection now. If using a proxy with https, then we
- * must tell the proxy to CONNECT to the host we want to talk to. Only
- * after the connect has occurred, can we start talking SSL
- */
-
- if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
-
- /* either SSL over proxy, or explicitly asked for */
- result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
- conn->host.name,
- conn->remote_port);
- if(CURLE_OK != result)
- return result;
- }
-
- if(!data->state.this_is_a_follow) {
- /* this is not a followed location, get the original host name */
- if (data->state.first_host)
- /* Free to avoid leaking memory on multiple requests*/
- free(data->state.first_host);
-
- data->state.first_host = strdup(conn->host.name);
- if(!data->state.first_host)
- return CURLE_OUT_OF_MEMORY;
- }
-
- if(conn->protocol & PROT_HTTPS) {
- /* perform SSL initialization */
- if(data->state.used_interface == Curl_if_multi) {
- result = Curl_https_connecting(conn, done);
- if(result)
- return result;
- }
- else {
- /* BLOCKING */
- result = Curl_ssl_connect(conn, FIRSTSOCKET);
- if(result)
- return result;
- *done = TRUE;
- }
- }
- else {
- *done = TRUE;
- }
-
- return CURLE_OK;
-}
-
-CURLcode Curl_https_connecting(struct connectdata *conn, bool *done)
-{
- CURLcode result;
- curlassert(conn->protocol & PROT_HTTPS);
-
- /* perform SSL initialization for this socket */
- result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, done);
- if(result)
- return result;
-
- return CURLE_OK;
-}
-
-#ifdef USE_SSLEAY
-/* This function is OpenSSL-specific. It should be made to query the generic
- SSL layer instead. */
-int Curl_https_getsock(struct connectdata *conn,
- curl_socket_t *socks,
- int numsocks)
-{
- if (conn->protocol & PROT_HTTPS) {
- struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET];
-
- if(!numsocks)
- return GETSOCK_BLANK;
-
- if (connssl->connecting_state == ssl_connect_2_writing) {
- /* write mode */
- socks[0] = conn->sock[FIRSTSOCKET];
- return GETSOCK_WRITESOCK(0);
- }
- else if (connssl->connecting_state == ssl_connect_2_reading) {
- /* read mode */
- socks[0] = conn->sock[FIRSTSOCKET];
- return GETSOCK_READSOCK(0);
- }
- }
- return CURLE_OK;
-}
-#else
-#ifdef USE_GNUTLS
-int Curl_https_getsock(struct connectdata *conn,
- curl_socket_t *socks,
- int numsocks)
-{
- (void)conn;
- (void)socks;
- (void)numsocks;
- return GETSOCK_BLANK;
-}
-#endif
-#endif
-
-/*
- * Curl_http_done() gets called from Curl_done() after a single HTTP request
- * has been performed.
- */
-
-CURLcode Curl_http_done(struct connectdata *conn,
- CURLcode status, bool premature)
-{
- struct SessionHandle *data = conn->data;
- struct HTTP *http =data->reqdata.proto.http;
- struct Curl_transfer_keeper *k = &data->reqdata.keep;
- (void)premature; /* not used */
-
- /* set the proper values (possibly modified on POST) */
- conn->fread = data->set.fread; /* restore */
- conn->fread_in = data->set.in; /* restore */
-
- if (http == NULL)
- return CURLE_OK;
-
- if(http->send_buffer) {
- send_buffer *buff = http->send_buffer;
-
- free(buff->buffer);
- free(buff);
- http->send_buffer = NULL; /* clear the pointer */
- }
-
- if(HTTPREQ_POST_FORM == data->set.httpreq) {
- k->bytecount = http->readbytecount + http->writebytecount;
-
- 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)
- k->bytecount = http->readbytecount + http->writebytecount;
-
- if (status != CURLE_OK)
- return (status);
-
- if(!conn->bits.retry &&
- ((http->readbytecount +
- conn->headerbytecount -
- conn->deductheadercount)) <= 0) {
- /* If this connection isn't simply closed to be retried, AND nothing was
- read from the HTTP server (that counts), this can't be right so we
- return an error here */
- failf(data, "Empty reply from server");
- return CURLE_GOT_NOTHING;
- }
-
- return CURLE_OK;
-}
-
-/* check and possibly add an Expect: header */
-static CURLcode expect100(struct SessionHandle *data,
- send_buffer *req_buffer)
-{
- CURLcode result = CURLE_OK;
- data->state.expect100header = FALSE; /* default to false unless it is set
- to TRUE below */
- if((data->set.httpversion != CURL_HTTP_VERSION_1_0) &&
- !checkheaders(data, "Expect:")) {
- /* if not doing HTTP 1.0 or disabled explicitly, we add a Expect:
- 100-continue to the headers which actually speeds up post
- operations (as there is one packet coming back from the web
- server) */
- result = add_bufferf(req_buffer,
- "Expect: 100-continue\r\n");
- if(result == CURLE_OK)
- data->state.expect100header = TRUE;
- }
- return result;
-}
-
-static CURLcode add_custom_headers(struct connectdata *conn,
- send_buffer *req_buffer)
-{
- CURLcode result = CURLE_OK;
- char *ptr;
- struct curl_slist *headers=conn->data->set.headers;
-
- while(headers) {
- ptr = strchr(headers->data, ':');
- if(ptr) {
- /* we require a colon for this to be a true header */
-
- ptr++; /* pass the colon */
- while(*ptr && ISSPACE(*ptr))
- ptr++;
-
- if(*ptr) {
- /* only send this if the contents was non-blank */
-
- if(conn->allocptr.host &&
- /* a Host: header was sent already, don't pass on any custom Host:
- header as that will produce *two* in the same request! */
- curl_strnequal("Host:", headers->data, 5))
- ;
- else if(conn->data->set.httpreq == HTTPREQ_POST_FORM &&
- /* this header (extended by formdata.c) is sent later */
- curl_strnequal("Content-Type:", headers->data,
- strlen("Content-Type:")))
- ;
- else {
- result = add_bufferf(req_buffer, "%s\r\n", headers->data);
- if(result)
- return result;
- }
- }
- }
- headers = headers->next;
- }
- return result;
-}
-
-/*
- * Curl_http() gets called from the generic Curl_do() function when a HTTP
- * request is to be performed. This creates and sends a properly constructed
- * HTTP request.
- */
-CURLcode Curl_http(struct connectdata *conn, bool *done)
-{
- struct SessionHandle *data=conn->data;
- char *buf = data->state.buffer; /* this is a short cut to the buffer */
- CURLcode result=CURLE_OK;
- struct HTTP *http;
- char *ppath = data->reqdata.path;
- char *host = conn->host.name;
- const char *te = ""; /* transfer-encoding */
- char *ptr;
- char *request;
- Curl_HttpReq httpreq = data->set.httpreq;
- char *addcookies = NULL;
- curl_off_t included_body = 0;
-
- /* 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
- the rest of the request in the PERFORM phase. */
- *done = TRUE;
-
- if(!data->reqdata.proto.http) {
- /* Only allocate this struct if we don't already have it! */
-
- http = (struct HTTP *)malloc(sizeof(struct HTTP));
- if(!http)
- return CURLE_OUT_OF_MEMORY;
- memset(http, 0, sizeof(struct HTTP));
- data->reqdata.proto.http = http;
- }
- else
- http = data->reqdata.proto.http;
-
- /* We default to persistent connections */
- conn->bits.close = FALSE;
-
- if ( (conn->protocol&(PROT_HTTP|PROT_FTP)) &&
- data->set.upload) {
- httpreq = HTTPREQ_PUT;
- }
-
- /* Now set the 'request' pointer to the proper request string */
- if(data->set.customrequest)
- request = data->set.customrequest;
- else {
- if(conn->bits.no_body)
- request = (char *)"HEAD";
- else {
- curlassert((httpreq > HTTPREQ_NONE) && (httpreq < HTTPREQ_LAST));
- switch(httpreq) {
- case HTTPREQ_POST:
- case HTTPREQ_POST_FORM:
- request = (char *)"POST";
- break;
- case HTTPREQ_PUT:
- request = (char *)"PUT";
- break;
- default: /* this should never happen */
- case HTTPREQ_GET:
- request = (char *)"GET";
- break;
- case HTTPREQ_HEAD:
- request = (char *)"HEAD";
- break;
- }
- }
- }
-
- /* The User-Agent string might have been allocated in url.c already, because
- 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(checkheaders(data, "User-Agent:") && conn->allocptr.uagent) {
- free(conn->allocptr.uagent);
- conn->allocptr.uagent=NULL;
- }
-
- /* setup the authentication headers */
- result = Curl_http_output_auth(conn, request, ppath, FALSE);
- if(result)
- return result;
-
- if((data->state.authhost.multi || data->state.authproxy.multi) &&
- (httpreq != HTTPREQ_GET) &&
- (httpreq != HTTPREQ_HEAD)) {
- /* Auth is required and we are not authenticated yet. Make a PUT or POST
- with content-length zero as a "probe". */
- conn->bits.authneg = TRUE;
- }
- else
- conn->bits.authneg = FALSE;
-
- Curl_safefree(conn->allocptr.ref);
- if(data->change.referer && !checkheaders(data, "Referer:"))
- conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer);
- else
- conn->allocptr.ref = NULL;
-
- if(data->set.cookie && !checkheaders(data, "Cookie:"))
- addcookies = data->set.cookie;
-
- if(!checkheaders(data, "Accept-Encoding:") &&
- data->set.encoding) {
- Curl_safefree(conn->allocptr.accept_encoding);
- conn->allocptr.accept_encoding =
- aprintf("Accept-Encoding: %s\r\n", data->set.encoding);
- if(!conn->allocptr.accept_encoding)
- return CURLE_OUT_OF_MEMORY;
- }
-
- ptr = checkheaders(data, "Transfer-Encoding:");
- if(ptr) {
- /* Some kind of TE is requested, check if 'chunked' is chosen */
- conn->bits.upload_chunky =
- Curl_compareheader(ptr, "Transfer-Encoding:", "chunked");
- }
- else {
- if (httpreq == HTTPREQ_GET)
- conn->bits.upload_chunky = FALSE;
- if(conn->bits.upload_chunky)
- te = "Transfer-Encoding: chunked\r\n";
- }
-
- Curl_safefree(conn->allocptr.host);
-
- ptr = checkheaders(data, "Host:");
- if(ptr && (!data->state.this_is_a_follow ||
- curl_strequal(data->state.first_host, conn->host.name))) {
-#if !defined(CURL_DISABLE_COOKIES)
- /* If we have a given custom Host: header, we extract the host name in
- order to possibly use it for cookie reasons later on. We only allow the
- custom Host: header if this is NOT a redirect, as setting Host: in the
- redirected request is being out on thin ice. Except if the host name
- is the same as the first one! */
- char *start = ptr+strlen("Host:");
- while(*start && ISSPACE(*start ))
- start++;
- ptr = start; /* start host-scanning here */
-
- /* scan through the string to find the end (space or colon) */
- while(*ptr && !ISSPACE(*ptr) && !(':'==*ptr))
- ptr++;
-
- if(ptr != start) {
- size_t len=ptr-start;
- Curl_safefree(conn->allocptr.cookiehost);
- conn->allocptr.cookiehost = malloc(len+1);
- if(!conn->allocptr.cookiehost)
- return CURLE_OUT_OF_MEMORY;
- memcpy(conn->allocptr.cookiehost, start, len);
- conn->allocptr.cookiehost[len]=0;
- }
-#endif
-
- conn->allocptr.host = NULL;
- }
- else {
- /* When building Host: headers, we must put the host name within
- [brackets] if the host name is a plain IPv6-address. RFC2732-style. */
-
- if(((conn->protocol&PROT_HTTPS) && (conn->remote_port == PORT_HTTPS)) ||
- (!(conn->protocol&PROT_HTTPS) && (conn->remote_port == PORT_HTTP)) )
- /* If (HTTPS on port 443) OR (non-HTTPS on port 80) then don't include
- the port number in the host string */
- conn->allocptr.host = aprintf("Host: %s%s%s\r\n",
- conn->bits.ipv6_ip?"[":"",
- host,
- conn->bits.ipv6_ip?"]":"");
- else
- conn->allocptr.host = aprintf("Host: %s%s%s:%d\r\n",
- conn->bits.ipv6_ip?"[":"",
- host,
- conn->bits.ipv6_ip?"]":"",
- conn->remote_port);
-
- if(!conn->allocptr.host)
- /* without Host: we can't make a nice request */
- return CURLE_OUT_OF_MEMORY;
- }
-
- 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
- host is a IDN-name, we must make sure that the request we produce only
- uses the encoded host name! */
- if(conn->host.dispname != conn->host.name) {
- char *url = data->change.url;
- ptr = strstr(url, conn->host.dispname);
- if(ptr) {
- /* This is where the display name starts in the URL, now replace this
- part with the encoded name. TODO: This method of replacing the host
- name is rather crude as I believe there's a slight risk that the
- user has entered a user name or password that contain the host name
- string. */
- size_t currlen = strlen(conn->host.dispname);
- size_t newlen = strlen(conn->host.name);
- size_t urllen = strlen(url);
-
- char *newurl;
-
- newurl = malloc(urllen + newlen - currlen + 1);
- if(newurl) {
- /* copy the part before the host name */
- memcpy(newurl, url, ptr - url);
- /* append the new host name instead of the old */
- memcpy(newurl + (ptr - url), conn->host.name, newlen);
- /* append the piece after the host name */
- memcpy(newurl + newlen + (ptr - url),
- ptr + currlen, /* copy the trailing zero byte too */
- urllen - (ptr-url) - currlen + 1);
- if(data->change.url_alloc)
- free(data->change.url);
- data->change.url = newurl;
- data->change.url_alloc = TRUE;
- }
- else
- return CURLE_OUT_OF_MEMORY;
- }
- }
- ppath = data->change.url;
- }
- if(HTTPREQ_POST_FORM == httpreq) {
- /* we must build the whole darned post sequence first, so that we have
- a size of the whole shebang before we start to send it */
- result = Curl_getFormData(&http->sendit, data->set.httppost,
- checkheaders(data, "Content-Type:"),
- &http->postsize);
- if(CURLE_OK != result) {
- /* Curl_getFormData() doesn't use failf() */
- failf(data, "failed creating formpost data");
- return result;
- }
- }
-
-
- http->p_pragma =
- (!checkheaders(data, "Pragma:") &&
- (conn->bits.httpproxy && !conn->bits.tunnel_proxy) )?
- "Pragma: no-cache\r\n":NULL;
-
- if(!checkheaders(data, "Accept:"))
- http->p_accept = "Accept: */*\r\n";
-
- if(( (HTTPREQ_POST == httpreq) ||
- (HTTPREQ_POST_FORM == httpreq) ||
- (HTTPREQ_PUT == httpreq) ) &&
- data->reqdata.resume_from) {
- /**********************************************************************
- * Resuming upload in HTTP means that we PUT or POST and that we have
- * got a resume_from value set. The resume value has already created
- * 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.
- *********************************************************************/
-
- if(data->reqdata.resume_from < 0 ) {
- /*
- * This is meant to get the size of the present remote-file by itself.
- * We don't support this now. Bail out!
- */
- data->reqdata.resume_from = 0;
- }
-
- if(data->reqdata.resume_from) {
- /* do we still game? */
- curl_off_t passed=0;
-
- /* Now, let's read off the proper amount of bytes from the
- input. If we knew it was a proper file we could've just
- fseek()ed but we only have a stream here */
- do {
- size_t readthisamountnow = (size_t)(data->reqdata.resume_from - passed);
- size_t actuallyread;
-
- if(readthisamountnow > BUFSIZE)
- readthisamountnow = BUFSIZE;
-
- actuallyread =
- data->set.fread(data->state.buffer, 1, (size_t)readthisamountnow,
- data->set.in);
-
- passed += actuallyread;
- if(actuallyread != readthisamountnow) {
- failf(data, "Could only read %" FORMAT_OFF_T
- " bytes from the input",
- passed);
- return CURLE_READ_ERROR;
- }
- } while(passed != data->reqdata.resume_from); /* loop until done */
-
- /* now, decrease the size of the read */
- if(data->set.infilesize>0) {
- data->set.infilesize -= data->reqdata.resume_from;
-
- if(data->set.infilesize <= 0) {
- failf(data, "File already completely uploaded");
- return CURLE_PARTIAL_FILE;
- }
- }
- /* we've passed, proceed as normal */
- }
- }
- if(data->reqdata.use_range) {
- /*
- * A range is selected. We use different headers whether we're downloading
- * or uploading and we always let customized headers override our internal
- * ones if any such are specified.
- */
- if((httpreq == HTTPREQ_GET) &&
- !checkheaders(data, "Range:")) {
- /* if a line like this was already allocated, free the previous one */
- if(conn->allocptr.rangeline)
- free(conn->allocptr.rangeline);
- conn->allocptr.rangeline = aprintf("Range: bytes=%s\r\n", data->reqdata.range);
- }
- else if((httpreq != HTTPREQ_GET) &&
- !checkheaders(data, "Content-Range:")) {
-
- if(data->reqdata.resume_from) {
- /* This is because "resume" was selected */
- curl_off_t total_expected_size=
- data->reqdata.resume_from + data->set.infilesize;
- conn->allocptr.rangeline =
- aprintf("Content-Range: bytes %s%" FORMAT_OFF_T
- "/%" FORMAT_OFF_T "\r\n",
- data->reqdata.range, total_expected_size-1,
- total_expected_size);
- }
- else {
- /* Range was selected and then we just pass the incoming range and
- append total size */
- conn->allocptr.rangeline =
- aprintf("Content-Range: bytes %s/%" FORMAT_OFF_T "\r\n",
- data->reqdata.range, data->set.infilesize);
- }
- }
- }
-
- {
- /* Use 1.1 unless the use specificly asked for 1.0 */
- const char *httpstring=
- data->set.httpversion==CURL_HTTP_VERSION_1_0?"1.0":"1.1";
-
- send_buffer *req_buffer;
- curl_off_t postsize; /* off_t type to be able to hold a large file size */
-
- /* initialize a dynamic send-buffer */
- req_buffer = add_buffer_init();
-
- if(!req_buffer)
- return CURLE_OUT_OF_MEMORY;
-
- /* add the main request stuff */
- result =
- add_bufferf(req_buffer,
- "%s " /* GET/HEAD/POST/PUT */
- "%s HTTP/%s\r\n" /* path + HTTP version */
- "%s" /* proxyuserpwd */
- "%s" /* userpwd */
- "%s" /* range */
- "%s" /* user agent */
- "%s" /* host */
- "%s" /* pragma */
- "%s" /* accept */
- "%s" /* accept-encoding */
- "%s" /* referer */
- "%s" /* Proxy-Connection */
- "%s",/* transfer-encoding */
-
- request,
- ppath,
- httpstring,
- conn->allocptr.proxyuserpwd?
- conn->allocptr.proxyuserpwd:"",
- conn->allocptr.userpwd?conn->allocptr.userpwd:"",
- (data->reqdata.use_range && conn->allocptr.rangeline)?
- conn->allocptr.rangeline:"",
- (data->set.useragent && *data->set.useragent && conn->allocptr.uagent)?
- conn->allocptr.uagent:"",
- (conn->allocptr.host?conn->allocptr.host:""), /* Host: host */
- http->p_pragma?http->p_pragma:"",
- http->p_accept?http->p_accept:"",
- (data->set.encoding && *data->set.encoding && conn->allocptr.accept_encoding)?
- conn->allocptr.accept_encoding:"",
- (data->change.referer && conn->allocptr.ref)?conn->allocptr.ref:"" /* Referer: <data> */,
- (conn->bits.httpproxy &&
- !conn->bits.tunnel_proxy &&
- !checkheaders(data, "Proxy-Connection:"))?
- "Proxy-Connection: Keep-Alive\r\n":"",
- te
- );
-
- if(result)
- return result;
-
-#if !defined(CURL_DISABLE_COOKIES)
- if(data->cookies || addcookies) {
- 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);
- co = Curl_cookie_getlist(data->cookies,
- conn->allocptr.cookiehost?
- conn->allocptr.cookiehost:host, data->reqdata.path,
- (bool)(conn->protocol&PROT_HTTPS?TRUE:FALSE));
- Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
- }
- if(co) {
- struct Cookie *store=co;
- /* now loop through all cookies that matched */
- while(co) {
- if(co->value) {
- if(0 == count) {
- result = add_bufferf(req_buffer, "Cookie: ");
- if(result)
- break;
- }
- result = add_bufferf(req_buffer,
- "%s%s=%s", count?"; ":"",
- co->name, co->value);
- if(result)
- break;
- count++;
- }
- co = co->next; /* next cookie please */
- }
- Curl_cookie_freelist(store); /* free the cookie list */
- }
- if(addcookies && (CURLE_OK == result)) {
- if(!count)
- result = add_bufferf(req_buffer, "Cookie: ");
- if(CURLE_OK == result) {
- result = add_bufferf(req_buffer, "%s%s",
- count?"; ":"",
- addcookies);
- count++;
- }
- }
- if(count && (CURLE_OK == result))
- result = add_buffer(req_buffer, "\r\n", 2);
-
- if(result)
- return result;
- }
-#endif
-
- if(data->set.timecondition) {
- struct tm *tm;
-
- /* Phil Karn (Fri, 13 Apr 2001) pointed out that 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 purposes of HTTP, GMT is exactly
- * equal to UTC (Coordinated Universal Time)." (see page 20 of RFC2616).
- */
-
-#ifdef HAVE_GMTIME_R
- /* thread-safe version */
- struct tm keeptime;
- tm = (struct tm *)gmtime_r(&data->set.timevalue, &keeptime);
-#else
- tm = gmtime(&data->set.timevalue);
-#endif
-
- /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
- snprintf(buf, BUFSIZE-1,
- "%s, %02d %s %4d %02d:%02d:%02d GMT",
- Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
- tm->tm_mday,
- Curl_month[tm->tm_mon],
- tm->tm_year + 1900,
- tm->tm_hour,
- tm->tm_min,
- tm->tm_sec);
-
- switch(data->set.timecondition) {
- case CURL_TIMECOND_IFMODSINCE:
- default:
- result = add_bufferf(req_buffer,
- "If-Modified-Since: %s\r\n", buf);
- break;
- case CURL_TIMECOND_IFUNMODSINCE:
- result = add_bufferf(req_buffer,
- "If-Unmodified-Since: %s\r\n", buf);
- break;
- case CURL_TIMECOND_LASTMOD:
- result = add_bufferf(req_buffer,
- "Last-Modified: %s\r\n", buf);
- break;
- }
- if(result)
- return result;
- }
-
- result = add_custom_headers(conn, req_buffer);
- if(result)
- return result;
-
- http->postdata = NULL; /* nothing to post at this point */
- Curl_pgrsSetUploadSize(data, 0); /* upload size is 0 atm */
-
- /* If 'authdone' is FALSE, we must not set the write socket index to the
- Curl_transfer() call below, as we're not ready to actually upload any
- data yet. */
-
- switch(httpreq) {
-
- case HTTPREQ_POST_FORM:
- if(!http->sendit || conn->bits.authneg) {
- /* nothing to post! */
- result = add_bufferf(req_buffer, "Content-Length: 0\r\n\r\n");
- if(result)
- return result;
-
- result = 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 */
- result = 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;
- }
-
- /* set the read function to read from the generated form data */
- conn->fread = (curl_read_callback)Curl_FormReader;
- conn->fread_in = &http->form;
-
- http->sending = HTTPSEND_BODY;
-
- if(!conn->bits.upload_chunky) {
- /* only add Content-Length if not uploading chunked */
- result = add_bufferf(req_buffer,
- "Content-Length: %" FORMAT_OFF_T "\r\n",
- http->postsize);
- if(result)
- return result;
- }
-
- result = expect100(data, 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 = add_buffer(req_buffer, contentType, linelength);
- if(result)
- return result;
- }
-
- /* make the request end in a true CRLF */
- result = add_buffer(req_buffer, "\r\n", 2);
- if(result)
- return result;
-
- /* set upload size to the progress meter */
- Curl_pgrsSetUploadSize(data, http->postsize);
-
- /* fire away the whole request to the server */
- result = 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 */
- result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
- &http->readbytecount,
- FIRSTSOCKET,
- &http->writebytecount);
-
- if(result) {
- Curl_formclean(&http->sendit); /* free that whole lot */
- return result;
- }
-#ifdef CURL_DOES_CONVERSIONS
-/* time to convert the form data... */
- result = Curl_formconvert(data, http->sendit);
- if(result) {
- Curl_formclean(&http->sendit); /* free that whole lot */
- return result;
- }
-#endif /* CURL_DOES_CONVERSIONS */
- break;
-
- case HTTPREQ_PUT: /* Let's PUT the data to the server! */
-
- if(conn->bits.authneg)
- postsize = 0;
- else
- postsize = data->set.infilesize;
-
- if((postsize != -1) && !conn->bits.upload_chunky) {
- /* only add Content-Length if not uploading chunked */
- result = add_bufferf(req_buffer,
- "Content-Length: %" FORMAT_OFF_T "\r\n",
- postsize );
- if(result)
- return result;
- }
-
- result = expect100(data, req_buffer);
- if(result)
- return result;
-
- result = add_buffer(req_buffer, "\r\n", 2); /* end of headers */
- if(result)
- return result;
-
- /* set the upload size to the progress meter */
- Curl_pgrsSetUploadSize(data, postsize);
-
- /* this sends the buffer and frees all the buffer resources */
- result = add_buffer_send(req_buffer, conn,
- &data->info.request_size, 0, FIRSTSOCKET);
- if(result)
- failf(data, "Failed sending PUT request");
- else
- /* prepare for transfer */
- result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
- &http->readbytecount,
- postsize?FIRSTSOCKET:-1,
- postsize?&http->writebytecount:NULL);
- if(result)
- return result;
- break;
-
- case HTTPREQ_POST:
- /* this is the simple POST, using x-www-form-urlencoded style */
-
- if(conn->bits.authneg)
- postsize = 0;
- else
- /* figure out the size of the postfields */
- postsize = (data->set.postfieldsize != -1)?
- data->set.postfieldsize:
- (data->set.postfields?(curl_off_t)strlen(data->set.postfields):0);
-
- if(!conn->bits.upload_chunky) {
- /* 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(!checkheaders(data, "Content-Length:")) {
- /* we allow replacing this header, although it isn't very wise to
- actually set your own */
- result = add_bufferf(req_buffer,
- "Content-Length: %" FORMAT_OFF_T"\r\n",
- postsize);
- if(result)
- return result;
- }
- }
-
- if(!checkheaders(data, "Content-Type:")) {
- result = add_bufferf(req_buffer,
- "Content-Type: application/x-www-form-urlencoded\r\n");
- if(result)
- return result;
- }
-
- if(data->set.postfields) {
-
- /* 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 */
- if(postsize > TINY_INITIAL_POST_SIZE) {
- result = expect100(data, req_buffer);
- if(result)
- return result;
- }
- else
- data->state.expect100header = FALSE;
-
- if(!data->state.expect100header &&
- (postsize < MAX_INITIAL_POST_SIZE)) {
- /* if we don't use expect:-100 AND
- postsize is less than MAX_INITIAL_POST_SIZE
-
- then append the post data to the HTTP request header. This limit
- is no magic limit but only set to prevent really huge POSTs to
- get the data duplicated with malloc() and family. */
-
- result = add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
- if(result)
- return result;
-
- if(!conn->bits.upload_chunky) {
- /* We're not sending it 'chunked', append it to the request
- already now to reduce the number if send() calls */
- result = add_buffer(req_buffer, data->set.postfields,
- (size_t)postsize);
- included_body = postsize;
- }
- else {
- /* Append the POST data chunky-style */
- result = add_bufferf(req_buffer, "%x\r\n", (int)postsize);
- if(CURLE_OK == result)
- result = add_buffer(req_buffer, data->set.postfields,
- (size_t)postsize);
- if(CURLE_OK == result)
- result = add_buffer(req_buffer,
- "\x0d\x0a\x30\x0d\x0a\x0d\x0a", 7);
- /* CR LF 0 CR LF CR LF */
- included_body = postsize + 7;
- }
- if(result)
- return result;
- }
- else {
- /* A huge POST coming up, do data separate from the request */
- http->postsize = postsize;
- http->postdata = data->set.postfields;
-
- http->sending = HTTPSEND_BODY;
-
- conn->fread = (curl_read_callback)readmoredata;
- conn->fread_in = (void *)conn;
-
- /* set the upload size to the progress meter */
- Curl_pgrsSetUploadSize(data, http->postsize);
-
- add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
- }
- }
- else {
- add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
-
- if(data->set.postfieldsize) {
- /* set the upload size to the progress meter */
- Curl_pgrsSetUploadSize(data, postsize?postsize:-1);
-
- /* set the pointer to mark that we will send the post body using
- the read callback */
- http->postdata = (char *)&http->postdata;
- }
- }
- /* issue the request */
- result = add_buffer_send(req_buffer, conn, &data->info.request_size,
- (size_t)included_body, FIRSTSOCKET);
-
- if(result)
- failf(data, "Failed sending HTTP POST request");
- else
- result =
- Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
- &http->readbytecount,
- http->postdata?FIRSTSOCKET:-1,
- http->postdata?&http->writebytecount:NULL);
- break;
-
- default:
- add_buffer(req_buffer, "\r\n", 2);
-
- /* issue the request */
- result = add_buffer_send(req_buffer, conn,
- &data->info.request_size, 0, FIRSTSOCKET);
-
- if(result)
- failf(data, "Failed sending HTTP request");
- else
- /* HTTP GET/HEAD download: */
- result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
- &http->readbytecount,
- http->postdata?FIRSTSOCKET:-1,
- http->postdata?&http->writebytecount:NULL);
- }
- if(result)
- return result;
- }
-
- return CURLE_OK;
-}
-#endif