Skip to content
http.c 108 KiB
Newer Older
/***************************************************************************
 *                                  _   _ ____  _
 *  Project                     ___| | | |  _ \| |
 *                             / __| | | | |_) | |
 *                            | (__| |_| |  _ <| |___
Daniel Stenberg's avatar
Daniel Stenberg committed
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) 1998 - 2011, 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

Daniel Stenberg's avatar
Daniel Stenberg committed

#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
#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

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

#include "urldata.h"
#include <curl/curl.h>
Daniel Stenberg's avatar
Daniel Stenberg committed
#include "sendf.h"
#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 "curl_memory.h"
#include "select.h"
#include "parsedate.h" /* for the week day and month names */
#include "http_proxy.h"
#include "warnless.h"
#include "non-ascii.h"
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"

static int http_getsock_do(struct connectdata *conn,
                           curl_socket_t *socks,
                           int numsocks);
static int http_should_fail(struct connectdata *conn);

#ifdef USE_SSL
static CURLcode https_connecting(struct connectdata *conn, bool *done);
static int https_getsock(struct connectdata *conn,
#else
#define https_connecting(x,y) CURLE_COULDNT_CONNECT

/*
 * 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 */
  ZERO_NULL,                            /* readwrite */
  CURLPROTO_HTTP,                       /* protocol */
  PROTOPT_NONE                          /* flags */
};

#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 */
  ZERO_NULL,                            /* readwrite */
  CURLPROTO_HTTP | CURLPROTO_HTTPS,     /* protocol */
  PROTOPT_SSL                           /* flags */
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.
char *Curl_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(Curl_raw_nequal(head->data, thisheader, thislen))
/*
 * 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.
 */
static char *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 != ':'))
    /* Skip over colon */
    ++h;

  /* Find the first non-space letter */
Yang Tse's avatar
Yang Tse committed
  start = h;
  while(*start && ISSPACE(*start))
    start++;

  /* 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');
Yang Tse's avatar
Yang Tse committed
  if(!end)
    return NULL;

  /* skip all trailing space letters */
Yang Tse's avatar
Yang Tse committed
  while((end > start) && ISSPACE(*end))
    end--;

  /* 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;

  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
Dan Fandrich's avatar
Dan Fandrich committed
     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;
Mandy Wu's avatar
Mandy Wu committed
  else if(avail & CURLAUTH_NTLM_SSO)
    pick->picked = CURLAUTH_NTLM_SSO;
  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
 *     }
 *   }
 * }
 */
Daniel Stenberg's avatar
Daniel Stenberg committed
static CURLcode 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)
    /* If this is still NULL, we have not reach very far and we can safely
       skip this rewinding stuff */
  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;
      else if(data->set.postfields)
        expectsend = (curl_off_t)strlen(data->set.postfields);
      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) ||
Mandy Wu's avatar
Mandy Wu committed
       (data->state.authhost.picked == CURLAUTH_NTLM) ||
       (data->state.authproxy.picked == CURLAUTH_NTLM_SSO) ||
       (data->state.authhost.picked == CURLAUTH_NTLM_SSO)) {
      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) {
          infof(data, "Rewind stream after send\n");
        }

        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;
}

Dan Fandrich's avatar
Dan Fandrich committed
 * Curl_http_auth_act() gets called when all HTTP headers have been received
 * and it checks what authentication methods that are available and decides
Dan Fandrich's avatar
Dan Fandrich committed
 * which one (if any) to use. It will set 'newurl' if an auth method was
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) {
    /* In case this is GSS auth, the newurl field is already allocated so
       we must make sure to free it before allocating a new one. As figured
       out in bug #2284386 */
    Curl_safefree(data->req.newurl);
    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) {
Daniel Stenberg's avatar
Daniel Stenberg committed
      code = http_perhapsrewind(conn);
          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;
  if(http_should_fail(conn)) {
    failf (data, "The requested URL returned error: %d",
    code = CURLE_HTTP_RETURNED_ERROR;
  }

  return code;

/*
 * Output the correct authentication header depending on the auth type
 * and whether or not it is to a proxy.
 */
static CURLcode
output_auth_headers(struct connectdata *conn,
                    struct auth *authstatus,
                    const char *request,
                    const char *path,
                    bool proxy)
{
  struct SessionHandle *data = conn->data;
  const char *auth=NULL;
  CURLcode result = CURLE_OK;
#ifdef USE_HTTP_NEGOTIATE
  struct negotiatedata *negdata = proxy?
    &data->state.proxyneg:&data->state.negotiate;
#endif
#ifdef CURL_DISABLE_CRYPTO_AUTH
#ifdef USE_HTTP_NEGOTIATE
Marcus Sundberg's avatar
Marcus Sundberg committed
  negdata->state = GSS_AUTHNONE;
  if((authstatus->picked == CURLAUTH_GSSNEGOTIATE) &&
     negdata->context && !GSS_ERROR(negdata->status)) {
    auth="GSS-Negotiate";
    result = Curl_output_negotiate(conn, proxy);
    if(result)
      return result;
    authstatus->done = TRUE;
  }
  else
#endif
#ifdef USE_NTLM
  if(authstatus->picked == CURLAUTH_NTLM) {
    auth="NTLM";
    result = Curl_output_ntlm(conn, proxy);
    if(result)
      return result;
  }
  else
#endif
Mandy Wu's avatar
Mandy Wu committed
#ifdef USE_NTLM_SSO
  if(authstatus->picked == CURLAUTH_NTLM_SSO) {
    auth="NTLM_SSO";
#ifdef WINBIND_NTLM_AUTH_ENABLED
Mandy Wu's avatar
Mandy Wu committed
    result = Curl_output_ntlm_sso(conn, proxy);
    if(result)
      return result;
#else
    return CURLE_REMOTE_ACCESS_DENIED;
#endif
Mandy Wu's avatar
Mandy Wu committed
  }
  else
#endif
#ifndef CURL_DISABLE_CRYPTO_AUTH
  if(authstatus->picked == CURLAUTH_DIGEST) {
    auth="Digest";
    result = Curl_output_digest(conn,
                                proxy,
                                (const unsigned char *)request,
                                (const unsigned char *)path);
    if(result)
      return result;
  }
  else
#endif
  if(authstatus->picked == CURLAUTH_BASIC) {
    /* Basic */
    if((proxy && conn->bits.proxy_user_passwd &&
       !Curl_checkheaders(data, "Proxy-authorization:")) ||
       (!proxy && conn->bits.user_passwd &&
       !Curl_checkheaders(data, "Authorization:"))) {
      auth="Basic";
      result = http_output_basic(conn, proxy);
      if(result)
        return result;
    }
    /* NOTE: this function should set 'done' TRUE, as the other auth
       functions work that way */
    authstatus->done = TRUE;
  }

  if(auth) {
    infof(data, "%s auth using %s with user '%s'\n",
          proxy?"Proxy":"Server", auth,
          proxy?(conn->proxyuser?conn->proxyuser:""):
                (conn->user?conn->user:""));
    authstatus->multi = (bool)(!authstatus->done);
  }
  else
    authstatus->multi = FALSE;

  return CURLE_OK;
}

 * 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
 *
CURLcode
Curl_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;
#ifndef CURL_DISABLE_PROXY
  /* Send proxy authentication header if needed */
      (conn->bits.tunnel_proxy == proxytunnel)) {
    result = output_auth_headers(conn, authproxy, request, path, TRUE);
    if(result)
      return result;
  }
#else
  (void)proxytunnel;
#endif /* CURL_DISABLE_PROXY */
    /* 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->set.http_disable_hostname_check_before_authentication ||
     Curl_raw_equal(data->state.first_host, conn->host.name)) {
    result = output_auth_headers(conn, authhost, request, path, 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
   *
   * Note:
   *
   * ->picked is first set to the 'want' value (one or more bits) before the
   * request is sent, and then it is again set _after_ all response 401/407
   * headers have been received but then only to a single preferred method
   * (bit).
   *
#ifdef USE_HTTP_NEGOTIATE
  if(checkprefix("GSS-Negotiate", start) ||
    *availp |= CURLAUTH_GSSNEGOTIATE;
    authp->avail |= CURLAUTH_GSSNEGOTIATE;

    if(data->state.negotiate.state == GSS_AUTHSENT) {
      /* if we sent GSS authentication in the outgoing request and we get this
         back, we're in trouble */
      infof(data, "Authentication problem. Ignoring this.\n");
      data->state.authproblem = TRUE;
    }
    else {
      neg = Curl_input_negotiate(conn, (bool)(httpcode == 407), start);
        DEBUGASSERT(!data->req.newurl);
        data->req.newurl = strdup(data->change.url);
        if(!data->req.newurl)
          return CURLE_OUT_OF_MEMORY;
        data->state.authproblem = FALSE;
        /* we received GSS auth info and we dealt with it fine */
        data->state.negotiate.state = GSS_AUTHRECV;
      else {
        data->state.authproblem = TRUE;
      }
    /* NTLM support requires the SSL crypto libs */
    if(checkprefix("NTLM", start)) {
      authp->avail |= CURLAUTH_NTLM;
Mandy Wu's avatar
Mandy Wu committed
      if(authp->picked == CURLAUTH_NTLM ||
         authp->picked == CURLAUTH_NTLM_SSO) {
        /* NTLM authentication is picked and activated */
          Curl_input_ntlm(conn, (bool)(httpcode == 407), start);
          data->state.authproblem = FALSE;
#ifdef WINBIND_NTLM_AUTH_ENABLED
Mandy Wu's avatar
Mandy Wu committed
          if(authp->picked == CURLAUTH_NTLM_SSO) {
            *availp &= ~CURLAUTH_NTLM;
            authp->avail &= ~CURLAUTH_NTLM;
            *availp |= CURLAUTH_NTLM_SSO;
            authp->avail |= CURLAUTH_NTLM_SSO;

            /* Get the challenge-message which will be passed to
             * ntlm_auth for generating the type 3 message later */
            while(*start && ISSPACE(*start))
              start++;
            if(checkprefix("NTLM", start)) {
              start += strlen("NTLM");
              while(*start && ISSPACE(*start))
                start++;
              if(*start)
                if((conn->challenge_header = strdup(start)) == NULL)
                  return CURLE_OUT_OF_MEMORY;
            }
          }
#endif
        }
          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
Dan Fandrich's avatar
Dan Fandrich committed
             anyway, which basically means our name+password isn't
          authp->avail = CURLAUTH_NONE;
          infof(data, "Authentication problem. Ignoring this.\n");
 * http_should_fail() determines whether an HTTP response has gotten us
Daniel Stenberg's avatar
Daniel Stenberg committed
 * 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
 */
static int http_should_fail(struct connectdata *conn)

  /*
  ** 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.
  */

  /*
  ** 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;
}

/* ------------------------------------------------------------------------- */
 * Curl_add_buffer_init() sets up and returns a fine buffer struct
Curl_send_buffer *Curl_add_buffer_init(void)
  return calloc(1, sizeof(Curl_send_buffer));
 * Curl_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 Curl_add_buffer_send(Curl_send_buffer *in,
                              struct connectdata *conn,

                               /* add the number of sent bytes to this
                                  counter */
                              long *bytes_written,

                               /* how much of the buffer contains body data */