Skip to content
url.c 137 KiB
Newer Older
  int sval;
  bool ret_val = TRUE;
  sval = Curl_socket_ready(sock, CURL_SOCKET_BAD, 0);
Daniel Stenberg's avatar
Daniel Stenberg committed
    /* timeout */
Daniel Stenberg's avatar
Daniel Stenberg committed
  return ret_val;
}

static bool IsPipeliningPossible(const struct SessionHandle *handle)
{
  if (handle->multi && Curl_multi_canPipeline(handle->multi) &&
      (handle->set.httpreq == HTTPREQ_GET ||
       handle->set.httpreq == HTTPREQ_HEAD) &&
      handle->set.httpversion != CURL_HTTP_VERSION_1_0)
    return TRUE;

  return FALSE;
}

static bool IsPipeliningEnabled(const struct SessionHandle *handle)
{
  if (handle->multi && Curl_multi_canPipeline(handle->multi))
    return TRUE;

  return FALSE;
}

CURLcode Curl_addHandleToPipeline(struct SessionHandle *data,
                                  struct curl_llist *pipe)
#ifdef CURLDEBUG
  if(!IsPipeliningPossible(data)) {
    /* when not pipelined, there MUST be no handle in the list already */
    if(pipe->head)
      infof(data, "PIPE when no PIPE supposed!\n");
  }
#endif
  if (!Curl_llist_insert_next(pipe, pipe->tail, data))
    return CURLE_OUT_OF_MEMORY;
  return CURLE_OK;
int Curl_removeHandleFromPipeline(struct SessionHandle *handle,
                                   struct curl_llist *pipe)
{
  struct curl_llist_element *curr;

