Skip to content
url.c 67 KiB
Newer Older
Daniel Stenberg's avatar
Daniel Stenberg committed
/*****************************************************************************
 *                                  _   _ ____  _     
 *  Project                     ___| | | |  _ \| |    
 *                             / __| | | | |_) | |    
 *                            | (__| |_| |  _ <| |___ 
 *                             \___|\___/|_| \_\_____|
 *
Daniel Stenberg's avatar
Daniel Stenberg committed
 * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al.
Daniel Stenberg's avatar
Daniel Stenberg committed
 *
Daniel Stenberg's avatar
Daniel Stenberg committed
 * In order to be useful for every potential user, curl and libcurl are
 * dual-licensed under the MPL and the MIT/X-derivate licenses.
Daniel Stenberg's avatar
Daniel Stenberg committed
 *
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 MPL or the MIT/X-derivate
 * licenses. You may pick one of these licenses.
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

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

#include <errno.h>

#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
#include <winsock.h>
#include <time.h>
#include <io.h>
#else
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/resource.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <netdb.h>
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
#include <sys/ioctl.h>
#include <signal.h>

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

#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif

#ifndef HAVE_SELECT
#error "We can't compile without select() support!"
#endif
#ifndef HAVE_SOCKET
#error "We can't compile without socket() support!"
#endif

#endif

#include "urldata.h"
#include "netrc.h"

#include "formdata.h"
#include "base64.h"
#include "ssluse.h"
#include "hostip.h"
#include "if2ip.h"
Daniel Stenberg's avatar
Daniel Stenberg committed
#include "sendf.h"
#include "getpass.h"
#include "progress.h"
#include "cookie.h"
#include "strequal.h"
Daniel Stenberg's avatar
Daniel Stenberg committed

/* And now for the protocols */
#include "ftp.h"
#include "dict.h"
#include "telnet.h"
#include "http.h"
#include "file.h"
#include "ldap.h"

#include <curl/types.h>
Daniel Stenberg's avatar
Daniel Stenberg committed

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

Daniel Stenberg's avatar
Daniel Stenberg committed
#ifdef KRB4
#include "security.h"
#endif
/* The last #include file should be: */
#ifdef MALLOCDEBUG
#include "memdebug.h"
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed

/* Local static prototypes */
static int ConnectionKillOne(struct UrlData *data);
static bool ConnectionExists(struct UrlData *data,
                             struct connectdata *needle,
                             struct connectdata **usethis);
static unsigned int ConnectionStore(struct UrlData *data,
                                    struct connectdata *conn);
#if !defined(WIN32)||defined(__CYGWIN32__)
#ifndef RETSIGTYPE
#define RETSIGTYPE void
#endif
static
RETSIGTYPE alarmfunc(int signal)
{
  /* this is for "-ansi -Wall -pedantic" to stop complaining!   (rabe) */
  (void)signal;
  return;
}
#endif

