Skip to content
http.c 92.8 KiB
Newer Older
/***************************************************************************
 *                                  _   _ ____  _
 *  Project                     ___| | | |  _ \| |
 *                             / __| | | | |_) | |
 *                            | (__| |_| |  _ <| |___
Daniel Stenberg's avatar
Daniel Stenberg committed
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al.
Daniel Stenberg's avatar
Daniel Stenberg committed
 *
 * 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.
Daniel Stenberg's avatar
Daniel Stenberg committed
 * 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.
Daniel Stenberg's avatar
Daniel Stenberg committed
 *
Daniel Stenberg's avatar
Daniel Stenberg committed
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
Daniel Stenberg's avatar
Daniel Stenberg committed
 *
Daniel Stenberg's avatar
Daniel Stenberg committed
 * $Id$
 ***************************************************************************/
Daniel Stenberg's avatar
Daniel Stenberg committed

Daniel Stenberg's avatar
Daniel Stenberg committed
/* -- WIN32 approved -- */
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>

#ifdef WIN32
Daniel Stenberg's avatar
Daniel Stenberg committed
#include <time.h>
#include <io.h>
#else
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
Daniel Stenberg's avatar
Daniel Stenberg committed
#include <netinet/in.h>
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed
#include <sys/time.h>
Gisle Vanem's avatar
Gisle Vanem committed
#endif
#ifdef HAVE_TIME_H
#ifdef TIME_WITH_SYS_TIME
#include <time.h>
#endif
#endif

Daniel Stenberg's avatar
Daniel Stenberg committed
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed
#include <netdb.h>
Daniel Stenberg's avatar
Daniel Stenberg committed
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed
#include <sys/ioctl.h>
Daniel Stenberg's avatar
Daniel Stenberg committed
#include <signal.h>

#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif

#endif

#include "urldata.h"
#include <curl/curl.h>
Daniel Stenberg's avatar
Daniel Stenberg committed
#include "sendf.h"
#include "easyif.h" /* for Curl_convert_... prototypes */
Daniel Stenberg's avatar
Daniel Stenberg committed
#include "formdata.h"
#include "progress.h"
#include "curl_base64.h"
Daniel Stenberg's avatar
Daniel Stenberg committed
#include "cookie.h"
#include "strequal.h"
#include "http_digest.h"
#include "http_negotiate.h"
#include "share.h"
#include "hostip.h"
#include "select.h"
#include "parsedate.h" /* for the week day and month names */
Daniel Stenberg's avatar
Daniel Stenberg committed

#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>

/* The last #include file should be: */
#include "memdebug.h"

Dan Fandrich's avatar
Dan Fandrich committed
/* Default proxy timeout in milliseconds */
#define PROXY_TIMEOUT (3600*1000)
static int http_getsock_do(struct connectdata *conn,
                           curl_socket_t *socks,
                           int numsocks);
static CURLcode https_connecting(struct connectdata *conn, bool *done);
#ifdef USE_SSL
static int https_getsock(struct connectdata *conn,

/*
 * HTTP handler interface.
 */
const struct Curl_handler Curl_handler_http = {
  "HTTP",                               /* scheme */
  ZERO_NULL,                            /* setup_connection */
  Curl_http,                            /* do_it */
  Curl_http_done,                       /* done */
  ZERO_NULL,                            /* do_more */
  ZERO_NULL,                            /* connecting */
  ZERO_NULL,                            /* doing */
  ZERO_NULL,                            /* proto_getsock */
  ZERO_NULL,                            /* disconnect */
  PORT_HTTP,                            /* defport */
  PROT_HTTP,                            /* protocol */
};

#ifdef USE_SSL
/*
 * HTTPS handler interface.
 */
const struct Curl_handler Curl_handler_https = {
  "HTTPS",                              /* scheme */
  ZERO_NULL,                            /* setup_connection */
  Curl_http,                            /* do_it */
  Curl_http_done,                       /* done */
  ZERO_NULL,                            /* do_more */
  ZERO_NULL,                            /* doing */
  ZERO_NULL,                            /* disconnect */
  PORT_HTTPS,                           /* defport */
  PROT_HTTP | PROT_HTTPS | PROT_SSL     /* protocol */
};
#endif


Daniel Stenberg's avatar
Daniel Stenberg committed
 * 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;
}

/*
 * Strip off leading and trailing whitespace from the value in the
 * given HTTP header line and return a strdupped copy. Returns NULL in
 * case of allocation failure. Returns an empty string if the header value
 * consists entirely of whitespace.
 */
char *Curl_copy_header_value(const char *h)
{
  const char *start;
  const char *end;
  char *value;
  size_t len;

  DEBUGASSERT(h);

  /* Find the end of the header name */
  while (*h && (*h != ':'))
    ++h;

  if (*h)
    /* Skip over colon */
    ++h;

  /* Find the first non-space letter */
  for(start=h;
      *start && ISSPACE(*start);
      start++)
    ;  /* empty loop */

  /* data is in the host encoding so
     use '\r' and '\n' instead of 0x0d and 0x0a */
  end = strchr(start, '\r');
  if(!end)
    end = strchr(start, '\n');
  if(!end)
    end = strchr(start, '\0');

  /* skip all trailing space letters */
  for(; ISSPACE(*end) && (end > start); end--)
    ;  /* empty loop */

  /* get length of the type */
  len = end-start+1;

  value = malloc(len + 1);
  if(!value)
    return NULL;

  memcpy(value, start, len);
  value[len] = 0; /* zero terminate */

  return value;
}

Daniel Stenberg's avatar
Daniel Stenberg committed
/*
 * http_output_basic() sets up an Authorization: header (or the proxy version)
 * for HTTP Basic authentication.
Daniel Stenberg's avatar
Daniel Stenberg committed
 *
 * Returns CURLcode.
 */
static CURLcode http_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);
    if(!*userp)
      return CURLE_OUT_OF_MEMORY;
/* pickoneauth() selects the most favourable authentication method from the
 * ones available and the ones we want.
Daniel Stenberg's avatar
Daniel Stenberg committed
 *
 * return TRUE if one was picked
Daniel Stenberg's avatar
Daniel Stenberg committed
 */
static bool pickoneauth(struct auth *pick)
  /* 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 existance 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 */
 *
 * 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
 *     }
 *   }
 * }
 */
CURLcode Curl_http_perhapsrewind(struct connectdata *conn)
{
  struct SessionHandle *data = conn->data;
  struct HTTP *http = data->state.proto.http;
  curl_off_t expectsend = -1; /* default is unknown */

  if(!http || !(conn->protocol & PROT_HTTP))
    /* If this is still NULL, we have not reach very far and we can
       safely skip this rewinding stuff, or this is attempted to get used
       when HTTP isn't activated */
  switch(data->set.httpreq) {
  case HTTPREQ_GET:
  case HTTPREQ_HEAD:
    return CURLE_OK;
  default:
    break;
  }

  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
Yang Tse's avatar
Yang Tse committed
            " bytes\n", (curl_off_t)(expectsend - bytessent));

    /* This is not NTLM or NTLM with many bytes left to send: close
     */
    conn->bits.close = TRUE;
    data->req.size = 0; /* don't download any more than 0 bytes */

    /* There still is data left to send, but this connection is marked for
       closure so we can safely do the rewind right now */
    /* we rewind now at once since if we already sent something */
    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->req.httpcode && 199 >= data->req.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;
     ((data->req.httpcode == 401) ||
      (conn->bits.authneg && data->req.httpcode < 300))) {
    pickhost = pickoneauth(&data->state.authhost);
      data->state.authproblem = TRUE;
     ((data->req.httpcode == 407) ||
      (conn->bits.authneg && data->req.httpcode < 300))) {
    pickproxy = pickoneauth(&data->state.authproxy);
      data->state.authproblem = TRUE;
  }
  if(pickhost || pickproxy) {
    data->req.newurl = strdup(data->change.url); /* clone URL */
    if(!data->req.newurl)
      return CURLE_OUT_OF_MEMORY;
    if((data->set.httpreq != HTTPREQ_GET) &&
       (data->set.httpreq != HTTPREQ_HEAD) &&
       !conn->bits.rewindaftersend) {
          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->req.newurl = strdup(data->change.url); /* clone URL */
      if(!data->req.newurl)
        return CURLE_OUT_OF_MEMORY;
      data->state.authhost.done = TRUE;
    failf (data, "The requested URL returned error: %d",
    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"
Daniel Stenberg's avatar
Daniel Stenberg committed
 *
http_output_auth(struct connectdata *conn,
                 const char *request,
                 const char *path,
                 bool proxytunnel) /* TRUE if this is the request setting
                                      up the proxy tunnel */
{
  CURLcode result = CURLE_OK;
  struct SessionHandle *data = conn->data;
  struct auth *authhost;
  struct auth *authproxy;
  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 */
      (conn->bits.tunnel_proxy == proxytunnel)) {
#ifdef HAVE_GSSAPI
    if((authproxy->picked == CURLAUTH_GSSNEGOTIATE) &&
       data->state.negotiate.context &&
       !GSS_ERROR(data->state.negotiate.status)) {
      auth="GSS-Negotiate";
      result = Curl_output_negotiate(conn, TRUE);
    if(authproxy->picked == CURLAUTH_NTLM) {
      result = Curl_output_ntlm(conn, TRUE);
      if(result)
        return result;
    }
    else
      if(authproxy->picked == CURLAUTH_BASIC) {
        /* Basic */
        if(conn->bits.proxy_user_passwd &&
           !checkheaders(data, "Proxy-authorization:")) {
          result = http_output_basic(conn, TRUE);
        /* NOTE: http_output_basic() should set 'done' TRUE, as the other auth
           functions work that way */
        authproxy->done = TRUE;
      else if(authproxy->picked == CURLAUTH_DIGEST) {
        result = Curl_output_digest(conn,
                                    TRUE, /* proxy */
                                    (const unsigned char *)request,
                                    (const unsigned char *)path);
        if(result)
          return result;
      }
      if(auth) {
        infof(data, "Proxy auth using %s with user '%s'\n",
              auth, conn->proxyuser?conn->proxyuser:"");
Yang Tse's avatar
Yang Tse committed
        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 ||
     !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 */
      if((authhost->picked == CURLAUTH_GSSNEGOTIATE) &&
         !GSS_ERROR(data->state.negotiate.status)) {
        result = Curl_output_negotiate(conn, FALSE);
        authhost->done = TRUE;
      if(authhost->picked == CURLAUTH_NTLM) {
        result = Curl_output_ntlm(conn, FALSE);
        if(authhost->picked == CURLAUTH_DIGEST) {
                                      FALSE, /* not a proxy */
                                      (const unsigned char *)request,
                                      (const unsigned char *)path);
        if(authhost->picked == CURLAUTH_BASIC) {
          if(conn->bits.user_passwd &&
             !checkheaders(data, "Authorization:")) {
            result = http_output_basic(conn, FALSE);
            if(result)
              return result;
          }
          /* basic is always ready */
          authhost->done = TRUE;
        infof(data, "Server auth using %s with user '%s'\n",
Yang Tse's avatar
Yang Tse committed
        authhost->multi = (bool)(!authhost->done);
      }
      else
        authhost->multi = FALSE;
    authhost->done = TRUE;
 * 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,
                              const char *header) /* the first non-space */
{
  /*
   * This resource requires authentication
   */
  struct SessionHandle *data = conn->data;

  struct auth *authp;
    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;
   * 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
  if(checkprefix("GSS-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, (bool)(httpcode == 407), start);
        DEBUGASSERT(!data->req.newurl);
        data->req.newurl = strdup(data->change.url);
        data->state.authproblem = (data->req.newurl == NULL);
Daniel Stenberg's avatar
Daniel Stenberg committed
      }
      else {
        infof(data, "Authentication problem. Ignoring this.\n");
        data->state.authproblem = TRUE;
      }
    /* NTLM support requires the SSL crypto libs */
    if(checkprefix("NTLM", start)) {
      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;
          infof(data, "Authentication problem. Ignoring this.\n");
        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)) {
        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");
Daniel Stenberg's avatar
Daniel Stenberg committed
 * 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;

  /*
  ** If we haven't been asked to fail on error,
  ** don't fail.
  */
  if(data->state.resume_from &&
     (data->set.httpreq==HTTPREQ_GET) &&
     (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
  */
    return 1;

  /*
  ** All we have left to deal with is 401 and 407
  */
  DEBUGASSERT((httpcode == 401) || (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->req.newurl ?
        data->req.newurl : "(null)");
  infof(data,"%s: authproblem = %d\n",__FUNCTION__,data->state.authproblem);

  /*
  ** Either we're not authenticating, or we're supposed to
  ** be authenticating something else.  This is an error.
  */
  if((httpcode == 401) && !conn->bits.user_passwd)
  if((httpcode == 407) && !conn->bits.proxy_user_passwd)
  return data->state.authproblem;
Daniel Stenberg's avatar
Daniel Stenberg committed
/*
 * 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->state.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->data->req.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_func = http->backup.fread_func;
      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);
 add_buffer(send_buffer *in, const void *inptr, size_t size);
Daniel Stenberg's avatar
Daniel Stenberg committed
 * 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.
Daniel Stenberg's avatar
Daniel Stenberg committed
 *
 * Returns CURLcode
CURLcode add_buffer_send(send_buffer *in,
                         struct connectdata *conn,
                         long *bytes_written, /* add the number of sent bytes
                                                 to this counter */
Yang Tse's avatar
Yang Tse committed
                         size_t included_body_bytes, /* how much of the buffer