  curr = pipe->head;
  while (curr) {
    if (curr->ptr == handle) {
      Curl_llist_remove(pipe, curr, NULL);
#if 0 /* this code is saved here as it is useful for debugging purposes */
static void Curl_printPipeline(struct curl_llist *pipe)
{
  struct curl_llist_element *curr;

  curr = pipe->head;
  while (curr) {
    struct SessionHandle *data = (struct SessionHandle *) curr->ptr;
    infof(data, "Handle in pipeline: %s\n", data->reqdata.path);
    curr = curr->next;
  }
}
#endif

bool Curl_isHandleAtHead(struct SessionHandle *handle,
                         struct curl_llist *pipe)
{
  struct curl_llist_element *curr = pipe->head;
  if (curr) {
Yang Tse's avatar
Yang Tse committed
    return (bool)(curr->ptr == handle);
static struct SessionHandle* gethandleathead(struct curl_llist *pipe)
{
  struct curl_llist_element *curr = pipe->head;
  if (curr) {
    return (struct SessionHandle *) curr->ptr;
  }

  return NULL;
}

static void signalPipeClose(struct curl_llist *pipe)
  curr = pipe->head;
  while (curr) {
    struct curl_llist_element *next = curr->next;
    struct SessionHandle *data = (struct SessionHandle *) curr->ptr;

#ifdef CURLDEBUG /* debug-only code */
    if(data->magic != CURLEASY_MAGIC_NUMBER) {
      /* MAJOR BADNESS */
      infof(data, "signalPipeClose() found BAAD easy handle\n");
    Curl_multi_handlePipeBreak(data);
 * Given one filled in connection struct (named needle), this function should
 * detect if there already is one that has all the significant details
 * exactly the same and thus should be used instead.
 *
 * If there is a match, this function returns TRUE - and has marked the
 * connection as 'in-use'. It must later be called with ConnectionDone() to
 * return back to 'idle' (unused) state.
ConnectionExists(struct SessionHandle *data,
                 struct connectdata *needle,
                 struct connectdata **usethis)
Daniel Stenberg's avatar
Daniel Stenberg committed
{
  struct connectdata *check;
  bool canPipeline = IsPipeliningPossible(data);
  for(i=0; i< data->state.connc->num; i++) {
Yang Tse's avatar
Yang Tse committed
    size_t pipeLen = 0;
    /*
     * Note that if we use a HTTP proxy, we check connections to that
     * proxy and not to the actual remote server.
     */
    check = data->state.connc->connects[i];
    if(!check)
      /* NULL pointer means not filled-in entry */
      continue;
    pipeLen = check->send_pipe->size + check->recv_pipe->size;

    if (check->connectindex == -1) {
      check->connectindex = i; /* Set this appropriately since it might have
                                  been set to -1 when the easy was removed
                                  from the multi */
    }

      /* can only happen within multi handles, and means that another easy
    /* ip_addr_str is NULL only if the resolving of the name hasn't completed
       yet and until then we don't re-use this connection */
            "Connection #%ld hasn't finished name resolve, can't reuse\n",
    if ((check->sock[FIRSTSOCKET] == CURL_SOCKET_BAD) || check->bits.close) {
      /* Don't pick a connection that hasn't connected yet or that is going to
         get closed. */
      infof(data, "Connection #%ld isn't open enough, can't reuse\n",
            check->connectindex);
#ifdef CURLDEBUG
      if (check->recv_pipe->size > 0) {
        infof(data, "BAD! Unconnected #%ld has a non-empty recv pipeline!\n",
              check->connectindex);
      }
#endif
      continue;
    }

    if (pipeLen >= MAX_PIPELINE_LENGTH) {
      infof(data, "Connection #%ld has its pipeline full, can't reuse\n",
            check->connectindex);
    if (canPipeline) {
      /* Make sure the pipe has only GET requests */
      struct SessionHandle* sh = gethandleathead(check->send_pipe);
      struct SessionHandle* rh = gethandleathead(check->recv_pipe);
      if (sh) {
        if(!IsPipeliningPossible(sh))
          continue;
      }
      else if (rh) {
        if(!IsPipeliningPossible(rh))
          continue;
      }
    }

    if((needle->protocol&PROT_SSL) != (check->protocol&PROT_SSL))
      /* don't do mixed SSL and non-SSL connections */
      continue;

    if(!needle->bits.httpproxy || needle->protocol&PROT_SSL) {
      /* The requested connection does not use a HTTP proxy or it
         uses SSL. */

      if(!(needle->protocol&PROT_SSL) && check->bits.httpproxy)
        /* we don't do SSL but the cached connection has a proxy,
           then don't match this */
        continue;

      if(strequal(needle->protostr, check->protostr) &&
         strequal(needle->host.name, check->host.name) &&
         (needle->remote_port == check->remote_port) ) {
        if(needle->protocol & PROT_SSL) {
          /* This is SSL, verify that we're using the same
             ssl options as well */
          if(!Curl_ssl_config_matches(&needle->ssl_config,
                                      &check->ssl_config)) {
            infof(data,
                  "Connection #%ld has different SSL parameters, "
                  "can't reuse\n",
                  check->connectindex );
        if((needle->protocol & PROT_FTP) ||
           ((needle->protocol & PROT_HTTP) &&
            (data->state.authhost.want==CURLAUTH_NTLM))) {
          /* This is FTP or HTTP+NTLM, verify that we're using the same name
             and password as well */
          if(!strequal(needle->user, check->user) ||
             !strequal(needle->passwd, check->passwd)) {
            /* one of them was different */
            continue;
          }
        }
Daniel Stenberg's avatar
Daniel Stenberg committed
      }
    else { /* The requested needle connection is using a proxy,
              is the checked one using the same host, port and type? */
      if(check->bits.proxy &&
         (needle->proxytype == check->proxytype) &&
         strequal(needle->proxy.name, check->proxy.name) &&
         needle->port == check->port) {
        /* This is the same proxy connection, use it! */
      if (!IsPipeliningEnabled(data)) {
        /* The check for a dead socket makes sense only in the
           non-pipelining case */
        bool dead = SocketIsDead(check->sock[FIRSTSOCKET]);
        if(dead) {
          infof(data, "Connection #%d seems to be dead!\n", i);

          Curl_disconnect(check); /* disconnect resources */
          data->state.connc->connects[i]=NULL; /* nothing here */
      check->inuse = TRUE; /* mark this as being in use so that no other
                              handle in a multi stack may nick it */
      if (canPipeline) {
        /* Mark the connection as being in a pipeline */
        check->is_in_pipeline = TRUE;
      *usethis = check;
      return TRUE; /* yes, we found one to use! */
  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.
 */
ConnectionKillOne(struct SessionHandle *data)
  struct connectdata *conn;
  long highscore=-1;
  long connindex=-1;
  long score;
  struct timeval now;

  now = Curl_tvnow();
  for(i=0; data->state.connc && (i< data->state.connc->num); i++) {
    conn = data->state.connc->connects[i];
    /* Set higher score for the age passed since the connection was used */
    score = Curl_tvdiff(now, conn->now);

    if(score > highscore) {
      highscore = score;
      connindex = i;
    }
  }
  if(connindex >= 0) {
    /* Set the connection's owner correctly */
    conn = data->state.connc->connects[connindex];
    conn->data = data;

    /* the winner gets the honour of being disconnected */

    /* clean the array entry */
    data->state.connc->connects[connindex] = NULL;
  }

  return connindex; /* return the available index or -1 */
}

/* this connection can now be marked 'idle' */
static void
ConnectionDone(struct connectdata *conn)
{
  conn->inuse = FALSE;
  if (!conn->send_pipe && !conn->recv_pipe)
/*
 * 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.
 */
ConnectionStore(struct SessionHandle *data,
                struct connectdata *conn)
{
  for(i=0; i< data->state.connc->num; i++) {
    if(!data->state.connc->connects[i])
    /* there was no room available, kill one */
    i = ConnectionKillOne(data);
      infof(data, "Connection (#%d) was killed to make room (holds %d)\n",
            i, data->state.connc->num);
    else
      infof(data, "This connection did not fit in the connection cache\n");
  conn->connectindex = i; /* Make the child know where the pointer to this
                             particular data is stored. But note that this -1
                             if this is not within the cache and this is
                             probably not checked for everywhere (yet). */
  conn->inuse = TRUE;
    /* Only do this if a true index was returned, if -1 was returned there
       is no room in the cache for an unknown reason and we cannot store
       this there.

       TODO: make sure we really can work with more handles than positions in
       the cache, or possibly we should (allow to automatically) resize the
       connection cache when we add more easy handles to a multi handle!
    */
    data->state.connc->connects[i] = conn; /* fill in this */
    conn->data = data;
static CURLcode ConnectPlease(struct SessionHandle *data,
                              struct connectdata *conn,
  CURLcode result;
Yang Tse's avatar
Yang Tse committed
#ifndef CURL_DISABLE_VERBOSE_STRINGS
  char *hostname = conn->bits.proxy?conn->proxy.name:conn->host.name;
  infof(data, "About to connect() to %s%s port %d (#%d)\n",
        hostname, conn->port, conn->connectindex);
Yang Tse's avatar
Yang Tse committed
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed

  /*************************************************************
   * Connect to server/proxy
   *************************************************************/
  result= Curl_connecthost(conn,
  if(CURLE_OK == result) {
    /* All is cool, then we store the current information */
    conn->dns_entry = hostaddr;
    conn->ip_addr = addr;
    result = Curl_store_ip_addr(conn);

    if(CURLE_OK == result) {

      switch(data->set.proxytype) {
      case CURLPROXY_SOCKS5:
	result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, conn->host.name,
			     conn->remote_port, FIRSTSOCKET, conn);
	break;
      case CURLPROXY_HTTP:
	/* do nothing here. handled later. */
	break;
      case CURLPROXY_SOCKS4:
	result = Curl_SOCKS4(conn->proxyuser, conn->host.name, conn->remote_port,
			     FIRSTSOCKET, conn);
	break;
      default:
	failf(data, "unknown proxytype option given");
	result = CURLE_COULDNT_CONNECT;
	break;
      }
  if(result)
    *connected = FALSE; /* mark it as not connected */
  return result;
 * verboseconnect() displays verbose information after a connect
Yang Tse's avatar
Yang Tse committed
#ifndef CURL_DISABLE_VERBOSE_STRINGS
static void verboseconnect(struct connectdata *conn)
  infof(conn->data, "Connected to %s (%s) port %d (#%d)\n",
        conn->bits.proxy ? conn->proxy.dispname : conn->host.dispname,
        conn->ip_addr_str, conn->port, conn->connectindex);
Yang Tse's avatar
Yang Tse committed
#endif
int Curl_protocol_getsock(struct connectdata *conn,
                          curl_socket_t *socks,
                          int numsocks)
  if(conn->curl_proto_getsock)
    return conn->curl_proto_getsock(conn, socks, numsocks);
  return GETSOCK_BLANK;
int Curl_doing_getsock(struct connectdata *conn,
                       curl_socket_t *socks,
                       int numsocks)
  if(conn && conn->curl_doing_getsock)
    return conn->curl_doing_getsock(conn, socks, numsocks);
  return GETSOCK_BLANK;
}

/*
 * We are doing protocol-specific connecting and this is being called over and
 * over from the multi interface until the connection phase is done on
 * protocol layer.
 */

CURLcode Curl_protocol_connecting(struct connectdata *conn,
                                  bool *done)
{
  CURLcode result=CURLE_OK;

  if(conn && conn->curl_connecting) {
    *done = FALSE;
    result = conn->curl_connecting(conn, done);
  }
  else
    *done = TRUE;

  return result;
}

/*
 * We are DOING this is being called over and over from the multi interface
 * until the DOING phase is done on protocol layer.
 */

CURLcode Curl_protocol_doing(struct connectdata *conn, bool *done)
{
  CURLcode result=CURLE_OK;

  if(conn && conn->curl_doing) {
    *done = FALSE;
    result = conn->curl_doing(conn, done);
  }
  else
    *done = TRUE;

  return result;
}

/*
 * We have discovered that the TCP connection has been successful, we can now
 * proceed with some action.
 *
 */
CURLcode Curl_protocol_connect(struct connectdata *conn,
                               bool *protocol_done)
  struct SessionHandle *data = conn->data;
  *protocol_done = FALSE;

  if(conn->bits.tcpconnect && conn->bits.protoconnstart) {
    /* We already are connected, get back. This may happen when the connect
       worked fine in the first call, like when we connect to a local server
       or proxy. Note that we don't know if the protocol is actually done.

       Unless this protocol doesn't have any protocol-connect callback, as
       then we know we're done. */
    if(!conn->curl_connecting)
      *protocol_done = TRUE;

    Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */
  if(!conn->bits.protoconnstart) {
    if(conn->curl_connect) {
      /* is there a protocol-specific connect() procedure? */
      /* Set start time here for timeout purposes in the connect procedure, it
         is later set again for the progress meter purpose */
      conn->now = Curl_tvnow();
      /* Call the protocol-specific connect function */
      result = conn->curl_connect(conn, protocol_done);
    }
    else
      *protocol_done = TRUE;

    /* it has started, possibly even completed but that knowledge isn't stored
       in this bit! */
    if (!result)
      conn->bits.protoconnstart = TRUE;
/*
 * Helpers for IDNA convertions.
 */
#ifdef USE_LIBIDN
static bool is_ASCII_name(const char *hostname)
{
  const unsigned char *ch = (const unsigned char*)hostname;

  while (*ch) {
    if (*ch++ & 0x80)
      return FALSE;
  }
  return TRUE;
}
Gisle Vanem's avatar
 
Gisle Vanem committed

/*
 * Check if characters in hostname is allowed in Top Level Domain.
 */
static bool tld_check_name(struct SessionHandle *data,
                           const char *ace_hostname)
Gisle Vanem's avatar
 
Gisle Vanem committed
{
  size_t err_pos;
  char *uc_name = NULL;
  int rc;
Yang Tse's avatar
Yang Tse committed
#ifndef CURL_DISABLE_VERBOSE_STRINGS
  char *tld_errmsg = (char *)"<no msg>";
Yang Tse's avatar
Yang Tse committed
#else
  (void)data;
#endif
Gisle Vanem's avatar
 
Gisle Vanem committed

  /* Convert (and downcase) ACE-name back into locale's character set */
  rc = idna_to_unicode_lzlz(ace_hostname, &uc_name, 0);
  if (rc != IDNA_SUCCESS)
Gisle Vanem's avatar
 
Gisle Vanem committed
    return (FALSE);
Gisle Vanem's avatar
 
Gisle Vanem committed

  rc = tld_check_lz(uc_name, &err_pos, NULL);
Yang Tse's avatar
Yang Tse committed
#ifndef CURL_DISABLE_VERBOSE_STRINGS
Yang Tse's avatar
Yang Tse committed
  if (rc != TLD_SUCCESS)
Yang Tse's avatar
Yang Tse committed
    tld_errmsg = (char *)tld_strerror((Tld_rc)rc);
Yang Tse's avatar
Yang Tse committed
  if (rc == TLD_INVALID)
    infof(data, "WARNING: %s; pos %u = `%c'/0x%02X\n",
          tld_errmsg, err_pos, uc_name[err_pos],
          uc_name[err_pos] & 255);
Gisle Vanem's avatar
 
Gisle Vanem committed
  else if (rc != TLD_SUCCESS)
Yang Tse's avatar
Yang Tse committed
    infof(data, "WARNING: TLD check for %s failed; %s\n",
          uc_name, tld_errmsg);
#endif /* CURL_DISABLE_VERBOSE_STRINGS */
Gisle Vanem's avatar
 
Gisle Vanem committed
  if (uc_name)
     idn_free(uc_name);
Yang Tse's avatar
Yang Tse committed
  return (bool)(rc == TLD_SUCCESS);
Gisle Vanem's avatar
 
Gisle Vanem committed
}
static void fix_hostname(struct SessionHandle *data,
                         struct connectdata *conn, struct hostname *host)
Yang Tse's avatar
Yang Tse committed
#ifndef USE_LIBIDN
  (void)data;
  (void)conn;
#elif defined(CURL_DISABLE_VERBOSE_STRINGS)
  (void)conn;
#endif

  /* set the name we use to display the host name */
Daniel Stenberg's avatar
Daniel Stenberg committed
  host->dispname = host->name;

#ifdef USE_LIBIDN
  /*************************************************************
   * Check name for non-ASCII and convert hostname to ACE form.
   *************************************************************/
  if (!is_ASCII_name(host->name) &&
      stringprep_check_version(LIBIDN_REQUIRED_VERSION)) {
    char *ace_hostname = NULL;
    int rc = idna_to_ascii_lz(host->name, &ace_hostname, 0);
    infof (data, "Input domain encoded as `%s'\n",
           stringprep_locale_charset ());
    if (rc != IDNA_SUCCESS)
      infof(data, "Failed to convert %s to ACE; %s\n",
            host->name, Curl_idn_strerror(conn,rc));
      /* tld_check_name() displays a warning if the host name contains
         "illegal" characters for this TLD */
      (void)tld_check_name(data, ace_hostname);
Gisle Vanem's avatar
 
Gisle Vanem committed

      host->encalloc = ace_hostname;
      /* change the name pointer to point to the encoded hostname */
      host->name = host->encalloc;
    }
  }
Gisle Vanem's avatar
Gisle Vanem committed
#endif
/*
 * Parse URL and fill in the relevant members of the connection struct.
static CURLcode ParseURLAndFillConnection(struct SessionHandle *data,
                                          struct connectdata *conn)
Daniel Stenberg's avatar
Daniel Stenberg committed
  /*************************************************************
   * Parse the URL.
   *
   * We need to parse the url even when using the proxy, because we will need
   * the hostname and port in case we are trying to SSL connect through the
   * proxy -- and we don't know if we will need to use SSL until we parse the
   * url ...
   ************************************************************/
  if((2 == sscanf(data->change.url, "%15[^:]:%[^\n]",
                  path)) && strequal(conn->protostr, "file")) {
    if(path[0] == '/' && path[1] == '/') {
      /* Allow omitted hostname (e.g. file:/<path>).  This is not strictly
       * speaking a valid file: URL by RFC 1738, but treating file:/<path> as
       * file://localhost/<path> is similar to how other schemes treat missing
       * hostnames.  See RFC 1808. */

      /* This cannot be done with strcpy() in a portable manner, since the
         memory areas overlap! */
      memmove(path, path + 2, strlen(path + 2)+1);
Daniel Stenberg's avatar
Daniel Stenberg committed
    /*
     * we deal with file://<host>/<path> differently since it supports no
     * hostname other than "localhost" and "127.0.0.1", which is unique among
     * the URL protocols specified in RFC 1738
     */
Daniel Stenberg's avatar
Daniel Stenberg committed
      /* the URL included a host name, we ignore host names in file:// URLs
         as the standards don't define what to do with them */
Daniel Stenberg's avatar
Daniel Stenberg committed
      if(ptr) {
        /* there was a slash present
Daniel Stenberg's avatar
Daniel Stenberg committed
           RFC1738 (section 3.1, page 5) says:
Daniel Stenberg's avatar
Daniel Stenberg committed
           The rest of the locator consists of data specific to the scheme,
           and is known as the "url-path". It supplies the details of how the
           specified resource can be accessed. Note that the "/" between the
           host (or port) and the url-path is NOT part of the url-path.
Daniel Stenberg's avatar
Daniel Stenberg committed
           As most agents use file://localhost/foo to get '/foo' although the
           slash preceding foo is a separator and not a slash for the path,
Daniel Stenberg's avatar
Daniel Stenberg committed
           a URL as file://localhost//foo must be valid as well, to refer to
           the same file with an absolute path.
        */

        if(ptr[1] && ('/' == ptr[1]))
          /* if there was two slashes, we skip the first one as that is then
             used truly as a separator */
        /* This cannot be made with strcpy, as the memory chunks overlap! */
    strcpy(conn->protostr, "file"); /* store protocol string lowercase */
Daniel Stenberg's avatar
Daniel Stenberg committed
  }
Daniel Stenberg's avatar
Daniel Stenberg committed
      /*
       * The URL was badly formatted, let's try the browser-style _without_
       * protocol specified like 'http://'.
       */
      if((1 > sscanf(data->change.url, "%[^\n/]%[^\n]",
Daniel Stenberg's avatar
Daniel Stenberg committed
        /*
         * We couldn't even get this format.
         */
        failf(data, "<url> malformed");
        return CURLE_URL_MALFORMAT;
      }
Daniel Stenberg's avatar
Daniel Stenberg committed

      /*
       * Since there was no protocol part specified, we guess what protocol it
       * is based on the first letters of the server name.
       */

      /* Note: if you add a new protocol, please update the list in
       * lib/version.c too! */

      if(checkprefix("FTP.", conn->host.name))
        strcpy(conn->protostr, "ftp");
      else if (checkprefix("DICT.", conn->host.name))
        strcpy(conn->protostr, "DICT");
      else if (checkprefix("LDAP.", conn->host.name))
        strcpy(conn->protostr, "LDAP");
        strcpy(conn->protostr, "http");
      }

      conn->protocol |= PROT_MISSING; /* not given in URL */
    }
  /* We search for '?' in the host name (but only on the right side of a
   * @-letter to allow ?-letters in username and password) to handle things
   * like http://example.com?param= (notice the missing '/').
   */
  at = strchr(conn->host.name, '@');
  if(at)
    tmp = strchr(at+1, '?');
  else
    tmp = strchr(conn->host.name, '?');

  if(tmp) {
    /* We must insert a slash before the '?'-letter in the URL. If the URL had
       a slash after the '?', that is where the path currently begins and the
       '?string' is still part of the host name.

       We must move the trailing part from the host name and put it first in
       the path. And have it all prefixed with a slash.
    */

    size_t hostlen = strlen(tmp);

    /* move the existing path plus the zero byte forward, to make room for
       the host-name part */
    memmove(path+hostlen+1, path, pathlen+1);

     /* now copy the trailing host part in front of the existing path */
    path[0]='/'; /* prepend the missing slash */
    *tmp=0; /* now cut off the hostname at the ? */
  }
    /* if there's no path set, use a single slash */
  /* If the URL is malformatted (missing a '/' after hostname before path) we
   * insert a slash here. The only letter except '/' we accept to start a path
   * is '?'.
   */
    /* We need this function to deal with overlapping memory areas. We know
       that the memory area 'path' points to is 'urllen' bytes big and that
       is bigger than the path. Use +1 to move the zero byte too. */
    memmove(&path[1], path, strlen(path)+1);
    path[0] = '/';
  /*
   * So if the URL was A://B/C,
   *   conn->protostr is A
   *   data->reqdata.path is /C
   */

  return CURLE_OK;
}

static void llist_dtor(void *user, void *element)
{
  (void)user;
  (void)element;
  /* Do nothing */
}

static CURLcode setup_range(struct SessionHandle *data)
{
  /*
   * If we're doing a resumed transfer, we need to setup our stuff
   * properly.
   */
  struct HandleData *req = &data->reqdata;

  req->resume_from = data->set.set_resume_from;
  if (req->resume_from || data->set.str[STRING_SET_RANGE]) {
    if (req->rangestringalloc)
      free(req->range);

    if(req->resume_from)
      req->range = aprintf("%" FORMAT_OFF_T "-", req->resume_from);
    else
      req->range = strdup(data->set.str[STRING_SET_RANGE]);

    req->rangestringalloc = req->range?TRUE:FALSE;

    if(!req->range)
      return CURLE_OUT_OF_MEMORY;

    /* tell ourselves to fetch this range */
    req->use_range = TRUE;        /* enable range download */
  }
  else
    req->use_range = FALSE; /* disable range download */

  return CURLE_OK;
}
/***************************************************************
* Setup connection internals specific to the requested protocol
***************************************************************/
static CURLcode setup_connection_internals(struct SessionHandle *data,
					   struct connectdata *conn)
  conn->socktype = SOCK_STREAM; /* most of them are TCP streams */

  if (strequal(conn->protostr, "HTTP")) {
    conn->protocol |= PROT_HTTP;
    conn->curl_do_more = (Curl_do_more_func)ZERO_NULL;
    conn->curl_done = Curl_http_done;
    conn->curl_connect = Curl_http_connect;
#else
    failf(data, LIBCURL_NAME
          " was built with HTTP disabled, http: not supported!");
    return CURLE_UNSUPPORTED_PROTOCOL;
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed
  }
  else if (strequal(conn->protostr, "HTTPS")) {
#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP)
    conn->protocol |= PROT_HTTP|PROT_HTTPS|PROT_SSL;
    conn->curl_do_more = (Curl_do_more_func)ZERO_NULL;
    conn->curl_done = Curl_http_done;
    conn->curl_connect = Curl_http_connect;
    conn->curl_connecting = Curl_https_connecting;
    conn->curl_proto_getsock = Curl_https_getsock;
    failf(data, LIBCURL_NAME
          " was built with SSL disabled, https: not supported!");
    return CURLE_UNSUPPORTED_PROTOCOL;
Daniel Stenberg's avatar
Daniel Stenberg committed
  }
Daniel Stenberg's avatar
Daniel Stenberg committed
  else if(strequal(conn->protostr, "FTP") ||
          strequal(conn->protostr, "FTPS")) {
Daniel Stenberg's avatar
Daniel Stenberg committed
    char *type;
Daniel Stenberg's avatar
Daniel Stenberg committed

    if(strequal(conn->protostr, "FTPS")) {
      conn->protocol |= PROT_FTPS|PROT_SSL;
      /* send data securely unless specifically requested otherwise */
      conn->ssl[SECONDARYSOCKET].use = data->set.ftp_ssl != CURLFTPSSL_CONTROL;
Daniel Stenberg's avatar
Daniel Stenberg committed
#else
      failf(data, LIBCURL_NAME
            " was built with SSL disabled, ftps: not supported!");
Daniel Stenberg's avatar
Daniel Stenberg committed
      return CURLE_UNSUPPORTED_PROTOCOL;
Yang Tse's avatar
Yang Tse committed
    conn->remote_port = (unsigned short)port;
Daniel Stenberg's avatar
Daniel Stenberg committed

    if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
      /* Unless we have asked to tunnel ftp operations through the proxy, we
         switch and use HTTP operations only */
      conn->curl_do = Curl_http;
      conn->curl_done = Curl_http_done;
      conn->protocol = PROT_HTTP; /* switch to HTTP */
#else
      failf(data, "FTP over http proxy requires HTTP support built-in!");
      return CURLE_UNSUPPORTED_PROTOCOL;
#endif
      conn->curl_do_more = Curl_ftp_nextconnect;
      conn->curl_done = Curl_ftp_done;
      conn->curl_connect = Curl_ftp_connect;
      conn->curl_connecting = Curl_ftp_multi_statemach;
      conn->curl_doing = Curl_ftp_doing;
      conn->curl_proto_getsock = Curl_ftp_getsock;
      conn->curl_doing_getsock = Curl_ftp_getsock;
Daniel Stenberg's avatar
Daniel Stenberg committed
      conn->curl_disconnect = Curl_ftp_disconnect;
    data->reqdata.path++; /* don't include the initial slash */
Daniel Stenberg's avatar
Daniel Stenberg committed

    /* FTP URLs support an extension like ";type=<typecode>" that
Daniel Stenberg's avatar
Daniel Stenberg committed
     * we'll try to get now! */
    type=strstr(data->reqdata.path, ";type=");
Daniel Stenberg's avatar
Daniel Stenberg committed
    if(!type) {
      type=strstr(conn->host.rawalloc, ";type=");
Daniel Stenberg's avatar
Daniel Stenberg committed
    }
    if(type) {
      char command;
      *type=0;                     /* it was in the middle of the hostname */
Yang Tse's avatar
Yang Tse committed
      command = (char)toupper((int)type[6]);
Daniel Stenberg's avatar
Daniel Stenberg committed
      switch(command) {