CURLcode Curl_close(CURL *curl)
Daniel Stenberg's avatar
Daniel Stenberg committed
{
  struct UrlData *data=(struct UrlData *)curl;
  
  /* Loop through all open connections and kill them one by one */
  while(-1 != ConnectionKillOne(data));
Daniel Stenberg's avatar
Daniel Stenberg committed

  if(data->bits.proxystringalloc) {
    data->bits.proxystringalloc=FALSE;;
    free(data->proxy);
    data->proxy=NULL;

    /* Since we allocated the string the previous round, it means that we
       "discovered" the proxy in the environment variables and thus we must
       switch off that knowledge again... */
    data->bits.httpproxy=FALSE;

  /* check for allocated [URL] memory to free: */
  if(data->freethis)
    free(data->freethis);
Daniel Stenberg's avatar
Daniel Stenberg committed

  if(data->headerbuff)
    free(data->headerbuff);
  if(data->free_referer)
    free(data->referer);
  if(data->bits.urlstringalloc)
    /* the URL is allocated, free it! */
    free(data->url);
Daniel Stenberg's avatar
Daniel Stenberg committed

  Curl_cookie_cleanup(data->cookies);
Daniel Stenberg's avatar
Daniel Stenberg committed

  /* free the connection cache */
  free(data->connects);
  return CURLE_OK;
}
int my_getpass(void *clientp, char *prompt, char* buffer, int buflen )
{
  char *retbuf;
  retbuf = getpass_r(prompt, buffer, buflen);
  if(NULL == retbuf)
    return 1;
  else
    return 0; /* success */
CURLcode Curl_open(CURL **curl, char *url)
{
  /* We don't yet support specifying the URL at this point */
  struct UrlData *data;
#ifdef HAVE_SIGACTION
  struct sigaction sigact;
#endif

  /* Very simple start-up: alloc the struct, init it with zeroes and return */
  data = (struct UrlData *)malloc(sizeof(struct UrlData));
    memset(data, 0, sizeof(struct UrlData));

    /* We do some initial setup here, all those fields that can't be just 0 */

    data-> headerbuff=(char*)malloc(HEADERSIZE);
    if(!data->headerbuff) {
      free(data); /* free the memory again */
      return CURLE_OUT_OF_MEMORY;
    }


    data->out = stdout; /* default output to stdout */
    data->in  = stdin;  /* default input from stdin */
    data->err  = stderr;  /* default stderr to stderr */

    /* use fwrite as default function to store output */
    data->fwrite = (curl_write_callback)fwrite;

    /* use fread as default function to read input */
    data->fread = (curl_read_callback)fread;
    /* set the default passwd function */
    data->fpasswd = my_getpass;

    data->infilesize = -1; /* we don't know any size */

    data->current_speed = -1; /* init to negative == impossible */

    data->httpreq = HTTPREQ_GET; /* Default HTTP request */

    /* create an array with connection data struct pointers */
    data->numconnects = 5; /* hard-coded right now */
    data->connects = (struct connectdata **)
      malloc(sizeof(struct connectdata *) * data->numconnects);

    if(!data->connects) {
      free(data);
      return CURLE_OUT_OF_MEMORY;
    }

    memset(data->connects, 0, sizeof(struct connectdata *)*data->numconnects);


    /*************************************************************
     * Set signal handler
     *************************************************************/
#ifdef HAVE_SIGACTION
    sigaction(SIGALRM, NULL, &sigact);
    sigact.sa_handler = alarmfunc;
#ifdef SA_RESTART
    /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
    sigact.sa_flags &= ~SA_RESTART;
#endif
    sigaction(SIGALRM, &sigact, NULL);
#else
    /* no sigaction(), revert to the much lamer signal() */
#ifdef HAVE_SIGNAL
    signal(SIGALRM, alarmfunc);
#endif

#endif

    return CURLE_OK;
  }

  /* this is a very serious error */
  return CURLE_OUT_OF_MEMORY;
CURLcode Curl_setopt(CURL *curl, CURLoption option, ...)
{
  struct UrlData *data = curl;
  va_list param;
  char *cookiefile;

  va_start(param, option);

  switch(option) {
  case CURLOPT_RANDOM_FILE:
    /*
     * This is the path name to a file that contains random data to seed
     * the random SSL stuff with. The file is only used for reading.
     */
    data->ssl.random_file = va_arg(param, char *);
    break;
  case CURLOPT_EGDSOCKET:
    /*
     * The Entropy Gathering Daemon socket pathname
     */
    data->ssl.egdsocket = va_arg(param, char *);
    break;
  case CURLOPT_MAXCONNECTS:
    /*
     * Set the absolute number of maximum simultaneous alive connection that
     * libcurl is allowed to have.
     */
    {
      long newconnects= va_arg(param, long);
      struct connectdata **newptr;

      if(newconnects < data->numconnects) {
        /* Since this number is *decreased* from the existing number, we must
           close the possibly open connections that live on the indexes that
           are being removed! */
        int i;
        for(i=newconnects; i< data->numconnects; i++)
          Curl_disconnect(data->connects[i]);
      }
      if(newconnects) {
        newptr= (struct connectdata **)
          realloc(data->connects,
                  sizeof(struct connectdata *) * newconnects);
        if(!newptr)
          /* we closed a few connections in vain, but so what? */
          return CURLE_OUT_OF_MEMORY;
        data->connects = newptr;
        data->numconnects = newconnects;
      }
      else {
        /* zero makes NO cache at all */
        if(data->connects)
          free(data->connects);
        data->connects=NULL;
        data->numconnects=0;
      }
    }
    break;
  case CURLOPT_FORBID_REUSE:
    /*
     * When this transfer is done, it must not be left to be reused by a
     * subsequent transfer but shall be closed immediately.
     */
    data->bits.reuse_forbid = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_FRESH_CONNECT:
    /*
     * This transfer shall not use a previously cached connection but
     * should be made with a fresh new connect!
     */
    data->bits.reuse_fresh = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_VERBOSE:
    /*
     * Verbose means infof() calls that give a lot of information about
     * the connection and transfer procedures as well as internal choices.
     */
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.verbose = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_HEADER:
    /*
     * Set to include the header in the general data output stream.
     */
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.http_include_header = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_NOPROGRESS:
    /*
     * Shut off the internal supported progress meter
     */
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.hide_progress = va_arg(param, long)?TRUE:FALSE;
    if(data->bits.hide_progress)
      data->progress.flags |= PGRS_HIDE;
    break;
  case CURLOPT_NOBODY:
    /*
     * Do not include the body part in the output data stream.
     */
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.no_body = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_FAILONERROR:
    /*
     * Don't output the >=300 error code HTML-page, but instead only
     * return error.
     */
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.http_fail_on_error = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_UPLOAD:
    /*
     * We want to sent data to the remote host
     */
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.upload = va_arg(param, long)?TRUE:FALSE;
    if(data->bits.upload)
      /* If this is HTTP, PUT is what's needed to "upload" */
      data->httpreq = HTTPREQ_PUT;
Daniel Stenberg's avatar
Daniel Stenberg committed
  case CURLOPT_FILETIME:
    /*
     * Try to get the file time of the remote document. The time will
     * later (possibly) become available using curl_easy_getinfo().
     */
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.get_filetime = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_FTPLISTONLY:
    /*
     * An FTP option that changes the command to one that asks for a list
     * only, no file info details.
     */
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.ftp_list_only = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_FTPAPPEND:
    /*
     * We want to upload and append to an existing (FTP) file.
     */
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.ftp_append = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_NETRC:
    /*
     * Parse the $HOME/.netrc file
     */
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.use_netrc = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_FOLLOWLOCATION:
    /*
     * Follow Location: header hints on a HTTP-server.
     */
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.http_follow_location = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_FTPASCII:
    /*
     * Transfer FTP using ASCII instead of BINARY.
     */
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.ftp_ascii = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_PUT:
    /*
     * Use the HTTP PUT request to transfer data.
     */
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.http_put = va_arg(param, long)?TRUE:FALSE;
    if(data->bits.http_put)
      data->httpreq = HTTPREQ_PUT;
    break;
  case CURLOPT_MUTE:
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.mute = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_TIMECONDITION:
    /*
     * Set HTTP time condition. This must be one of the defines in the
     * curl/curl.h header file.
     */
    data->timecondition = va_arg(param, long);
    break;
  case CURLOPT_TIMEVALUE:
    /*
     * This is the value to compare with the remote document with the
     * method set with CURLOPT_TIMECONDITION
     */
    data->timevalue = va_arg(param, long);
    break;
  case CURLOPT_SSLVERSION:
    /*
     * Set explicit SSL version to try to connect with, as some SSL
     * implementations are lame.
     */
    data->ssl.version = va_arg(param, long);
  case CURLOPT_COOKIEFILE:
    /*
     * Set cookie file to read and parse.
     */
    cookiefile = (char *)va_arg(param, void *);
    if(cookiefile) {
      data->cookies = Curl_cookie_init(cookiefile);
  case CURLOPT_WRITEHEADER:
    /*
     * Callback function for header data
     */
    data->writeheader = (FILE *)va_arg(param, FILE *);
    break;
  case CURLOPT_COOKIE:
    /*
     * Cookie string to send to the remote server in the request.
     */
    data->cookie = va_arg(param, char *);
    break;
  case CURLOPT_ERRORBUFFER:
    /*
     * Error buffer provided by the caller to get the human readable
     * error string in.
     */
    data->errorbuffer = va_arg(param, char *);
    break;
  case CURLOPT_FILE:
    /*
     * FILE pointer to write to or include in the data write callback
     */
    data->out = va_arg(param, FILE *);
    break;
  case CURLOPT_FTPPORT:
    /*
     * Use FTP PORT, this also specifies which IP address to use
     */
    data->ftpport = va_arg(param, char *);
    data->bits.ftp_use_port = data->ftpport?1:0;
  case CURLOPT_HTTPHEADER:
    /*
     * Set a list with HTTP headers to use (or replace internals with)
     */
    data->headers = va_arg(param, struct curl_slist *);
  case CURLOPT_CUSTOMREQUEST:
    /*
     * Set a custom string to use as request
     */
    data->customrequest = va_arg(param, char *);
    if(data->customrequest)
      data->httpreq = HTTPREQ_CUSTOM;
  case CURLOPT_HTTPPOST:
    /*
     * Set to make us do HTTP POST
     */
    data->httppost = va_arg(param, struct HttpPost *);
    data->bits.http_formpost = data->httppost?1:0;
    if(data->bits.http_formpost)
      data->httpreq = HTTPREQ_POST_FORM;
  case CURLOPT_INFILE:
    /*
     * FILE pointer to read the file to be uploaded from. Or possibly
     * used as argument to the read callback.
     */
    data->in = va_arg(param, FILE *);
    break;
  case CURLOPT_INFILESIZE:
    /*
     * If known, this should inform curl about the file size of the
     * to-be-uploaded file.
     */
    data->infilesize = va_arg(param, long);
    break;
  case CURLOPT_LOW_SPEED_LIMIT:
    /*
     * The low speed limit that if transfers are below this for
     * CURLOPT_LOW_SPEED_TIME, the transfer is aborted.
     */
    data->low_speed_limit=va_arg(param, long);
    break;
  case CURLOPT_LOW_SPEED_TIME:
    /*
     * The low speed time that if transfers are below the set
     * CURLOPT_LOW_SPEED_LIMIT during this time, the transfer is aborted.
     */
    data->low_speed_time=va_arg(param, long);
    break;
  case CURLOPT_URL:
    data->url = va_arg(param, char *);
    break;
  case CURLOPT_PORT:
    /*
     * The port number to use when getting the URL
     */
    data->use_port = va_arg(param, long);
  case CURLOPT_POST:
    /* Does this option serve a purpose anymore? */
    data->bits.http_post = va_arg(param, long)?TRUE:FALSE;
    if(data->bits.http_post)
      data->httpreq = HTTPREQ_POST;
    break;
  case CURLOPT_POSTFIELDS:
    /*
     * A string with POST data. Makes curl HTTP POST.
     */
    data->postfields = va_arg(param, char *);
    data->bits.http_post = data->postfields?TRUE:FALSE;
    if(data->bits.http_post)
      data->httpreq = HTTPREQ_POST;
  case CURLOPT_POSTFIELDSIZE:
    /*
     * The size of the POSTFIELD data, if curl should now do a strlen
     * to find out. Enables binary posts.
     */
    data->postfieldsize = va_arg(param, long);
    break;
  case CURLOPT_REFERER:
    /*
     * String to set in the HTTP Referer: field.
     */
    data->referer = va_arg(param, char *);
    data->bits.http_set_referer = (data->referer && *data->referer)?1:0;
    /*
     * Switch on automatic referer that gets set if curl follows locations.
     */
    data->bits.http_auto_referer = va_arg(param, long)?1:0;
    break;
  case CURLOPT_PROXY:
    /*
     * Set proxy server:port to use as HTTP proxy
     */
    data->proxy = va_arg(param, char *);
    data->bits.httpproxy = data->proxy?1:0;
  case CURLOPT_HTTPPROXYTUNNEL:
    /*
     * Tunnel operations through the proxy instead of normal proxy use
     */
    data->bits.tunnel_thru_httpproxy = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_PROXYPORT:
    /*
     * Explicitly set HTTP proxy port number.
     */
    data->proxyport = va_arg(param, long);
  case CURLOPT_TIMEOUT:
    /*
     * The maximum time you allow curl to use for a single transfer
     * operation.
     */
    data->timeout = va_arg(param, long);
    break;
  case CURLOPT_CONNECTTIMEOUT:
    /*
     * The maximum time you allow curl to use to connect.
     */
    data->connecttimeout = va_arg(param, long);
    break;
  case CURLOPT_MAXREDIRS:
    /*
     * The maximum amount of hops you allow curl to follow Location:
     * headers. This should mostly be used to detect never-ending loops.
     */
    data->maxredirs = va_arg(param, long);
    break;
  case CURLOPT_USERAGENT:
    /*
     * String to use in the HTTP User-Agent field
     */
    data->useragent = va_arg(param, char *);
    break;
  case CURLOPT_USERPWD:
    /*
     * user:password to use in the operation
     */
    data->userpwd = va_arg(param, char *);
    break;
  case CURLOPT_POSTQUOTE:
    /*
     * List of RAW FTP commands to use after a transfer 
     */
    data->postquote = va_arg(param, struct curl_slist *);
    break;
  case CURLOPT_QUOTE:
    /*
     * List of RAW FTP commands to use before a transfer 
     */
    data->quote = va_arg(param, struct curl_slist *);
    break;
    data->fprogress = va_arg(param, curl_progress_callback);
    data->progress.callback = TRUE; /* no longer internal */
    /*
     * Custom client data to pass to the progress callback
     */
    data->progress_client = va_arg(param, void *);
    break;
  case CURLOPT_PASSWDFUNCTION:
    data->fpasswd = va_arg(param, curl_passwd_callback);
    break;
  case CURLOPT_PASSWDDATA:
    /*
     * Custom client data to pass to the password callback
     */
    data->passwd_client = va_arg(param, void *);
    break;
  case CURLOPT_PROXYUSERPWD:
    /*
     * user:password needed to use the proxy
     */
    data->proxyuserpwd = va_arg(param, char *);
    break;
  case CURLOPT_RANGE:
    /*
     * What range of the file you want to transfer
     */
    data->set_range = va_arg(param, char *);
    data->bits.set_range = data->set_range?1:0;
  case CURLOPT_RESUME_FROM:
    /*
     * Resume transfer at the give file position
     */
    data->set_resume_from = va_arg(param, long);
  case CURLOPT_STDERR:
    /*
     * Set to a FILE * that should receive all error writes. This
     * defaults to stderr for normal operations.
     */
    data->err = va_arg(param, FILE *);
    break;
  case CURLOPT_HEADERFUNCTION:
    /*
     * Set header write callback
     */
    data->fwrite_header = va_arg(param, curl_write_callback);
    break;
  case CURLOPT_WRITEFUNCTION:
    data->fwrite = va_arg(param, curl_write_callback);
  case CURLOPT_READFUNCTION:
    data->fread = va_arg(param, curl_read_callback);
  case CURLOPT_SSLCERT:
    /*
     * String that holds file name of the SSL certificate to use
     */
    data->cert = va_arg(param, char *);
    break;
  case CURLOPT_SSLCERTPASSWD:
    /*
     * String that holds the SSL certificate password.
     */
    data->cert_passwd = va_arg(param, char *);
    break;
  case CURLOPT_CRLF:
    /*
     * Kludgy option to enable CRLF convertions. Subject for
     * removal.
     */
    data->crlf = va_arg(param, long);
    break;
  case CURLOPT_INTERFACE:
    /*
     * Set what interface to bind to when performing an operation and thus
     * what from-IP your connection will use.
     */
    data->device = va_arg(param, char *);
    break;
Daniel Stenberg's avatar
Daniel Stenberg committed
  case CURLOPT_KRB4LEVEL:
    /*
     * A string that defines the krb4 security level.
     */
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->krb4_level = va_arg(param, char *);
    data->bits.krb4=data->krb4_level?TRUE:FALSE;
    break;
    data->ssl.verifypeer = va_arg(param, long);
    break;
  case CURLOPT_CAINFO:
    /*
     * Set CA info for SSL connection. Specify file name of the CA certificate
     */
    data->ssl.CAfile = va_arg(param, char *);
    data->ssl.CApath = NULL; /*This does not work on windows.*/
    break;
  case CURLOPT_TELNETOPTIONS:
    /*
     * Set a linked list of telnet options
     */
    data->telnet_options = va_arg(param, struct curl_slist *);
    break;
  default:
    /* unknown tag and its companion, just ignore: */
    return CURLE_READ_ERROR; /* correct this */
Daniel Stenberg's avatar
Daniel Stenberg committed
  }
  return CURLE_OK;
CURLcode Curl_disconnect(struct connectdata *conn)
  if(!conn)
    return CURLE_OK; /* this is closed and fine already */

  /*
   * The range string is usually freed in curl_done(), but we might
   * get here *instead* if we fail prematurely. Thus we need to be able
   * to free this resource here as well.
   */
  if(conn->bits.rangestringalloc) {
    free(conn->range);
    conn->bits.rangestringalloc = FALSE;
  }

  if(-1 != conn->connectindex) {
    infof(conn->data, "Closing live connection (#%d)\n", conn->connectindex);
    conn->data->connects[conn->connectindex] = NULL;
Daniel Stenberg's avatar
Daniel Stenberg committed
  if(conn->curl_disconnect)
    /* This is set if protocol-specific cleanups should be made */
    conn->curl_disconnect(conn);

  if(conn->proto.generic)
    free(conn->proto.generic);
#ifdef ENABLE_IPV6
Daniel Stenberg's avatar
Daniel Stenberg committed
  if(conn->hp) /* host name info */
    freeaddrinfo(conn->hp);
Daniel Stenberg's avatar
Daniel Stenberg committed
  if(conn->hostent_buf) /* host name info */
    free(conn->hostent_buf);
Daniel Stenberg's avatar
Daniel Stenberg committed

  if(conn->path) /* the URL path part */
    free(conn->path);

#ifdef USE_SSLEAY
  if (conn->ssl.use) {
    if(conn->ssl.handle) {
      (void)SSL_shutdown(conn->ssl.handle);
      SSL_set_connect_state(conn->ssl.handle);
      SSL_free (conn->ssl.handle);
      conn->ssl.handle = NULL;
    }
    if(conn->ssl.ctx) {
      SSL_CTX_free (conn->ssl.ctx);
      conn->ssl.ctx = NULL;
    }
    conn->ssl.use = FALSE; /* get back to ordinary socket usage */
  }
#endif /* USE_SSLEAY */

  /* close possibly still open sockets */
  if(-1 != conn->secondarysocket)
    sclose(conn->secondarysocket);
  if(-1 != conn->firstsocket)
    sclose(conn->firstsocket);

  if(conn->allocptr.proxyuserpwd)
    free(conn->allocptr.proxyuserpwd);
  if(conn->allocptr.uagent)
    free(conn->allocptr.uagent);
  if(conn->allocptr.userpwd)
    free(conn->allocptr.userpwd);
  if(conn->allocptr.rangeline)
    free(conn->allocptr.rangeline);
  if(conn->allocptr.ref)
    free(conn->allocptr.ref);
  if(conn->allocptr.cookie)
    free(conn->allocptr.cookie);
  if(conn->allocptr.host)
    free(conn->allocptr.host);

  if(conn->proxyhost)
    free(conn->proxyhost);

  free(conn); /* free all the connection oriented data */
Daniel Stenberg's avatar
Daniel Stenberg committed
/*
 * This function should return TRUE if the socket is to be assumed to
 * be dead. Most commonly this happens when the server has closed the
 * connection due to inactivity.
 */
static bool SocketIsDead(int sock) 
{ 
  int sval; 
  bool ret_val = TRUE; 
  fd_set check_set; 
  struct timeval to; 

  FD_ZERO(&check_set); 
  FD_SET(sock,&check_set); 

  to.tv_sec = 0; 
  to.tv_usec = 1; 

  sval = select(sock + 1, &check_set, 0, 0, &to);
  if(sval == 0) 
    /* timeout */
    ret_val = FALSE; 
  
  return ret_val;
}

/*
 * Given one filled in connection struct, this function should detect if there
 * already is one that have all the significant details exactly the same and
 * thus should be used instead.
 */
static bool
ConnectionExists(struct UrlData *data,
                 struct connectdata *needle,
                 struct connectdata **usethis)
Daniel Stenberg's avatar
Daniel Stenberg committed
{
  size_t i;
  struct connectdata *check;

  for(i=0; i< data->numconnects; i++) {
    /*
     * Note that if we use a HTTP proxy, we check connections to that
     * proxy and not to the actual remote server.
     */
    check = data->connects[i];
    if(!check)
      /* NULL pointer means not filled-in entry */
      continue;
    if(!needle->bits.httpproxy) {
      /* The requested connection does not use a HTTP proxy */

      if(strequal(needle->protostr, check->protostr) &&
         strequal(needle->name, check->name) &&
         (needle->remote_port == check->remote_port) ) {
Daniel Stenberg's avatar
Daniel Stenberg committed
        bool dead;
        if(strequal(needle->protostr, "FTP")) {
          /* This is FTP, verify that we're using the same name and
             password as well */
          if(!strequal(needle->data->user, check->proto.ftp->user) ||
             !strequal(needle->data->passwd, check->proto.ftp->passwd)) {
            /* one of them was different */
            continue;
          }
        }
Daniel Stenberg's avatar
Daniel Stenberg committed
        dead = SocketIsDead(check->firstsocket);
        if(dead) {
          infof(data, "Connection %d seems to be dead!\n", i);
          Curl_disconnect(check); /* disconnect resources */
Daniel Stenberg's avatar
Daniel Stenberg committed
          data->connects[i]=NULL; /* nothing here */
          continue; /* try another one now */
        }

        *usethis = check;
        return TRUE; /* yes, we found one to use! */
        
Daniel Stenberg's avatar
Daniel Stenberg committed
      }
    else { /* The requested needle connection is using a proxy,
              is the checked one using the same? */
      if(check->bits.httpproxy &&
         strequal(needle->proxyhost, check->proxyhost) &&
         needle->port == check->port) {
        /* This is the same proxy connection, use it! */
        *usethis = check;
        return TRUE;
      }
    }
  }
  return FALSE; /* no matching connecting exists */
}

/*
 * This function frees/closes a connection in the connection cache. This
 * should take the previously set policy into account when deciding which
 * of the connections to kill.
 */
static int
ConnectionKillOne(struct UrlData *data)
{
  size_t i;
  struct connectdata *conn;
  int highscore=-1;
  int connindex=-1;
  int score;
  CURLcode result;
  struct timeval now;

  now = Curl_tvnow();

  for(i=0; i< data->numconnects; i++) {
    conn = data->connects[i];
    
    if(!conn)
      continue;

    /*
     * By using the set policy, we score each connection.
     */
    switch(data->closepolicy) {
    case CURLCLOSEPOLICY_LEAST_RECENTLY_USED: