Skip to content
Snippets Groups Projects
url.c 62.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • 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);
    
    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;
    
      if(data->bits.rangestringalloc) {
        free(data->range);
        data->range=NULL;
    
        data->bits.rangestringalloc=0; /* free now */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
      /* 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;
    
    
      /* 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 = (size_t (*)(char *, size_t, size_t, FILE *))fwrite;
    
        /* use fread as default function to read input */
        data->fread = (size_t (*)(char *, size_t, size_t, FILE *))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);
    
    
        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_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_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 *);
    
        data->bits.user_passwd = data->userpwd?1:0;
    
      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 *);
    
        data->bits.proxy_user_passwd = data->proxyuserpwd?1:0;
    
      case CURLOPT_RANGE:
    
        /*
         * What range of the file you want to transfer
         */
    
        data->range = va_arg(param, char *);
    
        data->bits.set_range = data->range?1:0;
    
      case CURLOPT_RESUME_FROM:
    
        /*
         * Resume transfer at the give file position
         */
    
        data->resume_from = va_arg(param, long);
        break;
    
      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_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;
    
    #if !defined(WIN32)||defined(__CYGWIN32__)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #ifndef RETSIGTYPE
    #define RETSIGTYPE void
    #endif
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    RETSIGTYPE alarmfunc(int signal)
    {
      /* this is for "-ansi -Wall -pedantic" to stop complaining!   (rabe) */
      (void)signal;
      return;
    }
    #endif
    
    
    CURLcode Curl_disconnect(struct connectdata *conn)
    
      if(!conn)
        return CURLE_OK; /* this is closed and fine already */
    
    
      infof(conn->data, "Closing live connection (#%d)\n", conn->connectindex);
    
    
      if(-1 != conn->connectindex)
        /* unlink ourselves! */
        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->port == check->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! */
        }
    
        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;
    
      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) {
        default:
          score = 1; /* not implemented yet */
          break;
        }
    
        if(score > highscore) {
          highscore = score;
          connindex = i;
        }
      }
      if(connindex >= 0) {
    
        /* the winner gets the honour of being disconnected */
    
        result = Curl_disconnect(data->connects[connindex]);
    
    
        /* clean the array entry */
        data->connects[connindex] = NULL;
      }
    
      return connindex; /* return the available index or -1 */
    }
    
    /*
     * The given input connection struct pointer is to be stored. If the "cache"
     * is already full, we must clean out the most suitable using the previously
     * set policy.
     *
     * The given connection should be unique. That must've been checked prior to
     * this call.
     */
    static unsigned int
    ConnectionStore(struct UrlData *data,
                    struct connectdata *conn)
    {
      size_t i;
      for(i=0; i< data->numconnects; i++) {
        if(!data->connects[i])
          break;
      }
    
      if(i == data->numconnects) {
    
        /* there was no room available, kill one */
        i = ConnectionKillOne(data);
    
        infof(data, "Connection (#%d) was killed to make room\n", i);
      }
    
    
      data->connects[i] = conn; /* fill in this */
      conn->connectindex = i; /* make the child know where the pointer to this
                                 particular data is stored */
    
      return i;
    }
    
    static CURLcode ConnectPlease(struct UrlData *data,
                                  struct connectdata *conn)
    {
    
    #ifndef ENABLE_IPV6
      conn->firstsocket = socket(AF_INET, SOCK_STREAM, 0);