build: rename 93 lib/*.c files
authorYang Tse <yangsita@gmail.com>
Thu, 3 Jan 2013 05:13:18 +0000 (06:13 +0100)
committerYang Tse <yangsita@gmail.com>
Thu, 3 Jan 2013 05:13:18 +0000 (06:13 +0100)
93 lib/*.c source files renamed to use our standard naming scheme.

This commit only does the file renaming.

----------------------------------------

  renamed:    lib/amigaos.c -> lib/curl_amigaos.c
  renamed:    lib/asyn-ares.c -> lib/curl_asyn_ares.c
  renamed:    lib/asyn-thread.c -> lib/curl_asyn_thread.c
  renamed:    lib/axtls.c -> lib/curl_axtls.c
  renamed:    lib/base64.c -> lib/curl_base64.c
  renamed:    lib/bundles.c -> lib/curl_bundles.c
  renamed:    lib/conncache.c -> lib/curl_conncache.c
  renamed:    lib/connect.c -> lib/curl_connect.c
  renamed:    lib/content_encoding.c -> lib/curl_content_encoding.c
  renamed:    lib/cookie.c -> lib/curl_cookie.c
  renamed:    lib/cyassl.c -> lib/curl_cyassl.c
  renamed:    lib/dict.c -> lib/curl_dict.c
  renamed:    lib/easy.c -> lib/curl_easy.c
  renamed:    lib/escape.c -> lib/curl_escape.c
  renamed:    lib/file.c -> lib/curl_file.c
  renamed:    lib/fileinfo.c -> lib/curl_fileinfo.c
  renamed:    lib/formdata.c -> lib/curl_formdata.c
  renamed:    lib/ftp.c -> lib/curl_ftp.c
  renamed:    lib/ftplistparser.c -> lib/curl_ftplistparser.c
  renamed:    lib/getenv.c -> lib/curl_getenv.c
  renamed:    lib/getinfo.c -> lib/curl_getinfo.c
  renamed:    lib/gopher.c -> lib/curl_gopher.c
  renamed:    lib/gtls.c -> lib/curl_gtls.c
  renamed:    lib/hash.c -> lib/curl_hash.c
  renamed:    lib/hmac.c -> lib/curl_hmac.c
  renamed:    lib/hostasyn.c -> lib/curl_hostasyn.c
  renamed:    lib/hostcheck.c -> lib/curl_hostcheck.c
  renamed:    lib/hostip.c -> lib/curl_hostip.c
  renamed:    lib/hostip4.c -> lib/curl_hostip4.c
  renamed:    lib/hostip6.c -> lib/curl_hostip6.c
  renamed:    lib/hostsyn.c -> lib/curl_hostsyn.c
  renamed:    lib/http.c -> lib/curl_http.c
  renamed:    lib/http_chunks.c -> lib/curl_http_chunks.c
  renamed:    lib/http_digest.c -> lib/curl_http_digest.c
  renamed:    lib/http_negotiate.c -> lib/curl_http_negotiate.c
  renamed:    lib/http_negotiate_sspi.c -> lib/curl_http_negotiate_sspi.c
  renamed:    lib/http_proxy.c -> lib/curl_http_proxy.c
  renamed:    lib/idn_win32.c -> lib/curl_idn_win32.c
  renamed:    lib/if2ip.c -> lib/curl_if2ip.c
  renamed:    lib/imap.c -> lib/curl_imap.c
  renamed:    lib/inet_ntop.c -> lib/curl_inet_ntop.c
  renamed:    lib/inet_pton.c -> lib/curl_inet_pton.c
  renamed:    lib/krb4.c -> lib/curl_krb4.c
  renamed:    lib/krb5.c -> lib/curl_krb5.c
  renamed:    lib/ldap.c -> lib/curl_ldap.c
  renamed:    lib/llist.c -> lib/curl_llist.c
  renamed:    lib/md4.c -> lib/curl_md4.c
  renamed:    lib/md5.c -> lib/curl_md5.c
  renamed:    lib/memdebug.c -> lib/curl_memdebug.c
  renamed:    lib/mprintf.c -> lib/curl_mprintf.c
  renamed:    lib/multi.c -> lib/curl_multi.c
  renamed:    lib/netrc.c -> lib/curl_netrc.c
  renamed:    lib/non-ascii.c -> lib/curl_non_ascii.c
  renamed:    lib/curl_non-ascii.h -> lib/curl_non_ascii.h
  renamed:    lib/nonblock.c -> lib/curl_nonblock.c
  renamed:    lib/nss.c -> lib/curl_nss.c
  renamed:    lib/nwlib.c -> lib/curl_nwlib.c
  renamed:    lib/nwos.c -> lib/curl_nwos.c
  renamed:    lib/openldap.c -> lib/curl_openldap.c
  renamed:    lib/parsedate.c -> lib/curl_parsedate.c
  renamed:    lib/pingpong.c -> lib/curl_pingpong.c
  renamed:    lib/polarssl.c -> lib/curl_polarssl.c
  renamed:    lib/pop3.c -> lib/curl_pop3.c
  renamed:    lib/progress.c -> lib/curl_progress.c
  renamed:    lib/qssl.c -> lib/curl_qssl.c
  renamed:    lib/rawstr.c -> lib/curl_rawstr.c
  renamed:    lib/rtsp.c -> lib/curl_rtsp.c
  renamed:    lib/security.c -> lib/curl_security.c
  renamed:    lib/select.c -> lib/curl_select.c
  renamed:    lib/sendf.c -> lib/curl_sendf.c
  renamed:    lib/share.c -> lib/curl_share.c
  renamed:    lib/slist.c -> lib/curl_slist.c
  renamed:    lib/smtp.c -> lib/curl_smtp.c
  renamed:    lib/socks.c -> lib/curl_socks.c
  renamed:    lib/socks_gssapi.c -> lib/curl_socks_gssapi.c
  renamed:    lib/socks_sspi.c -> lib/curl_socks_sspi.c
  renamed:    lib/speedcheck.c -> lib/curl_speedcheck.c
  renamed:    lib/splay.c -> lib/curl_splay.c
  renamed:    lib/ssh.c -> lib/curl_ssh.c
  renamed:    lib/sslgen.c -> lib/curl_sslgen.c
  renamed:    lib/ssluse.c -> lib/curl_ssluse.c
  renamed:    lib/strdup.c -> lib/curl_strdup.c
  renamed:    lib/strequal.c -> lib/curl_strequal.c
  renamed:    lib/strerror.c -> lib/curl_strerror.c
  renamed:    lib/strtok.c -> lib/curl_strtok.c
  renamed:    lib/strtoofft.c -> lib/curl_strtoofft.c
  renamed:    lib/telnet.c -> lib/curl_telnet.c
  renamed:    lib/tftp.c -> lib/curl_tftp.c
  renamed:    lib/timeval.c -> lib/curl_timeval.c
  renamed:    lib/transfer.c -> lib/curl_transfer.c
  renamed:    lib/url.c -> lib/curl_url.c
  renamed:    lib/version.c -> lib/curl_version.c
  renamed:    lib/warnless.c -> lib/curl_warnless.c
  renamed:    lib/wildcard.c -> lib/curl_wildcard.c

----------------------------------------

188 files changed:
lib/amigaos.c [deleted file]
lib/asyn-ares.c [deleted file]
lib/asyn-thread.c [deleted file]
lib/axtls.c [deleted file]
lib/base64.c [deleted file]
lib/bundles.c [deleted file]
lib/conncache.c [deleted file]
lib/connect.c [deleted file]
lib/content_encoding.c [deleted file]
lib/cookie.c [deleted file]
lib/curl_amigaos.c [new file with mode: 0644]
lib/curl_asyn_ares.c [new file with mode: 0644]
lib/curl_asyn_thread.c [new file with mode: 0644]
lib/curl_axtls.c [new file with mode: 0644]
lib/curl_base64.c [new file with mode: 0644]
lib/curl_bundles.c [new file with mode: 0644]
lib/curl_conncache.c [new file with mode: 0644]
lib/curl_connect.c [new file with mode: 0644]
lib/curl_content_encoding.c [new file with mode: 0644]
lib/curl_cookie.c [new file with mode: 0644]
lib/curl_cyassl.c [new file with mode: 0644]
lib/curl_dict.c [new file with mode: 0644]
lib/curl_easy.c [new file with mode: 0644]
lib/curl_escape.c [new file with mode: 0644]
lib/curl_file.c [new file with mode: 0644]
lib/curl_fileinfo.c [new file with mode: 0644]
lib/curl_formdata.c [new file with mode: 0644]
lib/curl_ftp.c [new file with mode: 0644]
lib/curl_ftplistparser.c [new file with mode: 0644]
lib/curl_getenv.c [new file with mode: 0644]
lib/curl_getinfo.c [new file with mode: 0644]
lib/curl_gopher.c [new file with mode: 0644]
lib/curl_gtls.c [new file with mode: 0644]
lib/curl_hash.c [new file with mode: 0644]
lib/curl_hmac.c [new file with mode: 0644]
lib/curl_hostasyn.c [new file with mode: 0644]
lib/curl_hostcheck.c [new file with mode: 0644]
lib/curl_hostip.c [new file with mode: 0644]
lib/curl_hostip4.c [new file with mode: 0644]
lib/curl_hostip6.c [new file with mode: 0644]
lib/curl_hostsyn.c [new file with mode: 0644]
lib/curl_http.c [new file with mode: 0644]
lib/curl_http_chunks.c [new file with mode: 0644]
lib/curl_http_digest.c [new file with mode: 0644]
lib/curl_http_negotiate.c [new file with mode: 0644]
lib/curl_http_negotiate_sspi.c [new file with mode: 0644]
lib/curl_http_proxy.c [new file with mode: 0644]
lib/curl_idn_win32.c [new file with mode: 0644]
lib/curl_if2ip.c [new file with mode: 0644]
lib/curl_imap.c [new file with mode: 0644]
lib/curl_inet_ntop.c [new file with mode: 0644]
lib/curl_inet_pton.c [new file with mode: 0644]
lib/curl_krb4.c [new file with mode: 0644]
lib/curl_krb5.c [new file with mode: 0644]
lib/curl_ldap.c [new file with mode: 0644]
lib/curl_llist.c [new file with mode: 0644]
lib/curl_md4.c [new file with mode: 0644]
lib/curl_md5.c [new file with mode: 0644]
lib/curl_memdebug.c [new file with mode: 0644]
lib/curl_mprintf.c [new file with mode: 0644]
lib/curl_multi.c [new file with mode: 0644]
lib/curl_netrc.c [new file with mode: 0644]
lib/curl_non-ascii.h [deleted file]
lib/curl_non_ascii.c [new file with mode: 0644]
lib/curl_non_ascii.h [new file with mode: 0644]
lib/curl_nonblock.c [new file with mode: 0644]
lib/curl_nss.c [new file with mode: 0644]
lib/curl_nwlib.c [new file with mode: 0644]
lib/curl_nwos.c [new file with mode: 0644]
lib/curl_openldap.c [new file with mode: 0644]
lib/curl_parsedate.c [new file with mode: 0644]
lib/curl_pingpong.c [new file with mode: 0644]
lib/curl_polarssl.c [new file with mode: 0644]
lib/curl_pop3.c [new file with mode: 0644]
lib/curl_progress.c [new file with mode: 0644]
lib/curl_qssl.c [new file with mode: 0644]
lib/curl_rawstr.c [new file with mode: 0644]
lib/curl_rtsp.c [new file with mode: 0644]
lib/curl_security.c [new file with mode: 0644]
lib/curl_select.c [new file with mode: 0644]
lib/curl_sendf.c [new file with mode: 0644]
lib/curl_share.c [new file with mode: 0644]
lib/curl_slist.c [new file with mode: 0644]
lib/curl_smtp.c [new file with mode: 0644]
lib/curl_socks.c [new file with mode: 0644]
lib/curl_socks_gssapi.c [new file with mode: 0644]
lib/curl_socks_sspi.c [new file with mode: 0644]
lib/curl_speedcheck.c [new file with mode: 0644]
lib/curl_splay.c [new file with mode: 0644]
lib/curl_ssh.c [new file with mode: 0644]
lib/curl_sslgen.c [new file with mode: 0644]
lib/curl_ssluse.c [new file with mode: 0644]
lib/curl_strdup.c [new file with mode: 0644]
lib/curl_strequal.c [new file with mode: 0644]
lib/curl_strerror.c [new file with mode: 0644]
lib/curl_strtok.c [new file with mode: 0644]
lib/curl_strtoofft.c [new file with mode: 0644]
lib/curl_telnet.c [new file with mode: 0644]
lib/curl_tftp.c [new file with mode: 0644]
lib/curl_timeval.c [new file with mode: 0644]
lib/curl_transfer.c [new file with mode: 0644]
lib/curl_url.c [new file with mode: 0644]
lib/curl_version.c [new file with mode: 0644]
lib/curl_warnless.c [new file with mode: 0644]
lib/curl_wildcard.c [new file with mode: 0644]
lib/cyassl.c [deleted file]
lib/dict.c [deleted file]
lib/easy.c [deleted file]
lib/escape.c [deleted file]
lib/file.c [deleted file]
lib/fileinfo.c [deleted file]
lib/formdata.c [deleted file]
lib/ftp.c [deleted file]
lib/ftplistparser.c [deleted file]
lib/getenv.c [deleted file]
lib/getinfo.c [deleted file]
lib/gopher.c [deleted file]
lib/gtls.c [deleted file]
lib/hash.c [deleted file]
lib/hmac.c [deleted file]
lib/hostasyn.c [deleted file]
lib/hostcheck.c [deleted file]
lib/hostip.c [deleted file]
lib/hostip4.c [deleted file]
lib/hostip6.c [deleted file]
lib/hostsyn.c [deleted file]
lib/http.c [deleted file]
lib/http_chunks.c [deleted file]
lib/http_digest.c [deleted file]
lib/http_negotiate.c [deleted file]
lib/http_negotiate_sspi.c [deleted file]
lib/http_proxy.c [deleted file]
lib/idn_win32.c [deleted file]
lib/if2ip.c [deleted file]
lib/imap.c [deleted file]
lib/inet_ntop.c [deleted file]
lib/inet_pton.c [deleted file]
lib/krb4.c [deleted file]
lib/krb5.c [deleted file]
lib/ldap.c [deleted file]
lib/llist.c [deleted file]
lib/md4.c [deleted file]
lib/md5.c [deleted file]
lib/memdebug.c [deleted file]
lib/mprintf.c [deleted file]
lib/multi.c [deleted file]
lib/netrc.c [deleted file]
lib/non-ascii.c [deleted file]
lib/nonblock.c [deleted file]
lib/nss.c [deleted file]
lib/nwlib.c [deleted file]
lib/nwos.c [deleted file]
lib/openldap.c [deleted file]
lib/parsedate.c [deleted file]
lib/pingpong.c [deleted file]
lib/polarssl.c [deleted file]
lib/pop3.c [deleted file]
lib/progress.c [deleted file]
lib/qssl.c [deleted file]
lib/rawstr.c [deleted file]
lib/rtsp.c [deleted file]
lib/security.c [deleted file]
lib/select.c [deleted file]
lib/sendf.c [deleted file]
lib/share.c [deleted file]
lib/slist.c [deleted file]
lib/smtp.c [deleted file]
lib/socks.c [deleted file]
lib/socks_gssapi.c [deleted file]
lib/socks_sspi.c [deleted file]
lib/speedcheck.c [deleted file]
lib/splay.c [deleted file]
lib/ssh.c [deleted file]
lib/sslgen.c [deleted file]
lib/ssluse.c [deleted file]
lib/strdup.c [deleted file]
lib/strequal.c [deleted file]
lib/strerror.c [deleted file]
lib/strtok.c [deleted file]
lib/strtoofft.c [deleted file]
lib/telnet.c [deleted file]
lib/tftp.c [deleted file]
lib/timeval.c [deleted file]
lib/transfer.c [deleted file]
lib/url.c [deleted file]
lib/version.c [deleted file]
lib/warnless.c [deleted file]
lib/wildcard.c [deleted file]

diff --git a/lib/amigaos.c b/lib/amigaos.c
deleted file mode 100644 (file)
index c726abb..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#if defined(__AMIGA__) && !defined(__ixemul__)
-
-#include <amitcp/socketbasetags.h>
-
-#include "curl_amigaos.h"
-
-struct Library *SocketBase = NULL;
-extern int errno, h_errno;
-
-#ifdef __libnix__
-#include <stabs.h>
-void __request(const char *msg);
-#else
-# define __request( msg )       Printf( msg "\n\a")
-#endif
-
-void Curl_amiga_cleanup()
-{
-  if(SocketBase) {
-    CloseLibrary(SocketBase);
-    SocketBase = NULL;
-  }
-}
-
-bool Curl_amiga_init()
-{
-  if(!SocketBase)
-    SocketBase = OpenLibrary("bsdsocket.library", 4);
-
-  if(!SocketBase) {
-    __request("No TCP/IP Stack running!");
-    return FALSE;
-  }
-
-  if(SocketBaseTags(SBTM_SETVAL(SBTC_ERRNOPTR(sizeof(errno))), (ULONG) &errno,
-                    SBTM_SETVAL(SBTC_LOGTAGPTR), (ULONG) "cURL",
-                    TAG_DONE)) {
-    __request("SocketBaseTags ERROR");
-    return FALSE;
-  }
-
-#ifndef __libnix__
-  atexit(Curl_amiga_cleanup);
-#endif
-
-  return TRUE;
-}
-
-#ifdef __libnix__
-ADD2EXIT(Curl_amiga_cleanup,-50);
-#endif
-
-#endif /* __AMIGA__ && ! __ixemul__ */
diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c
deleted file mode 100644 (file)
index 6e09e9b..0000000
+++ /dev/null
@@ -1,639 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef HAVE_LIMITS_H
-#include <limits.h>
-#endif
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef __VMS
-#include <in.h>
-#include <inet.h>
-#endif
-
-#ifdef HAVE_PROCESS_H
-#include <process.h>
-#endif
-
-#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
-#undef in_addr_t
-#define in_addr_t unsigned long
-#endif
-
-/***********************************************************************
- * Only for ares-enabled builds
- * And only for functions that fulfill the asynch resolver backend API
- * as defined in curl_asyn.h, nothing else belongs in this file!
- **********************************************************************/
-
-#ifdef CURLRES_ARES
-
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_hostip.h"
-#include "curl_hash.h"
-#include "curl_share.h"
-#include "curl_strerror.h"
-#include "curl_url.h"
-#include "curl_multiif.h"
-#include "curl_inet_pton.h"
-#include "curl_connect.h"
-#include "curl_select.h"
-#include "curl_progress.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#  if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
-     (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__))
-#    define CARES_STATICLIB
-#  endif
-#  include <ares.h>
-#  include <ares_version.h> /* really old c-ares didn't include this by
-                               itself */
-
-#if ARES_VERSION >= 0x010500
-/* c-ares 1.5.0 or later, the callback proto is modified */
-#define HAVE_CARES_CALLBACK_TIMEOUTS 1
-#endif
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-struct ResolverResults {
-  int num_pending; /* number of ares_gethostbyname() requests */
-  Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares parts */
-  int last_status;
-};
-
-/*
- * Curl_resolver_global_init() - the generic low-level asynchronous name
- * resolve API.  Called from curl_global_init() to initialize global resolver
- * environment.  Initializes ares library.
- */
-int Curl_resolver_global_init(void)
-{
-#ifdef CARES_HAVE_ARES_LIBRARY_INIT
-  if(ares_library_init(ARES_LIB_INIT_ALL)) {
-    return CURLE_FAILED_INIT;
-  }
-#endif
-  return CURLE_OK;
-}
-
-/*
- * Curl_resolver_global_cleanup()
- *
- * Called from curl_global_cleanup() to destroy global resolver environment.
- * Deinitializes ares library.
- */
-void Curl_resolver_global_cleanup(void)
-{
-#ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
-  ares_library_cleanup();
-#endif
-}
-
-/*
- * Curl_resolver_init()
- *
- * Called from curl_easy_init() -> Curl_open() to initialize resolver
- * URL-state specific environment ('resolver' member of the UrlState
- * structure).  Fills the passed pointer by the initialized ares_channel.
- */
-CURLcode Curl_resolver_init(void **resolver)
-{
-  int status = ares_init((ares_channel*)resolver);
-  if(status != ARES_SUCCESS) {
-    if(status == ARES_ENOMEM)
-      return CURLE_OUT_OF_MEMORY;
-    else
-      return CURLE_FAILED_INIT;
-  }
-  return CURLE_OK;
-  /* make sure that all other returns from this function should destroy the
-     ares channel before returning error! */
-}
-
-/*
- * Curl_resolver_cleanup()
- *
- * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
- * URL-state specific environment ('resolver' member of the UrlState
- * structure).  Destroys the ares channel.
- */
-void Curl_resolver_cleanup(void *resolver)
-{
-  ares_destroy((ares_channel)resolver);
-}
-
-/*
- * Curl_resolver_duphandle()
- *
- * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
- * environment ('resolver' member of the UrlState structure).  Duplicates the
- * 'from' ares channel and passes the resulting channel to the 'to' pointer.
- */
-int Curl_resolver_duphandle(void **to, void *from)
-{
-  /* Clone the ares channel for the new handle */
-  if(ARES_SUCCESS != ares_dup((ares_channel*)to,(ares_channel)from))
-    return CURLE_FAILED_INIT;
-  return CURLE_OK;
-}
-
-static void destroy_async_data (struct Curl_async *async);
-
-/*
- * Cancel all possibly still on-going resolves for this connection.
- */
-void Curl_resolver_cancel(struct connectdata *conn)
-{
-  if(conn && conn->data && conn->data->state.resolver)
-    ares_cancel((ares_channel)conn->data->state.resolver);
-  destroy_async_data(&conn->async);
-}
-
-/*
- * destroy_async_data() cleans up async resolver data.
- */
-static void destroy_async_data (struct Curl_async *async)
-{
-  if(async->hostname)
-    free(async->hostname);
-
-  if(async->os_specific) {
-    struct ResolverResults *res = (struct ResolverResults *)async->os_specific;
-    if(res) {
-      if(res->temp_ai) {
-        Curl_freeaddrinfo(res->temp_ai);
-        res->temp_ai = NULL;
-      }
-      free(res);
-    }
-    async->os_specific = NULL;
-  }
-
-  async->hostname = NULL;
-}
-
-/*
- * Curl_resolver_fdset() is called when someone from the outside world (using
- * curl_multi_fdset()) wants to get our fd_set setup and we're talking with
- * ares. The caller must make sure that this function is only called when we
- * have a working ares channel.
- *
- * Returns: CURLE_OK always!
- */
-
-int Curl_resolver_getsock(struct connectdata *conn,
-                          curl_socket_t *socks,
-                          int numsocks)
-
-{
-  struct timeval maxtime;
-  struct timeval timebuf;
-  struct timeval *timeout;
-  long milli;
-  int max = ares_getsock((ares_channel)conn->data->state.resolver,
-                         (ares_socket_t *)socks, numsocks);
-
-  maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
-  maxtime.tv_usec = 0;
-
-  timeout = ares_timeout((ares_channel)conn->data->state.resolver, &maxtime,
-                         &timebuf);
-  milli = (timeout->tv_sec * 1000) + (timeout->tv_usec/1000);
-  if(milli == 0)
-    milli += 10;
-  Curl_expire(conn->data, milli);
-
-  return max;
-}
-
-/*
- * waitperform()
- *
- * 1) Ask ares what sockets it currently plays with, then
- * 2) wait for the timeout period to check for action on ares' sockets.
- * 3) tell ares to act on all the sockets marked as "with action"
- *
- * return number of sockets it worked on
- */
-
-static int waitperform(struct connectdata *conn, int timeout_ms)
-{
-  struct SessionHandle *data = conn->data;
-  int nfds;
-  int bitmask;
-  ares_socket_t socks[ARES_GETSOCK_MAXNUM];
-  struct pollfd pfd[ARES_GETSOCK_MAXNUM];
-  int i;
-  int num = 0;
-
-  bitmask = ares_getsock((ares_channel)data->state.resolver, socks,
-                         ARES_GETSOCK_MAXNUM);
-
-  for(i=0; i < ARES_GETSOCK_MAXNUM; i++) {
-    pfd[i].events = 0;
-    pfd[i].revents = 0;
-    if(ARES_GETSOCK_READABLE(bitmask, i)) {
-      pfd[i].fd = socks[i];
-      pfd[i].events |= POLLRDNORM|POLLIN;
-    }
-    if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
-      pfd[i].fd = socks[i];
-      pfd[i].events |= POLLWRNORM|POLLOUT;
-    }
-    if(pfd[i].events != 0)
-      num++;
-    else
-      break;
-  }
-
-  if(num)
-    nfds = Curl_poll(pfd, num, timeout_ms);
-  else
-    nfds = 0;
-
-  if(!nfds)
-    /* Call ares_process() unconditonally here, even if we simply timed out
-       above, as otherwise the ares name resolve won't timeout! */
-    ares_process_fd((ares_channel)data->state.resolver, ARES_SOCKET_BAD,
-                    ARES_SOCKET_BAD);
-  else {
-    /* move through the descriptors and ask for processing on them */
-    for(i=0; i < num; i++)
-      ares_process_fd((ares_channel)data->state.resolver,
-                      pfd[i].revents & (POLLRDNORM|POLLIN)?
-                      pfd[i].fd:ARES_SOCKET_BAD,
-                      pfd[i].revents & (POLLWRNORM|POLLOUT)?
-                      pfd[i].fd:ARES_SOCKET_BAD);
-  }
-  return nfds;
-}
-
-/*
- * Curl_resolver_is_resolved() is called repeatedly to check if a previous
- * name resolve request has completed. It should also make sure to time-out if
- * the operation seems to take too long.
- *
- * Returns normal CURLcode errors.
- */
-CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
-                                   struct Curl_dns_entry **dns)
-{
-  struct SessionHandle *data = conn->data;
-  struct ResolverResults *res = (struct ResolverResults *)
-    conn->async.os_specific;
-
-  *dns = NULL;
-
-  waitperform(conn, 0);
-
-  if(res && !res->num_pending) {
-    (void)Curl_addrinfo_callback(conn, res->last_status, res->temp_ai);
-    /* temp_ai ownership is moved to the connection, so we need not free-up
-       them */
-    res->temp_ai = NULL;
-    destroy_async_data(&conn->async);
-    if(!conn->async.dns) {
-      failf(data, "Could not resolve %s: %s (%s)",
-            conn->bits.proxy?"proxy":"host",
-            conn->host.dispname,
-            ares_strerror(conn->async.status));
-      return conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY:
-        CURLE_COULDNT_RESOLVE_HOST;
-    }
-    *dns = conn->async.dns;
-  }
-
-  return CURLE_OK;
-}
-
-/*
- * Curl_resolver_wait_resolv()
- *
- * waits for a resolve to finish. This function should be avoided since using
- * this risk getting the multi interface to "hang".
- *
- * If 'entry' is non-NULL, make it point to the resolved dns entry
- *
- * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
- * CURLE_OPERATION_TIMEDOUT if a time-out occurred.
- */
-CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
-                                   struct Curl_dns_entry **entry)
-{
-  CURLcode rc=CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  long timeout;
-  struct timeval now = Curl_tvnow();
-  struct Curl_dns_entry *temp_entry;
-
-  timeout = Curl_timeleft(data, &now, TRUE);
-  if(!timeout)
-    timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
-
-  /* Wait for the name resolve query to complete. */
-  for(;;) {
-    struct timeval *tvp, tv, store;
-    long timediff;
-    int itimeout;
-    int timeout_ms;
-
-    itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout;
-
-    store.tv_sec = itimeout/1000;
-    store.tv_usec = (itimeout%1000)*1000;
-
-    tvp = ares_timeout((ares_channel)data->state.resolver, &store, &tv);
-
-    /* use the timeout period ares returned to us above if less than one
-       second is left, otherwise just use 1000ms to make sure the progress
-       callback gets called frequent enough */
-    if(!tvp->tv_sec)
-      timeout_ms = (int)(tvp->tv_usec/1000);
-    else
-      timeout_ms = 1000;
-
-    waitperform(conn, timeout_ms);
-    Curl_resolver_is_resolved(conn,&temp_entry);
-
-    if(conn->async.done)
-      break;
-
-    if(Curl_pgrsUpdate(conn)) {
-      rc = CURLE_ABORTED_BY_CALLBACK;
-      timeout = -1; /* trigger the cancel below */
-    }
-    else {
-      struct timeval now2 = Curl_tvnow();
-      timediff = Curl_tvdiff(now2, now); /* spent time */
-      timeout -= timediff?timediff:1; /* always deduct at least 1 */
-      now = now2; /* for next loop */
-    }
-    if(timeout < 0) {
-      /* our timeout, so we cancel the ares operation */
-      ares_cancel((ares_channel)data->state.resolver);
-      break;
-    }
-  }
-
-  /* Operation complete, if the lookup was successful we now have the entry
-     in the cache. */
-
-  if(entry)
-    *entry = conn->async.dns;
-
-  if(!conn->async.dns) {
-    /* a name was not resolved */
-    if((timeout < 0) || (conn->async.status == ARES_ETIMEOUT)) {
-      if(conn->bits.proxy) {
-        failf(data, "Resolving proxy timed out: %s", conn->proxy.dispname);
-        rc = CURLE_COULDNT_RESOLVE_PROXY;
-      }
-      else {
-        failf(data, "Resolving host timed out: %s", conn->host.dispname);
-        rc = CURLE_COULDNT_RESOLVE_HOST;
-      }
-    }
-    else if(conn->async.done) {
-      if(conn->bits.proxy) {
-        failf(data, "Could not resolve proxy: %s (%s)", conn->proxy.dispname,
-              ares_strerror(conn->async.status));
-        rc = CURLE_COULDNT_RESOLVE_PROXY;
-      }
-      else {
-        failf(data, "Could not resolve host: %s (%s)", conn->host.dispname,
-              ares_strerror(conn->async.status));
-        rc = CURLE_COULDNT_RESOLVE_HOST;
-      }
-    }
-    else
-      rc = CURLE_OPERATION_TIMEDOUT;
-
-    /* close the connection, since we can't return failure here without
-       cleaning up this connection properly */
-    conn->bits.close = TRUE;
-  }
-
-  return rc;
-}
-
-/* Connects results to the list */
-static void compound_results(struct ResolverResults *res,
-                             Curl_addrinfo *ai)
-{
-  Curl_addrinfo *ai_tail;
-  if(!ai)
-    return;
-  ai_tail = ai;
-
-  while(ai_tail->ai_next)
-    ai_tail = ai_tail->ai_next;
-
-  /* Add the new results to the list of old results. */
-  ai_tail->ai_next = res->temp_ai;
-  res->temp_ai = ai;
-}
-
-/*
- * ares_query_completed_cb() is the callback that ares will call when
- * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
- * when using ares, is completed either successfully or with failure.
- */
-static void query_completed_cb(void *arg,  /* (struct connectdata *) */
-                               int status,
-#ifdef HAVE_CARES_CALLBACK_TIMEOUTS
-                               int timeouts,
-#endif
-                               struct hostent *hostent)
-{
-  struct connectdata *conn = (struct connectdata *)arg;
-  struct ResolverResults *res;
-
-#ifdef HAVE_CARES_CALLBACK_TIMEOUTS
-  (void)timeouts; /* ignored */
-#endif
-
-  if(ARES_EDESTRUCTION == status)
-    /* when this ares handle is getting destroyed, the 'arg' pointer may not
-       be valid so only defer it when we know the 'status' says its fine! */
-    return;
-
-  res = (struct ResolverResults *)conn->async.os_specific;
-  res->num_pending--;
-
-  if(CURL_ASYNC_SUCCESS == status) {
-    Curl_addrinfo *ai = Curl_he2ai(hostent, conn->async.port);
-    if(ai) {
-      compound_results(res, ai);
-    }
-  }
-  /* A successful result overwrites any previous error */
-  if(res->last_status != ARES_SUCCESS)
-    res->last_status = status;
-}
-
-/*
- * Curl_resolver_getaddrinfo() - when using ares
- *
- * Returns name information about the given hostname and port number. If
- * successful, the 'hostent' is returned and the forth argument will point to
- * memory we need to free after use. That memory *MUST* be freed with
- * Curl_freeaddrinfo(), nothing else.
- */
-Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
-                                         const char *hostname,
-                                         int port,
-                                         int *waitp)
-{
-  char *bufp;
-  struct SessionHandle *data = conn->data;
-  struct in_addr in;
-  int family = PF_INET;
-#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
-  struct in6_addr in6;
-#endif /* CURLRES_IPV6 */
-
-  *waitp = 0; /* default to synchronous response */
-
-  /* First check if this is an IPv4 address string */
-  if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
-    /* This is a dotted IP address 123.123.123.123-style */
-    return Curl_ip2addr(AF_INET, &in, hostname, port);
-  }
-
-#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
-  /* Otherwise, check if this is an IPv6 address string */
-  if(Curl_inet_pton (AF_INET6, hostname, &in6) > 0)
-    /* This must be an IPv6 address literal.  */
-    return Curl_ip2addr(AF_INET6, &in6, hostname, port);
-
-  switch(conn->ip_version) {
-  default:
-#if ARES_VERSION >= 0x010601
-    family = PF_UNSPEC; /* supported by c-ares since 1.6.1, so for older
-                           c-ares versions this just falls through and defaults
-                           to PF_INET */
-    break;
-#endif
-  case CURL_IPRESOLVE_V4:
-    family = PF_INET;
-    break;
-  case CURL_IPRESOLVE_V6:
-    family = PF_INET6;
-    break;
-  }
-#endif /* CURLRES_IPV6 */
-
-  bufp = strdup(hostname);
-  if(bufp) {
-    struct ResolverResults *res = NULL;
-    Curl_safefree(conn->async.hostname);
-    conn->async.hostname = bufp;
-    conn->async.port = port;
-    conn->async.done = FALSE;   /* not done */
-    conn->async.status = 0;     /* clear */
-    conn->async.dns = NULL;     /* clear */
-    res = calloc(sizeof(struct ResolverResults),1);
-    if(!res) {
-      Curl_safefree(conn->async.hostname);
-      conn->async.hostname = NULL;
-      return NULL;
-    }
-    conn->async.os_specific = res;
-
-    /* initial status - failed */
-    res->last_status = ARES_ENOTFOUND;
-#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
-    if(family == PF_UNSPEC) {
-      if(Curl_ipv6works()) {
-        res->num_pending = 2;
-
-        /* areschannel is already setup in the Curl_open() function */
-        ares_gethostbyname((ares_channel)data->state.resolver, hostname,
-                            PF_INET, query_completed_cb, conn);
-        ares_gethostbyname((ares_channel)data->state.resolver, hostname,
-                            PF_INET6, query_completed_cb, conn);
-      }
-      else {
-        res->num_pending = 1;
-
-        /* areschannel is already setup in the Curl_open() function */
-        ares_gethostbyname((ares_channel)data->state.resolver, hostname,
-                            PF_INET, query_completed_cb, conn);
-      }
-    }
-    else
-#endif /* CURLRES_IPV6 */
-    {
-      res->num_pending = 1;
-
-      /* areschannel is already setup in the Curl_open() function */
-      ares_gethostbyname((ares_channel)data->state.resolver, hostname, family,
-                         query_completed_cb, conn);
-    }
-
-    *waitp = 1; /* expect asynchronous response */
-  }
-  return NULL; /* no struct yet */
-}
-
-CURLcode Curl_set_dns_servers(struct SessionHandle *data,
-                              char *servers)
-{
-  CURLcode result = CURLE_NOT_BUILT_IN;
-#if (ARES_VERSION >= 0x010704)
-  int ares_result = ares_set_servers_csv(data->state.resolver, servers);
-  switch(ares_result) {
-  case ARES_SUCCESS:
-    result = CURLE_OK;
-    break;
-  case ARES_ENOMEM:
-    result = CURLE_OUT_OF_MEMORY;
-    break;
-  case ARES_ENOTINITIALIZED:
-  case ARES_ENODATA:
-  case ARES_EBADSTR:
-  default:
-    result = CURLE_BAD_FUNCTION_ARGUMENT;
-    break;
-  }
-#else /* too old c-ares version! */
-  (void)data;
-  (void)servers;
-#endif
-  return result;
-}
-#endif /* CURLRES_ARES */
diff --git a/lib/asyn-thread.c b/lib/asyn-thread.c
deleted file mode 100644 (file)
index 6d3667f..0000000
+++ /dev/null
@@ -1,679 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef __VMS
-#include <in.h>
-#include <inet.h>
-#endif
-
-#if defined(USE_THREADS_POSIX)
-#  ifdef HAVE_PTHREAD_H
-#    include <pthread.h>
-#  endif
-#elif defined(USE_THREADS_WIN32)
-#  ifdef HAVE_PROCESS_H
-#    include <process.h>
-#  endif
-#endif
-
-#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
-#undef in_addr_t
-#define in_addr_t unsigned long
-#endif
-
-#ifdef HAVE_GETADDRINFO
-#  define RESOLVER_ENOMEM  EAI_MEMORY
-#else
-#  define RESOLVER_ENOMEM  ENOMEM
-#endif
-
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_hostip.h"
-#include "curl_hash.h"
-#include "curl_share.h"
-#include "curl_strerror.h"
-#include "curl_url.h"
-#include "curl_multiif.h"
-#include "curl_inet_pton.h"
-#include "curl_inet_ntop.h"
-#include "curl_threads.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/***********************************************************************
- * Only for threaded name resolves builds
- **********************************************************************/
-#ifdef CURLRES_THREADED
-
-/*
- * Curl_resolver_global_init()
- * Called from curl_global_init() to initialize global resolver environment.
- * Does nothing here.
- */
-int Curl_resolver_global_init(void)
-{
-  return CURLE_OK;
-}
-
-/*
- * Curl_resolver_global_cleanup()
- * Called from curl_global_cleanup() to destroy global resolver environment.
- * Does nothing here.
- */
-void Curl_resolver_global_cleanup(void)
-{
-}
-
-/*
- * Curl_resolver_init()
- * Called from curl_easy_init() -> Curl_open() to initialize resolver
- * URL-state specific environment ('resolver' member of the UrlState
- * structure).  Does nothing here.
- */
-CURLcode Curl_resolver_init(void **resolver)
-{
-  (void)resolver;
-  return CURLE_OK;
-}
-
-/*
- * Curl_resolver_cleanup()
- * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
- * URL-state specific environment ('resolver' member of the UrlState
- * structure).  Does nothing here.
- */
-void Curl_resolver_cleanup(void *resolver)
-{
-  (void)resolver;
-}
-
-/*
- * Curl_resolver_duphandle()
- * Called from curl_easy_duphandle() to duplicate resolver URL state-specific
- * environment ('resolver' member of the UrlState structure).  Does nothing
- * here.
- */
-int Curl_resolver_duphandle(void **to, void *from)
-{
-  (void)to;
-  (void)from;
-  return CURLE_OK;
-}
-
-static void destroy_async_data(struct Curl_async *);
-
-/*
- * Cancel all possibly still on-going resolves for this connection.
- */
-void Curl_resolver_cancel(struct connectdata *conn)
-{
-  destroy_async_data(&conn->async);
-}
-
-/* This function is used to init a threaded resolve */
-static bool init_resolve_thread(struct connectdata *conn,
-                                const char *hostname, int port,
-                                const struct addrinfo *hints);
-
-
-/* Data for synchronization between resolver thread and its parent */
-struct thread_sync_data {
-  curl_mutex_t * mtx;
-  int done;
-
-  char * hostname;        /* hostname to resolve, Curl_async.hostname
-                             duplicate */
-  int port;
-  int sock_error;
-  Curl_addrinfo *res;
-#ifdef HAVE_GETADDRINFO
-  struct addrinfo hints;
-#endif
-};
-
-struct thread_data {
-  curl_thread_t thread_hnd;
-  unsigned int poll_interval;
-  int interval_end;
-  struct thread_sync_data tsd;
-};
-
-static struct thread_sync_data *conn_thread_sync_data(struct connectdata *conn)
-{
-  return &(((struct thread_data *)conn->async.os_specific)->tsd);
-}
-
-#define CONN_THREAD_SYNC_DATA(conn) &(((conn)->async.os_specific)->tsd);
-
-/* Destroy resolver thread synchronization data */
-static
-void destroy_thread_sync_data(struct thread_sync_data * tsd)
-{
-  if(tsd->mtx) {
-    Curl_mutex_destroy(tsd->mtx);
-    free(tsd->mtx);
-  }
-
-  if(tsd->hostname)
-    free(tsd->hostname);
-
-  if(tsd->res)
-    Curl_freeaddrinfo(tsd->res);
-
-  memset(tsd,0,sizeof(*tsd));
-}
-
-/* Initialize resolver thread synchronization data */
-static
-int init_thread_sync_data(struct thread_sync_data * tsd,
-                           const char * hostname,
-                           int port,
-                           const struct addrinfo *hints)
-{
-  memset(tsd, 0, sizeof(*tsd));
-
-  tsd->port = port;
-#ifdef CURLRES_IPV6
-  DEBUGASSERT(hints);
-  tsd->hints = *hints;
-#else
-  (void) hints;
-#endif
-
-  tsd->mtx = malloc(sizeof(curl_mutex_t));
-  if(tsd->mtx == NULL)
-    goto err_exit;
-
-  Curl_mutex_init(tsd->mtx);
-
-  tsd->sock_error = CURL_ASYNC_SUCCESS;
-
-  /* Copying hostname string because original can be destroyed by parent
-   * thread during gethostbyname execution.
-   */
-  tsd->hostname = strdup(hostname);
-  if(!tsd->hostname)
-    goto err_exit;
-
-  return 1;
-
- err_exit:
-  /* Memory allocation failed */
-  destroy_thread_sync_data(tsd);
-  return 0;
-}
-
-static int getaddrinfo_complete(struct connectdata *conn)
-{
-  struct thread_sync_data *tsd = conn_thread_sync_data(conn);
-  int rc;
-
-  rc = Curl_addrinfo_callback(conn, tsd->sock_error, tsd->res);
-  /* The tsd->res structure has been copied to async.dns and perhaps the DNS
-     cache.  Set our copy to NULL so destroy_thread_sync_data doesn't free it.
-  */
-  tsd->res = NULL;
-
-  return rc;
-}
-
-
-#ifdef HAVE_GETADDRINFO
-
-/*
- * getaddrinfo_thread() resolves a name and then exits.
- *
- * For builds without ARES, but with ENABLE_IPV6, create a resolver thread
- * and wait on it.
- */
-static unsigned int CURL_STDCALL getaddrinfo_thread (void *arg)
-{
-  struct thread_sync_data *tsd = (struct thread_sync_data*)arg;
-  char   service [NI_MAXSERV];
-  int rc;
-
-  snprintf(service, sizeof(service), "%d", tsd->port);
-
-  rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res);
-
-  if(rc != 0) {
-    tsd->sock_error = SOCKERRNO?SOCKERRNO:rc;
-    if(tsd->sock_error == 0)
-      tsd->sock_error = RESOLVER_ENOMEM;
-  }
-
-  Curl_mutex_acquire(tsd->mtx);
-  tsd->done = 1;
-  Curl_mutex_release(tsd->mtx);
-
-  return 0;
-}
-
-#else /* HAVE_GETADDRINFO */
-
-/*
- * gethostbyname_thread() resolves a name and then exits.
- */
-static unsigned int CURL_STDCALL gethostbyname_thread (void *arg)
-{
-  struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
-
-  tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port);
-
-  if(!tsd->res) {
-    tsd->sock_error = SOCKERRNO;
-    if(tsd->sock_error == 0)
-      tsd->sock_error = RESOLVER_ENOMEM;
-  }
-
-  Curl_mutex_acquire(tsd->mtx);
-  tsd->done = 1;
-  Curl_mutex_release(tsd->mtx);
-
-  return 0;
-}
-
-#endif /* HAVE_GETADDRINFO */
-
-/*
- * destroy_async_data() cleans up async resolver data and thread handle.
- */
-static void destroy_async_data (struct Curl_async *async)
-{
-  if(async->hostname)
-    free(async->hostname);
-
-  if(async->os_specific) {
-    struct thread_data *td = (struct thread_data*) async->os_specific;
-
-    if(td->thread_hnd != curl_thread_t_null)
-      Curl_thread_join(&td->thread_hnd);
-
-    destroy_thread_sync_data(&td->tsd);
-
-    free(async->os_specific);
-  }
-  async->hostname = NULL;
-  async->os_specific = NULL;
-}
-
-/*
- * init_resolve_thread() starts a new thread that performs the actual
- * resolve. This function returns before the resolve is done.
- *
- * Returns FALSE in case of failure, otherwise TRUE.
- */
-static bool init_resolve_thread (struct connectdata *conn,
-                                 const char *hostname, int port,
-                                 const struct addrinfo *hints)
-{
-  struct thread_data *td = calloc(1, sizeof(struct thread_data));
-  int err = RESOLVER_ENOMEM;
-
-  conn->async.os_specific = (void*) td;
-  if(!td)
-    goto err_exit;
-
-  conn->async.port = port;
-  conn->async.done = FALSE;
-  conn->async.status = 0;
-  conn->async.dns = NULL;
-  td->thread_hnd = curl_thread_t_null;
-
-  if(!init_thread_sync_data(&td->tsd, hostname, port, hints))
-    goto err_exit;
-
-  Curl_safefree(conn->async.hostname);
-  conn->async.hostname = strdup(hostname);
-  if(!conn->async.hostname)
-    goto err_exit;
-
-#ifdef HAVE_GETADDRINFO
-  td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
-#else
-  td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
-#endif
-
-  if(!td->thread_hnd) {
-#ifndef _WIN32_WCE
-    err = errno;
-#endif
-    goto err_exit;
-  }
-
-  return TRUE;
-
- err_exit:
-  destroy_async_data(&conn->async);
-
-  SET_ERRNO(err);
-
-  return FALSE;
-}
-
-#if defined(HAVE_GETADDRINFO) && !defined(HAVE_GAI_STRERROR) && !defined(WIN32)
-/* NetWare has getaddrinfo but lacks gai_strerror.
-   Windows has a gai_strerror but it is bad (not thread-safe) and the generic
-   socket error string function can be used for this pupose. */
-static const char *gai_strerror(int ecode)
-{
-  switch (ecode) {
-  case EAI_AGAIN:
-    return "The name could not be resolved at this time";
-  case EAI_BADFLAGS:
-    return "The flags parameter had an invalid value";
-  case EAI_FAIL:
-    return "A non-recoverable error occurred when attempting to "
-      "resolve the name";
-  case EAI_FAMILY:
-    return "The address family was not recognized";
-  case EAI_MEMORY:
-    return "Out of memory";
-  case EAI_NONAME:
-    return "The name does not resolve for the supplied parameters";
-  case EAI_SERVICE:
-    return "The service passed was not recognized for the "
-      "specified socket type"
-  case EAI_SOCKTYPE:
-    return "The intended socket type was not recognized"
-  case EAI_SYSTEM:
-    return "A system error occurred";
-  case EAI_OVERFLOW:
-    return "An argument buffer overflowed";
-  default:
-    return "Unknown error";
-
-/* define this now as this is a private implementation of said function */
-#define HAVE_GAI_STRERROR
-}
-#endif
-
-
-/*
- * resolver_error() calls failf() with the appropriate message after a resolve
- * error
- */
-
-static void resolver_error(struct connectdata *conn, const char *host_or_proxy)
-{
-  failf(conn->data, "Could not resolve %s: %s; %s", host_or_proxy,
-        conn->async.hostname,
-#ifdef HAVE_GAI_STRERROR
-        /* NetWare doesn't have gai_strerror and on Windows it isn't deemed
-           thread-safe */
-        gai_strerror(conn->async.status)
-#else
-        Curl_strerror(conn, conn->async.status)
-#endif
-    );
-}
-
-/*
- * Curl_resolver_wait_resolv()
- *
- * waits for a resolve to finish. This function should be avoided since using
- * this risk getting the multi interface to "hang".
- *
- * If 'entry' is non-NULL, make it point to the resolved dns entry
- *
- * This is the version for resolves-in-a-thread.
- */
-CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
-                                   struct Curl_dns_entry **entry)
-{
-  struct thread_data   *td = (struct thread_data*) conn->async.os_specific;
-  CURLcode rc = CURLE_OK;
-
-  DEBUGASSERT(conn && td);
-
-  /* wait for the thread to resolve the name */
-  if(Curl_thread_join(&td->thread_hnd))
-    rc = getaddrinfo_complete(conn);
-  else
-    DEBUGASSERT(0);
-
-  conn->async.done = TRUE;
-
-  if(entry)
-    *entry = conn->async.dns;
-
-  if(!conn->async.dns) {
-    /* a name was not resolved */
-    if(conn->bits.httpproxy) {
-      resolver_error(conn, "proxy");
-      rc = CURLE_COULDNT_RESOLVE_PROXY;
-    }
-    else {
-      resolver_error(conn, "host");
-      rc = CURLE_COULDNT_RESOLVE_HOST;
-    }
-  }
-
-  destroy_async_data(&conn->async);
-
-  if(!conn->async.dns)
-    conn->bits.close = TRUE;
-
-  return (rc);
-}
-
-/*
- * Curl_resolver_is_resolved() is called repeatedly to check if a previous
- * name resolve request has completed. It should also make sure to time-out if
- * the operation seems to take too long.
- */
-CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
-                                   struct Curl_dns_entry **entry)
-{
-  struct SessionHandle *data = conn->data;
-  struct thread_data   *td = (struct thread_data*) conn->async.os_specific;
-  int done = 0;
-
-  *entry = NULL;
-
-  if(!td) {
-    DEBUGASSERT(td);
-    return CURLE_COULDNT_RESOLVE_HOST;
-  }
-
-  Curl_mutex_acquire(td->tsd.mtx);
-  done = td->tsd.done;
-  Curl_mutex_release(td->tsd.mtx);
-
-  if(done) {
-    getaddrinfo_complete(conn);
-    destroy_async_data(&conn->async);
-
-    if(!conn->async.dns) {
-      resolver_error(conn, "host");
-      return CURLE_COULDNT_RESOLVE_HOST;
-    }
-    *entry = conn->async.dns;
-  }
-  else {
-    /* poll for name lookup done with exponential backoff up to 250ms */
-    int elapsed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle);
-    if(elapsed < 0)
-      elapsed = 0;
-
-    if(td->poll_interval == 0)
-      /* Start at 1ms poll interval */
-      td->poll_interval = 1;
-    else if(elapsed >= td->interval_end)
-      /* Back-off exponentially if last interval expired  */
-      td->poll_interval *= 2;
-
-    if(td->poll_interval > 250)
-      td->poll_interval = 250;
-
-    td->interval_end = elapsed + td->poll_interval;
-    Curl_expire(conn->data, td->poll_interval);
-  }
-
-  return CURLE_OK;
-}
-
-int Curl_resolver_getsock(struct connectdata *conn,
-                          curl_socket_t *socks,
-                          int numsocks)
-{
-  (void)conn;
-  (void)socks;
-  (void)numsocks;
-  return 0;
-}
-
-#ifndef HAVE_GETADDRINFO
-/*
- * Curl_getaddrinfo() - for platforms without getaddrinfo
- */
-Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
-                                         const char *hostname,
-                                         int port,
-                                         int *waitp)
-{
-  struct in_addr in;
-
-  *waitp = 0; /* default to synchronous response */
-
-  if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
-    /* This is a dotted IP address 123.123.123.123-style */
-    return Curl_ip2addr(AF_INET, &in, hostname, port);
-
-  /* fire up a new resolver thread! */
-  if(init_resolve_thread(conn, hostname, port, NULL)) {
-    *waitp = 1; /* expect asynchronous response */
-    return NULL;
-  }
-
-  /* fall-back to blocking version */
-  return Curl_ipv4_resolve_r(hostname, port);
-}
-
-#else /* !HAVE_GETADDRINFO */
-
-/*
- * Curl_resolver_getaddrinfo() - for getaddrinfo
- */
-Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
-                                         const char *hostname,
-                                         int port,
-                                         int *waitp)
-{
-  struct addrinfo hints;
-  struct in_addr in;
-  Curl_addrinfo *res;
-  int error;
-  char sbuf[NI_MAXSERV];
-  int pf = PF_INET;
-#ifdef CURLRES_IPV6
-  struct in6_addr in6;
-#endif /* CURLRES_IPV6 */
-
-  *waitp = 0; /* default to synchronous response */
-
-  /* First check if this is an IPv4 address string */
-  if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
-    /* This is a dotted IP address 123.123.123.123-style */
-    return Curl_ip2addr(AF_INET, &in, hostname, port);
-
-#ifdef CURLRES_IPV6
-  /* check if this is an IPv6 address string */
-  if(Curl_inet_pton (AF_INET6, hostname, &in6) > 0)
-    /* This is an IPv6 address literal */
-    return Curl_ip2addr(AF_INET6, &in6, hostname, port);
-
-  /*
-   * Check if a limited name resolve has been requested.
-   */
-  switch(conn->ip_version) {
-  case CURL_IPRESOLVE_V4:
-    pf = PF_INET;
-    break;
-  case CURL_IPRESOLVE_V6:
-    pf = PF_INET6;
-    break;
-  default:
-    pf = PF_UNSPEC;
-    break;
-  }
-
-  if((pf != PF_INET) && !Curl_ipv6works())
-    /* the stack seems to be a non-ipv6 one */
-    pf = PF_INET;
-
-#endif /* CURLRES_IPV6 */
-
-  memset(&hints, 0, sizeof(hints));
-  hints.ai_family = pf;
-  hints.ai_socktype = conn->socktype;
-
-  snprintf(sbuf, sizeof(sbuf), "%d", port);
-
-  /* fire up a new resolver thread! */
-  if(init_resolve_thread(conn, hostname, port, &hints)) {
-    *waitp = 1; /* expect asynchronous response */
-    return NULL;
-  }
-
-  /* fall-back to blocking version */
-  infof(conn->data, "init_resolve_thread() failed for %s; %s\n",
-        hostname, Curl_strerror(conn, ERRNO));
-
-  error = Curl_getaddrinfo_ex(hostname, sbuf, &hints, &res);
-  if(error) {
-    infof(conn->data, "getaddrinfo() failed for %s:%d; %s\n",
-          hostname, port, Curl_strerror(conn, SOCKERRNO));
-    return NULL;
-  }
-  return res;
-}
-
-#endif /* !HAVE_GETADDRINFO */
-
-CURLcode Curl_set_dns_servers(struct SessionHandle *data,
-                              char *servers)
-{
-  (void)data;
-  (void)servers;
-  return CURLE_NOT_BUILT_IN;
-
-}
-
-#endif /* CURLRES_THREADED */
diff --git a/lib/axtls.c b/lib/axtls.c
deleted file mode 100644 (file)
index 8bd606a..0000000
+++ /dev/null
@@ -1,547 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 2010, DirecTV * contact: Eric Hu <ehu@directv.com>
- * Copyright (C) 2010 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-/*
- * Source file for all axTLS-specific code for the TLS/SSL layer. No code
- * but curl_sslgen.c should ever call or use these functions.
- */
-
-#include "curl_setup.h"
-
-#ifdef USE_AXTLS
-#include <axTLS/ssl.h>
-#include "curl_axtls.h"
-
-#include "curl_sendf.h"
-#include "curl_inet_pton.h"
-#include "curl_sslgen.h"
-#include "curl_parsedate.h"
-#include "curl_connect.h" /* for the connect timeout */
-#include "curl_select.h"
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-#include "curl_hostcheck.h"
-
-
-/* SSL_read is opied from axTLS compat layer */
-static int SSL_read(SSL *ssl, void *buf, int num)
-{
-  uint8_t *read_buf;
-  int ret;
-
-  while((ret = ssl_read(ssl, &read_buf)) == SSL_OK);
-
-  if(ret > SSL_OK) {
-    memcpy(buf, read_buf, ret > num ? num : ret);
-  }
-
-  return ret;
-}
-
-/* Global axTLS init, called from Curl_ssl_init() */
-int Curl_axtls_init(void)
-{
-/* axTLS has no global init.  Everything is done through SSL and SSL_CTX
- * structs stored in connectdata structure.  Perhaps can move to curl_axtls.h.
- */
-  return 1;
-}
-
-int Curl_axtls_cleanup(void)
-{
-  /* axTLS has no global cleanup.  Perhaps can move this to curl_axtls.h. */
-  return 1;
-}
-
-static CURLcode map_error_to_curl(int axtls_err)
-{
-  switch (axtls_err) {
-  case SSL_ERROR_NOT_SUPPORTED:
-  case SSL_ERROR_INVALID_VERSION:
-  case -70:                       /* protocol version alert from server */
-    return CURLE_UNSUPPORTED_PROTOCOL;
-    break;
-  case SSL_ERROR_NO_CIPHER:
-    return CURLE_SSL_CIPHER;
-    break;
-  case SSL_ERROR_BAD_CERTIFICATE: /* this may be bad server cert too */
-  case SSL_ERROR_NO_CERT_DEFINED:
-  case -42:                       /* bad certificate alert from server */
-  case -43:                       /* unsupported cert alert from server */
-  case -44:                       /* cert revoked alert from server */
-  case -45:                       /* cert expired alert from server */
-  case -46:                       /* cert unknown alert from server */
-    return CURLE_SSL_CERTPROBLEM;
-    break;
-  case SSL_X509_ERROR(X509_NOT_OK):
-  case SSL_X509_ERROR(X509_VFY_ERROR_NO_TRUSTED_CERT):
-  case SSL_X509_ERROR(X509_VFY_ERROR_BAD_SIGNATURE):
-  case SSL_X509_ERROR(X509_VFY_ERROR_NOT_YET_VALID):
-  case SSL_X509_ERROR(X509_VFY_ERROR_EXPIRED):
-  case SSL_X509_ERROR(X509_VFY_ERROR_SELF_SIGNED):
-  case SSL_X509_ERROR(X509_VFY_ERROR_INVALID_CHAIN):
-  case SSL_X509_ERROR(X509_VFY_ERROR_UNSUPPORTED_DIGEST):
-  case SSL_X509_ERROR(X509_INVALID_PRIV_KEY):
-    return CURLE_PEER_FAILED_VERIFICATION;
-    break;
-  case -48:                       /* unknown ca alert from server */
-    return CURLE_SSL_CACERT;
-    break;
-  case -49:                       /* access denied alert from server */
-    return CURLE_REMOTE_ACCESS_DENIED;
-    break;
-  case SSL_ERROR_CONN_LOST:
-  case SSL_ERROR_SOCK_SETUP_FAILURE:
-  case SSL_ERROR_INVALID_HANDSHAKE:
-  case SSL_ERROR_INVALID_PROT_MSG:
-  case SSL_ERROR_INVALID_HMAC:
-  case SSL_ERROR_INVALID_SESSION:
-  case SSL_ERROR_INVALID_KEY:     /* it's too bad this doesn't map better */
-  case SSL_ERROR_FINISHED_INVALID:
-  case SSL_ERROR_NO_CLIENT_RENOG:
-  default:
-    return CURLE_SSL_CONNECT_ERROR;
-    break;
-  }
-}
-
-static Curl_recv axtls_recv;
-static Curl_send axtls_send;
-
-/*
- * This function is called after the TCP connect has completed. Setup the TLS
- * layer and do all necessary magic.
- */
-CURLcode
-Curl_axtls_connect(struct connectdata *conn,
-                  int sockindex)
-
-{
-  struct SessionHandle *data = conn->data;
-  SSL_CTX *ssl_ctx;
-  SSL *ssl;
-  int cert_types[] = {SSL_OBJ_X509_CERT, SSL_OBJ_PKCS12, 0};
-  int key_types[] = {SSL_OBJ_RSA_KEY, SSL_OBJ_PKCS8, SSL_OBJ_PKCS12, 0};
-  int i, ssl_fcn_return;
-  const uint8_t *ssl_sessionid;
-  size_t ssl_idsize;
-  const char *peer_CN;
-  uint32_t dns_altname_index;
-  const char *dns_altname;
-  int8_t found_subject_alt_names = 0;
-  int8_t found_subject_alt_name_matching_conn = 0;
-
-  /* Assuming users will not compile in custom key/cert to axTLS */
-  uint32_t client_option = SSL_NO_DEFAULT_KEY|SSL_SERVER_VERIFY_LATER;
-
-  if(conn->ssl[sockindex].state == ssl_connection_complete)
-    /* to make us tolerant against being called more than once for the
-       same connection */
-    return CURLE_OK;
-
-  /* axTLS only supports TLSv1 */
-  /* check to see if we've been told to use an explicit SSL/TLS version */
-  switch(data->set.ssl.version) {
-  case CURL_SSLVERSION_DEFAULT:
-  case CURL_SSLVERSION_TLSv1:
-    break;
-  default:
-    failf(data, "axTLS only supports TLSv1");
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-
-#ifdef  AXTLSDEBUG
-  client_option |= SSL_DISPLAY_STATES | SSL_DISPLAY_RSA | SSL_DISPLAY_CERTS;
-#endif /* AXTLSDEBUG */
-
-  /* Allocate an SSL_CTX struct */
-  ssl_ctx = ssl_ctx_new(client_option, SSL_DEFAULT_CLNT_SESS);
-  if(ssl_ctx == NULL) {
-    failf(data, "unable to create client SSL context");
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-
-  /* Load the trusted CA cert bundle file */
-  if(data->set.ssl.CAfile) {
-    if(ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CACERT, data->set.ssl.CAfile, NULL)
-       != SSL_OK) {
-      infof(data, "error reading ca cert file %s \n",
-            data->set.ssl.CAfile);
-      if(data->set.ssl.verifypeer) {
-        Curl_axtls_close(conn, sockindex);
-        return CURLE_SSL_CACERT_BADFILE;
-      }
-    }
-    else
-      infof(data, "found certificates in %s\n", data->set.ssl.CAfile);
-  }
-
-  /* curl_gtls.c tasks we're skipping for now:
-   * 1) certificate revocation list checking
-   * 2) dns name assignment to host
-   * 3) set protocol priority.  axTLS is TLSv1 only, so can probably ignore
-   * 4) set certificate priority.  axTLS ignores type and sends certs in
-   *  order added.  can probably ignore this.
-   */
-
-  /* Load client certificate */
-  if(data->set.str[STRING_CERT]) {
-    i=0;
-    /* Instead of trying to analyze cert type here, let axTLS try them all. */
-    while(cert_types[i] != 0) {
-      ssl_fcn_return = ssl_obj_load(ssl_ctx, cert_types[i],
-                                    data->set.str[STRING_CERT], NULL);
-      if(ssl_fcn_return == SSL_OK) {
-        infof(data, "successfully read cert file %s \n",
-              data->set.str[STRING_CERT]);
-        break;
-      }
-      i++;
-    }
-    /* Tried all cert types, none worked. */
-    if(cert_types[i] == 0) {
-      failf(data, "%s is not x509 or pkcs12 format",
-            data->set.str[STRING_CERT]);
-      Curl_axtls_close(conn, sockindex);
-      return CURLE_SSL_CERTPROBLEM;
-    }
-  }
-
-  /* Load client key.
-     If a pkcs12 file successfully loaded a cert, then there's nothing to do
-     because the key has already been loaded. */
-  if(data->set.str[STRING_KEY] && cert_types[i] != SSL_OBJ_PKCS12) {
-    i=0;
-    /* Instead of trying to analyze key type here, let axTLS try them all. */
-    while(key_types[i] != 0) {
-      ssl_fcn_return = ssl_obj_load(ssl_ctx, key_types[i],
-                                    data->set.str[STRING_KEY], NULL);
-      if(ssl_fcn_return == SSL_OK) {
-        infof(data, "successfully read key file %s \n",
-              data->set.str[STRING_KEY]);
-        break;
-      }
-      i++;
-    }
-    /* Tried all key types, none worked. */
-    if(key_types[i] == 0) {
-      failf(data, "Failure: %s is not a supported key file",
-            data->set.str[STRING_KEY]);
-      Curl_axtls_close(conn, sockindex);
-      return CURLE_SSL_CONNECT_ERROR;
-    }
-  }
-
-  /* curl_gtls.c does more here that is being left out for now
-   * 1) set session credentials.  can probably ignore since axtls puts this
-   *    info in the ssl_ctx struct
-   * 2) setting up callbacks.  these seem gnutls specific
-   */
-
-  /* In axTLS, handshaking happens inside ssl_client_new. */
-  if(!Curl_ssl_getsessionid(conn, (void **) &ssl_sessionid, &ssl_idsize)) {
-    /* we got a session id, use it! */
-    infof (data, "SSL re-using session ID\n");
-    ssl = ssl_client_new(ssl_ctx, conn->sock[sockindex],
-                         ssl_sessionid, (uint8_t)ssl_idsize);
-  }
-  else
-    ssl = ssl_client_new(ssl_ctx, conn->sock[sockindex], NULL, 0);
-
-  /* Check to make sure handshake was ok. */
-  ssl_fcn_return = ssl_handshake_status(ssl);
-  if(ssl_fcn_return != SSL_OK) {
-    Curl_axtls_close(conn, sockindex);
-    ssl_display_error(ssl_fcn_return); /* goes to stdout. */
-    return map_error_to_curl(ssl_fcn_return);
-  }
-  infof (data, "handshake completed successfully\n");
-
-  /* Here, curl_gtls.c gets the peer certificates and fails out depending on
-   * settings in "data."  axTLS api doesn't have get cert chain fcn, so omit?
-   */
-
-  /* Verify server's certificate */
-  if(data->set.ssl.verifypeer) {
-    if(ssl_verify_cert(ssl) != SSL_OK) {
-      Curl_axtls_close(conn, sockindex);
-      failf(data, "server cert verify failed");
-      return CURLE_SSL_CONNECT_ERROR;
-    }
-  }
-  else
-    infof(data, "\t server certificate verification SKIPPED\n");
-
-  /* Here, curl_gtls.c does issuer verification. axTLS has no straightforward
-   * equivalent, so omitting for now.*/
-
-  /* Here, curl_gtls.c does the following
-   * 1) x509 hostname checking per RFC2818.  axTLS doesn't support this, but
-   *    it seems useful. This is now implemented, by Oscar Koeroo
-   * 2) checks cert validity based on time.  axTLS does this in ssl_verify_cert
-   * 3) displays a bunch of cert information.  axTLS doesn't support most of
-   *    this, but a couple fields are available.
-   */
-
-
-  /* There is no (DNS) Altnames count in the version 1.4.8 API. There is a
-     risk of an inifite loop */
-  for(dns_altname_index = 0; ; dns_altname_index++) {
-    dns_altname = ssl_get_cert_subject_alt_dnsname(ssl, dns_altname_index);
-    if(dns_altname == NULL) {
-      break;
-    }
-    found_subject_alt_names = 1;
-
-    infof(data, "\tComparing subject alt name DNS with hostname: %s <-> %s\n",
-          dns_altname, conn->host.name);
-    if(Curl_cert_hostcheck(dns_altname, conn->host.name)) {
-      found_subject_alt_name_matching_conn = 1;
-      break;
-    }
-  }
-
-  /* RFC2818 checks */
-  if(found_subject_alt_names && !found_subject_alt_name_matching_conn) {
-    /* Break connection ! */
-    Curl_axtls_close(conn, sockindex);
-    failf(data, "\tsubjectAltName(s) do not match %s\n", conn->host.dispname);
-    return CURLE_PEER_FAILED_VERIFICATION;
-  }
-  else if(found_subject_alt_names == 0) {
-    /* Per RFC2818, when no Subject Alt Names were available, examine the peer
-       CN as a legacy fallback */
-    peer_CN = ssl_get_cert_dn(ssl, SSL_X509_CERT_COMMON_NAME);
-    if(peer_CN == NULL) {
-      /* Similar behaviour to the OpenSSL interface */
-      Curl_axtls_close(conn, sockindex);
-      failf(data, "unable to obtain common name from peer certificate");
-      return CURLE_PEER_FAILED_VERIFICATION;
-    }
-    else {
-      if(!Curl_cert_hostcheck((const char *)peer_CN, conn->host.name)) {
-        if(data->set.ssl.verifyhost) {
-          /* Break connection ! */
-          Curl_axtls_close(conn, sockindex);
-          failf(data, "\tcommon name \"%s\" does not match \"%s\"\n",
-                peer_CN, conn->host.dispname);
-          return CURLE_PEER_FAILED_VERIFICATION;
-        }
-        else
-          infof(data, "\tcommon name \"%s\" does not match \"%s\"\n",
-                peer_CN, conn->host.dispname);
-      }
-    }
-  }
-
-  /* General housekeeping */
-  conn->ssl[sockindex].state = ssl_connection_complete;
-  conn->ssl[sockindex].ssl = ssl;
-  conn->ssl[sockindex].ssl_ctx = ssl_ctx;
-  conn->recv[sockindex] = axtls_recv;
-  conn->send[sockindex] = axtls_send;
-
-  /* Put our freshly minted SSL session in cache */
-  ssl_idsize = ssl_get_session_id_size(ssl);
-  ssl_sessionid = ssl_get_session_id(ssl);
-  if(Curl_ssl_addsessionid(conn, (void *) ssl_sessionid, ssl_idsize)
-     != CURLE_OK)
-    infof (data, "failed to add session to cache\n");
-
-  return CURLE_OK;
-}
-
-
-/* return number of sent (non-SSL) bytes */
-static ssize_t axtls_send(struct connectdata *conn,
-                          int sockindex,
-                          const void *mem,
-                          size_t len,
-                          CURLcode *err)
-{
-  /* ssl_write() returns 'int' while write() and send() returns 'size_t' */
-  int rc = ssl_write(conn->ssl[sockindex].ssl, mem, (int)len);
-
-  infof(conn->data, "  axtls_send\n");
-
-  if(rc < 0 ) {
-    *err = map_error_to_curl(rc);
-    rc = -1; /* generic error code for send failure */
-  }
-
-  *err = CURLE_OK;
-  return rc;
-}
-
-void Curl_axtls_close_all(struct SessionHandle *data)
-{
-  (void)data;
-  infof(data, "  Curl_axtls_close_all\n");
-}
-
-void Curl_axtls_close(struct connectdata *conn, int sockindex)
-{
-  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-
-  infof(conn->data, "  Curl_axtls_close\n");
-  if(connssl->ssl) {
-    /* line from curl_ssluse.c: (void)SSL_shutdown(connssl->ssl);
-       axTLS compat layer does nothing for SSL_shutdown */
-
-    /* The following line is from curl_ssluse.c.  There seems to be no axTLS
-       equivalent.  ssl_free and ssl_ctx_free close things.
-       SSL_set_connect_state(connssl->handle); */
-
-    ssl_free (connssl->ssl);
-    connssl->ssl = NULL;
-  }
-  if(connssl->ssl_ctx) {
-    ssl_ctx_free (connssl->ssl_ctx);
-    connssl->ssl_ctx = NULL;
-  }
-}
-
-/*
- * This function is called to shut down the SSL layer but keep the
- * socket open (CCC - Clear Command Channel)
- */
-int Curl_axtls_shutdown(struct connectdata *conn, int sockindex)
-{
-  /* Outline taken from curl_ssluse.c since functions are in axTLS compat
-     layer.  axTLS's error set is much smaller, so a lot of error-handling
-     was removed.
-   */
-  int retval = 0;
-  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-  struct SessionHandle *data = conn->data;
-  char buf[120]; /* We will use this for the OpenSSL error buffer, so it has
-                    to be at least 120 bytes long. */
-  ssize_t nread;
-
-  infof(conn->data, "  Curl_axtls_shutdown\n");
-
-  /* This has only been tested on the proftpd server, and the mod_tls code
-     sends a close notify alert without waiting for a close notify alert in
-     response. Thus we wait for a close notify alert from the server, but
-     we do not send one. Let's hope other servers do the same... */
-
-  /* axTLS compat layer does nothing for SSL_shutdown, so we do nothing too
-  if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE)
-      (void)SSL_shutdown(connssl->ssl);
-  */
-
-  if(connssl->ssl) {
-    int what = Curl_socket_ready(conn->sock[sockindex],
-                                 CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT);
-    if(what > 0) {
-      /* Something to read, let's do it and hope that it is the close
-         notify alert from the server */
-      nread = (ssize_t)SSL_read(conn->ssl[sockindex].ssl, buf,
-                                sizeof(buf));
-
-      if(nread < SSL_OK) {
-        failf(data, "close notify alert not received during shutdown");
-        retval = -1;
-      }
-    }
-    else if(0 == what) {
-      /* timeout */
-      failf(data, "SSL shutdown timeout");
-    }
-    else {
-      /* anything that gets here is fatally bad */
-      failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
-      retval = -1;
-    }
-
-    ssl_free (connssl->ssl);
-    connssl->ssl = NULL;
-  }
-  return retval;
-}
-
-static ssize_t axtls_recv(struct connectdata *conn, /* connection data */
-                          int num,                  /* socketindex */
-                          char *buf,                /* store read data here */
-                          size_t buffersize,        /* max amount to read */
-                          CURLcode *err)
-{
-  struct ssl_connect_data *connssl = &conn->ssl[num];
-  ssize_t ret = 0;
-
-  infof(conn->data, "  axtls_recv\n");
-
-  if(connssl) {
-    ret = (ssize_t)SSL_read(conn->ssl[num].ssl, buf, (int)buffersize);
-
-    /* axTLS isn't terribly generous about error reporting */
-    /* With patched axTLS, SSL_CLOSE_NOTIFY=-3.  Hard-coding until axTLS
-       team approves proposed fix. */
-    if(ret == -3 ) {
-      Curl_axtls_close(conn, num);
-    }
-    else if(ret < 0) {
-      failf(conn->data, "axTLS recv error (%d)", (int)ret);
-      *err = map_error_to_curl(ret);
-      return -1;
-    }
-  }
-
-  *err = CURLE_OK;
-  return ret;
-}
-
-/*
- * Return codes:
- *     1 means the connection is still in place
- *     0 means the connection has been closed
- *    -1 means the connection status is unknown
- */
-int Curl_axtls_check_cxn(struct connectdata *conn)
-{
-  /* curl_ssluse.c line:
-     rc = SSL_peek(conn->ssl[FIRSTSOCKET].ssl, (void*)&buf, 1);
-     axTLS compat layer always returns the last argument, so connection is
-     always alive? */
-
-  infof(conn->data, "  Curl_axtls_check_cxn\n");
-   return 1; /* connection still in place */
-}
-
-void Curl_axtls_session_free(void *ptr)
-{
-  (void)ptr;
-  /* free the ID */
-  /* both curl_ssluse.c and curl_gtls.c do something here, but axTLS's
-     OpenSSL compatibility layer does nothing, so we do nothing too. */
-}
-
-size_t Curl_axtls_version(char *buffer, size_t size)
-{
-  return snprintf(buffer, size, "axTLS/%s", ssl_version());
-}
-
-#endif /* USE_AXTLS */
diff --git a/lib/base64.c b/lib/base64.c
deleted file mode 100644 (file)
index 45c7a95..0000000
+++ /dev/null
@@ -1,248 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-/* Base64 encoding/decoding */
-
-#include "curl_setup.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_urldata.h" /* for the SessionHandle definition */
-#include "curl_warnless.h"
-#include "curl_base64.h"
-#include "curl_memory.h"
-#include "curl_non_ascii.h"
-
-/* include curl_memdebug.h last */
-#include "curl_memdebug.h"
-
-/* ---- Base64 Encoding/Decoding Table --- */
-static const char table64[]=
-  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-static void decodeQuantum(unsigned char *dest, const char *src)
-{
-  const char *s, *p;
-  unsigned long i, v, x = 0;
-
-  for(i = 0, s = src; i < 4; i++, s++) {
-    v = 0;
-    p = table64;
-    while(*p && (*p != *s)) {
-      v++;
-      p++;
-    }
-    if(*p == *s)
-      x = (x << 6) + v;
-    else if(*s == '=')
-      x = (x << 6);
-  }
-
-  dest[2] = curlx_ultouc(x & 0xFFUL);
-  x >>= 8;
-  dest[1] = curlx_ultouc(x & 0xFFUL);
-  x >>= 8;
-  dest[0] = curlx_ultouc(x & 0xFFUL);
-}
-
-/*
- * Curl_base64_decode()
- *
- * Given a base64 NUL-terminated string at src, decode it and return a
- * pointer in *outptr to a newly allocated memory area holding decoded
- * data. Size of decoded data is returned in variable pointed by outlen.
- *
- * Returns CURLE_OK on success, otherwise specific error code. Function
- * output shall not be considered valid unless CURLE_OK is returned.
- *
- * When decoded data length is 0, returns NULL in *outptr.
- *
- * @unittest: 1302
- */
-CURLcode Curl_base64_decode(const char *src,
-                            unsigned char **outptr, size_t *outlen)
-{
-  size_t length = 0;
-  size_t equalsTerm = 0;
-  size_t i;
-  size_t numQuantums;
-  unsigned char lastQuantum[3];
-  size_t rawlen = 0;
-  unsigned char *newstr;
-
-  *outptr = NULL;
-  *outlen = 0;
-
-  while((src[length] != '=') && src[length])
-    length++;
-  /* A maximum of two = padding characters is allowed */
-  if(src[length] == '=') {
-    equalsTerm++;
-    if(src[length+equalsTerm] == '=')
-      equalsTerm++;
-  }
-  numQuantums = (length + equalsTerm) / 4;
-
-  /* Don't allocate a buffer if the decoded length is 0 */
-  if(numQuantums == 0)
-    return CURLE_OK;
-
-  rawlen = (numQuantums * 3) - equalsTerm;
-
-  /* The buffer must be large enough to make room for the last quantum
-  (which may be partially thrown out) and the zero terminator. */
-  newstr = malloc(rawlen+4);
-  if(!newstr)
-    return CURLE_OUT_OF_MEMORY;
-
-  *outptr = newstr;
-
-  /* Decode all but the last quantum (which may not decode to a
-  multiple of 3 bytes) */
-  for(i = 0; i < numQuantums - 1; i++) {
-    decodeQuantum(newstr, src);
-    newstr += 3; src += 4;
-  }
-
-  /* This final decode may actually read slightly past the end of the buffer
-  if the input string is missing pad bytes.  This will almost always be
-  harmless. */
-  decodeQuantum(lastQuantum, src);
-  for(i = 0; i < 3 - equalsTerm; i++)
-    newstr[i] = lastQuantum[i];
-
-  newstr[i] = '\0'; /* zero terminate */
-
-  *outlen = rawlen; /* return size of decoded data */
-
-  return CURLE_OK;
-}
-
-/*
- * Curl_base64_encode()
- *
- * Given a pointer to an input buffer and an input size, encode it and
- * return a pointer in *outptr to a newly allocated memory area holding
- * encoded data. Size of encoded data is returned in variable pointed by
- * outlen.
- *
- * Input length of 0 indicates input buffer holds a NUL-terminated string.
- *
- * Returns CURLE_OK on success, otherwise specific error code. Function
- * output shall not be considered valid unless CURLE_OK is returned.
- *
- * When encoded data length is 0, returns NULL in *outptr.
- *
- * @unittest: 1302
- */
-CURLcode Curl_base64_encode(struct SessionHandle *data,
-                            const char *inputbuff, size_t insize,
-                            char **outptr, size_t *outlen)
-{
-  CURLcode error;
-  unsigned char ibuf[3];
-  unsigned char obuf[4];
-  int i;
-  int inputparts;
-  char *output;
-  char *base64data;
-  char *convbuf = NULL;
-
-  const char *indata = inputbuff;
-
-  *outptr = NULL;
-  *outlen = 0;
-
-  if(0 == insize)
-    insize = strlen(indata);
-
-  base64data = output = malloc(insize*4/3+4);
-  if(NULL == output)
-    return CURLE_OUT_OF_MEMORY;
-
-  /*
-   * The base64 data needs to be created using the network encoding
-   * not the host encoding.  And we can't change the actual input
-   * so we copy it to a buffer, translate it, and use that instead.
-   */
-  error = Curl_convert_clone(data, indata, insize, &convbuf);
-  if(error) {
-    free(output);
-    return error;
-  }
-
-  if(convbuf)
-    indata = (char *)convbuf;
-
-  while(insize > 0) {
-    for(i = inputparts = 0; i < 3; i++) {
-      if(insize > 0) {
-        inputparts++;
-        ibuf[i] = (unsigned char) *indata;
-        indata++;
-        insize--;
-      }
-      else
-        ibuf[i] = 0;
-    }
-
-    obuf[0] = (unsigned char)  ((ibuf[0] & 0xFC) >> 2);
-    obuf[1] = (unsigned char) (((ibuf[0] & 0x03) << 4) | \
-                               ((ibuf[1] & 0xF0) >> 4));
-    obuf[2] = (unsigned char) (((ibuf[1] & 0x0F) << 2) | \
-                               ((ibuf[2] & 0xC0) >> 6));
-    obuf[3] = (unsigned char)   (ibuf[2] & 0x3F);
-
-    switch(inputparts) {
-    case 1: /* only one byte read */
-      snprintf(output, 5, "%c%c==",
-               table64[obuf[0]],
-               table64[obuf[1]]);
-      break;
-    case 2: /* two bytes read */
-      snprintf(output, 5, "%c%c%c=",
-               table64[obuf[0]],
-               table64[obuf[1]],
-               table64[obuf[2]]);
-      break;
-    default:
-      snprintf(output, 5, "%c%c%c%c",
-               table64[obuf[0]],
-               table64[obuf[1]],
-               table64[obuf[2]],
-               table64[obuf[3]] );
-      break;
-    }
-    output += 4;
-  }
-  *output = '\0';
-  *outptr = base64data; /* return pointer to new data, allocated memory */
-
-  if(convbuf)
-    free(convbuf);
-
-  *outlen = strlen(base64data); /* return the length of the new data */
-
-  return CURLE_OK;
-}
-/* ---- End of Base64 Encoding ---- */
diff --git a/lib/bundles.c b/lib/bundles.c
deleted file mode 100644 (file)
index efbaeee..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 2012, Linus Nielsen Feltzing, <linus@haxx.se>
- * Copyright (C) 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#include <curl/curl.h>
-
-#include "curl_urldata.h"
-#include "curl_url.h"
-#include "curl_progress.h"
-#include "curl_multiif.h"
-#include "curl_bundles.h"
-#include "curl_sendf.h"
-#include "curl_rawstr.h"
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-static void conn_llist_dtor(void *user, void *element)
-{
-  struct connectdata *data = element;
-  (void)user;
-
-  data->bundle = NULL;
-}
-
-CURLcode Curl_bundle_create(struct SessionHandle *data,
-                            struct connectbundle **cb_ptr)
-{
-  (void)data;
-  DEBUGASSERT(*cb_ptr == NULL);
-  *cb_ptr = malloc(sizeof(struct connectbundle));
-  if(!*cb_ptr)
-    return CURLE_OUT_OF_MEMORY;
-
-  (*cb_ptr)->num_connections = 0;
-  (*cb_ptr)->server_supports_pipelining = FALSE;
-
-  (*cb_ptr)->conn_list = Curl_llist_alloc((curl_llist_dtor) conn_llist_dtor);
-  if(!(*cb_ptr)->conn_list) {
-    Curl_safefree(*cb_ptr);
-    return CURLE_OUT_OF_MEMORY;
-  }
-  return CURLE_OK;
-}
-
-void Curl_bundle_destroy(struct connectbundle *cb_ptr)
-{
-  if(!cb_ptr)
-    return;
-
-  if(cb_ptr->conn_list) {
-    Curl_llist_destroy(cb_ptr->conn_list, NULL);
-    cb_ptr->conn_list = NULL;
-  }
-  Curl_safefree(cb_ptr);
-}
-
-/* Add a connection to a bundle */
-CURLcode Curl_bundle_add_conn(struct connectbundle *cb_ptr,
-                              struct connectdata *conn)
-{
-  if(!Curl_llist_insert_next(cb_ptr->conn_list, cb_ptr->conn_list->tail, conn))
-    return CURLE_OUT_OF_MEMORY;
-
-  conn->bundle = cb_ptr;
-
-  cb_ptr->num_connections++;
-  return CURLE_OK;
-}
-
-/* Remove a connection from a bundle */
-int Curl_bundle_remove_conn(struct connectbundle *cb_ptr,
-                            struct connectdata *conn)
-{
-  struct curl_llist_element *curr;
-
-  curr = cb_ptr->conn_list->head;
-  while(curr) {
-    if(curr->ptr == conn) {
-      Curl_llist_remove(cb_ptr->conn_list, curr, NULL);
-      cb_ptr->num_connections--;
-      conn->bundle = NULL;
-      return 1; /* we removed a handle */
-    }
-    curr = curr->next;
-  }
-  return 0;
-}
diff --git a/lib/conncache.c b/lib/conncache.c
deleted file mode 100644 (file)
index bc95e07..0000000
+++ /dev/null
@@ -1,285 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 2012, Linus Nielsen Feltzing, <linus@haxx.se>
- * Copyright (C) 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#include <curl/curl.h>
-
-#include "curl_urldata.h"
-#include "curl_url.h"
-#include "curl_progress.h"
-#include "curl_multiif.h"
-#include "curl_sendf.h"
-#include "curl_rawstr.h"
-#include "curl_bundles.h"
-#include "curl_conncache.h"
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-#define CONNECTION_HASH_SIZE 97
-
-static void free_bundle_hash_entry(void *freethis)
-{
-  struct connectbundle *b = (struct connectbundle *) freethis;
-
-  Curl_bundle_destroy(b);
-}
-
-struct conncache *Curl_conncache_init(conncachetype type)
-{
-  struct conncache *connc;
-
-  connc = calloc(1, sizeof(struct conncache));
-  if(!connc)
-    return NULL;
-
-  connc->hash = Curl_hash_alloc(CONNECTION_HASH_SIZE, Curl_hash_str,
-                                Curl_str_key_compare, free_bundle_hash_entry);
-
-  if(!connc->hash) {
-    free(connc);
-    return NULL;
-  }
-
-  connc->type = type;
-  connc->num_connections = 0;
-
-  return connc;
-}
-
-void Curl_conncache_destroy(struct conncache *connc)
-{
-  if(connc) {
-    Curl_hash_destroy(connc->hash);
-    connc->hash = NULL;
-    free(connc);
-  }
-}
-
-struct connectbundle *Curl_conncache_find_bundle(struct conncache *connc,
-                                                 char *hostname)
-{
-  struct connectbundle *bundle = NULL;
-
-  if(connc)
-    bundle = Curl_hash_pick(connc->hash, hostname, strlen(hostname)+1);
-
-  return bundle;
-}
-
-static bool conncache_add_bundle(struct conncache *connc,
-                                 char *hostname,
-                                 struct connectbundle *bundle)
-{
-  void *p;
-
-  p = Curl_hash_add(connc->hash, hostname, strlen(hostname)+1, bundle);
-
-  return p?TRUE:FALSE;
-}
-
-static void conncache_remove_bundle(struct conncache *connc,
-                                    struct connectbundle *bundle)
-{
-  struct curl_hash_iterator iter;
-  struct curl_hash_element *he;
-
-  if(!connc)
-    return;
-
-  Curl_hash_start_iterate(connc->hash, &iter);
-
-  he = Curl_hash_next_element(&iter);
-  while(he) {
-    if(he->ptr == bundle) {
-      /* The bundle is destroyed by the hash destructor function,
-         free_bundle_hash_entry() */
-      Curl_hash_delete(connc->hash, he->key, he->key_len);
-      return;
-    }
-
-    he = Curl_hash_next_element(&iter);
-  }
-}
-
-CURLcode Curl_conncache_add_conn(struct conncache *connc,
-                                 struct connectdata *conn)
-{
-  CURLcode result;
-  struct connectbundle *bundle;
-  struct connectbundle *new_bundle = NULL;
-  struct SessionHandle *data = conn->data;
-
-  bundle = Curl_conncache_find_bundle(data->state.conn_cache,
-                                      conn->host.name);
-  if(!bundle) {
-    result = Curl_bundle_create(data, &new_bundle);
-    if(result != CURLE_OK)
-      return result;
-
-    if(!conncache_add_bundle(data->state.conn_cache,
-                             conn->host.name, new_bundle)) {
-      Curl_bundle_destroy(new_bundle);
-      return CURLE_OUT_OF_MEMORY;
-    }
-    bundle = new_bundle;
-  }
-
-  result = Curl_bundle_add_conn(bundle, conn);
-  if(result != CURLE_OK) {
-    if(new_bundle)
-      conncache_remove_bundle(data->state.conn_cache, new_bundle);
-    return result;
-  }
-
-  connc->num_connections++;
-
-  return CURLE_OK;
-}
-
-void Curl_conncache_remove_conn(struct conncache *connc,
-                                struct connectdata *conn)
-{
-  struct connectbundle *bundle = conn->bundle;
-
-  /* The bundle pointer can be NULL, since this function can be called
-     due to a failed connection attempt, before being added to a bundle */
-  if(bundle) {
-    Curl_bundle_remove_conn(bundle, conn);
-    if(bundle->num_connections == 0) {
-      conncache_remove_bundle(connc, bundle);
-    }
-    connc->num_connections--;
-
-    DEBUGF(infof(conn->data, "The cache now contains %d members\n",
-                 connc->num_connections));
-  }
-}
-
-/* This function iterates the entire connection cache and calls the
-   function func() with the connection pointer as the first argument
-   and the supplied 'param' argument as the other,
-
-   Return 0 from func() to continue the loop, return 1 to abort it.
- */
-void Curl_conncache_foreach(struct conncache *connc,
-                            void *param,
-                            int (*func)(struct connectdata *conn, void *param))
-{
-  struct curl_hash_iterator iter;
-  struct curl_llist_element *curr;
-  struct curl_hash_element *he;
-
-  if(!connc)
-    return;
-
-  Curl_hash_start_iterate(connc->hash, &iter);
-
-  he = Curl_hash_next_element(&iter);
-  while(he) {
-    struct connectbundle *bundle;
-    struct connectdata *conn;
-
-    bundle = he->ptr;
-
-    curr = bundle->conn_list->head;
-    while(curr) {
-      /* Yes, we need to update curr before calling func(), because func()
-         might decide to remove the connection */
-      conn = curr->ptr;
-      curr = curr->next;
-
-      if(1 == func(conn, param))
-        return;
-    }
-
-    he = Curl_hash_next_element(&iter);
-  }
-}
-
-/* Return the first connection found in the cache. Used when closing all
-   connections */
-struct connectdata *
-Curl_conncache_find_first_connection(struct conncache *connc)
-{
-  struct curl_hash_iterator iter;
-  struct curl_llist_element *curr;
-  struct curl_hash_element *he;
-  struct connectbundle *bundle;
-
-  Curl_hash_start_iterate(connc->hash, &iter);
-
-  he = Curl_hash_next_element(&iter);
-  while(he) {
-    bundle = he->ptr;
-
-    curr = bundle->conn_list->head;
-    if(curr) {
-      return curr->ptr;
-    }
-
-    he = Curl_hash_next_element(&iter);
-  }
-
-  return NULL;
-}
-
-
-#if 0
-/* Useful for debugging the connection cache */
-void Curl_conncache_print(struct conncache *connc)
-{
-  struct curl_hash_iterator iter;
-  struct curl_llist_element *curr;
-  struct curl_hash_element *he;
-
-  if(!connc)
-    return;
-
-  fprintf(stderr, "=Bundle cache=\n");
-
-  Curl_hash_start_iterate(connc->hash, &iter);
-
-  he = Curl_hash_next_element(&iter);
-  while(he) {
-    struct connectbundle *bundle;
-    struct connectdata *conn;
-
-    bundle = he->ptr;
-
-    fprintf(stderr, "%s -", he->key);
-    curr = bundle->conn_list->head;
-    while(curr) {
-      conn = curr->ptr;
-
-      fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
-      curr = curr->next;
-    }
-    fprintf(stderr, "\n");
-
-    he = Curl_hash_next_element(&iter);
-  }
-}
-#endif
diff --git a/lib/connect.c b/lib/connect.c
deleted file mode 100644 (file)
index 85226d8..0000000
+++ /dev/null
@@ -1,1248 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h> /* <netinet/tcp.h> may need it */
-#endif
-#ifdef HAVE_SYS_UN_H
-#include <sys/un.h> /* for sockaddr_un */
-#endif
-#ifdef HAVE_NETINET_TCP_H
-#include <netinet/tcp.h> /* for TCP_NODELAY */
-#endif
-#ifdef HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-
-#if (defined(HAVE_IOCTL_FIONBIO) && defined(NETWARE))
-#include <sys/filio.h>
-#endif
-#ifdef NETWARE
-#undef in_addr_t
-#define in_addr_t unsigned long
-#endif
-#ifdef __VMS
-#include <in.h>
-#include <inet.h>
-#endif
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_if2ip.h"
-#include "curl_strerror.h"
-#include "curl_connect.h"
-#include "curl_memory.h"
-#include "curl_select.h"
-#include "curl_url.h"
-#include "curl_multiif.h"
-#include "curl_sockaddr.h" /* required for Curl_sockaddr_storage */
-#include "curl_inet_ntop.h"
-#include "curl_inet_pton.h"
-#include "curl_sslgen.h" /* for Curl_ssl_check_cxn() */
-#include "curl_progress.h"
-#include "curl_warnless.h"
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-#ifdef __SYMBIAN32__
-/* This isn't actually supported under Symbian OS */
-#undef SO_NOSIGPIPE
-#endif
-
-static bool verifyconnect(curl_socket_t sockfd, int *error);
-
-#ifdef __DragonFly__
-/* DragonFlyBSD uses millisecond as KEEPIDLE and KEEPINTVL units */
-#define KEEPALIVE_FACTOR(x) (x *= 1000)
-#else
-#define KEEPALIVE_FACTOR(x)
-#endif
-
-static void
-tcpkeepalive(struct SessionHandle *data,
-             curl_socket_t sockfd)
-{
-  int optval = data->set.tcp_keepalive?1:0;
-
-  /* only set IDLE and INTVL if setting KEEPALIVE is successful */
-  if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
-        (void *)&optval, sizeof(optval)) < 0) {
-    infof(data, "Failed to set SO_KEEPALIVE on fd %d\n", sockfd);
-  }
-  else {
-#ifdef TCP_KEEPIDLE
-    optval = curlx_sltosi(data->set.tcp_keepidle);
-    KEEPALIVE_FACTOR(optval);
-    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
-          (void *)&optval, sizeof(optval)) < 0) {
-      infof(data, "Failed to set TCP_KEEPIDLE on fd %d\n", sockfd);
-    }
-#endif
-#ifdef TCP_KEEPINTVL
-    optval = curlx_sltosi(data->set.tcp_keepintvl);
-    KEEPALIVE_FACTOR(optval);
-    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
-          (void *)&optval, sizeof(optval)) < 0) {
-      infof(data, "Failed to set TCP_KEEPINTVL on fd %d\n", sockfd);
-    }
-#endif
-  }
-}
-
-static CURLcode
-singleipconnect(struct connectdata *conn,
-                const Curl_addrinfo *ai, /* start connecting to this */
-                long timeout_ms,
-                curl_socket_t *sock,
-                bool *connected);
-
-/*
- * Curl_timeleft() returns the amount of milliseconds left allowed for the
- * transfer/connection. If the value is negative, the timeout time has already
- * elapsed.
- *
- * The start time is stored in progress.t_startsingle - as set with
- * Curl_pgrsTime(..., TIMER_STARTSINGLE);
- *
- * If 'nowp' is non-NULL, it points to the current time.
- * 'duringconnect' is FALSE if not during a connect, as then of course the
- * connect timeout is not taken into account!
- *
- * @unittest: 1303
- */
-long Curl_timeleft(struct SessionHandle *data,
-                   struct timeval *nowp,
-                   bool duringconnect)
-{
-  int timeout_set = 0;
-  long timeout_ms = duringconnect?DEFAULT_CONNECT_TIMEOUT:0;
-  struct timeval now;
-
-  /* if a timeout is set, use the most restrictive one */
-
-  if(data->set.timeout > 0)
-    timeout_set |= 1;
-  if(duringconnect && (data->set.connecttimeout > 0))
-    timeout_set |= 2;
-
-  switch (timeout_set) {
-  case 1:
-    timeout_ms = data->set.timeout;
-    break;
-  case 2:
-    timeout_ms = data->set.connecttimeout;
-    break;
-  case 3:
-    if(data->set.timeout < data->set.connecttimeout)
-      timeout_ms = data->set.timeout;
-    else
-      timeout_ms = data->set.connecttimeout;
-    break;
-  default:
-    /* use the default */
-    if(!duringconnect)
-      /* if we're not during connect, there's no default timeout so if we're
-         at zero we better just return zero and not make it a negative number
-         by the math below */
-      return 0;
-    break;
-  }
-
-  if(!nowp) {
-    now = Curl_tvnow();
-    nowp = &now;
-  }
-
-  /* subtract elapsed time */
-  timeout_ms -= Curl_tvdiff(*nowp, data->progress.t_startsingle);
-  if(!timeout_ms)
-    /* avoid returning 0 as that means no timeout! */
-    return -1;
-
-  return timeout_ms;
-}
-
-/*
- * waitconnect() waits for a TCP connect on the given socket for the specified
- * number if milliseconds. It returns:
- */
-
-#define WAITCONN_CONNECTED     0
-#define WAITCONN_SELECT_ERROR -1
-#define WAITCONN_TIMEOUT       1
-#define WAITCONN_FDSET_ERROR   2
-#define WAITCONN_ABORTED       3
-
-static
-int waitconnect(struct connectdata *conn,
-                curl_socket_t sockfd, /* socket */
-                long timeout_msec)
-{
-  int rc;
-#ifdef mpeix
-  /* Call this function once now, and ignore the results. We do this to
-     "clear" the error state on the socket so that we can later read it
-     reliably. This is reported necessary on the MPE/iX operating system. */
-  (void)verifyconnect(sockfd, NULL);
-#endif
-
-  for(;;) {
-
-    /* now select() until we get connect or timeout */
-    rc = Curl_socket_ready(CURL_SOCKET_BAD, sockfd, timeout_msec>1000?
-                           1000:timeout_msec);
-    if(Curl_pgrsUpdate(conn))
-      return WAITCONN_ABORTED;
-
-    if(-1 == rc)
-      /* error, no connect here, try next */
-      return WAITCONN_SELECT_ERROR;
-
-    else if(0 == rc) {
-      /* timeout */
-      timeout_msec -= 1000;
-      if(timeout_msec <= 0)
-        return WAITCONN_TIMEOUT;
-
-      continue;
-    }
-
-    if(rc & CURL_CSELECT_ERR)
-      /* error condition caught */
-      return WAITCONN_FDSET_ERROR;
-
-    break;
-  }
-  return WAITCONN_CONNECTED;
-}
-
-static CURLcode bindlocal(struct connectdata *conn,
-                          curl_socket_t sockfd, int af)
-{
-  struct SessionHandle *data = conn->data;
-
-  struct Curl_sockaddr_storage sa;
-  struct sockaddr *sock = (struct sockaddr *)&sa;  /* bind to this address */
-  curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */
-  struct sockaddr_in *si4 = (struct sockaddr_in *)&sa;
-#ifdef ENABLE_IPV6
-  struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
-#endif
-
-  struct Curl_dns_entry *h=NULL;
-  unsigned short port = data->set.localport; /* use this port number, 0 for
-                                                "random" */
-  /* how many port numbers to try to bind to, increasing one at a time */
-  int portnum = data->set.localportrange;
-  const char *dev = data->set.str[STRING_DEVICE];
-  int error;
-  char myhost[256] = "";
-  int done = 0; /* -1 for error, 1 for address found */
-  bool is_interface = FALSE;
-  bool is_host = FALSE;
-  static const char *if_prefix = "if!";
-  static const char *host_prefix = "host!";
-
-  /*************************************************************
-   * Select device to bind socket to
-   *************************************************************/
-  if(!dev && !port)
-    /* no local kind of binding was requested */
-    return CURLE_OK;
-
-  memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
-
-  if(dev && (strlen(dev)<255) ) {
-    if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) {
-      dev += strlen(if_prefix);
-      is_interface = TRUE;
-    }
-    else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) {
-      dev += strlen(host_prefix);
-      is_host = TRUE;
-    }
-
-    /* interface */
-    if(!is_host && (is_interface || Curl_if_is_interface_name(dev))) {
-      if(Curl_if2ip(af, dev, myhost, sizeof(myhost)) == NULL)
-        return CURLE_INTERFACE_FAILED;
-
-      /*
-       * We now have the numerical IP address in the 'myhost' buffer
-       */
-      infof(data, "Local Interface %s is ip %s using address family %i\n",
-            dev, myhost, af);
-      done = 1;
-
-#ifdef SO_BINDTODEVICE
-      /* I am not sure any other OSs than Linux that provide this feature, and
-       * at the least I cannot test. --Ben
-       *
-       * This feature allows one to tightly bind the local socket to a
-       * particular interface.  This will force even requests to other local
-       * interfaces to go out the external interface.
-       *
-       *
-       * Only bind to the interface when specified as interface, not just as a
-       * hostname or ip address.
-       */
-      if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
-                    dev, (curl_socklen_t)strlen(dev)+1) != 0) {
-        error = SOCKERRNO;
-        infof(data, "SO_BINDTODEVICE %s failed with errno %d: %s;"
-              " will do regular bind\n",
-              dev, error, Curl_strerror(conn, error));
-        /* This is typically "errno 1, error: Operation not permitted" if
-           you're not running as root or another suitable privileged user */
-      }
-#endif
-    }
-    else {
-      /*
-       * This was not an interface, resolve the name as a host name
-       * or IP number
-       *
-       * Temporarily force name resolution to use only the address type
-       * of the connection. The resolve functions should really be changed
-       * to take a type parameter instead.
-       */
-      long ipver = conn->ip_version;
-      int rc;
-
-      if(af == AF_INET)
-        conn->ip_version = CURL_IPRESOLVE_V4;
-#ifdef ENABLE_IPV6
-      else if(af == AF_INET6)
-        conn->ip_version = CURL_IPRESOLVE_V6;
-#endif
-
-      rc = Curl_resolv(conn, dev, 0, &h);
-      if(rc == CURLRESOLV_PENDING)
-        (void)Curl_resolver_wait_resolv(conn, &h);
-      conn->ip_version = ipver;
-
-      if(h) {
-        /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
-        Curl_printable_address(h->addr, myhost, sizeof(myhost));
-        infof(data, "Name '%s' family %i resolved to '%s' family %i\n",
-              dev, af, myhost, h->addr->ai_family);
-        Curl_resolv_unlock(data, h);
-        done = 1;
-      }
-      else {
-        /*
-         * provided dev was no interface (or interfaces are not supported
-         * e.g. solaris) no ip address and no domain we fail here
-         */
-        done = -1;
-      }
-    }
-
-    if(done > 0) {
-#ifdef ENABLE_IPV6
-      /* ipv6 address */
-      if((af == AF_INET6) &&
-         (Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0)) {
-        si6->sin6_family = AF_INET6;
-        si6->sin6_port = htons(port);
-        sizeof_sa = sizeof(struct sockaddr_in6);
-      }
-      else
-#endif
-      /* ipv4 address */
-      if((af == AF_INET) &&
-         (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) {
-        si4->sin_family = AF_INET;
-        si4->sin_port = htons(port);
-        sizeof_sa = sizeof(struct sockaddr_in);
-      }
-    }
-
-    if(done < 1) {
-      failf(data, "Couldn't bind to '%s'", dev);
-      return CURLE_INTERFACE_FAILED;
-    }
-  }
-  else {
-    /* no device was given, prepare sa to match af's needs */
-#ifdef ENABLE_IPV6
-    if(af == AF_INET6) {
-      si6->sin6_family = AF_INET6;
-      si6->sin6_port = htons(port);
-      sizeof_sa = sizeof(struct sockaddr_in6);
-    }
-    else
-#endif
-    if(af == AF_INET) {
-      si4->sin_family = AF_INET;
-      si4->sin_port = htons(port);
-      sizeof_sa = sizeof(struct sockaddr_in);
-    }
-  }
-
-  for(;;) {
-    if(bind(sockfd, sock, sizeof_sa) >= 0) {
-      /* we succeeded to bind */
-      struct Curl_sockaddr_storage add;
-      curl_socklen_t size = sizeof(add);
-      memset(&add, 0, sizeof(struct Curl_sockaddr_storage));
-      if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
-        data->state.os_errno = error = SOCKERRNO;
-        failf(data, "getsockname() failed with errno %d: %s",
-              error, Curl_strerror(conn, error));
-        return CURLE_INTERFACE_FAILED;
-      }
-      infof(data, "Local port: %hu\n", port);
-      conn->bits.bound = TRUE;
-      return CURLE_OK;
-    }
-
-    if(--portnum > 0) {
-      infof(data, "Bind to local port %hu failed, trying next\n", port);
-      port++; /* try next port */
-      /* We re-use/clobber the port variable here below */
-      if(sock->sa_family == AF_INET)
-        si4->sin_port = ntohs(port);
-#ifdef ENABLE_IPV6
-      else
-        si6->sin6_port = ntohs(port);
-#endif
-    }
-    else
-      break;
-  }
-
-  data->state.os_errno = error = SOCKERRNO;
-  failf(data, "bind failed with errno %d: %s",
-        error, Curl_strerror(conn, error));
-
-  return CURLE_INTERFACE_FAILED;
-}
-
-/*
- * verifyconnect() returns TRUE if the connect really has happened.
- */
-static bool verifyconnect(curl_socket_t sockfd, int *error)
-{
-  bool rc = TRUE;
-#ifdef SO_ERROR
-  int err = 0;
-  curl_socklen_t errSize = sizeof(err);
-
-#ifdef WIN32
-  /*
-   * In October 2003 we effectively nullified this function on Windows due to
-   * problems with it using all CPU in multi-threaded cases.
-   *
-   * In May 2004, we bring it back to offer more info back on connect failures.
-   * Gisle Vanem could reproduce the former problems with this function, but
-   * could avoid them by adding this SleepEx() call below:
-   *
-   *    "I don't have Rational Quantify, but the hint from his post was
-   *    ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe
-   *    just Sleep(0) would be enough?) would release whatever
-   *    mutex/critical-section the ntdll call is waiting on.
-   *
-   *    Someone got to verify this on Win-NT 4.0, 2000."
-   */
-
-#ifdef _WIN32_WCE
-  Sleep(0);
-#else
-  SleepEx(0, FALSE);
-#endif
-
-#endif
-
-  if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
-    err = SOCKERRNO;
-#ifdef _WIN32_WCE
-  /* Old WinCE versions don't support SO_ERROR */
-  if(WSAENOPROTOOPT == err) {
-    SET_SOCKERRNO(0);
-    err = 0;
-  }
-#endif
-#ifdef __minix
-  /* Minix 3.1.x doesn't support getsockopt on UDP sockets */
-  if(EBADIOCTL == err) {
-    SET_SOCKERRNO(0);
-    err = 0;
-  }
-#endif
-  if((0 == err) || (EISCONN == err))
-    /* we are connected, awesome! */
-    rc = TRUE;
-  else
-    /* This wasn't a successful connect */
-    rc = FALSE;
-  if(error)
-    *error = err;
-#else
-  (void)sockfd;
-  if(error)
-    *error = SOCKERRNO;
-#endif
-  return rc;
-}
-
-/* Used within the multi interface. Try next IP address, return TRUE if no
-   more address exists or error */
-static CURLcode trynextip(struct connectdata *conn,
-                          int sockindex,
-                          bool *connected)
-{
-  curl_socket_t sockfd;
-  Curl_addrinfo *ai;
-
-  /* First clean up after the failed socket.
-     Don't close it yet to ensure that the next IP's socket gets a different
-     file descriptor, which can prevent bugs when the curl_multi_socket_action
-     interface is used with certain select() replacements such as kqueue. */
-  curl_socket_t fd_to_close = conn->sock[sockindex];
-  conn->sock[sockindex] = CURL_SOCKET_BAD;
-  *connected = FALSE;
-
-  if(sockindex != FIRSTSOCKET) {
-    Curl_closesocket(conn, fd_to_close);
-    return CURLE_COULDNT_CONNECT; /* no next */
-  }
-
-  /* try the next address */
-  ai = conn->ip_addr->ai_next;
-
-  while(ai) {
-    CURLcode res = singleipconnect(conn, ai, 0L, &sockfd, connected);
-    if(res)
-      return res;
-    if(sockfd != CURL_SOCKET_BAD) {
-      /* store the new socket descriptor */
-      conn->sock[sockindex] = sockfd;
-      conn->ip_addr = ai;
-      Curl_closesocket(conn, fd_to_close);
-      return CURLE_OK;
-    }
-    ai = ai->ai_next;
-  }
-  Curl_closesocket(conn, fd_to_close);
-  return CURLE_COULDNT_CONNECT;
-}
-
-/* Copies connection info into the session handle to make it available
-   when the session handle is no longer associated with a connection. */
-void Curl_persistconninfo(struct connectdata *conn)
-{
-  memcpy(conn->data->info.conn_primary_ip, conn->primary_ip, MAX_IPADR_LEN);
-  memcpy(conn->data->info.conn_local_ip, conn->local_ip, MAX_IPADR_LEN);
-  conn->data->info.conn_primary_port = conn->primary_port;
-  conn->data->info.conn_local_port = conn->local_port;
-}
-
-/* retrieves ip address and port from a sockaddr structure */
-static bool getaddressinfo(struct sockaddr* sa, char* addr,
-                           long* port)
-{
-  unsigned short us_port;
-  struct sockaddr_in* si = NULL;
-#ifdef ENABLE_IPV6
-  struct sockaddr_in6* si6 = NULL;
-#endif
-#if defined(HAVE_SYS_UN_H) && defined(AF_UNIX)
-  struct sockaddr_un* su = NULL;
-#endif
-
-  switch (sa->sa_family) {
-    case AF_INET:
-      si = (struct sockaddr_in*) sa;
-      if(Curl_inet_ntop(sa->sa_family, &si->sin_addr,
-                        addr, MAX_IPADR_LEN)) {
-        us_port = ntohs(si->sin_port);
-        *port = us_port;
-        return TRUE;
-      }
-      break;
-#ifdef ENABLE_IPV6
-    case AF_INET6:
-      si6 = (struct sockaddr_in6*)sa;
-      if(Curl_inet_ntop(sa->sa_family, &si6->sin6_addr,
-                        addr, MAX_IPADR_LEN)) {
-        us_port = ntohs(si6->sin6_port);
-        *port = us_port;
-        return TRUE;
-      }
-      break;
-#endif
-#if defined(HAVE_SYS_UN_H) && defined(AF_UNIX)
-    case AF_UNIX:
-      su = (struct sockaddr_un*)sa;
-      snprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
-      *port = 0;
-      return TRUE;
-#endif
-    default:
-      break;
-  }
-
-  addr[0] = '\0';
-  *port = 0;
-
-  return FALSE;
-}
-
-/* retrieves the start/end point information of a socket of an established
-   connection */
-void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd)
-{
-  int error;
-  curl_socklen_t len;
-  struct Curl_sockaddr_storage ssrem;
-  struct Curl_sockaddr_storage ssloc;
-  struct SessionHandle *data = conn->data;
-
-  if(!conn->bits.reuse) {
-
-    len = sizeof(struct Curl_sockaddr_storage);
-    if(getpeername(sockfd, (struct sockaddr*) &ssrem, &len)) {
-      error = SOCKERRNO;
-      failf(data, "getpeername() failed with errno %d: %s",
-            error, Curl_strerror(conn, error));
-      return;
-    }
-
-    len = sizeof(struct Curl_sockaddr_storage);
-    if(getsockname(sockfd, (struct sockaddr*) &ssloc, &len)) {
-      error = SOCKERRNO;
-      failf(data, "getsockname() failed with errno %d: %s",
-            error, Curl_strerror(conn, error));
-      return;
-    }
-
-    if(!getaddressinfo((struct sockaddr*)&ssrem,
-                        conn->primary_ip, &conn->primary_port)) {
-      error = ERRNO;
-      failf(data, "ssrem inet_ntop() failed with errno %d: %s",
-            error, Curl_strerror(conn, error));
-      return;
-    }
-
-    if(!getaddressinfo((struct sockaddr*)&ssloc,
-                       conn->local_ip, &conn->local_port)) {
-      error = ERRNO;
-      failf(data, "ssloc inet_ntop() failed with errno %d: %s",
-            error, Curl_strerror(conn, error));
-      return;
-    }
-
-  }
-
-  /* persist connection info in session handle */
-  Curl_persistconninfo(conn);
-}
-
-/*
- * Curl_is_connected() is used from the multi interface to check if the
- * firstsocket has connected.
- */
-
-CURLcode Curl_is_connected(struct connectdata *conn,
-                           int sockindex,
-                           bool *connected)
-{
-  int rc;
-  struct SessionHandle *data = conn->data;
-  CURLcode code = CURLE_OK;
-  curl_socket_t sockfd = conn->sock[sockindex];
-  long allow = DEFAULT_CONNECT_TIMEOUT;
-  int error = 0;
-  struct timeval now;
-
-  DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET);
-
-  *connected = FALSE; /* a very negative world view is best */
-
-  if(conn->bits.tcpconnect[sockindex]) {
-    /* we are connected already! */
-    *connected = TRUE;
-    return CURLE_OK;
-  }
-
-  now = Curl_tvnow();
-
-  /* figure out how long time we have left to connect */
-  allow = Curl_timeleft(data, &now, TRUE);
-
-  if(allow < 0) {
-    /* time-out, bail out, go home */
-    failf(data, "Connection time-out");
-    return CURLE_OPERATION_TIMEDOUT;
-  }
-
-  /* check for connect without timeout as we want to return immediately */
-  rc = waitconnect(conn, sockfd, 0);
-  if(WAITCONN_TIMEOUT == rc) {
-    if(curlx_tvdiff(now, conn->connecttime) >= conn->timeoutms_per_addr) {
-      infof(data, "After %ldms connect time, move on!\n",
-            conn->timeoutms_per_addr);
-      goto next;
-    }
-
-    /* not an error, but also no connection yet */
-    return code;
-  }
-
-  if(WAITCONN_CONNECTED == rc) {
-    if(verifyconnect(sockfd, &error)) {
-      /* we are connected with TCP, awesome! */
-
-      /* see if we need to do any proxy magic first once we connected */
-      code = Curl_connected_proxy(conn);
-      if(code)
-        return code;
-
-      conn->bits.tcpconnect[sockindex] = TRUE;
-      *connected = TRUE;
-      if(sockindex == FIRSTSOCKET)
-        Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */
-      Curl_verboseconnect(conn);
-      Curl_updateconninfo(conn, sockfd);
-
-      return CURLE_OK;
-    }
-    /* nope, not connected for real */
-  }
-  else {
-    /* nope, not connected  */
-    if(WAITCONN_FDSET_ERROR == rc) {
-      (void)verifyconnect(sockfd, &error);
-      infof(data, "%s\n",Curl_strerror(conn, error));
-    }
-    else
-      infof(data, "Connection failed\n");
-  }
-
-  /*
-   * The connection failed here, we should attempt to connect to the "next
-   * address" for the given host. But first remember the latest error.
-   */
-  if(error) {
-    data->state.os_errno = error;
-    SET_SOCKERRNO(error);
-  }
-  next:
-
-  conn->timeoutms_per_addr = conn->ip_addr->ai_next == NULL ?
-                             allow : allow / 2;
-  code = trynextip(conn, sockindex, connected);
-
-  if(code) {
-    error = SOCKERRNO;
-    data->state.os_errno = error;
-    failf(data, "Failed connect to %s:%ld; %s",
-          conn->host.name, conn->port, Curl_strerror(conn, error));
-  }
-
-  return code;
-}
-
-static void tcpnodelay(struct connectdata *conn,
-                       curl_socket_t sockfd)
-{
-#ifdef TCP_NODELAY
-  struct SessionHandle *data= conn->data;
-  curl_socklen_t onoff = (curl_socklen_t) data->set.tcp_nodelay;
-  int level = IPPROTO_TCP;
-
-#if 0
-  /* The use of getprotobyname() is disabled since it isn't thread-safe on
-     numerous systems. On these getprotobyname_r() should be used instead, but
-     that exists in at least one 4 arg version and one 5 arg version, and
-     since the proto number rarely changes anyway we now just use the hard
-     coded number. The "proper" fix would need a configure check for the
-     correct function much in the same style the gethostbyname_r versions are
-     detected. */
-  struct protoent *pe = getprotobyname("tcp");
-  if(pe)
-    level = pe->p_proto;
-#endif
-
-  if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
-                sizeof(onoff)) < 0)
-    infof(data, "Could not set TCP_NODELAY: %s\n",
-          Curl_strerror(conn, SOCKERRNO));
-  else
-    infof(data,"TCP_NODELAY set\n");
-#else
-  (void)conn;
-  (void)sockfd;
-#endif
-}
-
-#ifdef SO_NOSIGPIPE
-/* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when
-   sending data to a dead peer (instead of relying on the 4th argument to send
-   being MSG_NOSIGNAL). Possibly also existing and in use on other BSD
-   systems? */
-static void nosigpipe(struct connectdata *conn,
-                      curl_socket_t sockfd)
-{
-  struct SessionHandle *data= conn->data;
-  int onoff = 1;
-  if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
-                sizeof(onoff)) < 0)
-    infof(data, "Could not set SO_NOSIGPIPE: %s\n",
-          Curl_strerror(conn, SOCKERRNO));
-}
-#else
-#define nosigpipe(x,y) Curl_nop_stmt
-#endif
-
-#ifdef USE_WINSOCK
-/* When you run a program that uses the Windows Sockets API, you may
-   experience slow performance when you copy data to a TCP server.
-
-   http://support.microsoft.com/kb/823764
-
-   Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
-   Buffer Size
-
-*/
-void Curl_sndbufset(curl_socket_t sockfd)
-{
-  int val = CURL_MAX_WRITE_SIZE + 32;
-  int curval = 0;
-  int curlen = sizeof(curval);
-
-  if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0)
-    if(curval > val)
-      return;
-
-  setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val));
-}
-#endif
-
-
-/*
- * singleipconnect()
- *
- * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to
- * CURL_SOCKET_BAD. Other errors will however return proper errors.
- *
- * singleipconnect() connects to the given IP only, and it may return without
- * having connected if used from the multi interface.
- */
-static CURLcode
-singleipconnect(struct connectdata *conn,
-                const Curl_addrinfo *ai,
-                long timeout_ms,
-                curl_socket_t *sockp,
-                bool *connected)
-{
-  struct Curl_sockaddr_ex addr;
-  int rc;
-  int error = 0;
-  bool isconnected = FALSE;
-  struct SessionHandle *data = conn->data;
-  curl_socket_t sockfd;
-  CURLcode res = CURLE_OK;
-
-  *sockp = CURL_SOCKET_BAD;
-  *connected = FALSE; /* default is not connected */
-
-  res = Curl_socket(conn, ai, &addr, &sockfd);
-  if(res)
-    /* Failed to create the socket, but still return OK since we signal the
-       lack of socket as well. This allows the parent function to keep looping
-       over alternative addresses/socket families etc. */
-    return CURLE_OK;
-
-  /* store remote address and port used in this connection attempt */
-  if(!getaddressinfo((struct sockaddr*)&addr.sa_addr,
-                     conn->primary_ip, &conn->primary_port)) {
-    /* malformed address or bug in inet_ntop, try next address */
-    error = ERRNO;
-    failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
-          error, Curl_strerror(conn, error));
-    Curl_closesocket(conn, sockfd);
-    return CURLE_OK;
-  }
-  memcpy(conn->ip_addr_str, conn->primary_ip, MAX_IPADR_LEN);
-  infof(data, "  Trying %s...\n", conn->ip_addr_str);
-
-  Curl_persistconninfo(conn);
-
-  if(data->set.tcp_nodelay)
-    tcpnodelay(conn, sockfd);
-
-  nosigpipe(conn, sockfd);
-
-  Curl_sndbufset(sockfd);
-
-  if(data->set.tcp_keepalive)
-    tcpkeepalive(data, sockfd);
-
-  if(data->set.fsockopt) {
-    /* activate callback for setting socket options */
-    error = data->set.fsockopt(data->set.sockopt_client,
-                               sockfd,
-                               CURLSOCKTYPE_IPCXN);
-
-    if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
-      isconnected = TRUE;
-    else if(error) {
-      Curl_closesocket(conn, sockfd); /* close the socket and bail out */
-      return CURLE_ABORTED_BY_CALLBACK;
-    }
-  }
-
-  /* possibly bind the local end to an IP, interface or port */
-  res = bindlocal(conn, sockfd, addr.family);
-  if(res) {
-    Curl_closesocket(conn, sockfd); /* close socket and bail out */
-    return res;
-  }
-
-  /* set socket non-blocking */
-  curlx_nonblock(sockfd, TRUE);
-
-  /* Connect TCP sockets, bind UDP */
-  if(!isconnected && (conn->socktype == SOCK_STREAM)) {
-    rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
-    if(-1 == rc)
-      error = SOCKERRNO;
-    conn->connecttime = Curl_tvnow();
-    if(conn->num_addr > 1)
-      Curl_expire(data, conn->timeoutms_per_addr);
-  }
-  else
-    rc = 0;
-
-  if(-1 == rc) {
-    switch (error) {
-    case EINPROGRESS:
-    case EWOULDBLOCK:
-#if defined(EAGAIN)
-#if (EAGAIN) != (EWOULDBLOCK)
-      /* On some platforms EAGAIN and EWOULDBLOCK are the
-       * same value, and on others they are different, hence
-       * the odd #if
-       */
-    case EAGAIN:
-#endif
-#endif
-      rc = waitconnect(conn, sockfd, timeout_ms);
-      if(WAITCONN_ABORTED == rc) {
-        Curl_closesocket(conn, sockfd);
-        return CURLE_ABORTED_BY_CALLBACK;
-      }
-      break;
-    default:
-      /* unknown error, fallthrough and try another address! */
-      failf(data, "Failed to connect to %s: %s",
-            conn->ip_addr_str, Curl_strerror(conn,error));
-      data->state.os_errno = error;
-      break;
-    }
-  }
-
-  /* The 'WAITCONN_TIMEOUT == rc' comes from the waitconnect(), and not from
-     connect(). We can be sure of this since connect() cannot return 1. */
-  if((WAITCONN_TIMEOUT == rc) &&
-     (data->state.used_interface == Curl_if_multi)) {
-    /* Timeout when running the multi interface */
-    *sockp = sockfd;
-    return CURLE_OK;
-  }
-
-  if(!isconnected)
-    isconnected = verifyconnect(sockfd, &error);
-
-  if(!rc && isconnected) {
-    /* we are connected, awesome! */
-    *connected = TRUE; /* this is a true connect */
-    infof(data, "connected\n");
-#ifdef ENABLE_IPV6
-    conn->bits.ipv6 = (addr.family == AF_INET6)?TRUE:FALSE;
-#endif
-
-    Curl_updateconninfo(conn, sockfd);
-    *sockp = sockfd;
-    return CURLE_OK;
-  }
-  else if(WAITCONN_TIMEOUT == rc)
-    infof(data, "Timeout\n");
-  else {
-    data->state.os_errno = error;
-    infof(data, "%s\n", Curl_strerror(conn, error));
-  }
-
-  /* connect failed or timed out */
-  Curl_closesocket(conn, sockfd);
-
-  return CURLE_OK;
-}
-
-/*
- * TCP connect to the given host with timeout, proxy or remote doesn't matter.
- * There might be more than one IP address to try out. Fill in the passed
- * pointer with the connected socket.
- */
-
-CURLcode Curl_connecthost(struct connectdata *conn,  /* context */
-                          const struct Curl_dns_entry *remotehost,
-                          curl_socket_t *sockconn,   /* the connected socket */
-                          Curl_addrinfo **addr,      /* the one we used */
-                          bool *connected)           /* really connected? */
-{
-  struct SessionHandle *data = conn->data;
-  curl_socket_t sockfd = CURL_SOCKET_BAD;
-  Curl_addrinfo *ai;
-  Curl_addrinfo *curr_addr;
-
-  struct timeval after;
-  struct timeval before = Curl_tvnow();
-
-  /*************************************************************
-   * Figure out what maximum time we have left
-   *************************************************************/
-  long timeout_ms;
-
-  DEBUGASSERT(sockconn);
-  *connected = FALSE; /* default to not connected */
-
-  /* get the timeout left */
-  timeout_ms = Curl_timeleft(data, &before, TRUE);
-
-  if(timeout_ms < 0) {
-    /* a precaution, no need to continue if time already is up */
-    failf(data, "Connection time-out");
-    return CURLE_OPERATION_TIMEDOUT;
-  }
-
-  conn->num_addr = Curl_num_addresses(remotehost->addr);
-
-  ai = remotehost->addr;
-
-  /* Below is the loop that attempts to connect to all IP-addresses we
-   * know for the given host. One by one until one IP succeeds.
-   */
-
-  /*
-   * Connecting with a Curl_addrinfo chain
-   */
-  for(curr_addr = ai; curr_addr; curr_addr = curr_addr->ai_next) {
-    CURLcode res;
-
-    /* Max time for the next address */
-    conn->timeoutms_per_addr = curr_addr->ai_next == NULL ?
-                               timeout_ms : timeout_ms / 2;
-
-    /* start connecting to the IP curr_addr points to */
-    res = singleipconnect(conn, curr_addr,
-                          /* don't hang when doing multi */
-                          (data->state.used_interface == Curl_if_multi)?0:
-                          conn->timeoutms_per_addr, &sockfd, connected);
-    if(res)
-      return res;
-
-    if(sockfd != CURL_SOCKET_BAD)
-      break;
-
-    /* get a new timeout for next attempt */
-    after = Curl_tvnow();
-    timeout_ms -= Curl_tvdiff(after, before);
-    if(timeout_ms < 0) {
-      failf(data, "connect() timed out!");
-      return CURLE_OPERATION_TIMEDOUT;
-    }
-    before = after;
-  }  /* end of connect-to-each-address loop */
-
-  *sockconn = sockfd;    /* the socket descriptor we've connected */
-
-  if(sockfd == CURL_SOCKET_BAD) {
-    /* no good connect was made */
-    failf(data, "couldn't connect to %s at %s:%d",
-          conn->bits.proxy?"proxy":"host",
-          conn->bits.proxy?conn->proxy.name:conn->host.name, conn->port);
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  /* leave the socket in non-blocking mode */
-
-  /* store the address we use */
-  if(addr)
-    *addr = curr_addr;
-
-  data->info.numconnects++; /* to track the number of connections made */
-
-  return CURLE_OK;
-}
-
-/*
- * Used to extract socket and connectdata struct for the most recent
- * transfer on the given SessionHandle.
- *
- * The returned socket will be CURL_SOCKET_BAD in case of failure!
- */
-curl_socket_t Curl_getconnectinfo(struct SessionHandle *data,
-                                  struct connectdata **connp)
-{
-  curl_socket_t sockfd;
-
-  DEBUGASSERT(data);
-
-  if(data->state.lastconnect) {
-    struct connectdata *c = data->state.lastconnect;
-    if(connp)
-      /* only store this if the caller cares for it */
-      *connp = c;
-    sockfd = c->sock[FIRSTSOCKET];
-    /* we have a socket connected, let's determine if the server shut down */
-    /* determine if ssl */
-    if(c->ssl[FIRSTSOCKET].use) {
-      /* use the SSL context */
-      if(!Curl_ssl_check_cxn(c))
-        return CURL_SOCKET_BAD;   /* FIN received */
-    }
-/* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */
-#ifdef MSG_PEEK
-    else {
-      /* use the socket */
-      char buf;
-      if(recv((RECV_TYPE_ARG1)c->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf,
-              (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK) == 0) {
-        return CURL_SOCKET_BAD;   /* FIN received */
-      }
-    }
-#endif
-  }
-  else
-    return CURL_SOCKET_BAD;
-
-  return sockfd;
-}
-
-/*
- * Close a socket.
- *
- * 'conn' can be NULL, beware!
- */
-int Curl_closesocket(struct connectdata *conn,
-                     curl_socket_t sock)
-{
-  if(conn && conn->fclosesocket) {
-    if((sock == conn->sock[SECONDARYSOCKET]) &&
-       conn->sock_accepted[SECONDARYSOCKET])
-      /* if this socket matches the second socket, and that was created with
-         accept, then we MUST NOT call the callback but clear the accepted
-         status */
-      conn->sock_accepted[SECONDARYSOCKET] = FALSE;
-    else
-      return conn->fclosesocket(conn->closesocket_client, sock);
-  }
-  return sclose(sock);
-}
-
-/*
- * Create a socket based on info from 'conn' and 'ai'.
- *
- * 'addr' should be a pointer to the correct struct to get data back, or NULL.
- * 'sockfd' must be a pointer to a socket descriptor.
- *
- * If the open socket callback is set, used that!
- *
- */
-CURLcode Curl_socket(struct connectdata *conn,
-                     const Curl_addrinfo *ai,
-                     struct Curl_sockaddr_ex *addr,
-                     curl_socket_t *sockfd)
-{
-  struct SessionHandle *data = conn->data;
-  struct Curl_sockaddr_ex dummy;
-
-  if(!addr)
-    /* if the caller doesn't want info back, use a local temp copy */
-    addr = &dummy;
-
-  /*
-   * The Curl_sockaddr_ex structure is basically libcurl's external API
-   * curl_sockaddr structure with enough space available to directly hold
-   * any protocol-specific address structures. The variable declared here
-   * will be used to pass / receive data to/from the fopensocket callback
-   * if this has been set, before that, it is initialized from parameters.
-   */
-
-  addr->family = ai->ai_family;
-  addr->socktype = conn->socktype;
-  addr->protocol = conn->socktype==SOCK_DGRAM?IPPROTO_UDP:ai->ai_protocol;
-  addr->addrlen = ai->ai_addrlen;
-
-  if(addr->addrlen > sizeof(struct Curl_sockaddr_storage))
-     addr->addrlen = sizeof(struct Curl_sockaddr_storage);
-  memcpy(&addr->sa_addr, ai->ai_addr, addr->addrlen);
-
-  if(data->set.fopensocket)
-   /*
-    * If the opensocket callback is set, all the destination address
-    * information is passed to the callback. Depending on this information the
-    * callback may opt to abort the connection, this is indicated returning
-    * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When
-    * the callback returns a valid socket the destination address information
-    * might have been changed and this 'new' address will actually be used
-    * here to connect.
-    */
-    *sockfd = data->set.fopensocket(data->set.opensocket_client,
-                                    CURLSOCKTYPE_IPCXN,
-                                    (struct curl_sockaddr *)addr);
-  else
-    /* opensocket callback not set, so simply create the socket now */
-    *sockfd = socket(addr->family, addr->socktype, addr->protocol);
-
-  if(*sockfd == CURL_SOCKET_BAD)
-    /* no socket, no connection */
-    return CURLE_COULDNT_CONNECT;
-
-#if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
-  if(conn->scope && (addr->family == AF_INET6)) {
-    struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr;
-    sa6->sin6_scope_id = conn->scope;
-  }
-#endif
-
-  return CURLE_OK;
-
-}
diff --git a/lib/content_encoding.c b/lib/content_encoding.c
deleted file mode 100644 (file)
index 6f4d142..0000000
+++ /dev/null
@@ -1,435 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef HAVE_LIBZ
-
-#include "curl_urldata.h"
-#include <curl/curl.h>
-#include "curl_sendf.h"
-#include "curl_content_encoding.h"
-#include "curl_memory.h"
-
-#include "curl_memdebug.h"
-
-/* Comment this out if zlib is always going to be at least ver. 1.2.0.4
-   (doing so will reduce code size slightly). */
-#define OLD_ZLIB_SUPPORT 1
-
-#define DSIZ CURL_MAX_WRITE_SIZE /* buffer size for decompressed data */
-
-#define GZIP_MAGIC_0 0x1f
-#define GZIP_MAGIC_1 0x8b
-
-/* gzip flag byte */
-#define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
-#define HEAD_CRC     0x02 /* bit 1 set: header CRC present */
-#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
-#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
-#define COMMENT      0x10 /* bit 4 set: file comment present */
-#define RESERVED     0xE0 /* bits 5..7: reserved */
-
-static voidpf
-zalloc_cb(voidpf opaque, unsigned int items, unsigned int size)
-{
-  (void) opaque;
-  /* not a typo, keep it calloc() */
-  return (voidpf) calloc(items, size);
-}
-
-static void
-zfree_cb(voidpf opaque, voidpf ptr)
-{
-  (void) opaque;
-  free(ptr);
-}
-
-static CURLcode
-process_zlib_error(struct connectdata *conn, z_stream *z)
-{
-  struct SessionHandle *data = conn->data;
-  if(z->msg)
-    failf (data, "Error while processing content unencoding: %s",
-           z->msg);
-  else
-    failf (data, "Error while processing content unencoding: "
-           "Unknown failure within decompression software.");
-
-  return CURLE_BAD_CONTENT_ENCODING;
-}
-
-static CURLcode
-exit_zlib(z_stream *z, zlibInitState *zlib_init, CURLcode result)
-{
-  inflateEnd(z);
-  *zlib_init = ZLIB_UNINIT;
-  return result;
-}
-
-static CURLcode
-inflate_stream(struct connectdata *conn,
-               struct SingleRequest *k)
-{
-  int allow_restart = 1;
-  z_stream *z = &k->z;          /* zlib state structure */
-  uInt nread = z->avail_in;
-  Bytef *orig_in = z->next_in;
-  int status;                   /* zlib status */
-  CURLcode result = CURLE_OK;   /* Curl_client_write status */
-  char *decomp;                 /* Put the decompressed data here. */
-
-  /* Dynamically allocate a buffer for decompression because it's uncommonly
-     large to hold on the stack */
-  decomp = malloc(DSIZ);
-  if(decomp == NULL) {
-    return exit_zlib(z, &k->zlib_init, CURLE_OUT_OF_MEMORY);
-  }
-
-  /* because the buffer size is fixed, iteratively decompress and transfer to
-     the client via client_write. */
-  for(;;) {
-    /* (re)set buffer for decompressed output for every iteration */
-    z->next_out = (Bytef *)decomp;
-    z->avail_out = DSIZ;
-
-    status = inflate(z, Z_SYNC_FLUSH);
-    if(status == Z_OK || status == Z_STREAM_END) {
-      allow_restart = 0;
-      if((DSIZ - z->avail_out) && (!k->ignorebody)) {
-        result = Curl_client_write(conn, CLIENTWRITE_BODY, decomp,
-                                   DSIZ - z->avail_out);
-        /* if !CURLE_OK, clean up, return */
-        if(result) {
-          free(decomp);
-          return exit_zlib(z, &k->zlib_init, result);
-        }
-      }
-
-      /* Done? clean up, return */
-      if(status == Z_STREAM_END) {
-        free(decomp);
-        if(inflateEnd(z) == Z_OK)
-          return exit_zlib(z, &k->zlib_init, result);
-        else
-          return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z));
-      }
-
-      /* Done with these bytes, exit */
-
-      /* status is always Z_OK at this point! */
-      if(z->avail_in == 0) {
-        free(decomp);
-        return result;
-      }
-    }
-    else if(allow_restart && status == Z_DATA_ERROR) {
-      /* some servers seem to not generate zlib headers, so this is an attempt
-         to fix and continue anyway */
-
-      (void) inflateEnd(z);     /* don't care about the return code */
-      if(inflateInit2(z, -MAX_WBITS) != Z_OK) {
-        free(decomp);
-        return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z));
-      }
-      z->next_in = orig_in;
-      z->avail_in = nread;
-      allow_restart = 0;
-      continue;
-    }
-    else {                      /* Error; exit loop, handle below */
-      free(decomp);
-      return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z));
-    }
-  }
-  /* Will never get here */
-}
-
-CURLcode
-Curl_unencode_deflate_write(struct connectdata *conn,
-                            struct SingleRequest *k,
-                            ssize_t nread)
-{
-  z_stream *z = &k->z;          /* zlib state structure */
-
-  /* Initialize zlib? */
-  if(k->zlib_init == ZLIB_UNINIT) {
-    memset(z, 0, sizeof(z_stream));
-    z->zalloc = (alloc_func)zalloc_cb;
-    z->zfree = (free_func)zfree_cb;
-
-    if(inflateInit(z) != Z_OK)
-      return process_zlib_error(conn, z);
-    k->zlib_init = ZLIB_INIT;
-  }
-
-  /* Set the compressed input when this function is called */
-  z->next_in = (Bytef *)k->str;
-  z->avail_in = (uInt)nread;
-
-  /* Now uncompress the data */
-  return inflate_stream(conn, k);
-}
-
-#ifdef OLD_ZLIB_SUPPORT
-/* Skip over the gzip header */
-static enum {
-  GZIP_OK,
-  GZIP_BAD,
-  GZIP_UNDERFLOW
-} check_gzip_header(unsigned char const *data, ssize_t len, ssize_t *headerlen)
-{
-  int method, flags;
-  const ssize_t totallen = len;
-
-  /* The shortest header is 10 bytes */
-  if(len < 10)
-    return GZIP_UNDERFLOW;
-
-  if((data[0] != GZIP_MAGIC_0) || (data[1] != GZIP_MAGIC_1))
-    return GZIP_BAD;
-
-  method = data[2];
-  flags = data[3];
-
-  if(method != Z_DEFLATED || (flags & RESERVED) != 0) {
-    /* Can't handle this compression method or unknown flag */
-    return GZIP_BAD;
-  }
-
-  /* Skip over time, xflags, OS code and all previous bytes */
-  len -= 10;
-  data += 10;
-
-  if(flags & EXTRA_FIELD) {
-    ssize_t extra_len;
-
-    if(len < 2)
-      return GZIP_UNDERFLOW;
-
-    extra_len = (data[1] << 8) | data[0];
-
-    if(len < (extra_len+2))
-      return GZIP_UNDERFLOW;
-
-    len -= (extra_len + 2);
-    data += (extra_len + 2);
-  }
-
-  if(flags & ORIG_NAME) {
-    /* Skip over NUL-terminated file name */
-    while(len && *data) {
-      --len;
-      ++data;
-    }
-    if(!len || *data)
-      return GZIP_UNDERFLOW;
-
-    /* Skip over the NUL */
-    --len;
-    ++data;
-  }
-
-  if(flags & COMMENT) {
-    /* Skip over NUL-terminated comment */
-    while(len && *data) {
-      --len;
-      ++data;
-    }
-    if(!len || *data)
-      return GZIP_UNDERFLOW;
-
-    /* Skip over the NUL */
-    --len;
-  }
-
-  if(flags & HEAD_CRC) {
-    if(len < 2)
-      return GZIP_UNDERFLOW;
-
-    len -= 2;
-  }
-
-  *headerlen = totallen - len;
-  return GZIP_OK;
-}
-#endif
-
-CURLcode
-Curl_unencode_gzip_write(struct connectdata *conn,
-                         struct SingleRequest *k,
-                         ssize_t nread)
-{
-  z_stream *z = &k->z;          /* zlib state structure */
-
-  /* Initialize zlib? */
-  if(k->zlib_init == ZLIB_UNINIT) {
-    memset(z, 0, sizeof(z_stream));
-    z->zalloc = (alloc_func)zalloc_cb;
-    z->zfree = (free_func)zfree_cb;
-
-    if(strcmp(zlibVersion(), "1.2.0.4") >= 0) {
-      /* zlib ver. >= 1.2.0.4 supports transparent gzip decompressing */
-      if(inflateInit2(z, MAX_WBITS+32) != Z_OK) {
-        return process_zlib_error(conn, z);
-      }
-      k->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */
-    }
-    else {
-      /* we must parse the gzip header ourselves */
-      if(inflateInit2(z, -MAX_WBITS) != Z_OK) {
-        return process_zlib_error(conn, z);
-      }
-      k->zlib_init = ZLIB_INIT;   /* Initial call state */
-    }
-  }
-
-  if(k->zlib_init == ZLIB_INIT_GZIP) {
-    /* Let zlib handle the gzip decompression entirely */
-    z->next_in = (Bytef *)k->str;
-    z->avail_in = (uInt)nread;
-    /* Now uncompress the data */
-    return inflate_stream(conn, k);
-  }
-
-#ifndef OLD_ZLIB_SUPPORT
-  /* Support for old zlib versions is compiled away and we are running with
-     an old version, so return an error. */
-  return exit_zlib(z, &k->zlib_init, CURLE_FUNCTION_NOT_FOUND);
-
-#else
-  /* This next mess is to get around the potential case where there isn't
-   * enough data passed in to skip over the gzip header.  If that happens, we
-   * malloc a block and copy what we have then wait for the next call.  If
-   * there still isn't enough (this is definitely a worst-case scenario), we
-   * make the block bigger, copy the next part in and keep waiting.
-   *
-   * This is only required with zlib versions < 1.2.0.4 as newer versions
-   * can handle the gzip header themselves.
-   */
-
-  switch (k->zlib_init) {
-  /* Skip over gzip header? */
-  case ZLIB_INIT:
-  {
-    /* Initial call state */
-    ssize_t hlen;
-
-    switch (check_gzip_header((unsigned char *)k->str, nread, &hlen)) {
-    case GZIP_OK:
-      z->next_in = (Bytef *)k->str + hlen;
-      z->avail_in = (uInt)(nread - hlen);
-      k->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */
-      break;
-
-    case GZIP_UNDERFLOW:
-      /* We need more data so we can find the end of the gzip header.  It's
-       * possible that the memory block we malloc here will never be freed if
-       * the transfer abruptly aborts after this point.  Since it's unlikely
-       * that circumstances will be right for this code path to be followed in
-       * the first place, and it's even more unlikely for a transfer to fail
-       * immediately afterwards, it should seldom be a problem.
-       */
-      z->avail_in = (uInt)nread;
-      z->next_in = malloc(z->avail_in);
-      if(z->next_in == NULL) {
-        return exit_zlib(z, &k->zlib_init, CURLE_OUT_OF_MEMORY);
-      }
-      memcpy(z->next_in, k->str, z->avail_in);
-      k->zlib_init = ZLIB_GZIP_HEADER;   /* Need more gzip header data state */
-      /* We don't have any data to inflate yet */
-      return CURLE_OK;
-
-    case GZIP_BAD:
-    default:
-      return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z));
-    }
-
-  }
-  break;
-
-  case ZLIB_GZIP_HEADER:
-  {
-    /* Need more gzip header data state */
-    ssize_t hlen;
-    unsigned char *oldblock = z->next_in;
-
-    z->avail_in += (uInt)nread;
-    z->next_in = realloc(z->next_in, z->avail_in);
-    if(z->next_in == NULL) {
-      free(oldblock);
-      return exit_zlib(z, &k->zlib_init, CURLE_OUT_OF_MEMORY);
-    }
-    /* Append the new block of data to the previous one */
-    memcpy(z->next_in + z->avail_in - nread, k->str, nread);
-
-    switch (check_gzip_header(z->next_in, z->avail_in, &hlen)) {
-    case GZIP_OK:
-      /* This is the zlib stream data */
-      free(z->next_in);
-      /* Don't point into the malloced block since we just freed it */
-      z->next_in = (Bytef *)k->str + hlen + nread - z->avail_in;
-      z->avail_in = (uInt)(z->avail_in - hlen);
-      k->zlib_init = ZLIB_GZIP_INFLATING;   /* Inflating stream state */
-      break;
-
-    case GZIP_UNDERFLOW:
-      /* We still don't have any data to inflate! */
-      return CURLE_OK;
-
-    case GZIP_BAD:
-    default:
-      free(z->next_in);
-      return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z));
-    }
-
-  }
-  break;
-
-  case ZLIB_GZIP_INFLATING:
-  default:
-    /* Inflating stream state */
-    z->next_in = (Bytef *)k->str;
-    z->avail_in = (uInt)nread;
-    break;
-  }
-
-  if(z->avail_in == 0) {
-    /* We don't have any data to inflate; wait until next time */
-    return CURLE_OK;
-  }
-
-  /* We've parsed the header, now uncompress the data */
-  return inflate_stream(conn, k);
-#endif
-}
-
-void Curl_unencode_cleanup(struct connectdata *conn)
-{
-  struct SessionHandle *data = conn->data;
-  struct SingleRequest *k = &data->req;
-  z_stream *z = &k->z;
-  if(k->zlib_init != ZLIB_UNINIT)
-    (void) exit_zlib(z, &k->zlib_init, CURLE_OK);
-}
-
-#endif /* HAVE_LIBZ */
diff --git a/lib/cookie.c b/lib/cookie.c
deleted file mode 100644 (file)
index 90ee884..0000000
+++ /dev/null
@@ -1,1163 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-/***
-
-
-RECEIVING COOKIE INFORMATION
-============================
-
-struct CookieInfo *cookie_init(char *file);
-
-        Inits a cookie struct to store data in a local file. This is always
-        called before any cookies are set.
-
-int cookies_set(struct CookieInfo *cookie, char *cookie_line);
-
-        The 'cookie_line' parameter is a full "Set-cookie:" line as
-        received from a server.
-
-        The function need to replace previously stored lines that this new
-        line superceeds.
-
-        It may remove lines that are expired.
-
-        It should return an indication of success/error.
-
-
-SENDING COOKIE INFORMATION
-==========================
-
-struct Cookies *cookie_getlist(struct CookieInfo *cookie,
-                               char *host, char *path, bool secure);
-
-        For a given host and path, return a linked list of cookies that
-        the client should send to the server if used now. The secure
-        boolean informs the cookie if a secure connection is achieved or
-        not.
-
-        It shall only return cookies that haven't expired.
-
-
-Example set of cookies:
-
-    Set-cookie: PRODUCTINFO=webxpress; domain=.fidelity.com; path=/; secure
-    Set-cookie: PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
-    domain=.fidelity.com; path=/ftgw; secure
-    Set-cookie: FidHist=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
-    domain=.fidelity.com; path=/; secure
-    Set-cookie: FidOrder=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
-    domain=.fidelity.com; path=/; secure
-    Set-cookie: DisPend=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
-    domain=.fidelity.com; path=/; secure
-    Set-cookie: FidDis=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
-    domain=.fidelity.com; path=/; secure
-    Set-cookie:
-    Session_Key@6791a9e0-901a-11d0-a1c8-9b012c88aa77=none;expires=Monday,
-    13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/; secure
-****/
-
-
-#include "curl_setup.h"
-
-#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
-
-#define _MPRINTF_REPLACE
-#include <curl/mprintf.h>
-
-#include "curl_urldata.h"
-#include "curl_cookie.h"
-#include "curl_strequal.h"
-#include "curl_strtok.h"
-#include "curl_sendf.h"
-#include "curl_memory.h"
-#include "curl_share.h"
-#include "curl_strtoofft.h"
-#include "curl_rawstr.h"
-#include "curl_memrchr.h"
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-static void freecookie(struct Cookie *co)
-{
-  if(co->expirestr)
-    free(co->expirestr);
-  if(co->domain)
-    free(co->domain);
-  if(co->path)
-    free(co->path);
-  if(co->name)
-    free(co->name);
-  if(co->value)
-    free(co->value);
-  if(co->maxage)
-    free(co->maxage);
-  if(co->version)
-    free(co->version);
-
-  free(co);
-}
-
-static bool tailmatch(const char *little, const char *bigone)
-{
-  size_t littlelen = strlen(little);
-  size_t biglen = strlen(bigone);
-
-  if(littlelen > biglen)
-    return FALSE;
-
-  return Curl_raw_equal(little, bigone+biglen-littlelen) ? TRUE : FALSE;
-}
-
-/*
- * Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
- */
-void Curl_cookie_loadfiles(struct SessionHandle *data)
-{
-  struct curl_slist *list = data->change.cookielist;
-  if(list) {
-    Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
-    while(list) {
-      data->cookies = Curl_cookie_init(data,
-                                       list->data,
-                                       data->cookies,
-                                       data->set.cookiesession);
-      list = list->next;
-    }
-    curl_slist_free_all(data->change.cookielist); /* clean up list */
-    data->change.cookielist = NULL; /* don't do this again! */
-    Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
-  }
-}
-
-/*
- * strstore() makes a strdup() on the 'newstr' and if '*str' is non-NULL
- * that will be freed before the allocated string is stored there.
- *
- * It is meant to easily replace strdup()
- */
-static void strstore(char **str, const char *newstr)
-{
-  if(*str)
-    free(*str);
-  *str = strdup(newstr);
-}
-
-
-/****************************************************************************
- *
- * Curl_cookie_add()
- *
- * Add a single cookie line to the cookie keeping object.
- *
- ***************************************************************************/
-
-struct Cookie *
-Curl_cookie_add(struct SessionHandle *data,
-                /* The 'data' pointer here may be NULL at times, and thus
-                   must only be used very carefully for things that can deal
-                   with data being NULL. Such as infof() and similar */
-
-                struct CookieInfo *c,
-                bool httpheader, /* TRUE if HTTP header-style line */
-                char *lineptr,   /* first character of the line */
-                const char *domain, /* default domain */
-                const char *path)   /* full path used when this cookie is set,
-                                       used to get default path for the cookie
-                                       unless set */
-{
-  struct Cookie *clist;
-  char name[MAX_NAME];
-  struct Cookie *co;
-  struct Cookie *lastc=NULL;
-  time_t now = time(NULL);
-  bool replace_old = FALSE;
-  bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
-
-#ifdef CURL_DISABLE_VERBOSE_STRINGS
-  (void)data;
-#endif
-
-  /* First, alloc and init a new struct for it */
-  co = calloc(1, sizeof(struct Cookie));
-  if(!co)
-    return NULL; /* bail out if we're this low on memory */
-
-  if(httpheader) {
-    /* This line was read off a HTTP-header */
-    const char *ptr;
-    const char *semiptr;
-    char *what;
-
-    what = malloc(MAX_COOKIE_LINE);
-    if(!what) {
-      free(co);
-      return NULL;
-    }
-
-    semiptr=strchr(lineptr, ';'); /* first, find a semicolon */
-
-    while(*lineptr && ISBLANK(*lineptr))
-      lineptr++;
-
-    ptr = lineptr;
-    do {
-      /* we have a <what>=<this> pair or a stand-alone word here */
-      name[0]=what[0]=0; /* init the buffers */
-      if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;\r\n =]=%"
-                     MAX_COOKIE_LINE_TXT "[^;\r\n]",
-                     name, what)) {
-        /* Use strstore() below to properly deal with received cookie
-           headers that have the same string property set more than once,
-           and then we use the last one. */
-        const char *whatptr;
-        bool done = FALSE;
-        bool sep;
-        size_t len=strlen(what);
-        const char *endofn = &ptr[ strlen(name) ];
-
-        /* skip trailing spaces in name */
-        while(*endofn && ISBLANK(*endofn))
-          endofn++;
-
-        /* name ends with a '=' ? */
-        sep = (*endofn == '=')?TRUE:FALSE;
-
-        /* Strip off trailing whitespace from the 'what' */
-        while(len && ISBLANK(what[len-1])) {
-          what[len-1]=0;
-          len--;
-        }
-
-        /* Skip leading whitespace from the 'what' */
-        whatptr=what;
-        while(*whatptr && ISBLANK(*whatptr))
-          whatptr++;
-
-        if(!len) {
-          /* this was a "<name>=" with no content, and we must allow
-             'secure' and 'httponly' specified this weirdly */
-          done = TRUE;
-          if(Curl_raw_equal("secure", name))
-            co->secure = TRUE;
-          else if(Curl_raw_equal("httponly", name))
-            co->httponly = TRUE;
-          else if(sep)
-            /* there was a '=' so we're not done parsing this field */
-            done = FALSE;
-        }
-        if(done)
-          ;
-        else if(Curl_raw_equal("path", name)) {
-          strstore(&co->path, whatptr);
-          if(!co->path) {
-            badcookie = TRUE; /* out of memory bad */
-            break;
-          }
-        }
-        else if(Curl_raw_equal("domain", name)) {
-          /* note that this name may or may not have a preceding dot, but
-             we don't care about that, we treat the names the same anyway */
-
-          const char *domptr=whatptr;
-          const char *nextptr;
-          int dotcount=1;
-
-          /* Count the dots, we need to make sure that there are enough
-             of them. */
-
-          if('.' == whatptr[0])
-            /* don't count the initial dot, assume it */
-            domptr++;
-
-          do {
-            nextptr = strchr(domptr, '.');
-            if(nextptr) {
-              if(domptr != nextptr)
-                dotcount++;
-              domptr = nextptr+1;
-            }
-          } while(nextptr);
-
-          /* The original Netscape cookie spec defined that this domain name
-             MUST have three dots (or two if one of the seven holy TLDs),
-             but it seems that these kinds of cookies are in use "out there"
-             so we cannot be that strict. I've therefore lowered the check
-             to not allow less than two dots. */
-
-          if(dotcount < 2) {
-            /* Received and skipped a cookie with a domain using too few
-               dots. */
-            badcookie=TRUE; /* mark this as a bad cookie */
-            infof(data, "skipped cookie with illegal dotcount domain: %s\n",
-                  whatptr);
-          }
-          else {
-            /* Now, we make sure that our host is within the given domain,
-               or the given domain is not valid and thus cannot be set. */
-
-            if('.' == whatptr[0])
-              whatptr++; /* ignore preceding dot */
-
-            if(!domain || tailmatch(whatptr, domain)) {
-              const char *tailptr=whatptr;
-              if(tailptr[0] == '.')
-                tailptr++;
-              strstore(&co->domain, tailptr); /* don't prefix w/dots
-                                                 internally */
-              if(!co->domain) {
-                badcookie = TRUE;
-                break;
-              }
-              co->tailmatch=TRUE; /* we always do that if the domain name was
-                                     given */
-            }
-            else {
-              /* we did not get a tailmatch and then the attempted set domain
-                 is not a domain to which the current host belongs. Mark as
-                 bad. */
-              badcookie=TRUE;
-              infof(data, "skipped cookie with bad tailmatch domain: %s\n",
-                    whatptr);
-            }
-          }
-        }
-        else if(Curl_raw_equal("version", name)) {
-          strstore(&co->version, whatptr);
-          if(!co->version) {
-            badcookie = TRUE;
-            break;
-          }
-        }
-        else if(Curl_raw_equal("max-age", name)) {
-          /* Defined in RFC2109:
-
-             Optional.  The Max-Age attribute defines the lifetime of the
-             cookie, in seconds.  The delta-seconds value is a decimal non-
-             negative integer.  After delta-seconds seconds elapse, the
-             client should discard the cookie.  A value of zero means the
-             cookie should be discarded immediately.
-
-          */
-          strstore(&co->maxage, whatptr);
-          if(!co->maxage) {
-            badcookie = TRUE;
-            break;
-          }
-          co->expires =
-            strtol((*co->maxage=='\"')?&co->maxage[1]:&co->maxage[0],NULL,10)
-            + (long)now;
-        }
-        else if(Curl_raw_equal("expires", name)) {
-          strstore(&co->expirestr, whatptr);
-          if(!co->expirestr) {
-            badcookie = TRUE;
-            break;
-          }
-          /* Note that if the date couldn't get parsed for whatever reason,
-             the cookie will be treated as a session cookie */
-          co->expires = curl_getdate(what, &now);
-
-          /* Session cookies have expires set to 0 so if we get that back
-             from the date parser let's add a second to make it a
-             non-session cookie */
-          if(co->expires == 0)
-            co->expires = 1;
-          else if(co->expires < 0)
-            co->expires = 0;
-        }
-        else if(!co->name) {
-          co->name = strdup(name);
-          co->value = strdup(whatptr);
-          if(!co->name || !co->value) {
-            badcookie = TRUE;
-            break;
-          }
-        }
-        /*
-          else this is the second (or more) name we don't know
-          about! */
-      }
-      else {
-        /* this is an "illegal" <what>=<this> pair */
-      }
-
-      if(!semiptr || !*semiptr) {
-        /* we already know there are no more cookies */
-        semiptr = NULL;
-        continue;
-      }
-
-      ptr=semiptr+1;
-      while(*ptr && ISBLANK(*ptr))
-        ptr++;
-      semiptr=strchr(ptr, ';'); /* now, find the next semicolon */
-
-      if(!semiptr && *ptr)
-        /* There are no more semicolons, but there's a final name=value pair
-           coming up */
-        semiptr=strchr(ptr, '\0');
-    } while(semiptr);
-
-    if(!badcookie && !co->domain) {
-      if(domain) {
-        /* no domain was given in the header line, set the default */
-        co->domain=strdup(domain);
-        if(!co->domain)
-          badcookie = TRUE;
-      }
-    }
-
-    if(!badcookie && !co->path && path) {
-      /* No path was given in the header line, set the default.
-         Note that the passed-in path to this function MAY have a '?' and
-         following part that MUST not be stored as part of the path. */
-      char *queryp = strchr(path, '?');
-
-      /* queryp is where the interesting part of the path ends, so now we
-         want to the find the last */
-      char *endslash;
-      if(!queryp)
-        endslash = strrchr(path, '/');
-      else
-        endslash = memrchr(path, '/', (size_t)(queryp - path));
-      if(endslash) {
-        size_t pathlen = (size_t)(endslash-path+1); /* include ending slash */
-        co->path=malloc(pathlen+1); /* one extra for the zero byte */
-        if(co->path) {
-          memcpy(co->path, path, pathlen);
-          co->path[pathlen]=0; /* zero terminate */
-        }
-        else
-          badcookie = TRUE;
-      }
-    }
-
-    free(what);
-
-    if(badcookie || !co->name) {
-      /* we didn't get a cookie name or a bad one,
-         this is an illegal line, bail out */
-      freecookie(co);
-      return NULL;
-    }
-
-  }
-  else {
-    /* This line is NOT a HTTP header style line, we do offer support for
-       reading the odd netscape cookies-file format here */
-    char *ptr;
-    char *firstptr;
-    char *tok_buf=NULL;
-    int fields;
-
-    /* IE introduced HTTP-only cookies to prevent XSS attacks. Cookies
-       marked with httpOnly after the domain name are not accessible
-       from javascripts, but since curl does not operate at javascript
-       level, we include them anyway. In Firefox's cookie files, these
-       lines are preceded with #HttpOnly_ and then everything is
-       as usual, so we skip 10 characters of the line..
-    */
-    if(strncmp(lineptr, "#HttpOnly_", 10) == 0) {
-      lineptr += 10;
-      co->httponly = TRUE;
-    }
-
-    if(lineptr[0]=='#') {
-      /* don't even try the comments */
-      free(co);
-      return NULL;
-    }
-    /* strip off the possible end-of-line characters */
-    ptr=strchr(lineptr, '\r');
-    if(ptr)
-      *ptr=0; /* clear it */
-    ptr=strchr(lineptr, '\n');
-    if(ptr)
-      *ptr=0; /* clear it */
-
-    firstptr=strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */
-
-    /* Here's a quick check to eliminate normal HTTP-headers from this */
-    if(!firstptr || strchr(firstptr, ':')) {
-      free(co);
-      return NULL;
-    }
-
-    /* Now loop through the fields and init the struct we already have
-       allocated */
-    for(ptr=firstptr, fields=0; ptr && !badcookie;
-        ptr=strtok_r(NULL, "\t", &tok_buf), fields++) {
-      switch(fields) {
-      case 0:
-        if(ptr[0]=='.') /* skip preceding dots */
-          ptr++;
-        co->domain = strdup(ptr);
-        if(!co->domain)
-          badcookie = TRUE;
-        break;
-      case 1:
-        /* This field got its explanation on the 23rd of May 2001 by
-           Andrés García:
-
-           flag: A TRUE/FALSE value indicating if all machines within a given
-           domain can access the variable. This value is set automatically by
-           the browser, depending on the value you set for the domain.
-
-           As far as I can see, it is set to true when the cookie says
-           .domain.com and to false when the domain is complete www.domain.com
-        */
-        co->tailmatch = Curl_raw_equal(ptr, "TRUE")?TRUE:FALSE;
-        break;
-      case 2:
-        /* It turns out, that sometimes the file format allows the path
-           field to remain not filled in, we try to detect this and work
-           around it! Andrés García made us aware of this... */
-        if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) {
-          /* only if the path doesn't look like a boolean option! */
-          co->path = strdup(ptr);
-          if(!co->path)
-            badcookie = TRUE;
-          break;
-        }
-        /* this doesn't look like a path, make one up! */
-        co->path = strdup("/");
-        if(!co->path)
-          badcookie = TRUE;
-        fields++; /* add a field and fall down to secure */
-        /* FALLTHROUGH */
-      case 3:
-        co->secure = Curl_raw_equal(ptr, "TRUE")?TRUE:FALSE;
-        break;
-      case 4:
-        co->expires = curlx_strtoofft(ptr, NULL, 10);
-        break;
-      case 5:
-        co->name = strdup(ptr);
-        if(!co->name)
-          badcookie = TRUE;
-        break;
-      case 6:
-        co->value = strdup(ptr);
-        if(!co->value)
-          badcookie = TRUE;
-        break;
-      }
-    }
-    if(6 == fields) {
-      /* we got a cookie with blank contents, fix it */
-      co->value = strdup("");
-      if(!co->value)
-        badcookie = TRUE;
-      else
-        fields++;
-    }
-
-    if(!badcookie && (7 != fields))
-      /* we did not find the sufficient number of fields */
-      badcookie = TRUE;
-
-    if(badcookie) {
-      freecookie(co);
-      return NULL;
-    }
-
-  }
-
-  if(!c->running &&    /* read from a file */
-     c->newsession &&  /* clean session cookies */
-     !co->expires) {   /* this is a session cookie since it doesn't expire! */
-    freecookie(co);
-    return NULL;
-  }
-
-  co->livecookie = c->running;
-
-  /* now, we have parsed the incoming line, we must now check if this
-     superceeds an already existing cookie, which it may if the previous have
-     the same domain and path as this */
-
-  clist = c->cookies;
-  replace_old = FALSE;
-  while(clist) {
-    if(Curl_raw_equal(clist->name, co->name)) {
-      /* the names are identical */
-
-      if(clist->domain && co->domain) {
-        if(Curl_raw_equal(clist->domain, co->domain))
-          /* The domains are identical */
-          replace_old=TRUE;
-      }
-      else if(!clist->domain && !co->domain)
-        replace_old = TRUE;
-
-      if(replace_old) {
-        /* the domains were identical */
-
-        if(clist->path && co->path) {
-          if(Curl_raw_equal(clist->path, co->path)) {
-            replace_old = TRUE;
-          }
-          else
-            replace_old = FALSE;
-        }
-        else if(!clist->path && !co->path)
-          replace_old = TRUE;
-        else
-          replace_old = FALSE;
-
-      }
-
-      if(replace_old && !co->livecookie && clist->livecookie) {
-        /* Both cookies matched fine, except that the already present
-           cookie is "live", which means it was set from a header, while
-           the new one isn't "live" and thus only read from a file. We let
-           live cookies stay alive */
-
-        /* Free the newcomer and get out of here! */
-        freecookie(co);
-        return NULL;
-      }
-
-      if(replace_old) {
-        co->next = clist->next; /* get the next-pointer first */
-
-        /* then free all the old pointers */
-        free(clist->name);
-        if(clist->value)
-          free(clist->value);
-        if(clist->domain)
-          free(clist->domain);
-        if(clist->path)
-          free(clist->path);
-        if(clist->expirestr)
-          free(clist->expirestr);
-
-        if(clist->version)
-          free(clist->version);
-        if(clist->maxage)
-          free(clist->maxage);
-
-        *clist = *co;  /* then store all the new data */
-
-        free(co);   /* free the newly alloced memory */
-        co = clist; /* point to the previous struct instead */
-
-        /* We have replaced a cookie, now skip the rest of the list but
-           make sure the 'lastc' pointer is properly set */
-        do {
-          lastc = clist;
-          clist = clist->next;
-        } while(clist);
-        break;
-      }
-    }
-    lastc = clist;
-    clist = clist->next;
-  }
-
-  if(c->running)
-    /* Only show this when NOT reading the cookies from a file */
-    infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, "
-          "expire %" FORMAT_OFF_T "\n",
-          replace_old?"Replaced":"Added", co->name, co->value,
-          co->domain, co->path, co->expires);
-
-  if(!replace_old) {
-    /* then make the last item point on this new one */
-    if(lastc)
-      lastc->next = co;
-    else
-      c->cookies = co;
-  }
-
-  c->numcookies++; /* one more cookie in the jar */
-  return co;
-}
-
-/*****************************************************************************
- *
- * Curl_cookie_init()
- *
- * Inits a cookie struct to read data from a local file. This is always
- * called before any cookies are set. File may be NULL.
- *
- * If 'newsession' is TRUE, discard all "session cookies" on read from file.
- *
- ****************************************************************************/
-struct CookieInfo *Curl_cookie_init(struct SessionHandle *data,
-                                    const char *file,
-                                    struct CookieInfo *inc,
-                                    bool newsession)
-{
-  struct CookieInfo *c;
-  FILE *fp;
-  bool fromfile=TRUE;
-
-  if(NULL == inc) {
-    /* we didn't get a struct, create one */
-    c = calloc(1, sizeof(struct CookieInfo));
-    if(!c)
-      return NULL; /* failed to get memory */
-    c->filename = strdup(file?file:"none"); /* copy the name just in case */
-  }
-  else {
-    /* we got an already existing one, use that */
-    c = inc;
-  }
-  c->running = FALSE; /* this is not running, this is init */
-
-  if(file && strequal(file, "-")) {
-    fp = stdin;
-    fromfile=FALSE;
-  }
-  else if(file && !*file) {
-    /* points to a "" string */
-    fp = NULL;
-  }
-  else
-    fp = file?fopen(file, "r"):NULL;
-
-  c->newsession = newsession; /* new session? */
-
-  if(fp) {
-    char *lineptr;
-    bool headerline;
-
-    char *line = malloc(MAX_COOKIE_LINE);
-    if(line) {
-      while(fgets(line, MAX_COOKIE_LINE, fp)) {
-        if(checkprefix("Set-Cookie:", line)) {
-          /* This is a cookie line, get it! */
-          lineptr=&line[11];
-          headerline=TRUE;
-        }
-        else {
-          lineptr=line;
-          headerline=FALSE;
-        }
-        while(*lineptr && ISBLANK(*lineptr))
-          lineptr++;
-
-        Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL);
-      }
-      free(line); /* free the line buffer */
-    }
-    if(fromfile)
-      fclose(fp);
-  }
-
-  c->running = TRUE;          /* now, we're running */
-
-  return c;
-}
-
-/* sort this so that the longest path gets before the shorter path */
-static int cookie_sort(const void *p1, const void *p2)
-{
-  struct Cookie *c1 = *(struct Cookie **)p1;
-  struct Cookie *c2 = *(struct Cookie **)p2;
-
-  size_t l1 = c1->path?strlen(c1->path):0;
-  size_t l2 = c2->path?strlen(c2->path):0;
-
-  return (l2 > l1) ? 1 : (l2 < l1) ? -1 : 0 ;
-}
-
-/*****************************************************************************
- *
- * Curl_cookie_getlist()
- *
- * For a given host and path, return a linked list of cookies that the
- * client should send to the server if used now. The secure boolean informs
- * the cookie if a secure connection is achieved or not.
- *
- * It shall only return cookies that haven't expired.
- *
- ****************************************************************************/
-
-struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
-                                   const char *host, const char *path,
-                                   bool secure)
-{
-  struct Cookie *newco;
-  struct Cookie *co;
-  time_t now = time(NULL);
-  struct Cookie *mainco=NULL;
-  size_t matches = 0;
-
-  if(!c || !c->cookies)
-    return NULL; /* no cookie struct or no cookies in the struct */
-
-  co = c->cookies;
-
-  while(co) {
-    /* only process this cookie if it is not expired or had no expire
-       date AND that if the cookie requires we're secure we must only
-       continue if we are! */
-    if((!co->expires || (co->expires > now)) &&
-       (co->secure?secure:TRUE)) {
-
-      /* now check if the domain is correct */
-      if(!co->domain ||
-         (co->tailmatch && tailmatch(co->domain, host)) ||
-         (!co->tailmatch && Curl_raw_equal(host, co->domain)) ) {
-        /* the right part of the host matches the domain stuff in the
-           cookie data */
-
-        /* now check the left part of the path with the cookies path
-           requirement */
-        if(!co->path ||
-           /* not using checkprefix() because matching should be
-              case-sensitive */
-           !strncmp(co->path, path, strlen(co->path)) ) {
-
-          /* and now, we know this is a match and we should create an
-             entry for the return-linked-list */
-
-          newco = malloc(sizeof(struct Cookie));
-          if(newco) {
-            /* first, copy the whole source cookie: */
-            memcpy(newco, co, sizeof(struct Cookie));
-
-            /* then modify our next */
-            newco->next = mainco;
-
-            /* point the main to us */
-            mainco = newco;
-
-            matches++;
-          }
-          else {
-            fail:
-            /* failure, clear up the allocated chain and return NULL */
-            while(mainco) {
-              co = mainco->next;
-              free(mainco);
-              mainco = co;
-            }
-
-            return NULL;
-          }
-        }
-      }
-    }
-    co = co->next;
-  }
-
-  if(matches) {
-    /* Now we need to make sure that if there is a name appearing more than
-       once, the longest specified path version comes first. To make this
-       the swiftest way, we just sort them all based on path length. */
-    struct Cookie **array;
-    size_t i;
-
-    /* alloc an array and store all cookie pointers */
-    array = malloc(sizeof(struct Cookie *) * matches);
-    if(!array)
-      goto fail;
-
-    co = mainco;
-
-    for(i=0; co; co = co->next)
-      array[i++] = co;
-
-    /* now sort the cookie pointers in path length order */
-    qsort(array, matches, sizeof(struct Cookie *), cookie_sort);
-
-    /* remake the linked list order according to the new order */
-
-    mainco = array[0]; /* start here */
-    for(i=0; i<matches-1; i++)
-      array[i]->next = array[i+1];
-    array[matches-1]->next = NULL; /* terminate the list */
-
-    free(array); /* remove the temporary data again */
-  }
-
-  return mainco; /* return the new list */
-}
-
-/*****************************************************************************
- *
- * Curl_cookie_clearall()
- *
- * Clear all existing cookies and reset the counter.
- *
- ****************************************************************************/
-void Curl_cookie_clearall(struct CookieInfo *cookies)
-{
-  if(cookies) {
-    Curl_cookie_freelist(cookies->cookies, TRUE);
-    cookies->cookies = NULL;
-    cookies->numcookies = 0;
-  }
-}
-
-/*****************************************************************************
- *
- * Curl_cookie_freelist()
- *
- * Free a list of cookies previously returned by Curl_cookie_getlist();
- *
- * The 'cookiestoo' argument tells this function whether to just free the
- * list or actually also free all cookies within the list as well.
- *
- ****************************************************************************/
-
-void Curl_cookie_freelist(struct Cookie *co, bool cookiestoo)
-{
-  struct Cookie *next;
-  if(co) {
-    while(co) {
-      next = co->next;
-      if(cookiestoo)
-        freecookie(co);
-      else
-        free(co); /* we only free the struct since the "members" are all just
-                     pointed out in the main cookie list! */
-      co = next;
-    }
-  }
-}
-
-
-/*****************************************************************************
- *
- * Curl_cookie_clearsess()
- *
- * Free all session cookies in the cookies list.
- *
- ****************************************************************************/
-void Curl_cookie_clearsess(struct CookieInfo *cookies)
-{
-  struct Cookie *first, *curr, *next, *prev = NULL;
-
-  if(!cookies || !cookies->cookies)
-    return;
-
-  first = curr = prev = cookies->cookies;
-
-  for(; curr; curr = next) {
-    next = curr->next;
-    if(!curr->expires) {
-      if(first == curr)
-        first = next;
-
-      if(prev == curr)
-        prev = next;
-      else
-        prev->next = next;
-
-      freecookie(curr);
-      cookies->numcookies--;
-    }
-    else
-      prev = curr;
-  }
-
-  cookies->cookies = first;
-}
-
-
-/*****************************************************************************
- *
- * Curl_cookie_cleanup()
- *
- * Free a "cookie object" previous created with cookie_init().
- *
- ****************************************************************************/
-void Curl_cookie_cleanup(struct CookieInfo *c)
-{
-  struct Cookie *co;
-  struct Cookie *next;
-  if(c) {
-    if(c->filename)
-      free(c->filename);
-    co = c->cookies;
-
-    while(co) {
-      next = co->next;
-      freecookie(co);
-      co = next;
-    }
-    free(c); /* free the base struct as well */
-  }
-}
-
-/* get_netscape_format()
- *
- * Formats a string for Netscape output file, w/o a newline at the end.
- *
- * Function returns a char * to a formatted line. Has to be free()d
-*/
-static char *get_netscape_format(const struct Cookie *co)
-{
-  return aprintf(
-    "%s"     /* httponly preamble */
-    "%s%s\t" /* domain */
-    "%s\t"   /* tailmatch */
-    "%s\t"   /* path */
-    "%s\t"   /* secure */
-    "%" FORMAT_OFF_T "\t"   /* expires */
-    "%s\t"   /* name */
-    "%s",    /* value */
-    co->httponly?"#HttpOnly_":"",
-    /* Make sure all domains are prefixed with a dot if they allow
-       tailmatching. This is Mozilla-style. */
-    (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"",
-    co->domain?co->domain:"unknown",
-    co->tailmatch?"TRUE":"FALSE",
-    co->path?co->path:"/",
-    co->secure?"TRUE":"FALSE",
-    co->expires,
-    co->name,
-    co->value?co->value:"");
-}
-
-/*
- * cookie_output()
- *
- * Writes all internally known cookies to the specified file. Specify
- * "-" as file name to write to stdout.
- *
- * The function returns non-zero on write failure.
- */
-static int cookie_output(struct CookieInfo *c, const char *dumphere)
-{
-  struct Cookie *co;
-  FILE *out;
-  bool use_stdout=FALSE;
-
-  if((NULL == c) || (0 == c->numcookies))
-    /* If there are no known cookies, we don't write or even create any
-       destination file */
-    return 0;
-
-  if(strequal("-", dumphere)) {
-    /* use stdout */
-    out = stdout;
-    use_stdout=TRUE;
-  }
-  else {
-    out = fopen(dumphere, "w");
-    if(!out)
-      return 1; /* failure */
-  }
-
-  if(c) {
-    char *format_ptr;
-
-    fputs("# Netscape HTTP Cookie File\n"
-          "# http://curl.haxx.se/docs/http-cookies.html\n"
-          "# This file was generated by libcurl! Edit at your own risk.\n\n",
-          out);
-    co = c->cookies;
-
-    while(co) {
-      format_ptr = get_netscape_format(co);
-      if(format_ptr == NULL) {
-        fprintf(out, "#\n# Fatal libcurl error\n");
-        if(!use_stdout)
-          fclose(out);
-        return 1;
-      }
-      fprintf(out, "%s\n", format_ptr);
-      free(format_ptr);
-      co=co->next;
-    }
-  }
-
-  if(!use_stdout)
-    fclose(out);
-
-  return 0;
-}
-
-struct curl_slist *Curl_cookie_list(struct SessionHandle *data)
-{
-  struct curl_slist *list = NULL;
-  struct curl_slist *beg;
-  struct Cookie *c;
-  char *line;
-
-  if((data->cookies == NULL) ||
-      (data->cookies->numcookies == 0))
-    return NULL;
-
-  c = data->cookies->cookies;
-
-  while(c) {
-    /* fill the list with _all_ the cookies we know */
-    line = get_netscape_format(c);
-    if(!line) {
-      curl_slist_free_all(list);
-      return NULL;
-    }
-    beg = curl_slist_append(list, line);
-    free(line);
-    if(!beg) {
-      curl_slist_free_all(list);
-      return NULL;
-    }
-    list = beg;
-    c = c->next;
-  }
-
-  return list;
-}
-
-void Curl_flush_cookies(struct SessionHandle *data, int cleanup)
-{
-  if(data->set.str[STRING_COOKIEJAR]) {
-    if(data->change.cookielist) {
-      /* If there is a list of cookie files to read, do it first so that
-         we have all the told files read before we write the new jar.
-         Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */
-      Curl_cookie_loadfiles(data);
-    }
-
-    Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
-
-    /* if we have a destination file for all the cookies to get dumped to */
-    if(cookie_output(data->cookies, data->set.str[STRING_COOKIEJAR]))
-      infof(data, "WARNING: failed to save cookies in %s\n",
-            data->set.str[STRING_COOKIEJAR]);
-  }
-  else {
-    if(cleanup && data->change.cookielist) {
-      /* since nothing is written, we can just free the list of cookie file
-         names */
-      curl_slist_free_all(data->change.cookielist); /* clean up list */
-      data->change.cookielist = NULL;
-    }
-    Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
-  }
-
-  if(cleanup && (!data->share || (data->cookies != data->share->cookies))) {
-    Curl_cookie_cleanup(data->cookies);
-  }
-  Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
-}
-
-#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */
diff --git a/lib/curl_amigaos.c b/lib/curl_amigaos.c
new file mode 100644 (file)
index 0000000..c726abb
--- /dev/null
@@ -0,0 +1,77 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if defined(__AMIGA__) && !defined(__ixemul__)
+
+#include <amitcp/socketbasetags.h>
+
+#include "curl_amigaos.h"
+
+struct Library *SocketBase = NULL;
+extern int errno, h_errno;
+
+#ifdef __libnix__
+#include <stabs.h>
+void __request(const char *msg);
+#else
+# define __request( msg )       Printf( msg "\n\a")
+#endif
+
+void Curl_amiga_cleanup()
+{
+  if(SocketBase) {
+    CloseLibrary(SocketBase);
+    SocketBase = NULL;
+  }
+}
+
+bool Curl_amiga_init()
+{
+  if(!SocketBase)
+    SocketBase = OpenLibrary("bsdsocket.library", 4);
+
+  if(!SocketBase) {
+    __request("No TCP/IP Stack running!");
+    return FALSE;
+  }
+
+  if(SocketBaseTags(SBTM_SETVAL(SBTC_ERRNOPTR(sizeof(errno))), (ULONG) &errno,
+                    SBTM_SETVAL(SBTC_LOGTAGPTR), (ULONG) "cURL",
+                    TAG_DONE)) {
+    __request("SocketBaseTags ERROR");
+    return FALSE;
+  }
+
+#ifndef __libnix__
+  atexit(Curl_amiga_cleanup);
+#endif
+
+  return TRUE;
+}
+
+#ifdef __libnix__
+ADD2EXIT(Curl_amiga_cleanup,-50);
+#endif
+
+#endif /* __AMIGA__ && ! __ixemul__ */
diff --git a/lib/curl_asyn_ares.c b/lib/curl_asyn_ares.c
new file mode 100644 (file)
index 0000000..6e09e9b
--- /dev/null
@@ -0,0 +1,639 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#ifdef HAVE_PROCESS_H
+#include <process.h>
+#endif
+
+#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
+#undef in_addr_t
+#define in_addr_t unsigned long
+#endif
+
+/***********************************************************************
+ * Only for ares-enabled builds
+ * And only for functions that fulfill the asynch resolver backend API
+ * as defined in curl_asyn.h, nothing else belongs in this file!
+ **********************************************************************/
+
+#ifdef CURLRES_ARES
+
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_hostip.h"
+#include "curl_hash.h"
+#include "curl_share.h"
+#include "curl_strerror.h"
+#include "curl_url.h"
+#include "curl_multiif.h"
+#include "curl_inet_pton.h"
+#include "curl_connect.h"
+#include "curl_select.h"
+#include "curl_progress.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#  if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
+     (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__))
+#    define CARES_STATICLIB
+#  endif
+#  include <ares.h>
+#  include <ares_version.h> /* really old c-ares didn't include this by
+                               itself */
+
+#if ARES_VERSION >= 0x010500
+/* c-ares 1.5.0 or later, the callback proto is modified */
+#define HAVE_CARES_CALLBACK_TIMEOUTS 1
+#endif
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+struct ResolverResults {
+  int num_pending; /* number of ares_gethostbyname() requests */
+  Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares parts */
+  int last_status;
+};
+
+/*
+ * Curl_resolver_global_init() - the generic low-level asynchronous name
+ * resolve API.  Called from curl_global_init() to initialize global resolver
+ * environment.  Initializes ares library.
+ */
+int Curl_resolver_global_init(void)
+{
+#ifdef CARES_HAVE_ARES_LIBRARY_INIT
+  if(ares_library_init(ARES_LIB_INIT_ALL)) {
+    return CURLE_FAILED_INIT;
+  }
+#endif
+  return CURLE_OK;
+}
+
+/*
+ * Curl_resolver_global_cleanup()
+ *
+ * Called from curl_global_cleanup() to destroy global resolver environment.
+ * Deinitializes ares library.
+ */
+void Curl_resolver_global_cleanup(void)
+{
+#ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
+  ares_library_cleanup();
+#endif
+}
+
+/*
+ * Curl_resolver_init()
+ *
+ * Called from curl_easy_init() -> Curl_open() to initialize resolver
+ * URL-state specific environment ('resolver' member of the UrlState
+ * structure).  Fills the passed pointer by the initialized ares_channel.
+ */
+CURLcode Curl_resolver_init(void **resolver)
+{
+  int status = ares_init((ares_channel*)resolver);
+  if(status != ARES_SUCCESS) {
+    if(status == ARES_ENOMEM)
+      return CURLE_OUT_OF_MEMORY;
+    else
+      return CURLE_FAILED_INIT;
+  }
+  return CURLE_OK;
+  /* make sure that all other returns from this function should destroy the
+     ares channel before returning error! */
+}
+
+/*
+ * Curl_resolver_cleanup()
+ *
+ * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
+ * URL-state specific environment ('resolver' member of the UrlState
+ * structure).  Destroys the ares channel.
+ */
+void Curl_resolver_cleanup(void *resolver)
+{
+  ares_destroy((ares_channel)resolver);
+}
+
+/*
+ * Curl_resolver_duphandle()
+ *
+ * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
+ * environment ('resolver' member of the UrlState structure).  Duplicates the
+ * 'from' ares channel and passes the resulting channel to the 'to' pointer.
+ */
+int Curl_resolver_duphandle(void **to, void *from)
+{
+  /* Clone the ares channel for the new handle */
+  if(ARES_SUCCESS != ares_dup((ares_channel*)to,(ares_channel)from))
+    return CURLE_FAILED_INIT;
+  return CURLE_OK;
+}
+
+static void destroy_async_data (struct Curl_async *async);
+
+/*
+ * Cancel all possibly still on-going resolves for this connection.
+ */
+void Curl_resolver_cancel(struct connectdata *conn)
+{
+  if(conn && conn->data && conn->data->state.resolver)
+    ares_cancel((ares_channel)conn->data->state.resolver);
+  destroy_async_data(&conn->async);
+}
+
+/*
+ * destroy_async_data() cleans up async resolver data.
+ */
+static void destroy_async_data (struct Curl_async *async)
+{
+  if(async->hostname)
+    free(async->hostname);
+
+  if(async->os_specific) {
+    struct ResolverResults *res = (struct ResolverResults *)async->os_specific;
+    if(res) {
+      if(res->temp_ai) {
+        Curl_freeaddrinfo(res->temp_ai);
+        res->temp_ai = NULL;
+      }
+      free(res);
+    }
+    async->os_specific = NULL;
+  }
+
+  async->hostname = NULL;
+}
+
+/*
+ * Curl_resolver_fdset() is called when someone from the outside world (using
+ * curl_multi_fdset()) wants to get our fd_set setup and we're talking with
+ * ares. The caller must make sure that this function is only called when we
+ * have a working ares channel.
+ *
+ * Returns: CURLE_OK always!
+ */
+
+int Curl_resolver_getsock(struct connectdata *conn,
+                          curl_socket_t *socks,
+                          int numsocks)
+
+{
+  struct timeval maxtime;
+  struct timeval timebuf;
+  struct timeval *timeout;
+  long milli;
+  int max = ares_getsock((ares_channel)conn->data->state.resolver,
+                         (ares_socket_t *)socks, numsocks);
+
+  maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
+  maxtime.tv_usec = 0;
+
+  timeout = ares_timeout((ares_channel)conn->data->state.resolver, &maxtime,
+                         &timebuf);
+  milli = (timeout->tv_sec * 1000) + (timeout->tv_usec/1000);
+  if(milli == 0)
+    milli += 10;
+  Curl_expire(conn->data, milli);
+
+  return max;
+}
+
+/*
+ * waitperform()
+ *
+ * 1) Ask ares what sockets it currently plays with, then
+ * 2) wait for the timeout period to check for action on ares' sockets.
+ * 3) tell ares to act on all the sockets marked as "with action"
+ *
+ * return number of sockets it worked on
+ */
+
+static int waitperform(struct connectdata *conn, int timeout_ms)
+{
+  struct SessionHandle *data = conn->data;
+  int nfds;
+  int bitmask;
+  ares_socket_t socks[ARES_GETSOCK_MAXNUM];
+  struct pollfd pfd[ARES_GETSOCK_MAXNUM];
+  int i;
+  int num = 0;
+
+  bitmask = ares_getsock((ares_channel)data->state.resolver, socks,
+                         ARES_GETSOCK_MAXNUM);
+
+  for(i=0; i < ARES_GETSOCK_MAXNUM; i++) {
+    pfd[i].events = 0;
+    pfd[i].revents = 0;
+    if(ARES_GETSOCK_READABLE(bitmask, i)) {
+      pfd[i].fd = socks[i];
+      pfd[i].events |= POLLRDNORM|POLLIN;
+    }
+    if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
+      pfd[i].fd = socks[i];
+      pfd[i].events |= POLLWRNORM|POLLOUT;
+    }
+    if(pfd[i].events != 0)
+      num++;
+    else
+      break;
+  }
+
+  if(num)
+    nfds = Curl_poll(pfd, num, timeout_ms);
+  else
+    nfds = 0;
+
+  if(!nfds)
+    /* Call ares_process() unconditonally here, even if we simply timed out
+       above, as otherwise the ares name resolve won't timeout! */
+    ares_process_fd((ares_channel)data->state.resolver, ARES_SOCKET_BAD,
+                    ARES_SOCKET_BAD);
+  else {
+    /* move through the descriptors and ask for processing on them */
+    for(i=0; i < num; i++)
+      ares_process_fd((ares_channel)data->state.resolver,
+                      pfd[i].revents & (POLLRDNORM|POLLIN)?
+                      pfd[i].fd:ARES_SOCKET_BAD,
+                      pfd[i].revents & (POLLWRNORM|POLLOUT)?
+                      pfd[i].fd:ARES_SOCKET_BAD);
+  }
+  return nfds;
+}
+
+/*
+ * Curl_resolver_is_resolved() is called repeatedly to check if a previous
+ * name resolve request has completed. It should also make sure to time-out if
+ * the operation seems to take too long.
+ *
+ * Returns normal CURLcode errors.
+ */
+CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
+                                   struct Curl_dns_entry **dns)
+{
+  struct SessionHandle *data = conn->data;
+  struct ResolverResults *res = (struct ResolverResults *)
+    conn->async.os_specific;
+
+  *dns = NULL;
+
+  waitperform(conn, 0);
+
+  if(res && !res->num_pending) {
+    (void)Curl_addrinfo_callback(conn, res->last_status, res->temp_ai);
+    /* temp_ai ownership is moved to the connection, so we need not free-up
+       them */
+    res->temp_ai = NULL;
+    destroy_async_data(&conn->async);
+    if(!conn->async.dns) {
+      failf(data, "Could not resolve %s: %s (%s)",
+            conn->bits.proxy?"proxy":"host",
+            conn->host.dispname,
+            ares_strerror(conn->async.status));
+      return conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY:
+        CURLE_COULDNT_RESOLVE_HOST;
+    }
+    *dns = conn->async.dns;
+  }
+
+  return CURLE_OK;
+}
+
+/*
+ * Curl_resolver_wait_resolv()
+ *
+ * waits for a resolve to finish. This function should be avoided since using
+ * this risk getting the multi interface to "hang".
+ *
+ * If 'entry' is non-NULL, make it point to the resolved dns entry
+ *
+ * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
+ * CURLE_OPERATION_TIMEDOUT if a time-out occurred.
+ */
+CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
+                                   struct Curl_dns_entry **entry)
+{
+  CURLcode rc=CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  long timeout;
+  struct timeval now = Curl_tvnow();
+  struct Curl_dns_entry *temp_entry;
+
+  timeout = Curl_timeleft(data, &now, TRUE);
+  if(!timeout)
+    timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
+
+  /* Wait for the name resolve query to complete. */
+  for(;;) {
+    struct timeval *tvp, tv, store;
+    long timediff;
+    int itimeout;
+    int timeout_ms;
+
+    itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout;
+
+    store.tv_sec = itimeout/1000;
+    store.tv_usec = (itimeout%1000)*1000;
+
+    tvp = ares_timeout((ares_channel)data->state.resolver, &store, &tv);
+
+    /* use the timeout period ares returned to us above if less than one
+       second is left, otherwise just use 1000ms to make sure the progress
+       callback gets called frequent enough */
+    if(!tvp->tv_sec)
+      timeout_ms = (int)(tvp->tv_usec/1000);
+    else
+      timeout_ms = 1000;
+
+    waitperform(conn, timeout_ms);
+    Curl_resolver_is_resolved(conn,&temp_entry);
+
+    if(conn->async.done)
+      break;
+
+    if(Curl_pgrsUpdate(conn)) {
+      rc = CURLE_ABORTED_BY_CALLBACK;
+      timeout = -1; /* trigger the cancel below */
+    }
+    else {
+      struct timeval now2 = Curl_tvnow();
+      timediff = Curl_tvdiff(now2, now); /* spent time */
+      timeout -= timediff?timediff:1; /* always deduct at least 1 */
+      now = now2; /* for next loop */
+    }
+    if(timeout < 0) {
+      /* our timeout, so we cancel the ares operation */
+      ares_cancel((ares_channel)data->state.resolver);
+      break;
+    }
+  }
+
+  /* Operation complete, if the lookup was successful we now have the entry
+     in the cache. */
+
+  if(entry)
+    *entry = conn->async.dns;
+
+  if(!conn->async.dns) {
+    /* a name was not resolved */
+    if((timeout < 0) || (conn->async.status == ARES_ETIMEOUT)) {
+      if(conn->bits.proxy) {
+        failf(data, "Resolving proxy timed out: %s", conn->proxy.dispname);
+        rc = CURLE_COULDNT_RESOLVE_PROXY;
+      }
+      else {
+        failf(data, "Resolving host timed out: %s", conn->host.dispname);
+        rc = CURLE_COULDNT_RESOLVE_HOST;
+      }
+    }
+    else if(conn->async.done) {
+      if(conn->bits.proxy) {
+        failf(data, "Could not resolve proxy: %s (%s)", conn->proxy.dispname,
+              ares_strerror(conn->async.status));
+        rc = CURLE_COULDNT_RESOLVE_PROXY;
+      }
+      else {
+        failf(data, "Could not resolve host: %s (%s)", conn->host.dispname,
+              ares_strerror(conn->async.status));
+        rc = CURLE_COULDNT_RESOLVE_HOST;
+      }
+    }
+    else
+      rc = CURLE_OPERATION_TIMEDOUT;
+
+    /* close the connection, since we can't return failure here without
+       cleaning up this connection properly */
+    conn->bits.close = TRUE;
+  }
+
+  return rc;
+}
+
+/* Connects results to the list */
+static void compound_results(struct ResolverResults *res,
+                             Curl_addrinfo *ai)
+{
+  Curl_addrinfo *ai_tail;
+  if(!ai)
+    return;
+  ai_tail = ai;
+
+  while(ai_tail->ai_next)
+    ai_tail = ai_tail->ai_next;
+
+  /* Add the new results to the list of old results. */
+  ai_tail->ai_next = res->temp_ai;
+  res->temp_ai = ai;
+}
+
+/*
+ * ares_query_completed_cb() is the callback that ares will call when
+ * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
+ * when using ares, is completed either successfully or with failure.
+ */
+static void query_completed_cb(void *arg,  /* (struct connectdata *) */
+                               int status,
+#ifdef HAVE_CARES_CALLBACK_TIMEOUTS
+                               int timeouts,
+#endif
+                               struct hostent *hostent)
+{
+  struct connectdata *conn = (struct connectdata *)arg;
+  struct ResolverResults *res;
+
+#ifdef HAVE_CARES_CALLBACK_TIMEOUTS
+  (void)timeouts; /* ignored */
+#endif
+
+  if(ARES_EDESTRUCTION == status)
+    /* when this ares handle is getting destroyed, the 'arg' pointer may not
+       be valid so only defer it when we know the 'status' says its fine! */
+    return;
+
+  res = (struct ResolverResults *)conn->async.os_specific;
+  res->num_pending--;
+
+  if(CURL_ASYNC_SUCCESS == status) {
+    Curl_addrinfo *ai = Curl_he2ai(hostent, conn->async.port);
+    if(ai) {
+      compound_results(res, ai);
+    }
+  }
+  /* A successful result overwrites any previous error */
+  if(res->last_status != ARES_SUCCESS)
+    res->last_status = status;
+}
+
+/*
+ * Curl_resolver_getaddrinfo() - when using ares
+ *
+ * Returns name information about the given hostname and port number. If
+ * successful, the 'hostent' is returned and the forth argument will point to
+ * memory we need to free after use. That memory *MUST* be freed with
+ * Curl_freeaddrinfo(), nothing else.
+ */
+Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
+                                         const char *hostname,
+                                         int port,
+                                         int *waitp)
+{
+  char *bufp;
+  struct SessionHandle *data = conn->data;
+  struct in_addr in;
+  int family = PF_INET;
+#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
+  struct in6_addr in6;
+#endif /* CURLRES_IPV6 */
+
+  *waitp = 0; /* default to synchronous response */
+
+  /* First check if this is an IPv4 address string */
+  if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
+    /* This is a dotted IP address 123.123.123.123-style */
+    return Curl_ip2addr(AF_INET, &in, hostname, port);
+  }
+
+#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
+  /* Otherwise, check if this is an IPv6 address string */
+  if(Curl_inet_pton (AF_INET6, hostname, &in6) > 0)
+    /* This must be an IPv6 address literal.  */
+    return Curl_ip2addr(AF_INET6, &in6, hostname, port);
+
+  switch(conn->ip_version) {
+  default:
+#if ARES_VERSION >= 0x010601
+    family = PF_UNSPEC; /* supported by c-ares since 1.6.1, so for older
+                           c-ares versions this just falls through and defaults
+                           to PF_INET */
+    break;
+#endif
+  case CURL_IPRESOLVE_V4:
+    family = PF_INET;
+    break;
+  case CURL_IPRESOLVE_V6:
+    family = PF_INET6;
+    break;
+  }
+#endif /* CURLRES_IPV6 */
+
+  bufp = strdup(hostname);
+  if(bufp) {
+    struct ResolverResults *res = NULL;
+    Curl_safefree(conn->async.hostname);
+    conn->async.hostname = bufp;
+    conn->async.port = port;
+    conn->async.done = FALSE;   /* not done */
+    conn->async.status = 0;     /* clear */
+    conn->async.dns = NULL;     /* clear */
+    res = calloc(sizeof(struct ResolverResults),1);
+    if(!res) {
+      Curl_safefree(conn->async.hostname);
+      conn->async.hostname = NULL;
+      return NULL;
+    }
+    conn->async.os_specific = res;
+
+    /* initial status - failed */
+    res->last_status = ARES_ENOTFOUND;
+#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
+    if(family == PF_UNSPEC) {
+      if(Curl_ipv6works()) {
+        res->num_pending = 2;
+
+        /* areschannel is already setup in the Curl_open() function */
+        ares_gethostbyname((ares_channel)data->state.resolver, hostname,
+                            PF_INET, query_completed_cb, conn);
+        ares_gethostbyname((ares_channel)data->state.resolver, hostname,
+                            PF_INET6, query_completed_cb, conn);
+      }
+      else {
+        res->num_pending = 1;
+
+        /* areschannel is already setup in the Curl_open() function */
+        ares_gethostbyname((ares_channel)data->state.resolver, hostname,
+                            PF_INET, query_completed_cb, conn);
+      }
+    }
+    else
+#endif /* CURLRES_IPV6 */
+    {
+      res->num_pending = 1;
+
+      /* areschannel is already setup in the Curl_open() function */
+      ares_gethostbyname((ares_channel)data->state.resolver, hostname, family,
+                         query_completed_cb, conn);
+    }
+
+    *waitp = 1; /* expect asynchronous response */
+  }
+  return NULL; /* no struct yet */
+}
+
+CURLcode Curl_set_dns_servers(struct SessionHandle *data,
+                              char *servers)
+{
+  CURLcode result = CURLE_NOT_BUILT_IN;
+#if (ARES_VERSION >= 0x010704)
+  int ares_result = ares_set_servers_csv(data->state.resolver, servers);
+  switch(ares_result) {
+  case ARES_SUCCESS:
+    result = CURLE_OK;
+    break;
+  case ARES_ENOMEM:
+    result = CURLE_OUT_OF_MEMORY;
+    break;
+  case ARES_ENOTINITIALIZED:
+  case ARES_ENODATA:
+  case ARES_EBADSTR:
+  default:
+    result = CURLE_BAD_FUNCTION_ARGUMENT;
+    break;
+  }
+#else /* too old c-ares version! */
+  (void)data;
+  (void)servers;
+#endif
+  return result;
+}
+#endif /* CURLRES_ARES */
diff --git a/lib/curl_asyn_thread.c b/lib/curl_asyn_thread.c
new file mode 100644 (file)
index 0000000..6d3667f
--- /dev/null
@@ -0,0 +1,679 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#if defined(USE_THREADS_POSIX)
+#  ifdef HAVE_PTHREAD_H
+#    include <pthread.h>
+#  endif
+#elif defined(USE_THREADS_WIN32)
+#  ifdef HAVE_PROCESS_H
+#    include <process.h>
+#  endif
+#endif
+
+#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
+#undef in_addr_t
+#define in_addr_t unsigned long
+#endif
+
+#ifdef HAVE_GETADDRINFO
+#  define RESOLVER_ENOMEM  EAI_MEMORY
+#else
+#  define RESOLVER_ENOMEM  ENOMEM
+#endif
+
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_hostip.h"
+#include "curl_hash.h"
+#include "curl_share.h"
+#include "curl_strerror.h"
+#include "curl_url.h"
+#include "curl_multiif.h"
+#include "curl_inet_pton.h"
+#include "curl_inet_ntop.h"
+#include "curl_threads.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/***********************************************************************
+ * Only for threaded name resolves builds
+ **********************************************************************/
+#ifdef CURLRES_THREADED
+
+/*
+ * Curl_resolver_global_init()
+ * Called from curl_global_init() to initialize global resolver environment.
+ * Does nothing here.
+ */
+int Curl_resolver_global_init(void)
+{
+  return CURLE_OK;
+}
+
+/*
+ * Curl_resolver_global_cleanup()
+ * Called from curl_global_cleanup() to destroy global resolver environment.
+ * Does nothing here.
+ */
+void Curl_resolver_global_cleanup(void)
+{
+}
+
+/*
+ * Curl_resolver_init()
+ * Called from curl_easy_init() -> Curl_open() to initialize resolver
+ * URL-state specific environment ('resolver' member of the UrlState
+ * structure).  Does nothing here.
+ */
+CURLcode Curl_resolver_init(void **resolver)
+{
+  (void)resolver;
+  return CURLE_OK;
+}
+
+/*
+ * Curl_resolver_cleanup()
+ * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
+ * URL-state specific environment ('resolver' member of the UrlState
+ * structure).  Does nothing here.
+ */
+void Curl_resolver_cleanup(void *resolver)
+{
+  (void)resolver;
+}
+
+/*
+ * Curl_resolver_duphandle()
+ * Called from curl_easy_duphandle() to duplicate resolver URL state-specific
+ * environment ('resolver' member of the UrlState structure).  Does nothing
+ * here.
+ */
+int Curl_resolver_duphandle(void **to, void *from)
+{
+  (void)to;
+  (void)from;
+  return CURLE_OK;
+}
+
+static void destroy_async_data(struct Curl_async *);
+
+/*
+ * Cancel all possibly still on-going resolves for this connection.
+ */
+void Curl_resolver_cancel(struct connectdata *conn)
+{
+  destroy_async_data(&conn->async);
+}
+
+/* This function is used to init a threaded resolve */
+static bool init_resolve_thread(struct connectdata *conn,
+                                const char *hostname, int port,
+                                const struct addrinfo *hints);
+
+
+/* Data for synchronization between resolver thread and its parent */
+struct thread_sync_data {
+  curl_mutex_t * mtx;
+  int done;
+
+  char * hostname;        /* hostname to resolve, Curl_async.hostname
+                             duplicate */
+  int port;
+  int sock_error;
+  Curl_addrinfo *res;
+#ifdef HAVE_GETADDRINFO
+  struct addrinfo hints;
+#endif
+};
+
+struct thread_data {
+  curl_thread_t thread_hnd;
+  unsigned int poll_interval;
+  int interval_end;
+  struct thread_sync_data tsd;
+};
+
+static struct thread_sync_data *conn_thread_sync_data(struct connectdata *conn)
+{
+  return &(((struct thread_data *)conn->async.os_specific)->tsd);
+}
+
+#define CONN_THREAD_SYNC_DATA(conn) &(((conn)->async.os_specific)->tsd);
+
+/* Destroy resolver thread synchronization data */
+static
+void destroy_thread_sync_data(struct thread_sync_data * tsd)
+{
+  if(tsd->mtx) {
+    Curl_mutex_destroy(tsd->mtx);
+    free(tsd->mtx);
+  }
+
+  if(tsd->hostname)
+    free(tsd->hostname);
+
+  if(tsd->res)
+    Curl_freeaddrinfo(tsd->res);
+
+  memset(tsd,0,sizeof(*tsd));
+}
+
+/* Initialize resolver thread synchronization data */
+static
+int init_thread_sync_data(struct thread_sync_data * tsd,
+                           const char * hostname,
+                           int port,
+                           const struct addrinfo *hints)
+{
+  memset(tsd, 0, sizeof(*tsd));
+
+  tsd->port = port;
+#ifdef CURLRES_IPV6
+  DEBUGASSERT(hints);
+  tsd->hints = *hints;
+#else
+  (void) hints;
+#endif
+
+  tsd->mtx = malloc(sizeof(curl_mutex_t));
+  if(tsd->mtx == NULL)
+    goto err_exit;
+
+  Curl_mutex_init(tsd->mtx);
+
+  tsd->sock_error = CURL_ASYNC_SUCCESS;
+
+  /* Copying hostname string because original can be destroyed by parent
+   * thread during gethostbyname execution.
+   */
+  tsd->hostname = strdup(hostname);
+  if(!tsd->hostname)
+    goto err_exit;
+
+  return 1;
+
+ err_exit:
+  /* Memory allocation failed */
+  destroy_thread_sync_data(tsd);
+  return 0;
+}
+
+static int getaddrinfo_complete(struct connectdata *conn)
+{
+  struct thread_sync_data *tsd = conn_thread_sync_data(conn);
+  int rc;
+
+  rc = Curl_addrinfo_callback(conn, tsd->sock_error, tsd->res);
+  /* The tsd->res structure has been copied to async.dns and perhaps the DNS
+     cache.  Set our copy to NULL so destroy_thread_sync_data doesn't free it.
+  */
+  tsd->res = NULL;
+
+  return rc;
+}
+
+
+#ifdef HAVE_GETADDRINFO
+
+/*
+ * getaddrinfo_thread() resolves a name and then exits.
+ *
+ * For builds without ARES, but with ENABLE_IPV6, create a resolver thread
+ * and wait on it.
+ */
+static unsigned int CURL_STDCALL getaddrinfo_thread (void *arg)
+{
+  struct thread_sync_data *tsd = (struct thread_sync_data*)arg;
+  char   service [NI_MAXSERV];
+  int rc;
+
+  snprintf(service, sizeof(service), "%d", tsd->port);
+
+  rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res);
+
+  if(rc != 0) {
+    tsd->sock_error = SOCKERRNO?SOCKERRNO:rc;
+    if(tsd->sock_error == 0)
+      tsd->sock_error = RESOLVER_ENOMEM;
+  }
+
+  Curl_mutex_acquire(tsd->mtx);
+  tsd->done = 1;
+  Curl_mutex_release(tsd->mtx);
+
+  return 0;
+}
+
+#else /* HAVE_GETADDRINFO */
+
+/*
+ * gethostbyname_thread() resolves a name and then exits.
+ */
+static unsigned int CURL_STDCALL gethostbyname_thread (void *arg)
+{
+  struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
+
+  tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port);
+
+  if(!tsd->res) {
+    tsd->sock_error = SOCKERRNO;
+    if(tsd->sock_error == 0)
+      tsd->sock_error = RESOLVER_ENOMEM;
+  }
+
+  Curl_mutex_acquire(tsd->mtx);
+  tsd->done = 1;
+  Curl_mutex_release(tsd->mtx);
+
+  return 0;
+}
+
+#endif /* HAVE_GETADDRINFO */
+
+/*
+ * destroy_async_data() cleans up async resolver data and thread handle.
+ */
+static void destroy_async_data (struct Curl_async *async)
+{
+  if(async->hostname)
+    free(async->hostname);
+
+  if(async->os_specific) {
+    struct thread_data *td = (struct thread_data*) async->os_specific;
+
+    if(td->thread_hnd != curl_thread_t_null)
+      Curl_thread_join(&td->thread_hnd);
+
+    destroy_thread_sync_data(&td->tsd);
+
+    free(async->os_specific);
+  }
+  async->hostname = NULL;
+  async->os_specific = NULL;
+}
+
+/*
+ * init_resolve_thread() starts a new thread that performs the actual
+ * resolve. This function returns before the resolve is done.
+ *
+ * Returns FALSE in case of failure, otherwise TRUE.
+ */
+static bool init_resolve_thread (struct connectdata *conn,
+                                 const char *hostname, int port,
+                                 const struct addrinfo *hints)
+{
+  struct thread_data *td = calloc(1, sizeof(struct thread_data));
+  int err = RESOLVER_ENOMEM;
+
+  conn->async.os_specific = (void*) td;
+  if(!td)
+    goto err_exit;
+
+  conn->async.port = port;
+  conn->async.done = FALSE;
+  conn->async.status = 0;
+  conn->async.dns = NULL;
+  td->thread_hnd = curl_thread_t_null;
+
+  if(!init_thread_sync_data(&td->tsd, hostname, port, hints))
+    goto err_exit;
+
+  Curl_safefree(conn->async.hostname);
+  conn->async.hostname = strdup(hostname);
+  if(!conn->async.hostname)
+    goto err_exit;
+
+#ifdef HAVE_GETADDRINFO
+  td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
+#else
+  td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
+#endif
+
+  if(!td->thread_hnd) {
+#ifndef _WIN32_WCE
+    err = errno;
+#endif
+    goto err_exit;
+  }
+
+  return TRUE;
+
+ err_exit:
+  destroy_async_data(&conn->async);
+
+  SET_ERRNO(err);
+
+  return FALSE;
+}
+
+#if defined(HAVE_GETADDRINFO) && !defined(HAVE_GAI_STRERROR) && !defined(WIN32)
+/* NetWare has getaddrinfo but lacks gai_strerror.
+   Windows has a gai_strerror but it is bad (not thread-safe) and the generic
+   socket error string function can be used for this pupose. */
+static const char *gai_strerror(int ecode)
+{
+  switch (ecode) {
+  case EAI_AGAIN:
+    return "The name could not be resolved at this time";
+  case EAI_BADFLAGS:
+    return "The flags parameter had an invalid value";
+  case EAI_FAIL:
+    return "A non-recoverable error occurred when attempting to "
+      "resolve the name";
+  case EAI_FAMILY:
+    return "The address family was not recognized";
+  case EAI_MEMORY:
+    return "Out of memory";
+  case EAI_NONAME:
+    return "The name does not resolve for the supplied parameters";
+  case EAI_SERVICE:
+    return "The service passed was not recognized for the "
+      "specified socket type"
+  case EAI_SOCKTYPE:
+    return "The intended socket type was not recognized"
+  case EAI_SYSTEM:
+    return "A system error occurred";
+  case EAI_OVERFLOW:
+    return "An argument buffer overflowed";
+  default:
+    return "Unknown error";
+
+/* define this now as this is a private implementation of said function */
+#define HAVE_GAI_STRERROR
+}
+#endif
+
+
+/*
+ * resolver_error() calls failf() with the appropriate message after a resolve
+ * error
+ */
+
+static void resolver_error(struct connectdata *conn, const char *host_or_proxy)
+{
+  failf(conn->data, "Could not resolve %s: %s; %s", host_or_proxy,
+        conn->async.hostname,
+#ifdef HAVE_GAI_STRERROR
+        /* NetWare doesn't have gai_strerror and on Windows it isn't deemed
+           thread-safe */
+        gai_strerror(conn->async.status)
+#else
+        Curl_strerror(conn, conn->async.status)
+#endif
+    );
+}
+
+/*
+ * Curl_resolver_wait_resolv()
+ *
+ * waits for a resolve to finish. This function should be avoided since using
+ * this risk getting the multi interface to "hang".
+ *
+ * If 'entry' is non-NULL, make it point to the resolved dns entry
+ *
+ * This is the version for resolves-in-a-thread.
+ */
+CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
+                                   struct Curl_dns_entry **entry)
+{
+  struct thread_data   *td = (struct thread_data*) conn->async.os_specific;
+  CURLcode rc = CURLE_OK;
+
+  DEBUGASSERT(conn && td);
+
+  /* wait for the thread to resolve the name */
+  if(Curl_thread_join(&td->thread_hnd))
+    rc = getaddrinfo_complete(conn);
+  else
+    DEBUGASSERT(0);
+
+  conn->async.done = TRUE;
+
+  if(entry)
+    *entry = conn->async.dns;
+
+  if(!conn->async.dns) {
+    /* a name was not resolved */
+    if(conn->bits.httpproxy) {
+      resolver_error(conn, "proxy");
+      rc = CURLE_COULDNT_RESOLVE_PROXY;
+    }
+    else {
+      resolver_error(conn, "host");
+      rc = CURLE_COULDNT_RESOLVE_HOST;
+    }
+  }
+
+  destroy_async_data(&conn->async);
+
+  if(!conn->async.dns)
+    conn->bits.close = TRUE;
+
+  return (rc);
+}
+
+/*
+ * Curl_resolver_is_resolved() is called repeatedly to check if a previous
+ * name resolve request has completed. It should also make sure to time-out if
+ * the operation seems to take too long.
+ */
+CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
+                                   struct Curl_dns_entry **entry)
+{
+  struct SessionHandle *data = conn->data;
+  struct thread_data   *td = (struct thread_data*) conn->async.os_specific;
+  int done = 0;
+
+  *entry = NULL;
+
+  if(!td) {
+    DEBUGASSERT(td);
+    return CURLE_COULDNT_RESOLVE_HOST;
+  }
+
+  Curl_mutex_acquire(td->tsd.mtx);
+  done = td->tsd.done;
+  Curl_mutex_release(td->tsd.mtx);
+
+  if(done) {
+    getaddrinfo_complete(conn);
+    destroy_async_data(&conn->async);
+
+    if(!conn->async.dns) {
+      resolver_error(conn, "host");
+      return CURLE_COULDNT_RESOLVE_HOST;
+    }
+    *entry = conn->async.dns;
+  }
+  else {
+    /* poll for name lookup done with exponential backoff up to 250ms */
+    int elapsed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle);
+    if(elapsed < 0)
+      elapsed = 0;
+
+    if(td->poll_interval == 0)
+      /* Start at 1ms poll interval */
+      td->poll_interval = 1;
+    else if(elapsed >= td->interval_end)
+      /* Back-off exponentially if last interval expired  */
+      td->poll_interval *= 2;
+
+    if(td->poll_interval > 250)
+      td->poll_interval = 250;
+
+    td->interval_end = elapsed + td->poll_interval;
+    Curl_expire(conn->data, td->poll_interval);
+  }
+
+  return CURLE_OK;
+}
+
+int Curl_resolver_getsock(struct connectdata *conn,
+                          curl_socket_t *socks,
+                          int numsocks)
+{
+  (void)conn;
+  (void)socks;
+  (void)numsocks;
+  return 0;
+}
+
+#ifndef HAVE_GETADDRINFO
+/*
+ * Curl_getaddrinfo() - for platforms without getaddrinfo
+ */
+Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
+                                         const char *hostname,
+                                         int port,
+                                         int *waitp)
+{
+  struct in_addr in;
+
+  *waitp = 0; /* default to synchronous response */
+
+  if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
+    /* This is a dotted IP address 123.123.123.123-style */
+    return Curl_ip2addr(AF_INET, &in, hostname, port);
+
+  /* fire up a new resolver thread! */
+  if(init_resolve_thread(conn, hostname, port, NULL)) {
+    *waitp = 1; /* expect asynchronous response */
+    return NULL;
+  }
+
+  /* fall-back to blocking version */
+  return Curl_ipv4_resolve_r(hostname, port);
+}
+
+#else /* !HAVE_GETADDRINFO */
+
+/*
+ * Curl_resolver_getaddrinfo() - for getaddrinfo
+ */
+Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
+                                         const char *hostname,
+                                         int port,
+                                         int *waitp)
+{
+  struct addrinfo hints;
+  struct in_addr in;
+  Curl_addrinfo *res;
+  int error;
+  char sbuf[NI_MAXSERV];
+  int pf = PF_INET;
+#ifdef CURLRES_IPV6
+  struct in6_addr in6;
+#endif /* CURLRES_IPV6 */
+
+  *waitp = 0; /* default to synchronous response */
+
+  /* First check if this is an IPv4 address string */
+  if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
+    /* This is a dotted IP address 123.123.123.123-style */
+    return Curl_ip2addr(AF_INET, &in, hostname, port);
+
+#ifdef CURLRES_IPV6
+  /* check if this is an IPv6 address string */
+  if(Curl_inet_pton (AF_INET6, hostname, &in6) > 0)
+    /* This is an IPv6 address literal */
+    return Curl_ip2addr(AF_INET6, &in6, hostname, port);
+
+  /*
+   * Check if a limited name resolve has been requested.
+   */
+  switch(conn->ip_version) {
+  case CURL_IPRESOLVE_V4:
+    pf = PF_INET;
+    break;
+  case CURL_IPRESOLVE_V6:
+    pf = PF_INET6;
+    break;
+  default:
+    pf = PF_UNSPEC;
+    break;
+  }
+
+  if((pf != PF_INET) && !Curl_ipv6works())
+    /* the stack seems to be a non-ipv6 one */
+    pf = PF_INET;
+
+#endif /* CURLRES_IPV6 */
+
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_family = pf;
+  hints.ai_socktype = conn->socktype;
+
+  snprintf(sbuf, sizeof(sbuf), "%d", port);
+
+  /* fire up a new resolver thread! */
+  if(init_resolve_thread(conn, hostname, port, &hints)) {
+    *waitp = 1; /* expect asynchronous response */
+    return NULL;
+  }
+
+  /* fall-back to blocking version */
+  infof(conn->data, "init_resolve_thread() failed for %s; %s\n",
+        hostname, Curl_strerror(conn, ERRNO));
+
+  error = Curl_getaddrinfo_ex(hostname, sbuf, &hints, &res);
+  if(error) {
+    infof(conn->data, "getaddrinfo() failed for %s:%d; %s\n",
+          hostname, port, Curl_strerror(conn, SOCKERRNO));
+    return NULL;
+  }
+  return res;
+}
+
+#endif /* !HAVE_GETADDRINFO */
+
+CURLcode Curl_set_dns_servers(struct SessionHandle *data,
+                              char *servers)
+{
+  (void)data;
+  (void)servers;
+  return CURLE_NOT_BUILT_IN;
+
+}
+
+#endif /* CURLRES_THREADED */
diff --git a/lib/curl_axtls.c b/lib/curl_axtls.c
new file mode 100644 (file)
index 0000000..8bd606a
--- /dev/null
@@ -0,0 +1,547 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2010, DirecTV * contact: Eric Hu <ehu@directv.com>
+ * Copyright (C) 2010 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/*
+ * Source file for all axTLS-specific code for the TLS/SSL layer. No code
+ * but curl_sslgen.c should ever call or use these functions.
+ */
+
+#include "curl_setup.h"
+
+#ifdef USE_AXTLS
+#include <axTLS/ssl.h>
+#include "curl_axtls.h"
+
+#include "curl_sendf.h"
+#include "curl_inet_pton.h"
+#include "curl_sslgen.h"
+#include "curl_parsedate.h"
+#include "curl_connect.h" /* for the connect timeout */
+#include "curl_select.h"
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+#include "curl_hostcheck.h"
+
+
+/* SSL_read is opied from axTLS compat layer */
+static int SSL_read(SSL *ssl, void *buf, int num)
+{
+  uint8_t *read_buf;
+  int ret;
+
+  while((ret = ssl_read(ssl, &read_buf)) == SSL_OK);
+
+  if(ret > SSL_OK) {
+    memcpy(buf, read_buf, ret > num ? num : ret);
+  }
+
+  return ret;
+}
+
+/* Global axTLS init, called from Curl_ssl_init() */
+int Curl_axtls_init(void)
+{
+/* axTLS has no global init.  Everything is done through SSL and SSL_CTX
+ * structs stored in connectdata structure.  Perhaps can move to curl_axtls.h.
+ */
+  return 1;
+}
+
+int Curl_axtls_cleanup(void)
+{
+  /* axTLS has no global cleanup.  Perhaps can move this to curl_axtls.h. */
+  return 1;
+}
+
+static CURLcode map_error_to_curl(int axtls_err)
+{
+  switch (axtls_err) {
+  case SSL_ERROR_NOT_SUPPORTED:
+  case SSL_ERROR_INVALID_VERSION:
+  case -70:                       /* protocol version alert from server */
+    return CURLE_UNSUPPORTED_PROTOCOL;
+    break;
+  case SSL_ERROR_NO_CIPHER:
+    return CURLE_SSL_CIPHER;
+    break;
+  case SSL_ERROR_BAD_CERTIFICATE: /* this may be bad server cert too */
+  case SSL_ERROR_NO_CERT_DEFINED:
+  case -42:                       /* bad certificate alert from server */
+  case -43:                       /* unsupported cert alert from server */
+  case -44:                       /* cert revoked alert from server */
+  case -45:                       /* cert expired alert from server */
+  case -46:                       /* cert unknown alert from server */
+    return CURLE_SSL_CERTPROBLEM;
+    break;
+  case SSL_X509_ERROR(X509_NOT_OK):
+  case SSL_X509_ERROR(X509_VFY_ERROR_NO_TRUSTED_CERT):
+  case SSL_X509_ERROR(X509_VFY_ERROR_BAD_SIGNATURE):
+  case SSL_X509_ERROR(X509_VFY_ERROR_NOT_YET_VALID):
+  case SSL_X509_ERROR(X509_VFY_ERROR_EXPIRED):
+  case SSL_X509_ERROR(X509_VFY_ERROR_SELF_SIGNED):
+  case SSL_X509_ERROR(X509_VFY_ERROR_INVALID_CHAIN):
+  case SSL_X509_ERROR(X509_VFY_ERROR_UNSUPPORTED_DIGEST):
+  case SSL_X509_ERROR(X509_INVALID_PRIV_KEY):
+    return CURLE_PEER_FAILED_VERIFICATION;
+    break;
+  case -48:                       /* unknown ca alert from server */
+    return CURLE_SSL_CACERT;
+    break;
+  case -49:                       /* access denied alert from server */
+    return CURLE_REMOTE_ACCESS_DENIED;
+    break;
+  case SSL_ERROR_CONN_LOST:
+  case SSL_ERROR_SOCK_SETUP_FAILURE:
+  case SSL_ERROR_INVALID_HANDSHAKE:
+  case SSL_ERROR_INVALID_PROT_MSG:
+  case SSL_ERROR_INVALID_HMAC:
+  case SSL_ERROR_INVALID_SESSION:
+  case SSL_ERROR_INVALID_KEY:     /* it's too bad this doesn't map better */
+  case SSL_ERROR_FINISHED_INVALID:
+  case SSL_ERROR_NO_CLIENT_RENOG:
+  default:
+    return CURLE_SSL_CONNECT_ERROR;
+    break;
+  }
+}
+
+static Curl_recv axtls_recv;
+static Curl_send axtls_send;
+
+/*
+ * This function is called after the TCP connect has completed. Setup the TLS
+ * layer and do all necessary magic.
+ */
+CURLcode
+Curl_axtls_connect(struct connectdata *conn,
+                  int sockindex)
+
+{
+  struct SessionHandle *data = conn->data;
+  SSL_CTX *ssl_ctx;
+  SSL *ssl;
+  int cert_types[] = {SSL_OBJ_X509_CERT, SSL_OBJ_PKCS12, 0};
+  int key_types[] = {SSL_OBJ_RSA_KEY, SSL_OBJ_PKCS8, SSL_OBJ_PKCS12, 0};
+  int i, ssl_fcn_return;
+  const uint8_t *ssl_sessionid;
+  size_t ssl_idsize;
+  const char *peer_CN;
+  uint32_t dns_altname_index;
+  const char *dns_altname;
+  int8_t found_subject_alt_names = 0;
+  int8_t found_subject_alt_name_matching_conn = 0;
+
+  /* Assuming users will not compile in custom key/cert to axTLS */
+  uint32_t client_option = SSL_NO_DEFAULT_KEY|SSL_SERVER_VERIFY_LATER;
+
+  if(conn->ssl[sockindex].state == ssl_connection_complete)
+    /* to make us tolerant against being called more than once for the
+       same connection */
+    return CURLE_OK;
+
+  /* axTLS only supports TLSv1 */
+  /* check to see if we've been told to use an explicit SSL/TLS version */
+  switch(data->set.ssl.version) {
+  case CURL_SSLVERSION_DEFAULT:
+  case CURL_SSLVERSION_TLSv1:
+    break;
+  default:
+    failf(data, "axTLS only supports TLSv1");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+#ifdef  AXTLSDEBUG
+  client_option |= SSL_DISPLAY_STATES | SSL_DISPLAY_RSA | SSL_DISPLAY_CERTS;
+#endif /* AXTLSDEBUG */
+
+  /* Allocate an SSL_CTX struct */
+  ssl_ctx = ssl_ctx_new(client_option, SSL_DEFAULT_CLNT_SESS);
+  if(ssl_ctx == NULL) {
+    failf(data, "unable to create client SSL context");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  /* Load the trusted CA cert bundle file */
+  if(data->set.ssl.CAfile) {
+    if(ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CACERT, data->set.ssl.CAfile, NULL)
+       != SSL_OK) {
+      infof(data, "error reading ca cert file %s \n",
+            data->set.ssl.CAfile);
+      if(data->set.ssl.verifypeer) {
+        Curl_axtls_close(conn, sockindex);
+        return CURLE_SSL_CACERT_BADFILE;
+      }
+    }
+    else
+      infof(data, "found certificates in %s\n", data->set.ssl.CAfile);
+  }
+
+  /* curl_gtls.c tasks we're skipping for now:
+   * 1) certificate revocation list checking
+   * 2) dns name assignment to host
+   * 3) set protocol priority.  axTLS is TLSv1 only, so can probably ignore
+   * 4) set certificate priority.  axTLS ignores type and sends certs in
+   *  order added.  can probably ignore this.
+   */
+
+  /* Load client certificate */
+  if(data->set.str[STRING_CERT]) {
+    i=0;
+    /* Instead of trying to analyze cert type here, let axTLS try them all. */
+    while(cert_types[i] != 0) {
+      ssl_fcn_return = ssl_obj_load(ssl_ctx, cert_types[i],
+                                    data->set.str[STRING_CERT], NULL);
+      if(ssl_fcn_return == SSL_OK) {
+        infof(data, "successfully read cert file %s \n",
+              data->set.str[STRING_CERT]);
+        break;
+      }
+      i++;
+    }
+    /* Tried all cert types, none worked. */
+    if(cert_types[i] == 0) {
+      failf(data, "%s is not x509 or pkcs12 format",
+            data->set.str[STRING_CERT]);
+      Curl_axtls_close(conn, sockindex);
+      return CURLE_SSL_CERTPROBLEM;
+    }
+  }
+
+  /* Load client key.
+     If a pkcs12 file successfully loaded a cert, then there's nothing to do
+     because the key has already been loaded. */
+  if(data->set.str[STRING_KEY] && cert_types[i] != SSL_OBJ_PKCS12) {
+    i=0;
+    /* Instead of trying to analyze key type here, let axTLS try them all. */
+    while(key_types[i] != 0) {
+      ssl_fcn_return = ssl_obj_load(ssl_ctx, key_types[i],
+                                    data->set.str[STRING_KEY], NULL);
+      if(ssl_fcn_return == SSL_OK) {
+        infof(data, "successfully read key file %s \n",
+              data->set.str[STRING_KEY]);
+        break;
+      }
+      i++;
+    }
+    /* Tried all key types, none worked. */
+    if(key_types[i] == 0) {
+      failf(data, "Failure: %s is not a supported key file",
+            data->set.str[STRING_KEY]);
+      Curl_axtls_close(conn, sockindex);
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+  }
+
+  /* curl_gtls.c does more here that is being left out for now
+   * 1) set session credentials.  can probably ignore since axtls puts this
+   *    info in the ssl_ctx struct
+   * 2) setting up callbacks.  these seem gnutls specific
+   */
+
+  /* In axTLS, handshaking happens inside ssl_client_new. */
+  if(!Curl_ssl_getsessionid(conn, (void **) &ssl_sessionid, &ssl_idsize)) {
+    /* we got a session id, use it! */
+    infof (data, "SSL re-using session ID\n");
+    ssl = ssl_client_new(ssl_ctx, conn->sock[sockindex],
+                         ssl_sessionid, (uint8_t)ssl_idsize);
+  }
+  else
+    ssl = ssl_client_new(ssl_ctx, conn->sock[sockindex], NULL, 0);
+
+  /* Check to make sure handshake was ok. */
+  ssl_fcn_return = ssl_handshake_status(ssl);
+  if(ssl_fcn_return != SSL_OK) {
+    Curl_axtls_close(conn, sockindex);
+    ssl_display_error(ssl_fcn_return); /* goes to stdout. */
+    return map_error_to_curl(ssl_fcn_return);
+  }
+  infof (data, "handshake completed successfully\n");
+
+  /* Here, curl_gtls.c gets the peer certificates and fails out depending on
+   * settings in "data."  axTLS api doesn't have get cert chain fcn, so omit?
+   */
+
+  /* Verify server's certificate */
+  if(data->set.ssl.verifypeer) {
+    if(ssl_verify_cert(ssl) != SSL_OK) {
+      Curl_axtls_close(conn, sockindex);
+      failf(data, "server cert verify failed");
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+  }
+  else
+    infof(data, "\t server certificate verification SKIPPED\n");
+
+  /* Here, curl_gtls.c does issuer verification. axTLS has no straightforward
+   * equivalent, so omitting for now.*/
+
+  /* Here, curl_gtls.c does the following
+   * 1) x509 hostname checking per RFC2818.  axTLS doesn't support this, but
+   *    it seems useful. This is now implemented, by Oscar Koeroo
+   * 2) checks cert validity based on time.  axTLS does this in ssl_verify_cert
+   * 3) displays a bunch of cert information.  axTLS doesn't support most of
+   *    this, but a couple fields are available.
+   */
+
+
+  /* There is no (DNS) Altnames count in the version 1.4.8 API. There is a
+     risk of an inifite loop */
+  for(dns_altname_index = 0; ; dns_altname_index++) {
+    dns_altname = ssl_get_cert_subject_alt_dnsname(ssl, dns_altname_index);
+    if(dns_altname == NULL) {
+      break;
+    }
+    found_subject_alt_names = 1;
+
+    infof(data, "\tComparing subject alt name DNS with hostname: %s <-> %s\n",
+          dns_altname, conn->host.name);
+    if(Curl_cert_hostcheck(dns_altname, conn->host.name)) {
+      found_subject_alt_name_matching_conn = 1;
+      break;
+    }
+  }
+
+  /* RFC2818 checks */
+  if(found_subject_alt_names && !found_subject_alt_name_matching_conn) {
+    /* Break connection ! */
+    Curl_axtls_close(conn, sockindex);
+    failf(data, "\tsubjectAltName(s) do not match %s\n", conn->host.dispname);
+    return CURLE_PEER_FAILED_VERIFICATION;
+  }
+  else if(found_subject_alt_names == 0) {
+    /* Per RFC2818, when no Subject Alt Names were available, examine the peer
+       CN as a legacy fallback */
+    peer_CN = ssl_get_cert_dn(ssl, SSL_X509_CERT_COMMON_NAME);
+    if(peer_CN == NULL) {
+      /* Similar behaviour to the OpenSSL interface */
+      Curl_axtls_close(conn, sockindex);
+      failf(data, "unable to obtain common name from peer certificate");
+      return CURLE_PEER_FAILED_VERIFICATION;
+    }
+    else {
+      if(!Curl_cert_hostcheck((const char *)peer_CN, conn->host.name)) {
+        if(data->set.ssl.verifyhost) {
+          /* Break connection ! */
+          Curl_axtls_close(conn, sockindex);
+          failf(data, "\tcommon name \"%s\" does not match \"%s\"\n",
+                peer_CN, conn->host.dispname);
+          return CURLE_PEER_FAILED_VERIFICATION;
+        }
+        else
+          infof(data, "\tcommon name \"%s\" does not match \"%s\"\n",
+                peer_CN, conn->host.dispname);
+      }
+    }
+  }
+
+  /* General housekeeping */
+  conn->ssl[sockindex].state = ssl_connection_complete;
+  conn->ssl[sockindex].ssl = ssl;
+  conn->ssl[sockindex].ssl_ctx = ssl_ctx;
+  conn->recv[sockindex] = axtls_recv;
+  conn->send[sockindex] = axtls_send;
+
+  /* Put our freshly minted SSL session in cache */
+  ssl_idsize = ssl_get_session_id_size(ssl);
+  ssl_sessionid = ssl_get_session_id(ssl);
+  if(Curl_ssl_addsessionid(conn, (void *) ssl_sessionid, ssl_idsize)
+     != CURLE_OK)
+    infof (data, "failed to add session to cache\n");
+
+  return CURLE_OK;
+}
+
+
+/* return number of sent (non-SSL) bytes */
+static ssize_t axtls_send(struct connectdata *conn,
+                          int sockindex,
+                          const void *mem,
+                          size_t len,
+                          CURLcode *err)
+{
+  /* ssl_write() returns 'int' while write() and send() returns 'size_t' */
+  int rc = ssl_write(conn->ssl[sockindex].ssl, mem, (int)len);
+
+  infof(conn->data, "  axtls_send\n");
+
+  if(rc < 0 ) {
+    *err = map_error_to_curl(rc);
+    rc = -1; /* generic error code for send failure */
+  }
+
+  *err = CURLE_OK;
+  return rc;
+}
+
+void Curl_axtls_close_all(struct SessionHandle *data)
+{
+  (void)data;
+  infof(data, "  Curl_axtls_close_all\n");
+}
+
+void Curl_axtls_close(struct connectdata *conn, int sockindex)
+{
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+
+  infof(conn->data, "  Curl_axtls_close\n");
+  if(connssl->ssl) {
+    /* line from curl_ssluse.c: (void)SSL_shutdown(connssl->ssl);
+       axTLS compat layer does nothing for SSL_shutdown */
+
+    /* The following line is from curl_ssluse.c.  There seems to be no axTLS
+       equivalent.  ssl_free and ssl_ctx_free close things.
+       SSL_set_connect_state(connssl->handle); */
+
+    ssl_free (connssl->ssl);
+    connssl->ssl = NULL;
+  }
+  if(connssl->ssl_ctx) {
+    ssl_ctx_free (connssl->ssl_ctx);
+    connssl->ssl_ctx = NULL;
+  }
+}
+
+/*
+ * This function is called to shut down the SSL layer but keep the
+ * socket open (CCC - Clear Command Channel)
+ */
+int Curl_axtls_shutdown(struct connectdata *conn, int sockindex)
+{
+  /* Outline taken from curl_ssluse.c since functions are in axTLS compat
+     layer.  axTLS's error set is much smaller, so a lot of error-handling
+     was removed.
+   */
+  int retval = 0;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  struct SessionHandle *data = conn->data;
+  char buf[120]; /* We will use this for the OpenSSL error buffer, so it has
+                    to be at least 120 bytes long. */
+  ssize_t nread;
+
+  infof(conn->data, "  Curl_axtls_shutdown\n");
+
+  /* This has only been tested on the proftpd server, and the mod_tls code
+     sends a close notify alert without waiting for a close notify alert in
+     response. Thus we wait for a close notify alert from the server, but
+     we do not send one. Let's hope other servers do the same... */
+
+  /* axTLS compat layer does nothing for SSL_shutdown, so we do nothing too
+  if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE)
+      (void)SSL_shutdown(connssl->ssl);
+  */
+
+  if(connssl->ssl) {
+    int what = Curl_socket_ready(conn->sock[sockindex],
+                                 CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT);
+    if(what > 0) {
+      /* Something to read, let's do it and hope that it is the close
+         notify alert from the server */
+      nread = (ssize_t)SSL_read(conn->ssl[sockindex].ssl, buf,
+                                sizeof(buf));
+
+      if(nread < SSL_OK) {
+        failf(data, "close notify alert not received during shutdown");
+        retval = -1;
+      }
+    }
+    else if(0 == what) {
+      /* timeout */
+      failf(data, "SSL shutdown timeout");
+    }
+    else {
+      /* anything that gets here is fatally bad */
+      failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+      retval = -1;
+    }
+
+    ssl_free (connssl->ssl);
+    connssl->ssl = NULL;
+  }
+  return retval;
+}
+
+static ssize_t axtls_recv(struct connectdata *conn, /* connection data */
+                          int num,                  /* socketindex */
+                          char *buf,                /* store read data here */
+                          size_t buffersize,        /* max amount to read */
+                          CURLcode *err)
+{
+  struct ssl_connect_data *connssl = &conn->ssl[num];
+  ssize_t ret = 0;
+
+  infof(conn->data, "  axtls_recv\n");
+
+  if(connssl) {
+    ret = (ssize_t)SSL_read(conn->ssl[num].ssl, buf, (int)buffersize);
+
+    /* axTLS isn't terribly generous about error reporting */
+    /* With patched axTLS, SSL_CLOSE_NOTIFY=-3.  Hard-coding until axTLS
+       team approves proposed fix. */
+    if(ret == -3 ) {
+      Curl_axtls_close(conn, num);
+    }
+    else if(ret < 0) {
+      failf(conn->data, "axTLS recv error (%d)", (int)ret);
+      *err = map_error_to_curl(ret);
+      return -1;
+    }
+  }
+
+  *err = CURLE_OK;
+  return ret;
+}
+
+/*
+ * Return codes:
+ *     1 means the connection is still in place
+ *     0 means the connection has been closed
+ *    -1 means the connection status is unknown
+ */
+int Curl_axtls_check_cxn(struct connectdata *conn)
+{
+  /* curl_ssluse.c line:
+     rc = SSL_peek(conn->ssl[FIRSTSOCKET].ssl, (void*)&buf, 1);
+     axTLS compat layer always returns the last argument, so connection is
+     always alive? */
+
+  infof(conn->data, "  Curl_axtls_check_cxn\n");
+   return 1; /* connection still in place */
+}
+
+void Curl_axtls_session_free(void *ptr)
+{
+  (void)ptr;
+  /* free the ID */
+  /* both curl_ssluse.c and curl_gtls.c do something here, but axTLS's
+     OpenSSL compatibility layer does nothing, so we do nothing too. */
+}
+
+size_t Curl_axtls_version(char *buffer, size_t size)
+{
+  return snprintf(buffer, size, "axTLS/%s", ssl_version());
+}
+
+#endif /* USE_AXTLS */
diff --git a/lib/curl_base64.c b/lib/curl_base64.c
new file mode 100644 (file)
index 0000000..45c7a95
--- /dev/null
@@ -0,0 +1,248 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/* Base64 encoding/decoding */
+
+#include "curl_setup.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_urldata.h" /* for the SessionHandle definition */
+#include "curl_warnless.h"
+#include "curl_base64.h"
+#include "curl_memory.h"
+#include "curl_non_ascii.h"
+
+/* include curl_memdebug.h last */
+#include "curl_memdebug.h"
+
+/* ---- Base64 Encoding/Decoding Table --- */
+static const char table64[]=
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static void decodeQuantum(unsigned char *dest, const char *src)
+{
+  const char *s, *p;
+  unsigned long i, v, x = 0;
+
+  for(i = 0, s = src; i < 4; i++, s++) {
+    v = 0;
+    p = table64;
+    while(*p && (*p != *s)) {
+      v++;
+      p++;
+    }
+    if(*p == *s)
+      x = (x << 6) + v;
+    else if(*s == '=')
+      x = (x << 6);
+  }
+
+  dest[2] = curlx_ultouc(x & 0xFFUL);
+  x >>= 8;
+  dest[1] = curlx_ultouc(x & 0xFFUL);
+  x >>= 8;
+  dest[0] = curlx_ultouc(x & 0xFFUL);
+}
+
+/*
+ * Curl_base64_decode()
+ *
+ * Given a base64 NUL-terminated string at src, decode it and return a
+ * pointer in *outptr to a newly allocated memory area holding decoded
+ * data. Size of decoded data is returned in variable pointed by outlen.
+ *
+ * Returns CURLE_OK on success, otherwise specific error code. Function
+ * output shall not be considered valid unless CURLE_OK is returned.
+ *
+ * When decoded data length is 0, returns NULL in *outptr.
+ *
+ * @unittest: 1302
+ */
+CURLcode Curl_base64_decode(const char *src,
+                            unsigned char **outptr, size_t *outlen)
+{
+  size_t length = 0;
+  size_t equalsTerm = 0;
+  size_t i;
+  size_t numQuantums;
+  unsigned char lastQuantum[3];
+  size_t rawlen = 0;
+  unsigned char *newstr;
+
+  *outptr = NULL;
+  *outlen = 0;
+
+  while((src[length] != '=') && src[length])
+    length++;
+  /* A maximum of two = padding characters is allowed */
+  if(src[length] == '=') {
+    equalsTerm++;
+    if(src[length+equalsTerm] == '=')
+      equalsTerm++;
+  }
+  numQuantums = (length + equalsTerm) / 4;
+
+  /* Don't allocate a buffer if the decoded length is 0 */
+  if(numQuantums == 0)
+    return CURLE_OK;
+
+  rawlen = (numQuantums * 3) - equalsTerm;
+
+  /* The buffer must be large enough to make room for the last quantum
+  (which may be partially thrown out) and the zero terminator. */
+  newstr = malloc(rawlen+4);
+  if(!newstr)
+    return CURLE_OUT_OF_MEMORY;
+
+  *outptr = newstr;
+
+  /* Decode all but the last quantum (which may not decode to a
+  multiple of 3 bytes) */
+  for(i = 0; i < numQuantums - 1; i++) {
+    decodeQuantum(newstr, src);
+    newstr += 3; src += 4;
+  }
+
+  /* This final decode may actually read slightly past the end of the buffer
+  if the input string is missing pad bytes.  This will almost always be
+  harmless. */
+  decodeQuantum(lastQuantum, src);
+  for(i = 0; i < 3 - equalsTerm; i++)
+    newstr[i] = lastQuantum[i];
+
+  newstr[i] = '\0'; /* zero terminate */
+
+  *outlen = rawlen; /* return size of decoded data */
+
+  return CURLE_OK;
+}
+
+/*
+ * Curl_base64_encode()
+ *
+ * Given a pointer to an input buffer and an input size, encode it and
+ * return a pointer in *outptr to a newly allocated memory area holding
+ * encoded data. Size of encoded data is returned in variable pointed by
+ * outlen.
+ *
+ * Input length of 0 indicates input buffer holds a NUL-terminated string.
+ *
+ * Returns CURLE_OK on success, otherwise specific error code. Function
+ * output shall not be considered valid unless CURLE_OK is returned.
+ *
+ * When encoded data length is 0, returns NULL in *outptr.
+ *
+ * @unittest: 1302
+ */
+CURLcode Curl_base64_encode(struct SessionHandle *data,
+                            const char *inputbuff, size_t insize,
+                            char **outptr, size_t *outlen)
+{
+  CURLcode error;
+  unsigned char ibuf[3];
+  unsigned char obuf[4];
+  int i;
+  int inputparts;
+  char *output;
+  char *base64data;
+  char *convbuf = NULL;
+
+  const char *indata = inputbuff;
+
+  *outptr = NULL;
+  *outlen = 0;
+
+  if(0 == insize)
+    insize = strlen(indata);
+
+  base64data = output = malloc(insize*4/3+4);
+  if(NULL == output)
+    return CURLE_OUT_OF_MEMORY;
+
+  /*
+   * The base64 data needs to be created using the network encoding
+   * not the host encoding.  And we can't change the actual input
+   * so we copy it to a buffer, translate it, and use that instead.
+   */
+  error = Curl_convert_clone(data, indata, insize, &convbuf);
+  if(error) {
+    free(output);
+    return error;
+  }
+
+  if(convbuf)
+    indata = (char *)convbuf;
+
+  while(insize > 0) {
+    for(i = inputparts = 0; i < 3; i++) {
+      if(insize > 0) {
+        inputparts++;
+        ibuf[i] = (unsigned char) *indata;
+        indata++;
+        insize--;
+      }
+      else
+        ibuf[i] = 0;
+    }
+
+    obuf[0] = (unsigned char)  ((ibuf[0] & 0xFC) >> 2);
+    obuf[1] = (unsigned char) (((ibuf[0] & 0x03) << 4) | \
+                               ((ibuf[1] & 0xF0) >> 4));
+    obuf[2] = (unsigned char) (((ibuf[1] & 0x0F) << 2) | \
+                               ((ibuf[2] & 0xC0) >> 6));
+    obuf[3] = (unsigned char)   (ibuf[2] & 0x3F);
+
+    switch(inputparts) {
+    case 1: /* only one byte read */
+      snprintf(output, 5, "%c%c==",
+               table64[obuf[0]],
+               table64[obuf[1]]);
+      break;
+    case 2: /* two bytes read */
+      snprintf(output, 5, "%c%c%c=",
+               table64[obuf[0]],
+               table64[obuf[1]],
+               table64[obuf[2]]);
+      break;
+    default:
+      snprintf(output, 5, "%c%c%c%c",
+               table64[obuf[0]],
+               table64[obuf[1]],
+               table64[obuf[2]],
+               table64[obuf[3]] );
+      break;
+    }
+    output += 4;
+  }
+  *output = '\0';
+  *outptr = base64data; /* return pointer to new data, allocated memory */
+
+  if(convbuf)
+    free(convbuf);
+
+  *outlen = strlen(base64data); /* return the length of the new data */
+
+  return CURLE_OK;
+}
+/* ---- End of Base64 Encoding ---- */
diff --git a/lib/curl_bundles.c b/lib/curl_bundles.c
new file mode 100644 (file)
index 0000000..efbaeee
--- /dev/null
@@ -0,0 +1,110 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012, Linus Nielsen Feltzing, <linus@haxx.se>
+ * Copyright (C) 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+#include "curl_urldata.h"
+#include "curl_url.h"
+#include "curl_progress.h"
+#include "curl_multiif.h"
+#include "curl_bundles.h"
+#include "curl_sendf.h"
+#include "curl_rawstr.h"
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+static void conn_llist_dtor(void *user, void *element)
+{
+  struct connectdata *data = element;
+  (void)user;
+
+  data->bundle = NULL;
+}
+
+CURLcode Curl_bundle_create(struct SessionHandle *data,
+                            struct connectbundle **cb_ptr)
+{
+  (void)data;
+  DEBUGASSERT(*cb_ptr == NULL);
+  *cb_ptr = malloc(sizeof(struct connectbundle));
+  if(!*cb_ptr)
+    return CURLE_OUT_OF_MEMORY;
+
+  (*cb_ptr)->num_connections = 0;
+  (*cb_ptr)->server_supports_pipelining = FALSE;
+
+  (*cb_ptr)->conn_list = Curl_llist_alloc((curl_llist_dtor) conn_llist_dtor);
+  if(!(*cb_ptr)->conn_list) {
+    Curl_safefree(*cb_ptr);
+    return CURLE_OUT_OF_MEMORY;
+  }
+  return CURLE_OK;
+}
+
+void Curl_bundle_destroy(struct connectbundle *cb_ptr)
+{
+  if(!cb_ptr)
+    return;
+
+  if(cb_ptr->conn_list) {
+    Curl_llist_destroy(cb_ptr->conn_list, NULL);
+    cb_ptr->conn_list = NULL;
+  }
+  Curl_safefree(cb_ptr);
+}
+
+/* Add a connection to a bundle */
+CURLcode Curl_bundle_add_conn(struct connectbundle *cb_ptr,
+                              struct connectdata *conn)
+{
+  if(!Curl_llist_insert_next(cb_ptr->conn_list, cb_ptr->conn_list->tail, conn))
+    return CURLE_OUT_OF_MEMORY;
+
+  conn->bundle = cb_ptr;
+
+  cb_ptr->num_connections++;
+  return CURLE_OK;
+}
+
+/* Remove a connection from a bundle */
+int Curl_bundle_remove_conn(struct connectbundle *cb_ptr,
+                            struct connectdata *conn)
+{
+  struct curl_llist_element *curr;
+
+  curr = cb_ptr->conn_list->head;
+  while(curr) {
+    if(curr->ptr == conn) {
+      Curl_llist_remove(cb_ptr->conn_list, curr, NULL);
+      cb_ptr->num_connections--;
+      conn->bundle = NULL;
+      return 1; /* we removed a handle */
+    }
+    curr = curr->next;
+  }
+  return 0;
+}
diff --git a/lib/curl_conncache.c b/lib/curl_conncache.c
new file mode 100644 (file)
index 0000000..bc95e07
--- /dev/null
@@ -0,0 +1,285 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012, Linus Nielsen Feltzing, <linus@haxx.se>
+ * Copyright (C) 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+#include "curl_urldata.h"
+#include "curl_url.h"
+#include "curl_progress.h"
+#include "curl_multiif.h"
+#include "curl_sendf.h"
+#include "curl_rawstr.h"
+#include "curl_bundles.h"
+#include "curl_conncache.h"
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+#define CONNECTION_HASH_SIZE 97
+
+static void free_bundle_hash_entry(void *freethis)
+{
+  struct connectbundle *b = (struct connectbundle *) freethis;
+
+  Curl_bundle_destroy(b);
+}
+
+struct conncache *Curl_conncache_init(conncachetype type)
+{
+  struct conncache *connc;
+
+  connc = calloc(1, sizeof(struct conncache));
+  if(!connc)
+    return NULL;
+
+  connc->hash = Curl_hash_alloc(CONNECTION_HASH_SIZE, Curl_hash_str,
+                                Curl_str_key_compare, free_bundle_hash_entry);
+
+  if(!connc->hash) {
+    free(connc);
+    return NULL;
+  }
+
+  connc->type = type;
+  connc->num_connections = 0;
+
+  return connc;
+}
+
+void Curl_conncache_destroy(struct conncache *connc)
+{
+  if(connc) {
+    Curl_hash_destroy(connc->hash);
+    connc->hash = NULL;
+    free(connc);
+  }
+}
+
+struct connectbundle *Curl_conncache_find_bundle(struct conncache *connc,
+                                                 char *hostname)
+{
+  struct connectbundle *bundle = NULL;
+
+  if(connc)
+    bundle = Curl_hash_pick(connc->hash, hostname, strlen(hostname)+1);
+
+  return bundle;
+}
+
+static bool conncache_add_bundle(struct conncache *connc,
+                                 char *hostname,
+                                 struct connectbundle *bundle)
+{
+  void *p;
+
+  p = Curl_hash_add(connc->hash, hostname, strlen(hostname)+1, bundle);
+
+  return p?TRUE:FALSE;
+}
+
+static void conncache_remove_bundle(struct conncache *connc,
+                                    struct connectbundle *bundle)
+{
+  struct curl_hash_iterator iter;
+  struct curl_hash_element *he;
+
+  if(!connc)
+    return;
+
+  Curl_hash_start_iterate(connc->hash, &iter);
+
+  he = Curl_hash_next_element(&iter);
+  while(he) {
+    if(he->ptr == bundle) {
+      /* The bundle is destroyed by the hash destructor function,
+         free_bundle_hash_entry() */
+      Curl_hash_delete(connc->hash, he->key, he->key_len);
+      return;
+    }
+
+    he = Curl_hash_next_element(&iter);
+  }
+}
+
+CURLcode Curl_conncache_add_conn(struct conncache *connc,
+                                 struct connectdata *conn)
+{
+  CURLcode result;
+  struct connectbundle *bundle;
+  struct connectbundle *new_bundle = NULL;
+  struct SessionHandle *data = conn->data;
+
+  bundle = Curl_conncache_find_bundle(data->state.conn_cache,
+                                      conn->host.name);
+  if(!bundle) {
+    result = Curl_bundle_create(data, &new_bundle);
+    if(result != CURLE_OK)
+      return result;
+
+    if(!conncache_add_bundle(data->state.conn_cache,
+                             conn->host.name, new_bundle)) {
+      Curl_bundle_destroy(new_bundle);
+      return CURLE_OUT_OF_MEMORY;
+    }
+    bundle = new_bundle;
+  }
+
+  result = Curl_bundle_add_conn(bundle, conn);
+  if(result != CURLE_OK) {
+    if(new_bundle)
+      conncache_remove_bundle(data->state.conn_cache, new_bundle);
+    return result;
+  }
+
+  connc->num_connections++;
+
+  return CURLE_OK;
+}
+
+void Curl_conncache_remove_conn(struct conncache *connc,
+                                struct connectdata *conn)
+{
+  struct connectbundle *bundle = conn->bundle;
+
+  /* The bundle pointer can be NULL, since this function can be called
+     due to a failed connection attempt, before being added to a bundle */
+  if(bundle) {
+    Curl_bundle_remove_conn(bundle, conn);
+    if(bundle->num_connections == 0) {
+      conncache_remove_bundle(connc, bundle);
+    }
+    connc->num_connections--;
+
+    DEBUGF(infof(conn->data, "The cache now contains %d members\n",
+                 connc->num_connections));
+  }
+}
+
+/* This function iterates the entire connection cache and calls the
+   function func() with the connection pointer as the first argument
+   and the supplied 'param' argument as the other,
+
+   Return 0 from func() to continue the loop, return 1 to abort it.
+ */
+void Curl_conncache_foreach(struct conncache *connc,
+                            void *param,
+                            int (*func)(struct connectdata *conn, void *param))
+{
+  struct curl_hash_iterator iter;
+  struct curl_llist_element *curr;
+  struct curl_hash_element *he;
+
+  if(!connc)
+    return;
+
+  Curl_hash_start_iterate(connc->hash, &iter);
+
+  he = Curl_hash_next_element(&iter);
+  while(he) {
+    struct connectbundle *bundle;
+    struct connectdata *conn;
+
+    bundle = he->ptr;
+
+    curr = bundle->conn_list->head;
+    while(curr) {
+      /* Yes, we need to update curr before calling func(), because func()
+         might decide to remove the connection */
+      conn = curr->ptr;
+      curr = curr->next;
+
+      if(1 == func(conn, param))
+        return;
+    }
+
+    he = Curl_hash_next_element(&iter);
+  }
+}
+
+/* Return the first connection found in the cache. Used when closing all
+   connections */
+struct connectdata *
+Curl_conncache_find_first_connection(struct conncache *connc)
+{
+  struct curl_hash_iterator iter;
+  struct curl_llist_element *curr;
+  struct curl_hash_element *he;
+  struct connectbundle *bundle;
+
+  Curl_hash_start_iterate(connc->hash, &iter);
+
+  he = Curl_hash_next_element(&iter);
+  while(he) {
+    bundle = he->ptr;
+
+    curr = bundle->conn_list->head;
+    if(curr) {
+      return curr->ptr;
+    }
+
+    he = Curl_hash_next_element(&iter);
+  }
+
+  return NULL;
+}
+
+
+#if 0
+/* Useful for debugging the connection cache */
+void Curl_conncache_print(struct conncache *connc)
+{
+  struct curl_hash_iterator iter;
+  struct curl_llist_element *curr;
+  struct curl_hash_element *he;
+
+  if(!connc)
+    return;
+
+  fprintf(stderr, "=Bundle cache=\n");
+
+  Curl_hash_start_iterate(connc->hash, &iter);
+
+  he = Curl_hash_next_element(&iter);
+  while(he) {
+    struct connectbundle *bundle;
+    struct connectdata *conn;
+
+    bundle = he->ptr;
+
+    fprintf(stderr, "%s -", he->key);
+    curr = bundle->conn_list->head;
+    while(curr) {
+      conn = curr->ptr;
+
+      fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
+      curr = curr->next;
+    }
+    fprintf(stderr, "\n");
+
+    he = Curl_hash_next_element(&iter);
+  }
+}
+#endif
diff --git a/lib/curl_connect.c b/lib/curl_connect.c
new file mode 100644 (file)
index 0000000..85226d8
--- /dev/null
@@ -0,0 +1,1248 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h> /* <netinet/tcp.h> may need it */
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h> /* for sockaddr_un */
+#endif
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h> /* for TCP_NODELAY */
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#if (defined(HAVE_IOCTL_FIONBIO) && defined(NETWARE))
+#include <sys/filio.h>
+#endif
+#ifdef NETWARE
+#undef in_addr_t
+#define in_addr_t unsigned long
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_if2ip.h"
+#include "curl_strerror.h"
+#include "curl_connect.h"
+#include "curl_memory.h"
+#include "curl_select.h"
+#include "curl_url.h"
+#include "curl_multiif.h"
+#include "curl_sockaddr.h" /* required for Curl_sockaddr_storage */
+#include "curl_inet_ntop.h"
+#include "curl_inet_pton.h"
+#include "curl_sslgen.h" /* for Curl_ssl_check_cxn() */
+#include "curl_progress.h"
+#include "curl_warnless.h"
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+#ifdef __SYMBIAN32__
+/* This isn't actually supported under Symbian OS */
+#undef SO_NOSIGPIPE
+#endif
+
+static bool verifyconnect(curl_socket_t sockfd, int *error);
+
+#ifdef __DragonFly__
+/* DragonFlyBSD uses millisecond as KEEPIDLE and KEEPINTVL units */
+#define KEEPALIVE_FACTOR(x) (x *= 1000)
+#else
+#define KEEPALIVE_FACTOR(x)
+#endif
+
+static void
+tcpkeepalive(struct SessionHandle *data,
+             curl_socket_t sockfd)
+{
+  int optval = data->set.tcp_keepalive?1:0;
+
+  /* only set IDLE and INTVL if setting KEEPALIVE is successful */
+  if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
+        (void *)&optval, sizeof(optval)) < 0) {
+    infof(data, "Failed to set SO_KEEPALIVE on fd %d\n", sockfd);
+  }
+  else {
+#ifdef TCP_KEEPIDLE
+    optval = curlx_sltosi(data->set.tcp_keepidle);
+    KEEPALIVE_FACTOR(optval);
+    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
+          (void *)&optval, sizeof(optval)) < 0) {
+      infof(data, "Failed to set TCP_KEEPIDLE on fd %d\n", sockfd);
+    }
+#endif
+#ifdef TCP_KEEPINTVL
+    optval = curlx_sltosi(data->set.tcp_keepintvl);
+    KEEPALIVE_FACTOR(optval);
+    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
+          (void *)&optval, sizeof(optval)) < 0) {
+      infof(data, "Failed to set TCP_KEEPINTVL on fd %d\n", sockfd);
+    }
+#endif
+  }
+}
+
+static CURLcode
+singleipconnect(struct connectdata *conn,
+                const Curl_addrinfo *ai, /* start connecting to this */
+                long timeout_ms,
+                curl_socket_t *sock,
+                bool *connected);
+
+/*
+ * Curl_timeleft() returns the amount of milliseconds left allowed for the
+ * transfer/connection. If the value is negative, the timeout time has already
+ * elapsed.
+ *
+ * The start time is stored in progress.t_startsingle - as set with
+ * Curl_pgrsTime(..., TIMER_STARTSINGLE);
+ *
+ * If 'nowp' is non-NULL, it points to the current time.
+ * 'duringconnect' is FALSE if not during a connect, as then of course the
+ * connect timeout is not taken into account!
+ *
+ * @unittest: 1303
+ */
+long Curl_timeleft(struct SessionHandle *data,
+                   struct timeval *nowp,
+                   bool duringconnect)
+{
+  int timeout_set = 0;
+  long timeout_ms = duringconnect?DEFAULT_CONNECT_TIMEOUT:0;
+  struct timeval now;
+
+  /* if a timeout is set, use the most restrictive one */
+
+  if(data->set.timeout > 0)
+    timeout_set |= 1;
+  if(duringconnect && (data->set.connecttimeout > 0))
+    timeout_set |= 2;
+
+  switch (timeout_set) {
+  case 1:
+    timeout_ms = data->set.timeout;
+    break;
+  case 2:
+    timeout_ms = data->set.connecttimeout;
+    break;
+  case 3:
+    if(data->set.timeout < data->set.connecttimeout)
+      timeout_ms = data->set.timeout;
+    else
+      timeout_ms = data->set.connecttimeout;
+    break;
+  default:
+    /* use the default */
+    if(!duringconnect)
+      /* if we're not during connect, there's no default timeout so if we're
+         at zero we better just return zero and not make it a negative number
+         by the math below */
+      return 0;
+    break;
+  }
+
+  if(!nowp) {
+    now = Curl_tvnow();
+    nowp = &now;
+  }
+
+  /* subtract elapsed time */
+  timeout_ms -= Curl_tvdiff(*nowp, data->progress.t_startsingle);
+  if(!timeout_ms)
+    /* avoid returning 0 as that means no timeout! */
+    return -1;
+
+  return timeout_ms;
+}
+
+/*
+ * waitconnect() waits for a TCP connect on the given socket for the specified
+ * number if milliseconds. It returns:
+ */
+
+#define WAITCONN_CONNECTED     0
+#define WAITCONN_SELECT_ERROR -1
+#define WAITCONN_TIMEOUT       1
+#define WAITCONN_FDSET_ERROR   2
+#define WAITCONN_ABORTED       3
+
+static
+int waitconnect(struct connectdata *conn,
+                curl_socket_t sockfd, /* socket */
+                long timeout_msec)
+{
+  int rc;
+#ifdef mpeix
+  /* Call this function once now, and ignore the results. We do this to
+     "clear" the error state on the socket so that we can later read it
+     reliably. This is reported necessary on the MPE/iX operating system. */
+  (void)verifyconnect(sockfd, NULL);
+#endif
+
+  for(;;) {
+
+    /* now select() until we get connect or timeout */
+    rc = Curl_socket_ready(CURL_SOCKET_BAD, sockfd, timeout_msec>1000?
+                           1000:timeout_msec);
+    if(Curl_pgrsUpdate(conn))
+      return WAITCONN_ABORTED;
+
+    if(-1 == rc)
+      /* error, no connect here, try next */
+      return WAITCONN_SELECT_ERROR;
+
+    else if(0 == rc) {
+      /* timeout */
+      timeout_msec -= 1000;
+      if(timeout_msec <= 0)
+        return WAITCONN_TIMEOUT;
+
+      continue;
+    }
+
+    if(rc & CURL_CSELECT_ERR)
+      /* error condition caught */
+      return WAITCONN_FDSET_ERROR;
+
+    break;
+  }
+  return WAITCONN_CONNECTED;
+}
+
+static CURLcode bindlocal(struct connectdata *conn,
+                          curl_socket_t sockfd, int af)
+{
+  struct SessionHandle *data = conn->data;
+
+  struct Curl_sockaddr_storage sa;
+  struct sockaddr *sock = (struct sockaddr *)&sa;  /* bind to this address */
+  curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */
+  struct sockaddr_in *si4 = (struct sockaddr_in *)&sa;
+#ifdef ENABLE_IPV6
+  struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
+#endif
+
+  struct Curl_dns_entry *h=NULL;
+  unsigned short port = data->set.localport; /* use this port number, 0 for
+                                                "random" */
+  /* how many port numbers to try to bind to, increasing one at a time */
+  int portnum = data->set.localportrange;
+  const char *dev = data->set.str[STRING_DEVICE];
+  int error;
+  char myhost[256] = "";
+  int done = 0; /* -1 for error, 1 for address found */
+  bool is_interface = FALSE;
+  bool is_host = FALSE;
+  static const char *if_prefix = "if!";
+  static const char *host_prefix = "host!";
+
+  /*************************************************************
+   * Select device to bind socket to
+   *************************************************************/
+  if(!dev && !port)
+    /* no local kind of binding was requested */
+    return CURLE_OK;
+
+  memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
+
+  if(dev && (strlen(dev)<255) ) {
+    if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) {
+      dev += strlen(if_prefix);
+      is_interface = TRUE;
+    }
+    else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) {
+      dev += strlen(host_prefix);
+      is_host = TRUE;
+    }
+
+    /* interface */
+    if(!is_host && (is_interface || Curl_if_is_interface_name(dev))) {
+      if(Curl_if2ip(af, dev, myhost, sizeof(myhost)) == NULL)
+        return CURLE_INTERFACE_FAILED;
+
+      /*
+       * We now have the numerical IP address in the 'myhost' buffer
+       */
+      infof(data, "Local Interface %s is ip %s using address family %i\n",
+            dev, myhost, af);
+      done = 1;
+
+#ifdef SO_BINDTODEVICE
+      /* I am not sure any other OSs than Linux that provide this feature, and
+       * at the least I cannot test. --Ben
+       *
+       * This feature allows one to tightly bind the local socket to a
+       * particular interface.  This will force even requests to other local
+       * interfaces to go out the external interface.
+       *
+       *
+       * Only bind to the interface when specified as interface, not just as a
+       * hostname or ip address.
+       */
+      if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
+                    dev, (curl_socklen_t)strlen(dev)+1) != 0) {
+        error = SOCKERRNO;
+        infof(data, "SO_BINDTODEVICE %s failed with errno %d: %s;"
+              " will do regular bind\n",
+              dev, error, Curl_strerror(conn, error));
+        /* This is typically "errno 1, error: Operation not permitted" if
+           you're not running as root or another suitable privileged user */
+      }
+#endif
+    }
+    else {
+      /*
+       * This was not an interface, resolve the name as a host name
+       * or IP number
+       *
+       * Temporarily force name resolution to use only the address type
+       * of the connection. The resolve functions should really be changed
+       * to take a type parameter instead.
+       */
+      long ipver = conn->ip_version;
+      int rc;
+
+      if(af == AF_INET)
+        conn->ip_version = CURL_IPRESOLVE_V4;
+#ifdef ENABLE_IPV6
+      else if(af == AF_INET6)
+        conn->ip_version = CURL_IPRESOLVE_V6;
+#endif
+
+      rc = Curl_resolv(conn, dev, 0, &h);
+      if(rc == CURLRESOLV_PENDING)
+        (void)Curl_resolver_wait_resolv(conn, &h);
+      conn->ip_version = ipver;
+
+      if(h) {
+        /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
+        Curl_printable_address(h->addr, myhost, sizeof(myhost));
+        infof(data, "Name '%s' family %i resolved to '%s' family %i\n",
+              dev, af, myhost, h->addr->ai_family);
+        Curl_resolv_unlock(data, h);
+        done = 1;
+      }
+      else {
+        /*
+         * provided dev was no interface (or interfaces are not supported
+         * e.g. solaris) no ip address and no domain we fail here
+         */
+        done = -1;
+      }
+    }
+
+    if(done > 0) {
+#ifdef ENABLE_IPV6
+      /* ipv6 address */
+      if((af == AF_INET6) &&
+         (Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0)) {
+        si6->sin6_family = AF_INET6;
+        si6->sin6_port = htons(port);
+        sizeof_sa = sizeof(struct sockaddr_in6);
+      }
+      else
+#endif
+      /* ipv4 address */
+      if((af == AF_INET) &&
+         (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) {
+        si4->sin_family = AF_INET;
+        si4->sin_port = htons(port);
+        sizeof_sa = sizeof(struct sockaddr_in);
+      }
+    }
+
+    if(done < 1) {
+      failf(data, "Couldn't bind to '%s'", dev);
+      return CURLE_INTERFACE_FAILED;
+    }
+  }
+  else {
+    /* no device was given, prepare sa to match af's needs */
+#ifdef ENABLE_IPV6
+    if(af == AF_INET6) {
+      si6->sin6_family = AF_INET6;
+      si6->sin6_port = htons(port);
+      sizeof_sa = sizeof(struct sockaddr_in6);
+    }
+    else
+#endif
+    if(af == AF_INET) {
+      si4->sin_family = AF_INET;
+      si4->sin_port = htons(port);
+      sizeof_sa = sizeof(struct sockaddr_in);
+    }
+  }
+
+  for(;;) {
+    if(bind(sockfd, sock, sizeof_sa) >= 0) {
+      /* we succeeded to bind */
+      struct Curl_sockaddr_storage add;
+      curl_socklen_t size = sizeof(add);
+      memset(&add, 0, sizeof(struct Curl_sockaddr_storage));
+      if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
+        data->state.os_errno = error = SOCKERRNO;
+        failf(data, "getsockname() failed with errno %d: %s",
+              error, Curl_strerror(conn, error));
+        return CURLE_INTERFACE_FAILED;
+      }
+      infof(data, "Local port: %hu\n", port);
+      conn->bits.bound = TRUE;
+      return CURLE_OK;
+    }
+
+    if(--portnum > 0) {
+      infof(data, "Bind to local port %hu failed, trying next\n", port);
+      port++; /* try next port */
+      /* We re-use/clobber the port variable here below */
+      if(sock->sa_family == AF_INET)
+        si4->sin_port = ntohs(port);
+#ifdef ENABLE_IPV6
+      else
+        si6->sin6_port = ntohs(port);
+#endif
+    }
+    else
+      break;
+  }
+
+  data->state.os_errno = error = SOCKERRNO;
+  failf(data, "bind failed with errno %d: %s",
+        error, Curl_strerror(conn, error));
+
+  return CURLE_INTERFACE_FAILED;
+}
+
+/*
+ * verifyconnect() returns TRUE if the connect really has happened.
+ */
+static bool verifyconnect(curl_socket_t sockfd, int *error)
+{
+  bool rc = TRUE;
+#ifdef SO_ERROR
+  int err = 0;
+  curl_socklen_t errSize = sizeof(err);
+
+#ifdef WIN32
+  /*
+   * In October 2003 we effectively nullified this function on Windows due to
+   * problems with it using all CPU in multi-threaded cases.
+   *
+   * In May 2004, we bring it back to offer more info back on connect failures.
+   * Gisle Vanem could reproduce the former problems with this function, but
+   * could avoid them by adding this SleepEx() call below:
+   *
+   *    "I don't have Rational Quantify, but the hint from his post was
+   *    ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe
+   *    just Sleep(0) would be enough?) would release whatever
+   *    mutex/critical-section the ntdll call is waiting on.
+   *
+   *    Someone got to verify this on Win-NT 4.0, 2000."
+   */
+
+#ifdef _WIN32_WCE
+  Sleep(0);
+#else
+  SleepEx(0, FALSE);
+#endif
+
+#endif
+
+  if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
+    err = SOCKERRNO;
+#ifdef _WIN32_WCE
+  /* Old WinCE versions don't support SO_ERROR */
+  if(WSAENOPROTOOPT == err) {
+    SET_SOCKERRNO(0);
+    err = 0;
+  }
+#endif
+#ifdef __minix
+  /* Minix 3.1.x doesn't support getsockopt on UDP sockets */
+  if(EBADIOCTL == err) {
+    SET_SOCKERRNO(0);
+    err = 0;
+  }
+#endif
+  if((0 == err) || (EISCONN == err))
+    /* we are connected, awesome! */
+    rc = TRUE;
+  else
+    /* This wasn't a successful connect */
+    rc = FALSE;
+  if(error)
+    *error = err;
+#else
+  (void)sockfd;
+  if(error)
+    *error = SOCKERRNO;
+#endif
+  return rc;
+}
+
+/* Used within the multi interface. Try next IP address, return TRUE if no
+   more address exists or error */
+static CURLcode trynextip(struct connectdata *conn,
+                          int sockindex,
+                          bool *connected)
+{
+  curl_socket_t sockfd;
+  Curl_addrinfo *ai;
+
+  /* First clean up after the failed socket.
+     Don't close it yet to ensure that the next IP's socket gets a different
+     file descriptor, which can prevent bugs when the curl_multi_socket_action
+     interface is used with certain select() replacements such as kqueue. */
+  curl_socket_t fd_to_close = conn->sock[sockindex];
+  conn->sock[sockindex] = CURL_SOCKET_BAD;
+  *connected = FALSE;
+
+  if(sockindex != FIRSTSOCKET) {
+    Curl_closesocket(conn, fd_to_close);
+    return CURLE_COULDNT_CONNECT; /* no next */
+  }
+
+  /* try the next address */
+  ai = conn->ip_addr->ai_next;
+
+  while(ai) {
+    CURLcode res = singleipconnect(conn, ai, 0L, &sockfd, connected);
+    if(res)
+      return res;
+    if(sockfd != CURL_SOCKET_BAD) {
+      /* store the new socket descriptor */
+      conn->sock[sockindex] = sockfd;
+      conn->ip_addr = ai;
+      Curl_closesocket(conn, fd_to_close);
+      return CURLE_OK;
+    }
+    ai = ai->ai_next;
+  }
+  Curl_closesocket(conn, fd_to_close);
+  return CURLE_COULDNT_CONNECT;
+}
+
+/* Copies connection info into the session handle to make it available
+   when the session handle is no longer associated with a connection. */
+void Curl_persistconninfo(struct connectdata *conn)
+{
+  memcpy(conn->data->info.conn_primary_ip, conn->primary_ip, MAX_IPADR_LEN);
+  memcpy(conn->data->info.conn_local_ip, conn->local_ip, MAX_IPADR_LEN);
+  conn->data->info.conn_primary_port = conn->primary_port;
+  conn->data->info.conn_local_port = conn->local_port;
+}
+
+/* retrieves ip address and port from a sockaddr structure */
+static bool getaddressinfo(struct sockaddr* sa, char* addr,
+                           long* port)
+{
+  unsigned short us_port;
+  struct sockaddr_in* si = NULL;
+#ifdef ENABLE_IPV6
+  struct sockaddr_in6* si6 = NULL;
+#endif
+#if defined(HAVE_SYS_UN_H) && defined(AF_UNIX)
+  struct sockaddr_un* su = NULL;
+#endif
+
+  switch (sa->sa_family) {
+    case AF_INET:
+      si = (struct sockaddr_in*) sa;
+      if(Curl_inet_ntop(sa->sa_family, &si->sin_addr,
+                        addr, MAX_IPADR_LEN)) {
+        us_port = ntohs(si->sin_port);
+        *port = us_port;
+        return TRUE;
+      }
+      break;
+#ifdef ENABLE_IPV6
+    case AF_INET6:
+      si6 = (struct sockaddr_in6*)sa;
+      if(Curl_inet_ntop(sa->sa_family, &si6->sin6_addr,
+                        addr, MAX_IPADR_LEN)) {
+        us_port = ntohs(si6->sin6_port);
+        *port = us_port;
+        return TRUE;
+      }
+      break;
+#endif
+#if defined(HAVE_SYS_UN_H) && defined(AF_UNIX)
+    case AF_UNIX:
+      su = (struct sockaddr_un*)sa;
+      snprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
+      *port = 0;
+      return TRUE;
+#endif
+    default:
+      break;
+  }
+
+  addr[0] = '\0';
+  *port = 0;
+
+  return FALSE;
+}
+
+/* retrieves the start/end point information of a socket of an established
+   connection */
+void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd)
+{
+  int error;
+  curl_socklen_t len;
+  struct Curl_sockaddr_storage ssrem;
+  struct Curl_sockaddr_storage ssloc;
+  struct SessionHandle *data = conn->data;
+
+  if(!conn->bits.reuse) {
+
+    len = sizeof(struct Curl_sockaddr_storage);
+    if(getpeername(sockfd, (struct sockaddr*) &ssrem, &len)) {
+      error = SOCKERRNO;
+      failf(data, "getpeername() failed with errno %d: %s",
+            error, Curl_strerror(conn, error));
+      return;
+    }
+
+    len = sizeof(struct Curl_sockaddr_storage);
+    if(getsockname(sockfd, (struct sockaddr*) &ssloc, &len)) {
+      error = SOCKERRNO;
+      failf(data, "getsockname() failed with errno %d: %s",
+            error, Curl_strerror(conn, error));
+      return;
+    }
+
+    if(!getaddressinfo((struct sockaddr*)&ssrem,
+                        conn->primary_ip, &conn->primary_port)) {
+      error = ERRNO;
+      failf(data, "ssrem inet_ntop() failed with errno %d: %s",
+            error, Curl_strerror(conn, error));
+      return;
+    }
+
+    if(!getaddressinfo((struct sockaddr*)&ssloc,
+                       conn->local_ip, &conn->local_port)) {
+      error = ERRNO;
+      failf(data, "ssloc inet_ntop() failed with errno %d: %s",
+            error, Curl_strerror(conn, error));
+      return;
+    }
+
+  }
+
+  /* persist connection info in session handle */
+  Curl_persistconninfo(conn);
+}
+
+/*
+ * Curl_is_connected() is used from the multi interface to check if the
+ * firstsocket has connected.
+ */
+
+CURLcode Curl_is_connected(struct connectdata *conn,
+                           int sockindex,
+                           bool *connected)
+{
+  int rc;
+  struct SessionHandle *data = conn->data;
+  CURLcode code = CURLE_OK;
+  curl_socket_t sockfd = conn->sock[sockindex];
+  long allow = DEFAULT_CONNECT_TIMEOUT;
+  int error = 0;
+  struct timeval now;
+
+  DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET);
+
+  *connected = FALSE; /* a very negative world view is best */
+
+  if(conn->bits.tcpconnect[sockindex]) {
+    /* we are connected already! */
+    *connected = TRUE;
+    return CURLE_OK;
+  }
+
+  now = Curl_tvnow();
+
+  /* figure out how long time we have left to connect */
+  allow = Curl_timeleft(data, &now, TRUE);
+
+  if(allow < 0) {
+    /* time-out, bail out, go home */
+    failf(data, "Connection time-out");
+    return CURLE_OPERATION_TIMEDOUT;
+  }
+
+  /* check for connect without timeout as we want to return immediately */
+  rc = waitconnect(conn, sockfd, 0);
+  if(WAITCONN_TIMEOUT == rc) {
+    if(curlx_tvdiff(now, conn->connecttime) >= conn->timeoutms_per_addr) {
+      infof(data, "After %ldms connect time, move on!\n",
+            conn->timeoutms_per_addr);
+      goto next;
+    }
+
+    /* not an error, but also no connection yet */
+    return code;
+  }
+
+  if(WAITCONN_CONNECTED == rc) {
+    if(verifyconnect(sockfd, &error)) {
+      /* we are connected with TCP, awesome! */
+
+      /* see if we need to do any proxy magic first once we connected */
+      code = Curl_connected_proxy(conn);
+      if(code)
+        return code;
+
+      conn->bits.tcpconnect[sockindex] = TRUE;
+      *connected = TRUE;
+      if(sockindex == FIRSTSOCKET)
+        Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */
+      Curl_verboseconnect(conn);
+      Curl_updateconninfo(conn, sockfd);
+
+      return CURLE_OK;
+    }
+    /* nope, not connected for real */
+  }
+  else {
+    /* nope, not connected  */
+    if(WAITCONN_FDSET_ERROR == rc) {
+      (void)verifyconnect(sockfd, &error);
+      infof(data, "%s\n",Curl_strerror(conn, error));
+    }
+    else
+      infof(data, "Connection failed\n");
+  }
+
+  /*
+   * The connection failed here, we should attempt to connect to the "next
+   * address" for the given host. But first remember the latest error.
+   */
+  if(error) {
+    data->state.os_errno = error;
+    SET_SOCKERRNO(error);
+  }
+  next:
+
+  conn->timeoutms_per_addr = conn->ip_addr->ai_next == NULL ?
+                             allow : allow / 2;
+  code = trynextip(conn, sockindex, connected);
+
+  if(code) {
+    error = SOCKERRNO;
+    data->state.os_errno = error;
+    failf(data, "Failed connect to %s:%ld; %s",
+          conn->host.name, conn->port, Curl_strerror(conn, error));
+  }
+
+  return code;
+}
+
+static void tcpnodelay(struct connectdata *conn,
+                       curl_socket_t sockfd)
+{
+#ifdef TCP_NODELAY
+  struct SessionHandle *data= conn->data;
+  curl_socklen_t onoff = (curl_socklen_t) data->set.tcp_nodelay;
+  int level = IPPROTO_TCP;
+
+#if 0
+  /* The use of getprotobyname() is disabled since it isn't thread-safe on
+     numerous systems. On these getprotobyname_r() should be used instead, but
+     that exists in at least one 4 arg version and one 5 arg version, and
+     since the proto number rarely changes anyway we now just use the hard
+     coded number. The "proper" fix would need a configure check for the
+     correct function much in the same style the gethostbyname_r versions are
+     detected. */
+  struct protoent *pe = getprotobyname("tcp");
+  if(pe)
+    level = pe->p_proto;
+#endif
+
+  if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
+                sizeof(onoff)) < 0)
+    infof(data, "Could not set TCP_NODELAY: %s\n",
+          Curl_strerror(conn, SOCKERRNO));
+  else
+    infof(data,"TCP_NODELAY set\n");
+#else
+  (void)conn;
+  (void)sockfd;
+#endif
+}
+
+#ifdef SO_NOSIGPIPE
+/* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when
+   sending data to a dead peer (instead of relying on the 4th argument to send
+   being MSG_NOSIGNAL). Possibly also existing and in use on other BSD
+   systems? */
+static void nosigpipe(struct connectdata *conn,
+                      curl_socket_t sockfd)
+{
+  struct SessionHandle *data= conn->data;
+  int onoff = 1;
+  if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
+                sizeof(onoff)) < 0)
+    infof(data, "Could not set SO_NOSIGPIPE: %s\n",
+          Curl_strerror(conn, SOCKERRNO));
+}
+#else
+#define nosigpipe(x,y) Curl_nop_stmt
+#endif
+
+#ifdef USE_WINSOCK
+/* When you run a program that uses the Windows Sockets API, you may
+   experience slow performance when you copy data to a TCP server.
+
+   http://support.microsoft.com/kb/823764
+
+   Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
+   Buffer Size
+
+*/
+void Curl_sndbufset(curl_socket_t sockfd)
+{
+  int val = CURL_MAX_WRITE_SIZE + 32;
+  int curval = 0;
+  int curlen = sizeof(curval);
+
+  if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0)
+    if(curval > val)
+      return;
+
+  setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val));
+}
+#endif
+
+
+/*
+ * singleipconnect()
+ *
+ * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to
+ * CURL_SOCKET_BAD. Other errors will however return proper errors.
+ *
+ * singleipconnect() connects to the given IP only, and it may return without
+ * having connected if used from the multi interface.
+ */
+static CURLcode
+singleipconnect(struct connectdata *conn,
+                const Curl_addrinfo *ai,
+                long timeout_ms,
+                curl_socket_t *sockp,
+                bool *connected)
+{
+  struct Curl_sockaddr_ex addr;
+  int rc;
+  int error = 0;
+  bool isconnected = FALSE;
+  struct SessionHandle *data = conn->data;
+  curl_socket_t sockfd;
+  CURLcode res = CURLE_OK;
+
+  *sockp = CURL_SOCKET_BAD;
+  *connected = FALSE; /* default is not connected */
+
+  res = Curl_socket(conn, ai, &addr, &sockfd);
+  if(res)
+    /* Failed to create the socket, but still return OK since we signal the
+       lack of socket as well. This allows the parent function to keep looping
+       over alternative addresses/socket families etc. */
+    return CURLE_OK;
+
+  /* store remote address and port used in this connection attempt */
+  if(!getaddressinfo((struct sockaddr*)&addr.sa_addr,
+                     conn->primary_ip, &conn->primary_port)) {
+    /* malformed address or bug in inet_ntop, try next address */
+    error = ERRNO;
+    failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
+          error, Curl_strerror(conn, error));
+    Curl_closesocket(conn, sockfd);
+    return CURLE_OK;
+  }
+  memcpy(conn->ip_addr_str, conn->primary_ip, MAX_IPADR_LEN);
+  infof(data, "  Trying %s...\n", conn->ip_addr_str);
+
+  Curl_persistconninfo(conn);
+
+  if(data->set.tcp_nodelay)
+    tcpnodelay(conn, sockfd);
+
+  nosigpipe(conn, sockfd);
+
+  Curl_sndbufset(sockfd);
+
+  if(data->set.tcp_keepalive)
+    tcpkeepalive(data, sockfd);
+
+  if(data->set.fsockopt) {
+    /* activate callback for setting socket options */
+    error = data->set.fsockopt(data->set.sockopt_client,
+                               sockfd,
+                               CURLSOCKTYPE_IPCXN);
+
+    if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
+      isconnected = TRUE;
+    else if(error) {
+      Curl_closesocket(conn, sockfd); /* close the socket and bail out */
+      return CURLE_ABORTED_BY_CALLBACK;
+    }
+  }
+
+  /* possibly bind the local end to an IP, interface or port */
+  res = bindlocal(conn, sockfd, addr.family);
+  if(res) {
+    Curl_closesocket(conn, sockfd); /* close socket and bail out */
+    return res;
+  }
+
+  /* set socket non-blocking */
+  curlx_nonblock(sockfd, TRUE);
+
+  /* Connect TCP sockets, bind UDP */
+  if(!isconnected && (conn->socktype == SOCK_STREAM)) {
+    rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
+    if(-1 == rc)
+      error = SOCKERRNO;
+    conn->connecttime = Curl_tvnow();
+    if(conn->num_addr > 1)
+      Curl_expire(data, conn->timeoutms_per_addr);
+  }
+  else
+    rc = 0;
+
+  if(-1 == rc) {
+    switch (error) {
+    case EINPROGRESS:
+    case EWOULDBLOCK:
+#if defined(EAGAIN)
+#if (EAGAIN) != (EWOULDBLOCK)
+      /* On some platforms EAGAIN and EWOULDBLOCK are the
+       * same value, and on others they are different, hence
+       * the odd #if
+       */
+    case EAGAIN:
+#endif
+#endif
+      rc = waitconnect(conn, sockfd, timeout_ms);
+      if(WAITCONN_ABORTED == rc) {
+        Curl_closesocket(conn, sockfd);
+        return CURLE_ABORTED_BY_CALLBACK;
+      }
+      break;
+    default:
+      /* unknown error, fallthrough and try another address! */
+      failf(data, "Failed to connect to %s: %s",
+            conn->ip_addr_str, Curl_strerror(conn,error));
+      data->state.os_errno = error;
+      break;
+    }
+  }
+
+  /* The 'WAITCONN_TIMEOUT == rc' comes from the waitconnect(), and not from
+     connect(). We can be sure of this since connect() cannot return 1. */
+  if((WAITCONN_TIMEOUT == rc) &&
+     (data->state.used_interface == Curl_if_multi)) {
+    /* Timeout when running the multi interface */
+    *sockp = sockfd;
+    return CURLE_OK;
+  }
+
+  if(!isconnected)
+    isconnected = verifyconnect(sockfd, &error);
+
+  if(!rc && isconnected) {
+    /* we are connected, awesome! */
+    *connected = TRUE; /* this is a true connect */
+    infof(data, "connected\n");
+#ifdef ENABLE_IPV6
+    conn->bits.ipv6 = (addr.family == AF_INET6)?TRUE:FALSE;
+#endif
+
+    Curl_updateconninfo(conn, sockfd);
+    *sockp = sockfd;
+    return CURLE_OK;
+  }
+  else if(WAITCONN_TIMEOUT == rc)
+    infof(data, "Timeout\n");
+  else {
+    data->state.os_errno = error;
+    infof(data, "%s\n", Curl_strerror(conn, error));
+  }
+
+  /* connect failed or timed out */
+  Curl_closesocket(conn, sockfd);
+
+  return CURLE_OK;
+}
+
+/*
+ * TCP connect to the given host with timeout, proxy or remote doesn't matter.
+ * There might be more than one IP address to try out. Fill in the passed
+ * pointer with the connected socket.
+ */
+
+CURLcode Curl_connecthost(struct connectdata *conn,  /* context */
+                          const struct Curl_dns_entry *remotehost,
+                          curl_socket_t *sockconn,   /* the connected socket */
+                          Curl_addrinfo **addr,      /* the one we used */
+                          bool *connected)           /* really connected? */
+{
+  struct SessionHandle *data = conn->data;
+  curl_socket_t sockfd = CURL_SOCKET_BAD;
+  Curl_addrinfo *ai;
+  Curl_addrinfo *curr_addr;
+
+  struct timeval after;
+  struct timeval before = Curl_tvnow();
+
+  /*************************************************************
+   * Figure out what maximum time we have left
+   *************************************************************/
+  long timeout_ms;
+
+  DEBUGASSERT(sockconn);
+  *connected = FALSE; /* default to not connected */
+
+  /* get the timeout left */
+  timeout_ms = Curl_timeleft(data, &before, TRUE);
+
+  if(timeout_ms < 0) {
+    /* a precaution, no need to continue if time already is up */
+    failf(data, "Connection time-out");
+    return CURLE_OPERATION_TIMEDOUT;
+  }
+
+  conn->num_addr = Curl_num_addresses(remotehost->addr);
+
+  ai = remotehost->addr;
+
+  /* Below is the loop that attempts to connect to all IP-addresses we
+   * know for the given host. One by one until one IP succeeds.
+   */
+
+  /*
+   * Connecting with a Curl_addrinfo chain
+   */
+  for(curr_addr = ai; curr_addr; curr_addr = curr_addr->ai_next) {
+    CURLcode res;
+
+    /* Max time for the next address */
+    conn->timeoutms_per_addr = curr_addr->ai_next == NULL ?
+                               timeout_ms : timeout_ms / 2;
+
+    /* start connecting to the IP curr_addr points to */
+    res = singleipconnect(conn, curr_addr,
+                          /* don't hang when doing multi */
+                          (data->state.used_interface == Curl_if_multi)?0:
+                          conn->timeoutms_per_addr, &sockfd, connected);
+    if(res)
+      return res;
+
+    if(sockfd != CURL_SOCKET_BAD)
+      break;
+
+    /* get a new timeout for next attempt */
+    after = Curl_tvnow();
+    timeout_ms -= Curl_tvdiff(after, before);
+    if(timeout_ms < 0) {
+      failf(data, "connect() timed out!");
+      return CURLE_OPERATION_TIMEDOUT;
+    }
+    before = after;
+  }  /* end of connect-to-each-address loop */
+
+  *sockconn = sockfd;    /* the socket descriptor we've connected */
+
+  if(sockfd == CURL_SOCKET_BAD) {
+    /* no good connect was made */
+    failf(data, "couldn't connect to %s at %s:%d",
+          conn->bits.proxy?"proxy":"host",
+          conn->bits.proxy?conn->proxy.name:conn->host.name, conn->port);
+    return CURLE_COULDNT_CONNECT;
+  }
+
+  /* leave the socket in non-blocking mode */
+
+  /* store the address we use */
+  if(addr)
+    *addr = curr_addr;
+
+  data->info.numconnects++; /* to track the number of connections made */
+
+  return CURLE_OK;
+}
+
+/*
+ * Used to extract socket and connectdata struct for the most recent
+ * transfer on the given SessionHandle.
+ *
+ * The returned socket will be CURL_SOCKET_BAD in case of failure!
+ */
+curl_socket_t Curl_getconnectinfo(struct SessionHandle *data,
+                                  struct connectdata **connp)
+{
+  curl_socket_t sockfd;
+
+  DEBUGASSERT(data);
+
+  if(data->state.lastconnect) {
+    struct connectdata *c = data->state.lastconnect;
+    if(connp)
+      /* only store this if the caller cares for it */
+      *connp = c;
+    sockfd = c->sock[FIRSTSOCKET];
+    /* we have a socket connected, let's determine if the server shut down */
+    /* determine if ssl */
+    if(c->ssl[FIRSTSOCKET].use) {
+      /* use the SSL context */
+      if(!Curl_ssl_check_cxn(c))
+        return CURL_SOCKET_BAD;   /* FIN received */
+    }
+/* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */
+#ifdef MSG_PEEK
+    else {
+      /* use the socket */
+      char buf;
+      if(recv((RECV_TYPE_ARG1)c->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf,
+              (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK) == 0) {
+        return CURL_SOCKET_BAD;   /* FIN received */
+      }
+    }
+#endif
+  }
+  else
+    return CURL_SOCKET_BAD;
+
+  return sockfd;
+}
+
+/*
+ * Close a socket.
+ *
+ * 'conn' can be NULL, beware!
+ */
+int Curl_closesocket(struct connectdata *conn,
+                     curl_socket_t sock)
+{
+  if(conn && conn->fclosesocket) {
+    if((sock == conn->sock[SECONDARYSOCKET]) &&
+       conn->sock_accepted[SECONDARYSOCKET])
+      /* if this socket matches the second socket, and that was created with
+         accept, then we MUST NOT call the callback but clear the accepted
+         status */
+      conn->sock_accepted[SECONDARYSOCKET] = FALSE;
+    else
+      return conn->fclosesocket(conn->closesocket_client, sock);
+  }
+  return sclose(sock);
+}
+
+/*
+ * Create a socket based on info from 'conn' and 'ai'.
+ *
+ * 'addr' should be a pointer to the correct struct to get data back, or NULL.
+ * 'sockfd' must be a pointer to a socket descriptor.
+ *
+ * If the open socket callback is set, used that!
+ *
+ */
+CURLcode Curl_socket(struct connectdata *conn,
+                     const Curl_addrinfo *ai,
+                     struct Curl_sockaddr_ex *addr,
+                     curl_socket_t *sockfd)
+{
+  struct SessionHandle *data = conn->data;
+  struct Curl_sockaddr_ex dummy;
+
+  if(!addr)
+    /* if the caller doesn't want info back, use a local temp copy */
+    addr = &dummy;
+
+  /*
+   * The Curl_sockaddr_ex structure is basically libcurl's external API
+   * curl_sockaddr structure with enough space available to directly hold
+   * any protocol-specific address structures. The variable declared here
+   * will be used to pass / receive data to/from the fopensocket callback
+   * if this has been set, before that, it is initialized from parameters.
+   */
+
+  addr->family = ai->ai_family;
+  addr->socktype = conn->socktype;
+  addr->protocol = conn->socktype==SOCK_DGRAM?IPPROTO_UDP:ai->ai_protocol;
+  addr->addrlen = ai->ai_addrlen;
+
+  if(addr->addrlen > sizeof(struct Curl_sockaddr_storage))
+     addr->addrlen = sizeof(struct Curl_sockaddr_storage);
+  memcpy(&addr->sa_addr, ai->ai_addr, addr->addrlen);
+
+  if(data->set.fopensocket)
+   /*
+    * If the opensocket callback is set, all the destination address
+    * information is passed to the callback. Depending on this information the
+    * callback may opt to abort the connection, this is indicated returning
+    * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When
+    * the callback returns a valid socket the destination address information
+    * might have been changed and this 'new' address will actually be used
+    * here to connect.
+    */
+    *sockfd = data->set.fopensocket(data->set.opensocket_client,
+                                    CURLSOCKTYPE_IPCXN,
+                                    (struct curl_sockaddr *)addr);
+  else
+    /* opensocket callback not set, so simply create the socket now */
+    *sockfd = socket(addr->family, addr->socktype, addr->protocol);
+
+  if(*sockfd == CURL_SOCKET_BAD)
+    /* no socket, no connection */
+    return CURLE_COULDNT_CONNECT;
+
+#if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
+  if(conn->scope && (addr->family == AF_INET6)) {
+    struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr;
+    sa6->sin6_scope_id = conn->scope;
+  }
+#endif
+
+  return CURLE_OK;
+
+}
diff --git a/lib/curl_content_encoding.c b/lib/curl_content_encoding.c
new file mode 100644 (file)
index 0000000..6f4d142
--- /dev/null
@@ -0,0 +1,435 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_LIBZ
+
+#include "curl_urldata.h"
+#include <curl/curl.h>
+#include "curl_sendf.h"
+#include "curl_content_encoding.h"
+#include "curl_memory.h"
+
+#include "curl_memdebug.h"
+
+/* Comment this out if zlib is always going to be at least ver. 1.2.0.4
+   (doing so will reduce code size slightly). */
+#define OLD_ZLIB_SUPPORT 1
+
+#define DSIZ CURL_MAX_WRITE_SIZE /* buffer size for decompressed data */
+
+#define GZIP_MAGIC_0 0x1f
+#define GZIP_MAGIC_1 0x8b
+
+/* gzip flag byte */
+#define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
+#define HEAD_CRC     0x02 /* bit 1 set: header CRC present */
+#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
+#define COMMENT      0x10 /* bit 4 set: file comment present */
+#define RESERVED     0xE0 /* bits 5..7: reserved */
+
+static voidpf
+zalloc_cb(voidpf opaque, unsigned int items, unsigned int size)
+{
+  (void) opaque;
+  /* not a typo, keep it calloc() */
+  return (voidpf) calloc(items, size);
+}
+
+static void
+zfree_cb(voidpf opaque, voidpf ptr)
+{
+  (void) opaque;
+  free(ptr);
+}
+
+static CURLcode
+process_zlib_error(struct connectdata *conn, z_stream *z)
+{
+  struct SessionHandle *data = conn->data;
+  if(z->msg)
+    failf (data, "Error while processing content unencoding: %s",
+           z->msg);
+  else
+    failf (data, "Error while processing content unencoding: "
+           "Unknown failure within decompression software.");
+
+  return CURLE_BAD_CONTENT_ENCODING;
+}
+
+static CURLcode
+exit_zlib(z_stream *z, zlibInitState *zlib_init, CURLcode result)
+{
+  inflateEnd(z);
+  *zlib_init = ZLIB_UNINIT;
+  return result;
+}
+
+static CURLcode
+inflate_stream(struct connectdata *conn,
+               struct SingleRequest *k)
+{
+  int allow_restart = 1;
+  z_stream *z = &k->z;          /* zlib state structure */
+  uInt nread = z->avail_in;
+  Bytef *orig_in = z->next_in;
+  int status;                   /* zlib status */
+  CURLcode result = CURLE_OK;   /* Curl_client_write status */
+  char *decomp;                 /* Put the decompressed data here. */
+
+  /* Dynamically allocate a buffer for decompression because it's uncommonly
+     large to hold on the stack */
+  decomp = malloc(DSIZ);
+  if(decomp == NULL) {
+    return exit_zlib(z, &k->zlib_init, CURLE_OUT_OF_MEMORY);
+  }
+
+  /* because the buffer size is fixed, iteratively decompress and transfer to
+     the client via client_write. */
+  for(;;) {
+    /* (re)set buffer for decompressed output for every iteration */
+    z->next_out = (Bytef *)decomp;
+    z->avail_out = DSIZ;
+
+    status = inflate(z, Z_SYNC_FLUSH);
+    if(status == Z_OK || status == Z_STREAM_END) {
+      allow_restart = 0;
+      if((DSIZ - z->avail_out) && (!k->ignorebody)) {
+        result = Curl_client_write(conn, CLIENTWRITE_BODY, decomp,
+                                   DSIZ - z->avail_out);
+        /* if !CURLE_OK, clean up, return */
+        if(result) {
+          free(decomp);
+          return exit_zlib(z, &k->zlib_init, result);
+        }
+      }
+
+      /* Done? clean up, return */
+      if(status == Z_STREAM_END) {
+        free(decomp);
+        if(inflateEnd(z) == Z_OK)
+          return exit_zlib(z, &k->zlib_init, result);
+        else
+          return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z));
+      }
+
+      /* Done with these bytes, exit */
+
+      /* status is always Z_OK at this point! */
+      if(z->avail_in == 0) {
+        free(decomp);
+        return result;
+      }
+    }
+    else if(allow_restart && status == Z_DATA_ERROR) {
+      /* some servers seem to not generate zlib headers, so this is an attempt
+         to fix and continue anyway */
+
+      (void) inflateEnd(z);     /* don't care about the return code */
+      if(inflateInit2(z, -MAX_WBITS) != Z_OK) {
+        free(decomp);
+        return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z));
+      }
+      z->next_in = orig_in;
+      z->avail_in = nread;
+      allow_restart = 0;
+      continue;
+    }
+    else {                      /* Error; exit loop, handle below */
+      free(decomp);
+      return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z));
+    }
+  }
+  /* Will never get here */
+}
+
+CURLcode
+Curl_unencode_deflate_write(struct connectdata *conn,
+                            struct SingleRequest *k,
+                            ssize_t nread)
+{
+  z_stream *z = &k->z;          /* zlib state structure */
+
+  /* Initialize zlib? */
+  if(k->zlib_init == ZLIB_UNINIT) {
+    memset(z, 0, sizeof(z_stream));
+    z->zalloc = (alloc_func)zalloc_cb;
+    z->zfree = (free_func)zfree_cb;
+
+    if(inflateInit(z) != Z_OK)
+      return process_zlib_error(conn, z);
+    k->zlib_init = ZLIB_INIT;
+  }
+
+  /* Set the compressed input when this function is called */
+  z->next_in = (Bytef *)k->str;
+  z->avail_in = (uInt)nread;
+
+  /* Now uncompress the data */
+  return inflate_stream(conn, k);
+}
+
+#ifdef OLD_ZLIB_SUPPORT
+/* Skip over the gzip header */
+static enum {
+  GZIP_OK,
+  GZIP_BAD,
+  GZIP_UNDERFLOW
+} check_gzip_header(unsigned char const *data, ssize_t len, ssize_t *headerlen)
+{
+  int method, flags;
+  const ssize_t totallen = len;
+
+  /* The shortest header is 10 bytes */
+  if(len < 10)
+    return GZIP_UNDERFLOW;
+
+  if((data[0] != GZIP_MAGIC_0) || (data[1] != GZIP_MAGIC_1))
+    return GZIP_BAD;
+
+  method = data[2];
+  flags = data[3];
+
+  if(method != Z_DEFLATED || (flags & RESERVED) != 0) {
+    /* Can't handle this compression method or unknown flag */
+    return GZIP_BAD;
+  }
+
+  /* Skip over time, xflags, OS code and all previous bytes */
+  len -= 10;
+  data += 10;
+
+  if(flags & EXTRA_FIELD) {
+    ssize_t extra_len;
+
+    if(len < 2)
+      return GZIP_UNDERFLOW;
+
+    extra_len = (data[1] << 8) | data[0];
+
+    if(len < (extra_len+2))
+      return GZIP_UNDERFLOW;
+
+    len -= (extra_len + 2);
+    data += (extra_len + 2);
+  }
+
+  if(flags & ORIG_NAME) {
+    /* Skip over NUL-terminated file name */
+    while(len && *data) {
+      --len;
+      ++data;
+    }
+    if(!len || *data)
+      return GZIP_UNDERFLOW;
+
+    /* Skip over the NUL */
+    --len;
+    ++data;
+  }
+
+  if(flags & COMMENT) {
+    /* Skip over NUL-terminated comment */
+    while(len && *data) {
+      --len;
+      ++data;
+    }
+    if(!len || *data)
+      return GZIP_UNDERFLOW;
+
+    /* Skip over the NUL */
+    --len;
+  }
+
+  if(flags & HEAD_CRC) {
+    if(len < 2)
+      return GZIP_UNDERFLOW;
+
+    len -= 2;
+  }
+
+  *headerlen = totallen - len;
+  return GZIP_OK;
+}
+#endif
+
+CURLcode
+Curl_unencode_gzip_write(struct connectdata *conn,
+                         struct SingleRequest *k,
+                         ssize_t nread)
+{
+  z_stream *z = &k->z;          /* zlib state structure */
+
+  /* Initialize zlib? */
+  if(k->zlib_init == ZLIB_UNINIT) {
+    memset(z, 0, sizeof(z_stream));
+    z->zalloc = (alloc_func)zalloc_cb;
+    z->zfree = (free_func)zfree_cb;
+
+    if(strcmp(zlibVersion(), "1.2.0.4") >= 0) {
+      /* zlib ver. >= 1.2.0.4 supports transparent gzip decompressing */
+      if(inflateInit2(z, MAX_WBITS+32) != Z_OK) {
+        return process_zlib_error(conn, z);
+      }
+      k->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */
+    }
+    else {
+      /* we must parse the gzip header ourselves */
+      if(inflateInit2(z, -MAX_WBITS) != Z_OK) {
+        return process_zlib_error(conn, z);
+      }
+      k->zlib_init = ZLIB_INIT;   /* Initial call state */
+    }
+  }
+
+  if(k->zlib_init == ZLIB_INIT_GZIP) {
+    /* Let zlib handle the gzip decompression entirely */
+    z->next_in = (Bytef *)k->str;
+    z->avail_in = (uInt)nread;
+    /* Now uncompress the data */
+    return inflate_stream(conn, k);
+  }
+
+#ifndef OLD_ZLIB_SUPPORT
+  /* Support for old zlib versions is compiled away and we are running with
+     an old version, so return an error. */
+  return exit_zlib(z, &k->zlib_init, CURLE_FUNCTION_NOT_FOUND);
+
+#else
+  /* This next mess is to get around the potential case where there isn't
+   * enough data passed in to skip over the gzip header.  If that happens, we
+   * malloc a block and copy what we have then wait for the next call.  If
+   * there still isn't enough (this is definitely a worst-case scenario), we
+   * make the block bigger, copy the next part in and keep waiting.
+   *
+   * This is only required with zlib versions < 1.2.0.4 as newer versions
+   * can handle the gzip header themselves.
+   */
+
+  switch (k->zlib_init) {
+  /* Skip over gzip header? */
+  case ZLIB_INIT:
+  {
+    /* Initial call state */
+    ssize_t hlen;
+
+    switch (check_gzip_header((unsigned char *)k->str, nread, &hlen)) {
+    case GZIP_OK:
+      z->next_in = (Bytef *)k->str + hlen;
+      z->avail_in = (uInt)(nread - hlen);
+      k->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */
+      break;
+
+    case GZIP_UNDERFLOW:
+      /* We need more data so we can find the end of the gzip header.  It's
+       * possible that the memory block we malloc here will never be freed if
+       * the transfer abruptly aborts after this point.  Since it's unlikely
+       * that circumstances will be right for this code path to be followed in
+       * the first place, and it's even more unlikely for a transfer to fail
+       * immediately afterwards, it should seldom be a problem.
+       */
+      z->avail_in = (uInt)nread;
+      z->next_in = malloc(z->avail_in);
+      if(z->next_in == NULL) {
+        return exit_zlib(z, &k->zlib_init, CURLE_OUT_OF_MEMORY);
+      }
+      memcpy(z->next_in, k->str, z->avail_in);
+      k->zlib_init = ZLIB_GZIP_HEADER;   /* Need more gzip header data state */
+      /* We don't have any data to inflate yet */
+      return CURLE_OK;
+
+    case GZIP_BAD:
+    default:
+      return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z));
+    }
+
+  }
+  break;
+
+  case ZLIB_GZIP_HEADER:
+  {
+    /* Need more gzip header data state */
+    ssize_t hlen;
+    unsigned char *oldblock = z->next_in;
+
+    z->avail_in += (uInt)nread;
+    z->next_in = realloc(z->next_in, z->avail_in);
+    if(z->next_in == NULL) {
+      free(oldblock);
+      return exit_zlib(z, &k->zlib_init, CURLE_OUT_OF_MEMORY);
+    }
+    /* Append the new block of data to the previous one */
+    memcpy(z->next_in + z->avail_in - nread, k->str, nread);
+
+    switch (check_gzip_header(z->next_in, z->avail_in, &hlen)) {
+    case GZIP_OK:
+      /* This is the zlib stream data */
+      free(z->next_in);
+      /* Don't point into the malloced block since we just freed it */
+      z->next_in = (Bytef *)k->str + hlen + nread - z->avail_in;
+      z->avail_in = (uInt)(z->avail_in - hlen);
+      k->zlib_init = ZLIB_GZIP_INFLATING;   /* Inflating stream state */
+      break;
+
+    case GZIP_UNDERFLOW:
+      /* We still don't have any data to inflate! */
+      return CURLE_OK;
+
+    case GZIP_BAD:
+    default:
+      free(z->next_in);
+      return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z));
+    }
+
+  }
+  break;
+
+  case ZLIB_GZIP_INFLATING:
+  default:
+    /* Inflating stream state */
+    z->next_in = (Bytef *)k->str;
+    z->avail_in = (uInt)nread;
+    break;
+  }
+
+  if(z->avail_in == 0) {
+    /* We don't have any data to inflate; wait until next time */
+    return CURLE_OK;
+  }
+
+  /* We've parsed the header, now uncompress the data */
+  return inflate_stream(conn, k);
+#endif
+}
+
+void Curl_unencode_cleanup(struct connectdata *conn)
+{
+  struct SessionHandle *data = conn->data;
+  struct SingleRequest *k = &data->req;
+  z_stream *z = &k->z;
+  if(k->zlib_init != ZLIB_UNINIT)
+    (void) exit_zlib(z, &k->zlib_init, CURLE_OK);
+}
+
+#endif /* HAVE_LIBZ */
diff --git a/lib/curl_cookie.c b/lib/curl_cookie.c
new file mode 100644 (file)
index 0000000..90ee884
--- /dev/null
@@ -0,0 +1,1163 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/***
+
+
+RECEIVING COOKIE INFORMATION
+============================
+
+struct CookieInfo *cookie_init(char *file);
+
+        Inits a cookie struct to store data in a local file. This is always
+        called before any cookies are set.
+
+int cookies_set(struct CookieInfo *cookie, char *cookie_line);
+
+        The 'cookie_line' parameter is a full "Set-cookie:" line as
+        received from a server.
+
+        The function need to replace previously stored lines that this new
+        line superceeds.
+
+        It may remove lines that are expired.
+
+        It should return an indication of success/error.
+
+
+SENDING COOKIE INFORMATION
+==========================
+
+struct Cookies *cookie_getlist(struct CookieInfo *cookie,
+                               char *host, char *path, bool secure);
+
+        For a given host and path, return a linked list of cookies that
+        the client should send to the server if used now. The secure
+        boolean informs the cookie if a secure connection is achieved or
+        not.
+
+        It shall only return cookies that haven't expired.
+
+
+Example set of cookies:
+
+    Set-cookie: PRODUCTINFO=webxpress; domain=.fidelity.com; path=/; secure
+    Set-cookie: PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
+    domain=.fidelity.com; path=/ftgw; secure
+    Set-cookie: FidHist=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
+    domain=.fidelity.com; path=/; secure
+    Set-cookie: FidOrder=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
+    domain=.fidelity.com; path=/; secure
+    Set-cookie: DisPend=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
+    domain=.fidelity.com; path=/; secure
+    Set-cookie: FidDis=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
+    domain=.fidelity.com; path=/; secure
+    Set-cookie:
+    Session_Key@6791a9e0-901a-11d0-a1c8-9b012c88aa77=none;expires=Monday,
+    13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/; secure
+****/
+
+
+#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
+
+#define _MPRINTF_REPLACE
+#include <curl/mprintf.h>
+
+#include "curl_urldata.h"
+#include "curl_cookie.h"
+#include "curl_strequal.h"
+#include "curl_strtok.h"
+#include "curl_sendf.h"
+#include "curl_memory.h"
+#include "curl_share.h"
+#include "curl_strtoofft.h"
+#include "curl_rawstr.h"
+#include "curl_memrchr.h"
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+static void freecookie(struct Cookie *co)
+{
+  if(co->expirestr)
+    free(co->expirestr);
+  if(co->domain)
+    free(co->domain);
+  if(co->path)
+    free(co->path);
+  if(co->name)
+    free(co->name);
+  if(co->value)
+    free(co->value);
+  if(co->maxage)
+    free(co->maxage);
+  if(co->version)
+    free(co->version);
+
+  free(co);
+}
+
+static bool tailmatch(const char *little, const char *bigone)
+{
+  size_t littlelen = strlen(little);
+  size_t biglen = strlen(bigone);
+
+  if(littlelen > biglen)
+    return FALSE;
+
+  return Curl_raw_equal(little, bigone+biglen-littlelen) ? TRUE : FALSE;
+}
+
+/*
+ * Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
+ */
+void Curl_cookie_loadfiles(struct SessionHandle *data)
+{
+  struct curl_slist *list = data->change.cookielist;
+  if(list) {
+    Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
+    while(list) {
+      data->cookies = Curl_cookie_init(data,
+                                       list->data,
+                                       data->cookies,
+                                       data->set.cookiesession);
+      list = list->next;
+    }
+    curl_slist_free_all(data->change.cookielist); /* clean up list */
+    data->change.cookielist = NULL; /* don't do this again! */
+    Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
+  }
+}
+
+/*
+ * strstore() makes a strdup() on the 'newstr' and if '*str' is non-NULL
+ * that will be freed before the allocated string is stored there.
+ *
+ * It is meant to easily replace strdup()
+ */
+static void strstore(char **str, const char *newstr)
+{
+  if(*str)
+    free(*str);
+  *str = strdup(newstr);
+}
+
+
+/****************************************************************************
+ *
+ * Curl_cookie_add()
+ *
+ * Add a single cookie line to the cookie keeping object.
+ *
+ ***************************************************************************/
+
+struct Cookie *
+Curl_cookie_add(struct SessionHandle *data,
+                /* The 'data' pointer here may be NULL at times, and thus
+                   must only be used very carefully for things that can deal
+                   with data being NULL. Such as infof() and similar */
+
+                struct CookieInfo *c,
+                bool httpheader, /* TRUE if HTTP header-style line */
+                char *lineptr,   /* first character of the line */
+                const char *domain, /* default domain */
+                const char *path)   /* full path used when this cookie is set,
+                                       used to get default path for the cookie
+                                       unless set */
+{
+  struct Cookie *clist;
+  char name[MAX_NAME];
+  struct Cookie *co;
+  struct Cookie *lastc=NULL;
+  time_t now = time(NULL);
+  bool replace_old = FALSE;
+  bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
+
+#ifdef CURL_DISABLE_VERBOSE_STRINGS
+  (void)data;
+#endif
+
+  /* First, alloc and init a new struct for it */
+  co = calloc(1, sizeof(struct Cookie));
+  if(!co)
+    return NULL; /* bail out if we're this low on memory */
+
+  if(httpheader) {
+    /* This line was read off a HTTP-header */
+    const char *ptr;
+    const char *semiptr;
+    char *what;
+
+    what = malloc(MAX_COOKIE_LINE);
+    if(!what) {
+      free(co);
+      return NULL;
+    }
+
+    semiptr=strchr(lineptr, ';'); /* first, find a semicolon */
+
+    while(*lineptr && ISBLANK(*lineptr))
+      lineptr++;
+
+    ptr = lineptr;
+    do {
+      /* we have a <what>=<this> pair or a stand-alone word here */
+      name[0]=what[0]=0; /* init the buffers */
+      if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;\r\n =]=%"
+                     MAX_COOKIE_LINE_TXT "[^;\r\n]",
+                     name, what)) {
+        /* Use strstore() below to properly deal with received cookie
+           headers that have the same string property set more than once,
+           and then we use the last one. */
+        const char *whatptr;
+        bool done = FALSE;
+        bool sep;
+        size_t len=strlen(what);
+        const char *endofn = &ptr[ strlen(name) ];
+
+        /* skip trailing spaces in name */
+        while(*endofn && ISBLANK(*endofn))
+          endofn++;
+
+        /* name ends with a '=' ? */
+        sep = (*endofn == '=')?TRUE:FALSE;
+
+        /* Strip off trailing whitespace from the 'what' */
+        while(len && ISBLANK(what[len-1])) {
+          what[len-1]=0;
+          len--;
+        }
+
+        /* Skip leading whitespace from the 'what' */
+        whatptr=what;
+        while(*whatptr && ISBLANK(*whatptr))
+          whatptr++;
+
+        if(!len) {
+          /* this was a "<name>=" with no content, and we must allow
+             'secure' and 'httponly' specified this weirdly */
+          done = TRUE;
+          if(Curl_raw_equal("secure", name))
+            co->secure = TRUE;
+          else if(Curl_raw_equal("httponly", name))
+            co->httponly = TRUE;
+          else if(sep)
+            /* there was a '=' so we're not done parsing this field */
+            done = FALSE;
+        }
+        if(done)
+          ;
+        else if(Curl_raw_equal("path", name)) {
+          strstore(&co->path, whatptr);
+          if(!co->path) {
+            badcookie = TRUE; /* out of memory bad */
+            break;
+          }
+        }
+        else if(Curl_raw_equal("domain", name)) {
+          /* note that this name may or may not have a preceding dot, but
+             we don't care about that, we treat the names the same anyway */
+
+          const char *domptr=whatptr;
+          const char *nextptr;
+          int dotcount=1;
+
+          /* Count the dots, we need to make sure that there are enough
+             of them. */
+
+          if('.' == whatptr[0])
+            /* don't count the initial dot, assume it */
+            domptr++;
+
+          do {
+            nextptr = strchr(domptr, '.');
+            if(nextptr) {
+              if(domptr != nextptr)
+                dotcount++;
+              domptr = nextptr+1;
+            }
+          } while(nextptr);
+
+          /* The original Netscape cookie spec defined that this domain name
+             MUST have three dots (or two if one of the seven holy TLDs),
+             but it seems that these kinds of cookies are in use "out there"
+             so we cannot be that strict. I've therefore lowered the check
+             to not allow less than two dots. */
+
+          if(dotcount < 2) {
+            /* Received and skipped a cookie with a domain using too few
+               dots. */
+            badcookie=TRUE; /* mark this as a bad cookie */
+            infof(data, "skipped cookie with illegal dotcount domain: %s\n",
+                  whatptr);
+          }
+          else {
+            /* Now, we make sure that our host is within the given domain,
+               or the given domain is not valid and thus cannot be set. */
+
+            if('.' == whatptr[0])
+              whatptr++; /* ignore preceding dot */
+
+            if(!domain || tailmatch(whatptr, domain)) {
+              const char *tailptr=whatptr;
+              if(tailptr[0] == '.')
+                tailptr++;
+              strstore(&co->domain, tailptr); /* don't prefix w/dots
+                                                 internally */
+              if(!co->domain) {
+                badcookie = TRUE;
+                break;
+              }
+              co->tailmatch=TRUE; /* we always do that if the domain name was
+                                     given */
+            }
+            else {
+              /* we did not get a tailmatch and then the attempted set domain
+                 is not a domain to which the current host belongs. Mark as
+                 bad. */
+              badcookie=TRUE;
+              infof(data, "skipped cookie with bad tailmatch domain: %s\n",
+                    whatptr);
+            }
+          }
+        }
+        else if(Curl_raw_equal("version", name)) {
+          strstore(&co->version, whatptr);
+          if(!co->version) {
+            badcookie = TRUE;
+            break;
+          }
+        }
+        else if(Curl_raw_equal("max-age", name)) {
+          /* Defined in RFC2109:
+
+             Optional.  The Max-Age attribute defines the lifetime of the
+             cookie, in seconds.  The delta-seconds value is a decimal non-
+             negative integer.  After delta-seconds seconds elapse, the
+             client should discard the cookie.  A value of zero means the
+             cookie should be discarded immediately.
+
+          */
+          strstore(&co->maxage, whatptr);
+          if(!co->maxage) {
+            badcookie = TRUE;
+            break;
+          }
+          co->expires =
+            strtol((*co->maxage=='\"')?&co->maxage[1]:&co->maxage[0],NULL,10)
+            + (long)now;
+        }
+        else if(Curl_raw_equal("expires", name)) {
+          strstore(&co->expirestr, whatptr);
+          if(!co->expirestr) {
+            badcookie = TRUE;
+            break;
+          }
+          /* Note that if the date couldn't get parsed for whatever reason,
+             the cookie will be treated as a session cookie */
+          co->expires = curl_getdate(what, &now);
+
+          /* Session cookies have expires set to 0 so if we get that back
+             from the date parser let's add a second to make it a
+             non-session cookie */
+          if(co->expires == 0)
+            co->expires = 1;
+          else if(co->expires < 0)
+            co->expires = 0;
+        }
+        else if(!co->name) {
+          co->name = strdup(name);
+          co->value = strdup(whatptr);
+          if(!co->name || !co->value) {
+            badcookie = TRUE;
+            break;
+          }
+        }
+        /*
+          else this is the second (or more) name we don't know
+          about! */
+      }
+      else {
+        /* this is an "illegal" <what>=<this> pair */
+      }
+
+      if(!semiptr || !*semiptr) {
+        /* we already know there are no more cookies */
+        semiptr = NULL;
+        continue;
+      }
+
+      ptr=semiptr+1;
+      while(*ptr && ISBLANK(*ptr))
+        ptr++;
+      semiptr=strchr(ptr, ';'); /* now, find the next semicolon */
+
+      if(!semiptr && *ptr)
+        /* There are no more semicolons, but there's a final name=value pair
+           coming up */
+        semiptr=strchr(ptr, '\0');
+    } while(semiptr);
+
+    if(!badcookie && !co->domain) {
+      if(domain) {
+        /* no domain was given in the header line, set the default */
+        co->domain=strdup(domain);
+        if(!co->domain)
+          badcookie = TRUE;
+      }
+    }
+
+    if(!badcookie && !co->path && path) {
+      /* No path was given in the header line, set the default.
+         Note that the passed-in path to this function MAY have a '?' and
+         following part that MUST not be stored as part of the path. */
+      char *queryp = strchr(path, '?');
+
+      /* queryp is where the interesting part of the path ends, so now we
+         want to the find the last */
+      char *endslash;
+      if(!queryp)
+        endslash = strrchr(path, '/');
+      else
+        endslash = memrchr(path, '/', (size_t)(queryp - path));
+      if(endslash) {
+        size_t pathlen = (size_t)(endslash-path+1); /* include ending slash */
+        co->path=malloc(pathlen+1); /* one extra for the zero byte */
+        if(co->path) {
+          memcpy(co->path, path, pathlen);
+          co->path[pathlen]=0; /* zero terminate */
+        }
+        else
+          badcookie = TRUE;
+      }
+    }
+
+    free(what);
+
+    if(badcookie || !co->name) {
+      /* we didn't get a cookie name or a bad one,
+         this is an illegal line, bail out */
+      freecookie(co);
+      return NULL;
+    }
+
+  }
+  else {
+    /* This line is NOT a HTTP header style line, we do offer support for
+       reading the odd netscape cookies-file format here */
+    char *ptr;
+    char *firstptr;
+    char *tok_buf=NULL;
+    int fields;
+
+    /* IE introduced HTTP-only cookies to prevent XSS attacks. Cookies
+       marked with httpOnly after the domain name are not accessible
+       from javascripts, but since curl does not operate at javascript
+       level, we include them anyway. In Firefox's cookie files, these
+       lines are preceded with #HttpOnly_ and then everything is
+       as usual, so we skip 10 characters of the line..
+    */
+    if(strncmp(lineptr, "#HttpOnly_", 10) == 0) {
+      lineptr += 10;
+      co->httponly = TRUE;
+    }
+
+    if(lineptr[0]=='#') {
+      /* don't even try the comments */
+      free(co);
+      return NULL;
+    }
+    /* strip off the possible end-of-line characters */
+    ptr=strchr(lineptr, '\r');
+    if(ptr)
+      *ptr=0; /* clear it */
+    ptr=strchr(lineptr, '\n');
+    if(ptr)
+      *ptr=0; /* clear it */
+
+    firstptr=strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */
+
+    /* Here's a quick check to eliminate normal HTTP-headers from this */
+    if(!firstptr || strchr(firstptr, ':')) {
+      free(co);
+      return NULL;
+    }
+
+    /* Now loop through the fields and init the struct we already have
+       allocated */
+    for(ptr=firstptr, fields=0; ptr && !badcookie;
+        ptr=strtok_r(NULL, "\t", &tok_buf), fields++) {
+      switch(fields) {
+      case 0:
+        if(ptr[0]=='.') /* skip preceding dots */
+          ptr++;
+        co->domain = strdup(ptr);
+        if(!co->domain)
+          badcookie = TRUE;
+        break;
+      case 1:
+        /* This field got its explanation on the 23rd of May 2001 by
+           Andrés García:
+
+           flag: A TRUE/FALSE value indicating if all machines within a given
+           domain can access the variable. This value is set automatically by
+           the browser, depending on the value you set for the domain.
+
+           As far as I can see, it is set to true when the cookie says
+           .domain.com and to false when the domain is complete www.domain.com
+        */
+        co->tailmatch = Curl_raw_equal(ptr, "TRUE")?TRUE:FALSE;
+        break;
+      case 2:
+        /* It turns out, that sometimes the file format allows the path
+           field to remain not filled in, we try to detect this and work
+           around it! Andrés García made us aware of this... */
+        if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) {
+          /* only if the path doesn't look like a boolean option! */
+          co->path = strdup(ptr);
+          if(!co->path)
+            badcookie = TRUE;
+          break;
+        }
+        /* this doesn't look like a path, make one up! */
+        co->path = strdup("/");
+        if(!co->path)
+          badcookie = TRUE;
+        fields++; /* add a field and fall down to secure */
+        /* FALLTHROUGH */
+      case 3:
+        co->secure = Curl_raw_equal(ptr, "TRUE")?TRUE:FALSE;
+        break;
+      case 4:
+        co->expires = curlx_strtoofft(ptr, NULL, 10);
+        break;
+      case 5:
+        co->name = strdup(ptr);
+        if(!co->name)
+          badcookie = TRUE;
+        break;
+      case 6:
+        co->value = strdup(ptr);
+        if(!co->value)
+          badcookie = TRUE;
+        break;
+      }
+    }
+    if(6 == fields) {
+      /* we got a cookie with blank contents, fix it */
+      co->value = strdup("");
+      if(!co->value)
+        badcookie = TRUE;
+      else
+        fields++;
+    }
+
+    if(!badcookie && (7 != fields))
+      /* we did not find the sufficient number of fields */
+      badcookie = TRUE;
+
+    if(badcookie) {
+      freecookie(co);
+      return NULL;
+    }
+
+  }
+
+  if(!c->running &&    /* read from a file */
+     c->newsession &&  /* clean session cookies */
+     !co->expires) {   /* this is a session cookie since it doesn't expire! */
+    freecookie(co);
+    return NULL;
+  }
+
+  co->livecookie = c->running;
+
+  /* now, we have parsed the incoming line, we must now check if this
+     superceeds an already existing cookie, which it may if the previous have
+     the same domain and path as this */
+
+  clist = c->cookies;
+  replace_old = FALSE;
+  while(clist) {
+    if(Curl_raw_equal(clist->name, co->name)) {
+      /* the names are identical */
+
+      if(clist->domain && co->domain) {
+        if(Curl_raw_equal(clist->domain, co->domain))
+          /* The domains are identical */
+          replace_old=TRUE;
+      }
+      else if(!clist->domain && !co->domain)
+        replace_old = TRUE;
+
+      if(replace_old) {
+        /* the domains were identical */
+
+        if(clist->path && co->path) {
+          if(Curl_raw_equal(clist->path, co->path)) {
+            replace_old = TRUE;
+          }
+          else
+            replace_old = FALSE;
+        }
+        else if(!clist->path && !co->path)
+          replace_old = TRUE;
+        else
+          replace_old = FALSE;
+
+      }
+
+      if(replace_old && !co->livecookie && clist->livecookie) {
+        /* Both cookies matched fine, except that the already present
+           cookie is "live", which means it was set from a header, while
+           the new one isn't "live" and thus only read from a file. We let
+           live cookies stay alive */
+
+        /* Free the newcomer and get out of here! */
+        freecookie(co);
+        return NULL;
+      }
+
+      if(replace_old) {
+        co->next = clist->next; /* get the next-pointer first */
+
+        /* then free all the old pointers */
+        free(clist->name);
+        if(clist->value)
+          free(clist->value);
+        if(clist->domain)
+          free(clist->domain);
+        if(clist->path)
+          free(clist->path);
+        if(clist->expirestr)
+          free(clist->expirestr);
+
+        if(clist->version)
+          free(clist->version);
+        if(clist->maxage)
+          free(clist->maxage);
+
+        *clist = *co;  /* then store all the new data */
+
+        free(co);   /* free the newly alloced memory */
+        co = clist; /* point to the previous struct instead */
+
+        /* We have replaced a cookie, now skip the rest of the list but
+           make sure the 'lastc' pointer is properly set */
+        do {
+          lastc = clist;
+          clist = clist->next;
+        } while(clist);
+        break;
+      }
+    }
+    lastc = clist;
+    clist = clist->next;
+  }
+
+  if(c->running)
+    /* Only show this when NOT reading the cookies from a file */
+    infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, "
+          "expire %" FORMAT_OFF_T "\n",
+          replace_old?"Replaced":"Added", co->name, co->value,
+          co->domain, co->path, co->expires);
+
+  if(!replace_old) {
+    /* then make the last item point on this new one */
+    if(lastc)
+      lastc->next = co;
+    else
+      c->cookies = co;
+  }
+
+  c->numcookies++; /* one more cookie in the jar */
+  return co;
+}
+
+/*****************************************************************************
+ *
+ * Curl_cookie_init()
+ *
+ * Inits a cookie struct to read data from a local file. This is always
+ * called before any cookies are set. File may be NULL.
+ *
+ * If 'newsession' is TRUE, discard all "session cookies" on read from file.
+ *
+ ****************************************************************************/
+struct CookieInfo *Curl_cookie_init(struct SessionHandle *data,
+                                    const char *file,
+                                    struct CookieInfo *inc,
+                                    bool newsession)
+{
+  struct CookieInfo *c;
+  FILE *fp;
+  bool fromfile=TRUE;
+
+  if(NULL == inc) {
+    /* we didn't get a struct, create one */
+    c = calloc(1, sizeof(struct CookieInfo));
+    if(!c)
+      return NULL; /* failed to get memory */
+    c->filename = strdup(file?file:"none"); /* copy the name just in case */
+  }
+  else {
+    /* we got an already existing one, use that */
+    c = inc;
+  }
+  c->running = FALSE; /* this is not running, this is init */
+
+  if(file && strequal(file, "-")) {
+    fp = stdin;
+    fromfile=FALSE;
+  }
+  else if(file && !*file) {
+    /* points to a "" string */
+    fp = NULL;
+  }
+  else
+    fp = file?fopen(file, "r"):NULL;
+
+  c->newsession = newsession; /* new session? */
+
+  if(fp) {
+    char *lineptr;
+    bool headerline;
+
+    char *line = malloc(MAX_COOKIE_LINE);
+    if(line) {
+      while(fgets(line, MAX_COOKIE_LINE, fp)) {
+        if(checkprefix("Set-Cookie:", line)) {
+          /* This is a cookie line, get it! */
+          lineptr=&line[11];
+          headerline=TRUE;
+        }
+        else {
+          lineptr=line;
+          headerline=FALSE;
+        }
+        while(*lineptr && ISBLANK(*lineptr))
+          lineptr++;
+
+        Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL);
+      }
+      free(line); /* free the line buffer */
+    }
+    if(fromfile)
+      fclose(fp);
+  }
+
+  c->running = TRUE;          /* now, we're running */
+
+  return c;
+}
+
+/* sort this so that the longest path gets before the shorter path */
+static int cookie_sort(const void *p1, const void *p2)
+{
+  struct Cookie *c1 = *(struct Cookie **)p1;
+  struct Cookie *c2 = *(struct Cookie **)p2;
+
+  size_t l1 = c1->path?strlen(c1->path):0;
+  size_t l2 = c2->path?strlen(c2->path):0;
+
+  return (l2 > l1) ? 1 : (l2 < l1) ? -1 : 0 ;
+}
+
+/*****************************************************************************
+ *
+ * Curl_cookie_getlist()
+ *
+ * For a given host and path, return a linked list of cookies that the
+ * client should send to the server if used now. The secure boolean informs
+ * the cookie if a secure connection is achieved or not.
+ *
+ * It shall only return cookies that haven't expired.
+ *
+ ****************************************************************************/
+
+struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
+                                   const char *host, const char *path,
+                                   bool secure)
+{
+  struct Cookie *newco;
+  struct Cookie *co;
+  time_t now = time(NULL);
+  struct Cookie *mainco=NULL;
+  size_t matches = 0;
+
+  if(!c || !c->cookies)
+    return NULL; /* no cookie struct or no cookies in the struct */
+
+  co = c->cookies;
+
+  while(co) {
+    /* only process this cookie if it is not expired or had no expire
+       date AND that if the cookie requires we're secure we must only
+       continue if we are! */
+    if((!co->expires || (co->expires > now)) &&
+       (co->secure?secure:TRUE)) {
+
+      /* now check if the domain is correct */
+      if(!co->domain ||
+         (co->tailmatch && tailmatch(co->domain, host)) ||
+         (!co->tailmatch && Curl_raw_equal(host, co->domain)) ) {
+        /* the right part of the host matches the domain stuff in the
+           cookie data */
+
+        /* now check the left part of the path with the cookies path
+           requirement */
+        if(!co->path ||
+           /* not using checkprefix() because matching should be
+              case-sensitive */
+           !strncmp(co->path, path, strlen(co->path)) ) {
+
+          /* and now, we know this is a match and we should create an
+             entry for the return-linked-list */
+
+          newco = malloc(sizeof(struct Cookie));
+          if(newco) {
+            /* first, copy the whole source cookie: */
+            memcpy(newco, co, sizeof(struct Cookie));
+
+            /* then modify our next */
+            newco->next = mainco;
+
+            /* point the main to us */
+            mainco = newco;
+
+            matches++;
+          }
+          else {
+            fail:
+            /* failure, clear up the allocated chain and return NULL */
+            while(mainco) {
+              co = mainco->next;
+              free(mainco);
+              mainco = co;
+            }
+
+            return NULL;
+          }
+        }
+      }
+    }
+    co = co->next;
+  }
+
+  if(matches) {
+    /* Now we need to make sure that if there is a name appearing more than
+       once, the longest specified path version comes first. To make this
+       the swiftest way, we just sort them all based on path length. */
+    struct Cookie **array;
+    size_t i;
+
+    /* alloc an array and store all cookie pointers */
+    array = malloc(sizeof(struct Cookie *) * matches);
+    if(!array)
+      goto fail;
+
+    co = mainco;
+
+    for(i=0; co; co = co->next)
+      array[i++] = co;
+
+    /* now sort the cookie pointers in path length order */
+    qsort(array, matches, sizeof(struct Cookie *), cookie_sort);
+
+    /* remake the linked list order according to the new order */
+
+    mainco = array[0]; /* start here */
+    for(i=0; i<matches-1; i++)
+      array[i]->next = array[i+1];
+    array[matches-1]->next = NULL; /* terminate the list */
+
+    free(array); /* remove the temporary data again */
+  }
+
+  return mainco; /* return the new list */
+}
+
+/*****************************************************************************
+ *
+ * Curl_cookie_clearall()
+ *
+ * Clear all existing cookies and reset the counter.
+ *
+ ****************************************************************************/
+void Curl_cookie_clearall(struct CookieInfo *cookies)
+{
+  if(cookies) {
+    Curl_cookie_freelist(cookies->cookies, TRUE);
+    cookies->cookies = NULL;
+    cookies->numcookies = 0;
+  }
+}
+
+/*****************************************************************************
+ *
+ * Curl_cookie_freelist()
+ *
+ * Free a list of cookies previously returned by Curl_cookie_getlist();
+ *
+ * The 'cookiestoo' argument tells this function whether to just free the
+ * list or actually also free all cookies within the list as well.
+ *
+ ****************************************************************************/
+
+void Curl_cookie_freelist(struct Cookie *co, bool cookiestoo)
+{
+  struct Cookie *next;
+  if(co) {
+    while(co) {
+      next = co->next;
+      if(cookiestoo)
+        freecookie(co);
+      else
+        free(co); /* we only free the struct since the "members" are all just
+                     pointed out in the main cookie list! */
+      co = next;
+    }
+  }
+}
+
+
+/*****************************************************************************
+ *
+ * Curl_cookie_clearsess()
+ *
+ * Free all session cookies in the cookies list.
+ *
+ ****************************************************************************/
+void Curl_cookie_clearsess(struct CookieInfo *cookies)
+{
+  struct Cookie *first, *curr, *next, *prev = NULL;
+
+  if(!cookies || !cookies->cookies)
+    return;
+
+  first = curr = prev = cookies->cookies;
+
+  for(; curr; curr = next) {
+    next = curr->next;
+    if(!curr->expires) {
+      if(first == curr)
+        first = next;
+
+      if(prev == curr)
+        prev = next;
+      else
+        prev->next = next;
+
+      freecookie(curr);
+      cookies->numcookies--;
+    }
+    else
+      prev = curr;
+  }
+
+  cookies->cookies = first;
+}
+
+
+/*****************************************************************************
+ *
+ * Curl_cookie_cleanup()
+ *
+ * Free a "cookie object" previous created with cookie_init().
+ *
+ ****************************************************************************/
+void Curl_cookie_cleanup(struct CookieInfo *c)
+{
+  struct Cookie *co;
+  struct Cookie *next;
+  if(c) {
+    if(c->filename)
+      free(c->filename);
+    co = c->cookies;
+
+    while(co) {
+      next = co->next;
+      freecookie(co);
+      co = next;
+    }
+    free(c); /* free the base struct as well */
+  }
+}
+
+/* get_netscape_format()
+ *
+ * Formats a string for Netscape output file, w/o a newline at the end.
+ *
+ * Function returns a char * to a formatted line. Has to be free()d
+*/
+static char *get_netscape_format(const struct Cookie *co)
+{
+  return aprintf(
+    "%s"     /* httponly preamble */
+    "%s%s\t" /* domain */
+    "%s\t"   /* tailmatch */
+    "%s\t"   /* path */
+    "%s\t"   /* secure */
+    "%" FORMAT_OFF_T "\t"   /* expires */
+    "%s\t"   /* name */
+    "%s",    /* value */
+    co->httponly?"#HttpOnly_":"",
+    /* Make sure all domains are prefixed with a dot if they allow
+       tailmatching. This is Mozilla-style. */
+    (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"",
+    co->domain?co->domain:"unknown",
+    co->tailmatch?"TRUE":"FALSE",
+    co->path?co->path:"/",
+    co->secure?"TRUE":"FALSE",
+    co->expires,
+    co->name,
+    co->value?co->value:"");
+}
+
+/*
+ * cookie_output()
+ *
+ * Writes all internally known cookies to the specified file. Specify
+ * "-" as file name to write to stdout.
+ *
+ * The function returns non-zero on write failure.
+ */
+static int cookie_output(struct CookieInfo *c, const char *dumphere)
+{
+  struct Cookie *co;
+  FILE *out;
+  bool use_stdout=FALSE;
+
+  if((NULL == c) || (0 == c->numcookies))
+    /* If there are no known cookies, we don't write or even create any
+       destination file */
+    return 0;
+
+  if(strequal("-", dumphere)) {
+    /* use stdout */
+    out = stdout;
+    use_stdout=TRUE;
+  }
+  else {
+    out = fopen(dumphere, "w");
+    if(!out)
+      return 1; /* failure */
+  }
+
+  if(c) {
+    char *format_ptr;
+
+    fputs("# Netscape HTTP Cookie File\n"
+          "# http://curl.haxx.se/docs/http-cookies.html\n"
+          "# This file was generated by libcurl! Edit at your own risk.\n\n",
+          out);
+    co = c->cookies;
+
+    while(co) {
+      format_ptr = get_netscape_format(co);
+      if(format_ptr == NULL) {
+        fprintf(out, "#\n# Fatal libcurl error\n");
+        if(!use_stdout)
+          fclose(out);
+        return 1;
+      }
+      fprintf(out, "%s\n", format_ptr);
+      free(format_ptr);
+      co=co->next;
+    }
+  }
+
+  if(!use_stdout)
+    fclose(out);
+
+  return 0;
+}
+
+struct curl_slist *Curl_cookie_list(struct SessionHandle *data)
+{
+  struct curl_slist *list = NULL;
+  struct curl_slist *beg;
+  struct Cookie *c;
+  char *line;
+
+  if((data->cookies == NULL) ||
+      (data->cookies->numcookies == 0))
+    return NULL;
+
+  c = data->cookies->cookies;
+
+  while(c) {
+    /* fill the list with _all_ the cookies we know */
+    line = get_netscape_format(c);
+    if(!line) {
+      curl_slist_free_all(list);
+      return NULL;
+    }
+    beg = curl_slist_append(list, line);
+    free(line);
+    if(!beg) {
+      curl_slist_free_all(list);
+      return NULL;
+    }
+    list = beg;
+    c = c->next;
+  }
+
+  return list;
+}
+
+void Curl_flush_cookies(struct SessionHandle *data, int cleanup)
+{
+  if(data->set.str[STRING_COOKIEJAR]) {
+    if(data->change.cookielist) {
+      /* If there is a list of cookie files to read, do it first so that
+         we have all the told files read before we write the new jar.
+         Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */
+      Curl_cookie_loadfiles(data);
+    }
+
+    Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
+
+    /* if we have a destination file for all the cookies to get dumped to */
+    if(cookie_output(data->cookies, data->set.str[STRING_COOKIEJAR]))
+      infof(data, "WARNING: failed to save cookies in %s\n",
+            data->set.str[STRING_COOKIEJAR]);
+  }
+  else {
+    if(cleanup && data->change.cookielist) {
+      /* since nothing is written, we can just free the list of cookie file
+         names */
+      curl_slist_free_all(data->change.cookielist); /* clean up list */
+      data->change.cookielist = NULL;
+    }
+    Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
+  }
+
+  if(cleanup && (!data->share || (data->cookies != data->share->cookies))) {
+    Curl_cookie_cleanup(data->cookies);
+  }
+  Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
+}
+
+#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */
diff --git a/lib/curl_cyassl.c b/lib/curl_cyassl.c
new file mode 100644 (file)
index 0000000..32f1cfe
--- /dev/null
@@ -0,0 +1,611 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/*
+ * Source file for all CyaSSL-specific code for the TLS/SSL layer. No code
+ * but curl_sslgen.c should ever call or use these functions.
+ *
+ */
+
+#include "curl_setup.h"
+
+#ifdef USE_CYASSL
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_inet_pton.h"
+#include "curl_cyassl.h"
+#include "curl_sslgen.h"
+#include "curl_parsedate.h"
+#include "curl_connect.h" /* for the connect timeout */
+#include "curl_select.h"
+#include "curl_rawstr.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+#include <cyassl/ssl.h>
+#include <cyassl/error.h>
+
+
+static Curl_recv cyassl_recv;
+static Curl_send cyassl_send;
+
+
+static int do_file_type(const char *type)
+{
+  if(!type || !type[0])
+    return SSL_FILETYPE_PEM;
+  if(Curl_raw_equal(type, "PEM"))
+    return SSL_FILETYPE_PEM;
+  if(Curl_raw_equal(type, "DER"))
+    return SSL_FILETYPE_ASN1;
+  return -1;
+}
+
+/*
+ * This function loads all the client/CA certificates and CRLs. Setup the TLS
+ * layer and do all necessary magic.
+ */
+static CURLcode
+cyassl_connect_step1(struct connectdata *conn,
+                     int sockindex)
+{
+  struct SessionHandle *data = conn->data;
+  struct ssl_connect_data* conssl = &conn->ssl[sockindex];
+  SSL_METHOD* req_method = NULL;
+  void* ssl_sessionid = NULL;
+  curl_socket_t sockfd = conn->sock[sockindex];
+
+  if(conssl->state == ssl_connection_complete)
+    return CURLE_OK;
+
+  /* CyaSSL doesn't support SSLv2 */
+  if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) {
+    failf(data, "CyaSSL does not support SSLv2");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  /* check to see if we've been told to use an explicit SSL/TLS version */
+  switch(data->set.ssl.version) {
+  case CURL_SSLVERSION_DEFAULT:
+    /* we try to figure out version */
+    req_method = SSLv23_client_method();
+    break;
+  case CURL_SSLVERSION_TLSv1:
+    req_method = TLSv1_client_method();
+    break;
+  case CURL_SSLVERSION_SSLv3:
+    req_method = SSLv3_client_method();
+    break;
+  default:
+    req_method = TLSv1_client_method();
+  }
+
+  if(!req_method) {
+    failf(data, "SSL: couldn't create a method!");
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  if(conssl->ctx)
+    SSL_CTX_free(conssl->ctx);
+  conssl->ctx = SSL_CTX_new(req_method);
+
+  if(!conssl->ctx) {
+    failf(data, "SSL: couldn't create a context!");
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+#ifndef NO_FILESYSTEM
+  /* load trusted cacert */
+  if(data->set.str[STRING_SSL_CAFILE]) {
+    if(!SSL_CTX_load_verify_locations(conssl->ctx,
+                                      data->set.str[STRING_SSL_CAFILE],
+                                      data->set.str[STRING_SSL_CAPATH])) {
+      if(data->set.ssl.verifypeer) {
+        /* Fail if we insiste on successfully verifying the server. */
+        failf(data,"error setting certificate verify locations:\n"
+              "  CAfile: %s\n  CApath: %s",
+              data->set.str[STRING_SSL_CAFILE]?
+              data->set.str[STRING_SSL_CAFILE]: "none",
+              data->set.str[STRING_SSL_CAPATH]?
+              data->set.str[STRING_SSL_CAPATH] : "none");
+        return CURLE_SSL_CACERT_BADFILE;
+      }
+      else {
+        /* Just continue with a warning if no strict  certificate
+           verification is required. */
+        infof(data, "error setting certificate verify locations,"
+              " continuing anyway:\n");
+      }
+    }
+    else {
+      /* Everything is fine. */
+      infof(data, "successfully set certificate verify locations:\n");
+    }
+    infof(data,
+          "  CAfile: %s\n"
+          "  CApath: %s\n",
+          data->set.str[STRING_SSL_CAFILE] ? data->set.str[STRING_SSL_CAFILE]:
+          "none",
+          data->set.str[STRING_SSL_CAPATH] ? data->set.str[STRING_SSL_CAPATH]:
+          "none");
+  }
+
+  /* Load the client certificate, and private key */
+  if(data->set.str[STRING_CERT] && data->set.str[STRING_KEY]) {
+    int file_type = do_file_type(data->set.str[STRING_CERT_TYPE]);
+
+    if(SSL_CTX_use_certificate_file(conssl->ctx, data->set.str[STRING_CERT],
+                                     file_type) != 1) {
+      failf(data, "unable to use client certificate (no key or wrong pass"
+            " phrase?)");
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+
+    file_type = do_file_type(data->set.str[STRING_KEY_TYPE]);
+    if(SSL_CTX_use_PrivateKey_file(conssl->ctx, data->set.str[STRING_KEY],
+                                    file_type) != 1) {
+      failf(data, "unable to set private key");
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+  }
+#else
+  if(CyaSSL_no_filesystem_verify(conssl->ctx)!= SSL_SUCCESS) {
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+#endif /* NO_FILESYSTEM */
+
+  /* SSL always tries to verify the peer, this only says whether it should
+   * fail to connect if the verification fails, or if it should continue
+   * anyway. In the latter case the result of the verification is checked with
+   * SSL_get_verify_result() below. */
+  SSL_CTX_set_verify(conssl->ctx,
+                     data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE,
+                     NULL);
+
+  /* Let's make an SSL structure */
+  if(conssl->handle)
+    SSL_free(conssl->handle);
+  conssl->handle = SSL_new(conssl->ctx);
+  if(!conssl->handle) {
+    failf(data, "SSL: couldn't create a context (handle)!");
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  /* Check if there's a cached ID we can/should use here! */
+  if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) {
+    /* we got a session id, use it! */
+    if(!SSL_set_session(conssl->handle, ssl_sessionid)) {
+      failf(data, "SSL: SSL_set_session failed: %s",
+            ERR_error_string(SSL_get_error(conssl->handle, 0),NULL));
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+    /* Informational message */
+    infof (data, "SSL re-using session ID\n");
+  }
+
+  /* pass the raw socket into the SSL layer */
+  if(!SSL_set_fd(conssl->handle, (int)sockfd)) {
+    failf(data, "SSL: SSL_set_fd failed");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  conssl->connecting_state = ssl_connect_2;
+  return CURLE_OK;
+}
+
+
+static CURLcode
+cyassl_connect_step2(struct connectdata *conn,
+                     int sockindex)
+{
+  int ret = -1;
+  struct SessionHandle *data = conn->data;
+  struct ssl_connect_data* conssl = &conn->ssl[sockindex];
+
+  infof(data, "CyaSSL: Connecting to %s:%d\n",
+        conn->host.name, conn->remote_port);
+
+  conn->recv[sockindex] = cyassl_recv;
+  conn->send[sockindex] = cyassl_send;
+
+  /* Enable RFC2818 checks */
+  if(data->set.ssl.verifyhost) {
+    ret = CyaSSL_check_domain_name(conssl->handle, conn->host.name);
+    if(ret == SSL_FAILURE)
+      return CURLE_OUT_OF_MEMORY;
+  }
+
+  ret = SSL_connect(conssl->handle);
+  if(ret != 1) {
+    char error_buffer[80];
+    int  detail = SSL_get_error(conssl->handle, ret);
+
+    if(SSL_ERROR_WANT_READ == detail) {
+      conssl->connecting_state = ssl_connect_2_reading;
+      return CURLE_OK;
+    }
+    else if(SSL_ERROR_WANT_WRITE == detail) {
+      conssl->connecting_state = ssl_connect_2_writing;
+      return CURLE_OK;
+    }
+    /* There is no easy way to override only the CN matching.
+     * This will enable the override of both mismatching SubjectAltNames
+     * as also mismatching CN fields */
+    else if(DOMAIN_NAME_MISMATCH == detail) {
+#if 1
+      failf(data, "\tsubject alt name(s) or common name do not match \"%s\"\n",
+            conn->host.dispname);
+      return CURLE_PEER_FAILED_VERIFICATION;
+#else
+      /* When the CyaSSL_check_domain_name() is used and you desire to continue
+       * on a DOMAIN_NAME_MISMATCH, i.e. 'data->set.ssl.verifyhost == 0',
+       * CyaSSL version 2.4.0 will fail with an INCOMPLETE_DATA error. The only
+       * way to do this is currently to switch the CyaSSL_check_domain_name()
+       * in and out based on the 'data->set.ssl.verifyhost' value. */
+      if(data->set.ssl.verifyhost) {
+        failf(data,
+              "\tsubject alt name(s) or common name do not match \"%s\"\n",
+              conn->host.dispname);
+        return CURLE_PEER_FAILED_VERIFICATION;
+      }
+      else {
+        infof(data,
+              "\tsubject alt name(s) and/or common name do not match \"%s\"\n",
+              conn->host.dispname);
+        return CURLE_OK;
+      }
+#endif
+    }
+    else {
+      failf(data, "SSL_connect failed with error %d: %s", detail,
+          ERR_error_string(detail, error_buffer));
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+  }
+
+  conssl->connecting_state = ssl_connect_3;
+  infof(data, "SSL connected\n");
+
+  return CURLE_OK;
+}
+
+
+static CURLcode
+cyassl_connect_step3(struct connectdata *conn,
+                     int sockindex)
+{
+  CURLcode retcode = CURLE_OK;
+  void *old_ssl_sessionid=NULL;
+  struct SessionHandle *data = conn->data;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  int incache;
+  SSL_SESSION *our_ssl_sessionid;
+
+  DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
+
+  our_ssl_sessionid = SSL_get_session(connssl->handle);
+
+  incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL));
+  if(incache) {
+    if(old_ssl_sessionid != our_ssl_sessionid) {
+      infof(data, "old SSL session ID is stale, removing\n");
+      Curl_ssl_delsessionid(conn, old_ssl_sessionid);
+      incache = FALSE;
+    }
+  }
+  if(!incache) {
+    retcode = Curl_ssl_addsessionid(conn, our_ssl_sessionid,
+                                    0 /* unknown size */);
+    if(retcode) {
+      failf(data, "failed to store ssl session");
+      return retcode;
+    }
+  }
+
+  connssl->connecting_state = ssl_connect_done;
+
+  return retcode;
+}
+
+
+static ssize_t cyassl_send(struct connectdata *conn,
+                           int sockindex,
+                           const void *mem,
+                           size_t len,
+                           CURLcode *curlcode)
+{
+  char error_buffer[80];
+  int  memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
+  int  rc     = SSL_write(conn->ssl[sockindex].handle, mem, memlen);
+
+  if(rc < 0) {
+    int err = SSL_get_error(conn->ssl[sockindex].handle, rc);
+
+    switch(err) {
+    case SSL_ERROR_WANT_READ:
+    case SSL_ERROR_WANT_WRITE:
+      /* there's data pending, re-invoke SSL_write() */
+      *curlcode = CURLE_AGAIN;
+      return -1;
+    default:
+      failf(conn->data, "SSL write: %s, errno %d",
+            ERR_error_string(err, error_buffer),
+            SOCKERRNO);
+      *curlcode = CURLE_SEND_ERROR;
+      return -1;
+    }
+  }
+  return rc;
+}
+
+void Curl_cyassl_close_all(struct SessionHandle *data)
+{
+  (void)data;
+}
+
+void Curl_cyassl_close(struct connectdata *conn, int sockindex)
+{
+  struct ssl_connect_data *conssl = &conn->ssl[sockindex];
+
+  if(conssl->handle) {
+    (void)SSL_shutdown(conssl->handle);
+    SSL_free (conssl->handle);
+    conssl->handle = NULL;
+  }
+  if(conssl->ctx) {
+    SSL_CTX_free (conssl->ctx);
+    conssl->ctx = NULL;
+  }
+}
+
+static ssize_t cyassl_recv(struct connectdata *conn,
+                           int num,
+                           char *buf,
+                           size_t buffersize,
+                           CURLcode *curlcode)
+{
+  char error_buffer[80];
+  int  buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
+  int  nread    = SSL_read(conn->ssl[num].handle, buf, buffsize);
+
+  if(nread < 0) {
+    int err = SSL_get_error(conn->ssl[num].handle, nread);
+
+    switch(err) {
+    case SSL_ERROR_ZERO_RETURN: /* no more data */
+      break;
+    case SSL_ERROR_WANT_READ:
+    case SSL_ERROR_WANT_WRITE:
+      /* there's data pending, re-invoke SSL_read() */
+      *curlcode = CURLE_AGAIN;
+      return -1;
+    default:
+      failf(conn->data, "SSL read: %s, errno %d",
+            ERR_error_string(err, error_buffer),
+            SOCKERRNO);
+      *curlcode = CURLE_RECV_ERROR;
+      return -1;
+    }
+  }
+  return nread;
+}
+
+
+void Curl_cyassl_session_free(void *ptr)
+{
+  (void)ptr;
+  /* CyaSSL reuses sessions on own, no free */
+}
+
+
+size_t Curl_cyassl_version(char *buffer, size_t size)
+{
+#ifdef CYASSL_VERSION
+  return snprintf(buffer, size, "CyaSSL/%s", CYASSL_VERSION);
+#else
+  return snprintf(buffer, size, "CyaSSL/%s", "<1.8.8");
+#endif
+}
+
+
+int Curl_cyassl_init(void)
+{
+  if(CyaSSL_Init() == 0)
+    return 1;
+
+  return -1;
+}
+
+
+bool Curl_cyassl_data_pending(const struct connectdata* conn, int connindex)
+{
+  if(conn->ssl[connindex].handle)   /* SSL is in use */
+    return (0 != SSL_pending(conn->ssl[connindex].handle)) ? TRUE : FALSE;
+  else
+    return FALSE;
+}
+
+
+/*
+ * This function is called to shut down the SSL layer but keep the
+ * socket open (CCC - Clear Command Channel)
+ */
+int Curl_cyassl_shutdown(struct connectdata *conn, int sockindex)
+{
+  int retval = 0;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+
+  if(connssl->handle) {
+    SSL_free (connssl->handle);
+    connssl->handle = NULL;
+  }
+  return retval;
+}
+
+
+static CURLcode
+cyassl_connect_common(struct connectdata *conn,
+                      int sockindex,
+                      bool nonblocking,
+                      bool *done)
+{
+  CURLcode retcode;
+  struct SessionHandle *data = conn->data;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  curl_socket_t sockfd = conn->sock[sockindex];
+  long timeout_ms;
+  int what;
+
+  /* check if the connection has already been established */
+  if(ssl_connection_complete == connssl->state) {
+    *done = TRUE;
+    return CURLE_OK;
+  }
+
+  if(ssl_connect_1==connssl->connecting_state) {
+    /* Find out how much more time we're allowed */
+    timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+    if(timeout_ms < 0) {
+      /* no need to continue if time already is up */
+      failf(data, "SSL connection timeout");
+      return CURLE_OPERATION_TIMEDOUT;
+    }
+    retcode = cyassl_connect_step1(conn, sockindex);
+    if(retcode)
+      return retcode;
+  }
+
+  while(ssl_connect_2 == connssl->connecting_state ||
+        ssl_connect_2_reading == connssl->connecting_state ||
+        ssl_connect_2_writing == connssl->connecting_state) {
+
+    /* check allowed time left */
+    timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+    if(timeout_ms < 0) {
+      /* no need to continue if time already is up */
+      failf(data, "SSL connection timeout");
+      return CURLE_OPERATION_TIMEDOUT;
+    }
+
+    /* if ssl is expecting something, check if it's available. */
+    if(connssl->connecting_state == ssl_connect_2_reading
+       || connssl->connecting_state == ssl_connect_2_writing) {
+
+      curl_socket_t writefd = ssl_connect_2_writing==
+        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+      curl_socket_t readfd = ssl_connect_2_reading==
+        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+
+      what = Curl_socket_ready(readfd, writefd, nonblocking?0:timeout_ms);
+      if(what < 0) {
+        /* fatal error */
+        failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+        return CURLE_SSL_CONNECT_ERROR;
+      }
+      else if(0 == what) {
+        if(nonblocking) {
+          *done = FALSE;
+          return CURLE_OK;
+        }
+        else {
+          /* timeout */
+          failf(data, "SSL connection timeout");
+          return CURLE_OPERATION_TIMEDOUT;
+        }
+      }
+      /* socket is readable or writable */
+    }
+
+    /* Run transaction, and return to the caller if it failed or if
+     * this connection is part of a multi handle and this loop would
+     * execute again. This permits the owner of a multi handle to
+     * abort a connection attempt before step2 has completed while
+     * ensuring that a client using select() or epoll() will always
+     * have a valid fdset to wait on.
+     */
+    retcode = cyassl_connect_step2(conn, sockindex);
+    if(retcode || (nonblocking &&
+                   (ssl_connect_2 == connssl->connecting_state ||
+                    ssl_connect_2_reading == connssl->connecting_state ||
+                    ssl_connect_2_writing == connssl->connecting_state)))
+      return retcode;
+
+  } /* repeat step2 until all transactions are done. */
+
+  if(ssl_connect_3==connssl->connecting_state) {
+    retcode = cyassl_connect_step3(conn, sockindex);
+    if(retcode)
+      return retcode;
+  }
+
+  if(ssl_connect_done==connssl->connecting_state) {
+    connssl->state = ssl_connection_complete;
+    conn->recv[sockindex] = cyassl_recv;
+    conn->send[sockindex] = cyassl_send;
+    *done = TRUE;
+  }
+  else
+    *done = FALSE;
+
+  /* Reset our connect state machine */
+  connssl->connecting_state = ssl_connect_1;
+
+  return CURLE_OK;
+}
+
+
+CURLcode
+Curl_cyassl_connect_nonblocking(struct connectdata *conn,
+                                int sockindex,
+                                bool *done)
+{
+  return cyassl_connect_common(conn, sockindex, TRUE, done);
+}
+
+
+CURLcode
+Curl_cyassl_connect(struct connectdata *conn,
+                    int sockindex)
+{
+  CURLcode retcode;
+  bool done = FALSE;
+
+  retcode = cyassl_connect_common(conn, sockindex, FALSE, &done);
+  if(retcode)
+    return retcode;
+
+  DEBUGASSERT(done);
+
+  return CURLE_OK;
+}
+
+#endif
diff --git a/lib/curl_dict.c b/lib/curl_dict.c
new file mode 100644 (file)
index 0000000..114ef7c
--- /dev/null
@@ -0,0 +1,284 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_DICT
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#include "curl_urldata.h"
+#include <curl/curl.h>
+#include "curl_transfer.h"
+#include "curl_sendf.h"
+
+#include "curl_progress.h"
+#include "curl_strequal.h"
+#include "curl_dict.h"
+#include "curl_rawstr.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/*
+ * Forward declarations.
+ */
+
+static CURLcode dict_do(struct connectdata *conn, bool *done);
+
+/*
+ * DICT protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_dict = {
+  "DICT",                               /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  dict_do,                              /* do_it */
+  ZERO_NULL,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  ZERO_NULL,                            /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  PORT_DICT,                            /* defport */
+  CURLPROTO_DICT,                       /* protocol */
+  PROTOPT_NONE | PROTOPT_NOURLQUERY      /* flags */
+};
+
+static char *unescape_word(struct SessionHandle *data, const char *inputbuff)
+{
+  char *newp;
+  char *dictp;
+  char *ptr;
+  int len;
+  char byte;
+  int olen=0;
+
+  newp = curl_easy_unescape(data, inputbuff, 0, &len);
+  if(!newp)
+    return NULL;
+
+  dictp = malloc(((size_t)len)*2 + 1); /* add one for terminating zero */
+  if(dictp) {
+    /* According to RFC2229 section 2.2, these letters need to be escaped with
+       \[letter] */
+    for(ptr = newp;
+        (byte = *ptr) != 0;
+        ptr++) {
+      if((byte <= 32) || (byte == 127) ||
+          (byte == '\'') || (byte == '\"') || (byte == '\\')) {
+        dictp[olen++] = '\\';
+      }
+      dictp[olen++] = byte;
+    }
+    dictp[olen]=0;
+
+    free(newp);
+  }
+  return dictp;
+}
+
+static CURLcode dict_do(struct connectdata *conn, bool *done)
+{
+  char *word;
+  char *eword;
+  char *ppath;
+  char *database = NULL;
+  char *strategy = NULL;
+  char *nthdef = NULL; /* This is not part of the protocol, but required
+                          by RFC 2229 */
+  CURLcode result=CURLE_OK;
+  struct SessionHandle *data=conn->data;
+  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
+
+  char *path = data->state.path;
+  curl_off_t *bytecount = &data->req.bytecount;
+
+  *done = TRUE; /* unconditionally */
+
+  if(conn->bits.user_passwd) {
+    /* AUTH is missing */
+  }
+
+  if(Curl_raw_nequal(path, DICT_MATCH, sizeof(DICT_MATCH)-1) ||
+      Curl_raw_nequal(path, DICT_MATCH2, sizeof(DICT_MATCH2)-1) ||
+      Curl_raw_nequal(path, DICT_MATCH3, sizeof(DICT_MATCH3)-1)) {
+
+    word = strchr(path, ':');
+    if(word) {
+      word++;
+      database = strchr(word, ':');
+      if(database) {
+        *database++ = (char)0;
+        strategy = strchr(database, ':');
+        if(strategy) {
+          *strategy++ = (char)0;
+          nthdef = strchr(strategy, ':');
+          if(nthdef) {
+            *nthdef = (char)0;
+          }
+        }
+      }
+    }
+
+    if((word == NULL) || (*word == (char)0)) {
+      infof(data, "lookup word is missing\n");
+      word=(char *)"default";
+    }
+    if((database == NULL) || (*database == (char)0)) {
+      database = (char *)"!";
+    }
+    if((strategy == NULL) || (*strategy == (char)0)) {
+      strategy = (char *)".";
+    }
+
+    eword = unescape_word(data, word);
+    if(!eword)
+      return CURLE_OUT_OF_MEMORY;
+
+    result = Curl_sendf(sockfd, conn,
+                        "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
+                        "MATCH "
+                        "%s "    /* database */
+                        "%s "    /* strategy */
+                        "%s\r\n" /* word */
+                        "QUIT\r\n",
+
+                        database,
+                        strategy,
+                        eword
+                        );
+
+    free(eword);
+
+    if(result) {
+      failf(data, "Failed sending DICT request");
+      return result;
+    }
+    Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount,
+                        -1, NULL); /* no upload */
+  }
+  else if(Curl_raw_nequal(path, DICT_DEFINE, sizeof(DICT_DEFINE)-1) ||
+           Curl_raw_nequal(path, DICT_DEFINE2, sizeof(DICT_DEFINE2)-1) ||
+           Curl_raw_nequal(path, DICT_DEFINE3, sizeof(DICT_DEFINE3)-1)) {
+
+    word = strchr(path, ':');
+    if(word) {
+      word++;
+      database = strchr(word, ':');
+      if(database) {
+        *database++ = (char)0;
+        nthdef = strchr(database, ':');
+        if(nthdef) {
+          *nthdef = (char)0;
+        }
+      }
+    }
+
+    if((word == NULL) || (*word == (char)0)) {
+      infof(data, "lookup word is missing\n");
+      word=(char *)"default";
+    }
+    if((database == NULL) || (*database == (char)0)) {
+      database = (char *)"!";
+    }
+
+    eword = unescape_word(data, word);
+    if(!eword)
+      return CURLE_OUT_OF_MEMORY;
+
+    result = Curl_sendf(sockfd, conn,
+                        "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
+                        "DEFINE "
+                        "%s "     /* database */
+                        "%s\r\n"  /* word */
+                        "QUIT\r\n",
+                        database,
+                        eword);
+
+    free(eword);
+
+    if(result) {
+      failf(data, "Failed sending DICT request");
+      return result;
+    }
+    Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount,
+                        -1, NULL); /* no upload */
+  }
+  else {
+
+    ppath = strchr(path, '/');
+    if(ppath) {
+      int i;
+
+      ppath++;
+      for(i = 0; ppath[i]; i++) {
+        if(ppath[i] == ':')
+          ppath[i] = ' ';
+      }
+      result = Curl_sendf(sockfd, conn,
+                          "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
+                          "%s\r\n"
+                          "QUIT\r\n", ppath);
+      if(result) {
+        failf(data, "Failed sending DICT request");
+        return result;
+      }
+
+      Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount, -1, NULL);
+    }
+  }
+
+  return CURLE_OK;
+}
+#endif /*CURL_DISABLE_DICT*/
diff --git a/lib/curl_easy.c b/lib/curl_easy.c
new file mode 100644 (file)
index 0000000..a2181cc
--- /dev/null
@@ -0,0 +1,897 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#include "curl_strequal.h"
+#include "curl_urldata.h"
+#include <curl/curl.h>
+#include "curl_transfer.h"
+#include "curl_sslgen.h"
+#include "curl_url.h"
+#include "curl_getinfo.h"
+#include "curl_hostip.h"
+#include "curl_share.h"
+#include "curl_strdup.h"
+#include "curl_memory.h"
+#include "curl_progress.h"
+#include "curl_easyif.h"
+#include "curl_select.h"
+#include "curl_sendf.h" /* for failf function prototype */
+#include "curl_ntlm.h"
+#include "curl_connect.h" /* for Curl_getconnectinfo */
+#include "curl_slist.h"
+#include "curl_amigaos.h"
+#include "curl_rand.h"
+#include "curl_non_ascii.h"
+#include "curl_warnless.h"
+#include "curl_conncache.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/* win32_cleanup() is for win32 socket cleanup functionality, the opposite
+   of win32_init() */
+static void win32_cleanup(void)
+{
+#ifdef USE_WINSOCK
+  WSACleanup();
+#endif
+#ifdef USE_WINDOWS_SSPI
+  Curl_sspi_global_cleanup();
+#endif
+}
+
+/* win32_init() performs win32 socket initialization to properly setup the
+   stack to allow networking */
+static CURLcode win32_init(void)
+{
+#ifdef USE_WINSOCK
+  WORD wVersionRequested;
+  WSADATA wsaData;
+  int res;
+
+#if defined(ENABLE_IPV6) && (USE_WINSOCK < 2)
+  Error IPV6_requires_winsock2
+#endif
+
+  wVersionRequested = MAKEWORD(USE_WINSOCK, USE_WINSOCK);
+
+  res = WSAStartup(wVersionRequested, &wsaData);
+
+  if(res != 0)
+    /* Tell the user that we couldn't find a useable */
+    /* winsock.dll.     */
+    return CURLE_FAILED_INIT;
+
+  /* Confirm that the Windows Sockets DLL supports what we need.*/
+  /* Note that if the DLL supports versions greater */
+  /* than wVersionRequested, it will still return */
+  /* wVersionRequested in wVersion. wHighVersion contains the */
+  /* highest supported version. */
+
+  if(LOBYTE( wsaData.wVersion ) != LOBYTE(wVersionRequested) ||
+     HIBYTE( wsaData.wVersion ) != HIBYTE(wVersionRequested) ) {
+    /* Tell the user that we couldn't find a useable */
+
+    /* winsock.dll. */
+    WSACleanup();
+    return CURLE_FAILED_INIT;
+  }
+  /* The Windows Sockets DLL is acceptable. Proceed. */
+#elif defined(USE_LWIPSOCK)
+  lwip_init();
+#endif
+
+#ifdef USE_WINDOWS_SSPI
+  {
+    CURLcode err = Curl_sspi_global_init();
+    if(err != CURLE_OK)
+      return err;
+  }
+#endif
+
+  return CURLE_OK;
+}
+
+#ifdef USE_LIBIDN
+/*
+ * Initialise use of IDNA library.
+ * It falls back to ASCII if $CHARSET isn't defined. This doesn't work for
+ * idna_to_ascii_lz().
+ */
+static void idna_init (void)
+{
+#ifdef WIN32
+  char buf[60];
+  UINT cp = GetACP();
+
+  if(!getenv("CHARSET") && cp > 0) {
+    snprintf(buf, sizeof(buf), "CHARSET=cp%u", cp);
+    putenv(buf);
+  }
+#else
+  /* to do? */
+#endif
+}
+#endif  /* USE_LIBIDN */
+
+/* true globals -- for curl_global_init() and curl_global_cleanup() */
+static unsigned int  initialized;
+static long          init_flags;
+
+/*
+ * strdup (and other memory functions) is redefined in complicated
+ * ways, but at this point it must be defined as the system-supplied strdup
+ * so the callback pointer is initialized correctly.
+ */
+#if defined(_WIN32_WCE)
+#define system_strdup _strdup
+#elif !defined(HAVE_STRDUP)
+#define system_strdup curlx_strdup
+#else
+#define system_strdup strdup
+#endif
+
+#if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__)
+#  pragma warning(disable:4232) /* MSVC extension, dllimport identity */
+#endif
+
+#ifndef __SYMBIAN32__
+/*
+ * If a memory-using function (like curl_getenv) is used before
+ * curl_global_init() is called, we need to have these pointers set already.
+ */
+curl_malloc_callback Curl_cmalloc = (curl_malloc_callback)malloc;
+curl_free_callback Curl_cfree = (curl_free_callback)free;
+curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc;
+curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup;
+curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc;
+#else
+/*
+ * Symbian OS doesn't support initialization to code in writeable static data.
+ * Initialization will occur in the curl_global_init() call.
+ */
+curl_malloc_callback Curl_cmalloc;
+curl_free_callback Curl_cfree;
+curl_realloc_callback Curl_crealloc;
+curl_strdup_callback Curl_cstrdup;
+curl_calloc_callback Curl_ccalloc;
+#endif
+
+#if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__)
+#  pragma warning(default:4232) /* MSVC extension, dllimport identity */
+#endif
+
+/**
+ * curl_global_init() globally initializes cURL given a bitwise set of the
+ * different features of what to initialize.
+ */
+CURLcode curl_global_init(long flags)
+{
+  if(initialized++)
+    return CURLE_OK;
+
+  /* Setup the default memory functions here (again) */
+  Curl_cmalloc = (curl_malloc_callback)malloc;
+  Curl_cfree = (curl_free_callback)free;
+  Curl_crealloc = (curl_realloc_callback)realloc;
+  Curl_cstrdup = (curl_strdup_callback)system_strdup;
+  Curl_ccalloc = (curl_calloc_callback)calloc;
+
+  if(flags & CURL_GLOBAL_SSL)
+    if(!Curl_ssl_init()) {
+      DEBUGF(fprintf(stderr, "Error: Curl_ssl_init failed\n"));
+      return CURLE_FAILED_INIT;
+    }
+
+  if(flags & CURL_GLOBAL_WIN32)
+    if(win32_init() != CURLE_OK) {
+      DEBUGF(fprintf(stderr, "Error: win32_init failed\n"));
+      return CURLE_FAILED_INIT;
+    }
+
+#ifdef __AMIGA__
+  if(!Curl_amiga_init()) {
+    DEBUGF(fprintf(stderr, "Error: Curl_amiga_init failed\n"));
+    return CURLE_FAILED_INIT;
+  }
+#endif
+
+#ifdef NETWARE
+  if(netware_init()) {
+    DEBUGF(fprintf(stderr, "Warning: LONG namespace not available\n"));
+  }
+#endif
+
+#ifdef USE_LIBIDN
+  idna_init();
+#endif
+
+  if(Curl_resolver_global_init() != CURLE_OK) {
+    DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n"));
+    return CURLE_FAILED_INIT;
+  }
+
+#if defined(USE_LIBSSH2) && defined(HAVE_LIBSSH2_INIT)
+  if(libssh2_init(0)) {
+    DEBUGF(fprintf(stderr, "Error: libssh2_init failed\n"));
+    return CURLE_FAILED_INIT;
+  }
+#endif
+
+  init_flags  = flags;
+
+  /* Preset pseudo-random number sequence. */
+
+  Curl_srand();
+
+  return CURLE_OK;
+}
+
+/*
+ * curl_global_init_mem() globally initializes cURL and also registers the
+ * user provided callback routines.
+ */
+CURLcode curl_global_init_mem(long flags, curl_malloc_callback m,
+                              curl_free_callback f, curl_realloc_callback r,
+                              curl_strdup_callback s, curl_calloc_callback c)
+{
+  CURLcode code = CURLE_OK;
+
+  /* Invalid input, return immediately */
+  if(!m || !f || !r || !s || !c)
+    return CURLE_FAILED_INIT;
+
+  /* Already initialized, don't do it again */
+  if(initialized)
+    return CURLE_OK;
+
+  /* Call the actual init function first */
+  code = curl_global_init(flags);
+  if(code == CURLE_OK) {
+    Curl_cmalloc = m;
+    Curl_cfree = f;
+    Curl_cstrdup = s;
+    Curl_crealloc = r;
+    Curl_ccalloc = c;
+  }
+
+  return code;
+}
+
+/**
+ * curl_global_cleanup() globally cleanups cURL, uses the value of
+ * "init_flags" to determine what needs to be cleaned up and what doesn't.
+ */
+void curl_global_cleanup(void)
+{
+  if(!initialized)
+    return;
+
+  if(--initialized)
+    return;
+
+  Curl_global_host_cache_dtor();
+
+  if(init_flags & CURL_GLOBAL_SSL)
+    Curl_ssl_cleanup();
+
+  Curl_resolver_global_cleanup();
+
+  if(init_flags & CURL_GLOBAL_WIN32)
+    win32_cleanup();
+
+  Curl_amiga_cleanup();
+
+#if defined(USE_LIBSSH2) && defined(HAVE_LIBSSH2_EXIT)
+  (void)libssh2_exit();
+#endif
+
+  init_flags  = 0;
+}
+
+/*
+ * curl_easy_init() is the external interface to alloc, setup and init an
+ * easy handle that is returned. If anything goes wrong, NULL is returned.
+ */
+CURL *curl_easy_init(void)
+{
+  CURLcode res;
+  struct SessionHandle *data;
+
+  /* Make sure we inited the global SSL stuff */
+  if(!initialized) {
+    res = curl_global_init(CURL_GLOBAL_DEFAULT);
+    if(res) {
+      /* something in the global init failed, return nothing */
+      DEBUGF(fprintf(stderr, "Error: curl_global_init failed\n"));
+      return NULL;
+    }
+  }
+
+  /* We use curl_open() with undefined URL so far */
+  res = Curl_open(&data);
+  if(res != CURLE_OK) {
+    DEBUGF(fprintf(stderr, "Error: Curl_open failed\n"));
+    return NULL;
+  }
+
+  return data;
+}
+
+/*
+ * curl_easy_setopt() is the external interface for setting options on an
+ * easy handle.
+ */
+
+#undef curl_easy_setopt
+CURLcode curl_easy_setopt(CURL *curl, CURLoption tag, ...)
+{
+  va_list arg;
+  struct SessionHandle *data = curl;
+  CURLcode ret;
+
+  if(!curl)
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+
+  va_start(arg, tag);
+
+  ret = Curl_setopt(data, tag, arg);
+
+  va_end(arg);
+  return ret;
+}
+
+#ifdef CURL_MULTIEASY
+/***************************************************************************
+ * This function is still only for testing purposes. It makes a great way
+ * to run the full test suite on the multi interface instead of the easy one.
+ ***************************************************************************
+ *
+ * The *new* curl_easy_perform() is the external interface that performs a
+ * transfer previously setup.
+ *
+ * Wrapper-function that: creates a multi handle, adds the easy handle to it,
+ * runs curl_multi_perform() until the transfer is done, then detaches the
+ * easy handle, destroys the multi handle and returns the easy handle's return
+ * code. This will make everything internally use and assume multi interface.
+ */
+CURLcode curl_easy_perform(CURL *easy)
+{
+  CURLM *multi;
+  CURLMcode mcode;
+  CURLcode code = CURLE_OK;
+  int still_running;
+  struct timeval timeout;
+  int rc;
+  CURLMsg *msg;
+  fd_set fdread;
+  fd_set fdwrite;
+  fd_set fdexcep;
+  int maxfd;
+
+  if(!easy)
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+
+  multi = curl_multi_init();
+  if(!multi)
+    return CURLE_OUT_OF_MEMORY;
+
+  mcode = curl_multi_add_handle(multi, easy);
+  if(mcode) {
+    curl_multi_cleanup(multi);
+    if(mcode == CURLM_OUT_OF_MEMORY)
+      return CURLE_OUT_OF_MEMORY;
+    else
+      return CURLE_FAILED_INIT;
+  }
+
+  /* we start some action by calling perform right away */
+
+  do {
+    while(CURLM_CALL_MULTI_PERFORM ==
+          curl_multi_perform(multi, &still_running));
+
+    if(!still_running)
+      break;
+
+    FD_ZERO(&fdread);
+    FD_ZERO(&fdwrite);
+    FD_ZERO(&fdexcep);
+
+    /* timeout once per second */
+    timeout.tv_sec = 1;
+    timeout.tv_usec = 0;
+
+    /* Old deprecated style: get file descriptors from the transfers */
+    curl_multi_fdset(multi, &fdread, &fdwrite, &fdexcep, &maxfd);
+    rc = Curl_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
+
+    /* The way is to extract the sockets and wait for them without using
+       select. This whole alternative version should probably rather use the
+       curl_multi_socket() approach. */
+
+    if(rc == -1)
+      /* select error */
+      break;
+
+    /* timeout or data to send/receive => loop! */
+  } while(still_running);
+
+  msg = curl_multi_info_read(multi, &rc);
+  if(msg)
+    code = msg->data.result;
+
+  mcode = curl_multi_remove_handle(multi, easy);
+  /* what to do if it fails? */
+
+  mcode = curl_multi_cleanup(multi);
+  /* what to do if it fails? */
+
+  return code;
+}
+#else
+/*
+ * curl_easy_perform() is the external interface that performs a transfer
+ * previously setup.
+ */
+CURLcode curl_easy_perform(CURL *curl)
+{
+  struct SessionHandle *data = (struct SessionHandle *)curl;
+
+  if(!data)
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+
+  if(! (data->share && data->share->hostcache)) {
+    /* this handle is not using a shared dns cache */
+
+    if(data->set.global_dns_cache &&
+       (data->dns.hostcachetype != HCACHE_GLOBAL)) {
+      /* global dns cache was requested but still isn't */
+      struct curl_hash *ptr;
+
+      if(data->dns.hostcachetype == HCACHE_PRIVATE) {
+        /* if the current cache is private, kill it first */
+        Curl_hash_destroy(data->dns.hostcache);
+        data->dns.hostcachetype = HCACHE_NONE;
+        data->dns.hostcache = NULL;
+      }
+
+      ptr = Curl_global_host_cache_init();
+      if(ptr) {
+        /* only do this if the global cache init works */
+        data->dns.hostcache = ptr;
+        data->dns.hostcachetype = HCACHE_GLOBAL;
+      }
+    }
+
+    if(!data->dns.hostcache) {
+      data->dns.hostcachetype = HCACHE_PRIVATE;
+      data->dns.hostcache = Curl_mk_dnscache();
+
+      if(!data->dns.hostcache)
+        /* While we possibly could survive and do good without a host cache,
+           the fact that creating it failed indicates that things are truly
+           screwed up and we should bail out! */
+        return CURLE_OUT_OF_MEMORY;
+    }
+
+  }
+
+  if(!data->state.conn_cache) {
+    /* Oops, no connection cache, create one */
+    data->state.conn_cache = Curl_conncache_init(CONNCACHE_PRIVATE);
+    if(!data->state.conn_cache)
+      return CURLE_OUT_OF_MEMORY;
+  }
+
+  return Curl_perform(data);
+}
+#endif
+
+/*
+ * curl_easy_cleanup() is the external interface to cleaning/freeing the given
+ * easy handle.
+ */
+void curl_easy_cleanup(CURL *curl)
+{
+  struct SessionHandle *data = (struct SessionHandle *)curl;
+
+  if(!data)
+    return;
+
+  Curl_close(data);
+}
+
+/*
+ * Store a pointed to the multi handle within the easy handle's data struct.
+ */
+void Curl_easy_addmulti(struct SessionHandle *data,
+                        void *multi)
+{
+  data->multi = multi;
+  if(multi == NULL)
+    /* the association is cleared, mark the easy handle as not used by an
+       interface */
+    data->state.used_interface = Curl_if_none;
+}
+
+void Curl_easy_initHandleData(struct SessionHandle *data)
+{
+    memset(&data->req, 0, sizeof(struct SingleRequest));
+
+    data->req.maxdownload = -1;
+}
+
+/*
+ * curl_easy_getinfo() is an external interface that allows an app to retrieve
+ * information from a performed transfer and similar.
+ */
+#undef curl_easy_getinfo
+CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...)
+{
+  va_list arg;
+  void *paramp;
+  struct SessionHandle *data = (struct SessionHandle *)curl;
+
+  va_start(arg, info);
+  paramp = va_arg(arg, void *);
+
+  return Curl_getinfo(data, info, paramp);
+}
+
+/*
+ * curl_easy_duphandle() is an external interface to allow duplication of a
+ * given input easy handle. The returned handle will be a new working handle
+ * with all options set exactly as the input source handle.
+ */
+CURL *curl_easy_duphandle(CURL *incurl)
+{
+  struct SessionHandle *data=(struct SessionHandle *)incurl;
+
+  struct SessionHandle *outcurl = calloc(1, sizeof(struct SessionHandle));
+  if(NULL == outcurl)
+    goto fail;
+
+  /*
+   * We setup a few buffers we need. We should probably make them
+   * get setup on-demand in the code, as that would probably decrease
+   * the likeliness of us forgetting to init a buffer here in the future.
+   */
+  outcurl->state.headerbuff = malloc(HEADERSIZE);
+  if(!outcurl->state.headerbuff)
+    goto fail;
+  outcurl->state.headersize = HEADERSIZE;
+
+  /* copy all userdefined values */
+  if(Curl_dupset(outcurl, data) != CURLE_OK)
+    goto fail;
+
+  /* the connection cache is setup on demand */
+  outcurl->state.conn_cache = NULL;
+
+  outcurl->state.lastconnect = NULL;
+
+  outcurl->progress.flags    = data->progress.flags;
+  outcurl->progress.callback = data->progress.callback;
+
+  if(data->cookies) {
+    /* If cookies are enabled in the parent handle, we enable them
+       in the clone as well! */
+    outcurl->cookies = Curl_cookie_init(data,
+                                        data->cookies->filename,
+                                        outcurl->cookies,
+                                        data->set.cookiesession);
+    if(!outcurl->cookies)
+      goto fail;
+  }
+
+  /* duplicate all values in 'change' */
+  if(data->change.cookielist) {
+    outcurl->change.cookielist =
+      Curl_slist_duplicate(data->change.cookielist);
+    if(!outcurl->change.cookielist)
+      goto fail;
+  }
+
+  if(data->change.url) {
+    outcurl->change.url = strdup(data->change.url);
+    if(!outcurl->change.url)
+      goto fail;
+    outcurl->change.url_alloc = TRUE;
+  }
+
+  if(data->change.referer) {
+    outcurl->change.referer = strdup(data->change.referer);
+    if(!outcurl->change.referer)
+      goto fail;
+    outcurl->change.referer_alloc = TRUE;
+  }
+
+  /* Clone the resolver handle, if present, for the new handle */
+  if(Curl_resolver_duphandle(&outcurl->state.resolver,
+                             data->state.resolver) != CURLE_OK)
+    goto fail;
+
+  Curl_convert_setup(outcurl);
+
+  Curl_easy_initHandleData(outcurl);
+
+  outcurl->magic = CURLEASY_MAGIC_NUMBER;
+
+  /* we reach this point and thus we are OK */
+
+  return outcurl;
+
+  fail:
+
+  if(outcurl) {
+    curl_slist_free_all(outcurl->change.cookielist);
+    outcurl->change.cookielist = NULL;
+    Curl_safefree(outcurl->state.headerbuff);
+    Curl_safefree(outcurl->change.url);
+    Curl_safefree(outcurl->change.referer);
+    Curl_freeset(outcurl);
+    free(outcurl);
+  }
+
+  return NULL;
+}
+
+/*
+ * curl_easy_reset() is an external interface that allows an app to re-
+ * initialize a session handle to the default values.
+ */
+void curl_easy_reset(CURL *curl)
+{
+  struct SessionHandle *data = (struct SessionHandle *)curl;
+
+  Curl_safefree(data->state.pathbuffer);
+
+  data->state.path = NULL;
+
+  Curl_safefree(data->state.proto.generic);
+
+  /* zero out UserDefined data: */
+  Curl_freeset(data);
+  memset(&data->set, 0, sizeof(struct UserDefined));
+  (void)Curl_init_userdefined(&data->set);
+
+  /* zero out Progress data: */
+  memset(&data->progress, 0, sizeof(struct Progress));
+
+  /* init Handle data */
+  Curl_easy_initHandleData(data);
+
+  data->progress.flags |= PGRS_HIDE;
+  data->state.current_speed = -1; /* init to negative == impossible */
+}
+
+/*
+ * curl_easy_pause() allows an application to pause or unpause a specific
+ * transfer and direction. This function sets the full new state for the
+ * current connection this easy handle operates on.
+ *
+ * NOTE: if you have the receiving paused and you call this function to remove
+ * the pausing, you may get your write callback called at this point.
+ *
+ * Action is a bitmask consisting of CURLPAUSE_* bits in curl/curl.h
+ */
+CURLcode curl_easy_pause(CURL *curl, int action)
+{
+  struct SessionHandle *data = (struct SessionHandle *)curl;
+  struct SingleRequest *k = &data->req;
+  CURLcode result = CURLE_OK;
+
+  /* first switch off both pause bits */
+  int newstate = k->keepon &~ (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE);
+
+  /* set the new desired pause bits */
+  newstate |= ((action & CURLPAUSE_RECV)?KEEP_RECV_PAUSE:0) |
+    ((action & CURLPAUSE_SEND)?KEEP_SEND_PAUSE:0);
+
+  /* put it back in the keepon */
+  k->keepon = newstate;
+
+  if(!(newstate & KEEP_RECV_PAUSE) && data->state.tempwrite) {
+    /* we have a buffer for sending that we now seem to be able to deliver
+       since the receive pausing is lifted! */
+
+    /* get the pointer, type and length in local copies since the function may
+       return PAUSE again and then we'll get a new copy allocted and stored in
+       the tempwrite variables */
+    char *tempwrite = data->state.tempwrite;
+    char *freewrite = tempwrite; /* store this pointer to free it later */
+    size_t tempsize = data->state.tempwritesize;
+    int temptype = data->state.tempwritetype;
+    size_t chunklen;
+
+    /* clear tempwrite here just to make sure it gets cleared if there's no
+       further use of it, and make sure we don't clear it after the function
+       invoke as it may have been set to a new value by then */
+    data->state.tempwrite = NULL;
+
+    /* since the write callback API is define to never exceed
+       CURL_MAX_WRITE_SIZE bytes in a single call, and since we may in fact
+       have more data than that in our buffer here, we must loop sending the
+       data in multiple calls until there's no data left or we get another
+       pause returned.
+
+       A tricky part is that the function we call will "buffer" the data
+       itself when it pauses on a particular buffer, so we may need to do some
+       extra trickery if we get a pause return here.
+    */
+    do {
+      chunklen = (tempsize > CURL_MAX_WRITE_SIZE)?CURL_MAX_WRITE_SIZE:tempsize;
+
+      result = Curl_client_write(data->state.current_conn,
+                                 temptype, tempwrite, chunklen);
+      if(result)
+        /* failures abort the loop at once */
+        break;
+
+      if(data->state.tempwrite && (tempsize - chunklen)) {
+        /* Ouch, the reading is again paused and the block we send is now
+           "cached". If this is the final chunk we can leave it like this, but
+           if we have more chunks that are cached after this, we need to free
+           the newly cached one and put back a version that is truly the entire
+           contents that is saved for later
+        */
+        char *newptr;
+
+        /* note that tempsize is still the size as before the callback was
+           used, and thus the whole piece of data to keep */
+        newptr = realloc(data->state.tempwrite, tempsize);
+
+        if(!newptr) {
+          free(data->state.tempwrite); /* free old area */
+          data->state.tempwrite = NULL;
+          result = CURLE_OUT_OF_MEMORY;
+          /* tempwrite will be freed further down */
+          break;
+        }
+        data->state.tempwrite = newptr; /* store new pointer */
+        memcpy(newptr, tempwrite, tempsize);
+        data->state.tempwritesize = tempsize; /* store new size */
+        /* tempwrite will be freed further down */
+        break; /* go back to pausing until further notice */
+      }
+      else {
+        tempsize -= chunklen;  /* left after the call above */
+        tempwrite += chunklen; /* advance the pointer */
+      }
+
+    } while((result == CURLE_OK) && tempsize);
+
+    free(freewrite); /* this is unconditionally no longer used */
+  }
+
+  return result;
+}
+
+
+static CURLcode easy_connection(struct SessionHandle *data,
+                                curl_socket_t *sfd,
+                                struct connectdata **connp)
+{
+  if(data == NULL)
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+
+  /* only allow these to be called on handles with CURLOPT_CONNECT_ONLY */
+  if(!data->set.connect_only) {
+    failf(data, "CONNECT_ONLY is required!");
+    return CURLE_UNSUPPORTED_PROTOCOL;
+  }
+
+  *sfd = Curl_getconnectinfo(data, connp);
+
+  if(*sfd == CURL_SOCKET_BAD) {
+    failf(data, "Failed to get recent socket");
+    return CURLE_UNSUPPORTED_PROTOCOL;
+  }
+
+  return CURLE_OK;
+}
+
+/*
+ * Receives data from the connected socket. Use after successful
+ * curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
+ * Returns CURLE_OK on success, error code on error.
+ */
+CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, size_t *n)
+{
+  curl_socket_t sfd;
+  CURLcode ret;
+  ssize_t n1;
+  struct connectdata *c;
+  struct SessionHandle *data = (struct SessionHandle *)curl;
+
+  ret = easy_connection(data, &sfd, &c);
+  if(ret)
+    return ret;
+
+  *n = 0;
+  ret = Curl_read(c, sfd, buffer, buflen, &n1);
+
+  if(ret != CURLE_OK)
+    return ret;
+
+  *n = (size_t)n1;
+
+  return CURLE_OK;
+}
+
+/*
+ * Sends data over the connected socket. Use after successful
+ * curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
+ */
+CURLcode curl_easy_send(CURL *curl, const void *buffer, size_t buflen,
+                        size_t *n)
+{
+  curl_socket_t sfd;
+  CURLcode ret;
+  ssize_t n1;
+  struct connectdata *c = NULL;
+  struct SessionHandle *data = (struct SessionHandle *)curl;
+
+  ret = easy_connection(data, &sfd, &c);
+  if(ret)
+    return ret;
+
+  *n = 0;
+  ret = Curl_write(c, sfd, buffer, buflen, &n1);
+
+  if(n1 == -1)
+    return CURLE_SEND_ERROR;
+
+  /* detect EAGAIN */
+  if((CURLE_OK == ret) && (0 == n1))
+    return CURLE_AGAIN;
+
+  *n = (size_t)n1;
+
+  return ret;
+}
diff --git a/lib/curl_escape.c b/lib/curl_escape.c
new file mode 100644 (file)
index 0000000..c0ed571
--- /dev/null
@@ -0,0 +1,233 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/* Escape and unescape URL encoding in strings. The functions return a new
+ * allocated string or NULL if an error occurred.  */
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+#include "curl_memory.h"
+#include "curl_urldata.h"
+#include "curl_warnless.h"
+#include "curl_non_ascii.h"
+#include "curl_escape.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/* Portable character check (remember EBCDIC). Do not use isalnum() because
+   its behavior is altered by the current locale.
+   See http://tools.ietf.org/html/rfc3986#section-2.3
+*/
+static bool Curl_isunreserved(unsigned char in)
+{
+  switch (in) {
+    case '0': case '1': case '2': case '3': case '4':
+    case '5': case '6': case '7': case '8': case '9':
+    case 'a': case 'b': case 'c': case 'd': case 'e':
+    case 'f': case 'g': case 'h': case 'i': case 'j':
+    case 'k': case 'l': case 'm': case 'n': case 'o':
+    case 'p': case 'q': case 'r': case 's': case 't':
+    case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
+    case 'A': case 'B': case 'C': case 'D': case 'E':
+    case 'F': case 'G': case 'H': case 'I': case 'J':
+    case 'K': case 'L': case 'M': case 'N': case 'O':
+    case 'P': case 'Q': case 'R': case 'S': case 'T':
+    case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
+    case '-': case '.': case '_': case '~':
+      return TRUE;
+    default:
+      break;
+  }
+  return FALSE;
+}
+
+/* for ABI-compatibility with previous versions */
+char *curl_escape(const char *string, int inlength)
+{
+  return curl_easy_escape(NULL, string, inlength);
+}
+
+/* for ABI-compatibility with previous versions */
+char *curl_unescape(const char *string, int length)
+{
+  return curl_easy_unescape(NULL, string, length, NULL);
+}
+
+char *curl_easy_escape(CURL *handle, const char *string, int inlength)
+{
+  size_t alloc = (inlength?(size_t)inlength:strlen(string))+1;
+  char *ns;
+  char *testing_ptr = NULL;
+  unsigned char in; /* we need to treat the characters unsigned */
+  size_t newlen = alloc;
+  size_t strindex=0;
+  size_t length;
+  CURLcode res;
+
+  ns = malloc(alloc);
+  if(!ns)
+    return NULL;
+
+  length = alloc-1;
+  while(length--) {
+    in = *string;
+
+    if(Curl_isunreserved(in))
+      /* just copy this */
+      ns[strindex++]=in;
+    else {
+      /* encode it */
+      newlen += 2; /* the size grows with two, since this'll become a %XX */
+      if(newlen > alloc) {
+        alloc *= 2;
+        testing_ptr = realloc(ns, alloc);
+        if(!testing_ptr) {
+          free( ns );
+          return NULL;
+        }
+        else {
+          ns = testing_ptr;
+        }
+      }
+
+      res = Curl_convert_to_network(handle, &in, 1);
+      if(res) {
+        /* Curl_convert_to_network calls failf if unsuccessful */
+        free(ns);
+        return NULL;
+      }
+
+      snprintf(&ns[strindex], 4, "%%%02X", in);
+
+      strindex+=3;
+    }
+    string++;
+  }
+  ns[strindex]=0; /* terminate it */
+  return ns;
+}
+
+/*
+ * Curl_urldecode() URL decodes the given string.
+ *
+ * Optionally detects control characters (byte codes lower than 32) in the
+ * data and rejects such data.
+ *
+ * Returns a pointer to a malloced string in *ostring with length given in
+ * *olen. If length == 0, the length is assumed to be strlen(string).
+ *
+ */
+CURLcode Curl_urldecode(struct SessionHandle *data,
+                        const char *string, size_t length,
+                        char **ostring, size_t *olen,
+                        bool reject_ctrl)
+{
+  size_t alloc = (length?length:strlen(string))+1;
+  char *ns = malloc(alloc);
+  unsigned char in;
+  size_t strindex=0;
+  unsigned long hex;
+  CURLcode res;
+
+  if(!ns)
+    return CURLE_OUT_OF_MEMORY;
+
+  while(--alloc > 0) {
+    in = *string;
+    if(('%' == in) && ISXDIGIT(string[1]) && ISXDIGIT(string[2])) {
+      /* this is two hexadecimal digits following a '%' */
+      char hexstr[3];
+      char *ptr;
+      hexstr[0] = string[1];
+      hexstr[1] = string[2];
+      hexstr[2] = 0;
+
+      hex = strtoul(hexstr, &ptr, 16);
+
+      in = curlx_ultouc(hex); /* this long is never bigger than 255 anyway */
+
+      res = Curl_convert_from_network(data, &in, 1);
+      if(res) {
+        /* Curl_convert_from_network calls failf if unsuccessful */
+        free(ns);
+        return res;
+      }
+
+      string+=2;
+      alloc-=2;
+    }
+    if(reject_ctrl && (in < 0x20)) {
+      free(ns);
+      return CURLE_URL_MALFORMAT;
+    }
+
+    ns[strindex++] = in;
+    string++;
+  }
+  ns[strindex]=0; /* terminate it */
+
+  if(olen)
+    /* store output size */
+    *olen = strindex;
+
+  if(ostring)
+    /* store output string */
+    *ostring = ns;
+
+  return CURLE_OK;
+}
+
+/*
+ * Unescapes the given URL escaped string of given length. Returns a
+ * pointer to a malloced string with length given in *olen.
+ * If length == 0, the length is assumed to be strlen(string).
+ * If olen == NULL, no output length is stored.
+ */
+char *curl_easy_unescape(CURL *handle, const char *string, int length,
+                         int *olen)
+{
+  char *str = NULL;
+  size_t inputlen = length;
+  size_t outputlen;
+  CURLcode res = Curl_urldecode(handle, string, inputlen, &str, &outputlen,
+                                FALSE);
+  if(res)
+    return NULL;
+  if(olen)
+    *olen = curlx_uztosi(outputlen);
+  return str;
+}
+
+/* For operating systems/environments that use different malloc/free
+   systems for the app and for this library, we provide a free that uses
+   the library's memory system */
+void curl_free(void *p)
+{
+  if(p)
+    free(p);
+}
diff --git a/lib/curl_file.c b/lib/curl_file.c
new file mode 100644 (file)
index 0000000..6ea2bd7
--- /dev/null
@@ -0,0 +1,591 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_FILE
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include "curl_strtoofft.h"
+#include "curl_urldata.h"
+#include <curl/curl.h>
+#include "curl_progress.h"
+#include "curl_sendf.h"
+#include "curl_escape.h"
+#include "curl_file.h"
+#include "curl_speedcheck.h"
+#include "curl_getinfo.h"
+#include "curl_transfer.h"
+#include "curl_url.h"
+#include "curl_memory.h"
+#include "curl_parsedate.h" /* for the week day and month names */
+#include "curl_warnless.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+#if defined(WIN32) || defined(MSDOS) || defined(__EMX__) || \
+  defined(__SYMBIAN32__)
+#define DOS_FILESYSTEM 1
+#endif
+
+#ifdef OPEN_NEEDS_ARG3
+#  define open_readonly(p,f) open((p),(f),(0))
+#else
+#  define open_readonly(p,f) open((p),(f))
+#endif
+
+/*
+ * Forward declarations.
+ */
+
+static CURLcode file_do(struct connectdata *, bool *done);
+static CURLcode file_done(struct connectdata *conn,
+                          CURLcode status, bool premature);
+static CURLcode file_connect(struct connectdata *conn, bool *done);
+static CURLcode file_disconnect(struct connectdata *conn,
+                                bool dead_connection);
+
+
+/*
+ * FILE scheme handler.
+ */
+
+const struct Curl_handler Curl_handler_file = {
+  "FILE",                               /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  file_do,                              /* do_it */
+  file_done,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  file_connect,                         /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  file_disconnect,                      /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  0,                                    /* defport */
+  CURLPROTO_FILE,                       /* protocol */
+  PROTOPT_NONETWORK | PROTOPT_NOURLQUERY /* flags */
+};
+
+
+ /*
+  Check if this is a range download, and if so, set the internal variables
+  properly. This code is copied from the FTP implementation and might as
+  well be factored out.
+ */
+static CURLcode file_range(struct connectdata *conn)
+{
+  curl_off_t from, to;
+  curl_off_t totalsize=-1;
+  char *ptr;
+  char *ptr2;
+  struct SessionHandle *data = conn->data;
+
+  if(data->state.use_range && data->state.range) {
+    from=curlx_strtoofft(data->state.range, &ptr, 0);
+    while(*ptr && (ISSPACE(*ptr) || (*ptr=='-')))
+      ptr++;
+    to=curlx_strtoofft(ptr, &ptr2, 0);
+    if(ptr == ptr2) {
+      /* we didn't get any digit */
+      to=-1;
+    }
+    if((-1 == to) && (from>=0)) {
+      /* X - */
+      data->state.resume_from = from;
+      DEBUGF(infof(data, "RANGE %" FORMAT_OFF_T " to end of file\n",
+                   from));
+    }
+    else if(from < 0) {
+      /* -Y */
+      data->req.maxdownload = -from;
+      data->state.resume_from = from;
+      DEBUGF(infof(data, "RANGE the last %" FORMAT_OFF_T " bytes\n",
+                   -from));
+    }
+    else {
+      /* X-Y */
+      totalsize = to-from;
+      data->req.maxdownload = totalsize+1; /* include last byte */
+      data->state.resume_from = from;
+      DEBUGF(infof(data, "RANGE from %" FORMAT_OFF_T
+                   " getting %" FORMAT_OFF_T " bytes\n",
+                   from, data->req.maxdownload));
+    }
+    DEBUGF(infof(data, "range-download from %" FORMAT_OFF_T
+                 " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
+                 from, to, data->req.maxdownload));
+  }
+  else
+    data->req.maxdownload = -1;
+  return CURLE_OK;
+}
+
+/*
+ * file_connect() gets called from Curl_protocol_connect() to allow us to
+ * do protocol-specific actions at connect-time.  We emulate a
+ * connect-then-transfer protocol and "connect" to the file here
+ */
+static CURLcode file_connect(struct connectdata *conn, bool *done)
+{
+  struct SessionHandle *data = conn->data;
+  char *real_path;
+  struct FILEPROTO *file;
+  int fd;
+#ifdef DOS_FILESYSTEM
+  int i;
+  char *actual_path;
+#endif
+
+  /* If there already is a protocol-specific struct allocated for this
+     sessionhandle, deal with it */
+  Curl_reset_reqproto(conn);
+
+  real_path = curl_easy_unescape(data, data->state.path, 0, NULL);
+  if(!real_path)
+    return CURLE_OUT_OF_MEMORY;
+
+  if(!data->state.proto.file) {
+    file = calloc(1, sizeof(struct FILEPROTO));
+    if(!file) {
+      free(real_path);
+      return CURLE_OUT_OF_MEMORY;
+    }
+    data->state.proto.file = file;
+  }
+  else {
+    /* file is not a protocol that can deal with "persistancy" */
+    file = data->state.proto.file;
+    Curl_safefree(file->freepath);
+    file->path = NULL;
+    if(file->fd != -1)
+      close(file->fd);
+    file->fd = -1;
+  }
+
+#ifdef DOS_FILESYSTEM
+  /* If the first character is a slash, and there's
+     something that looks like a drive at the beginning of
+     the path, skip the slash.  If we remove the initial
+     slash in all cases, paths without drive letters end up
+     relative to the current directory which isn't how
+     browsers work.
+
+     Some browsers accept | instead of : as the drive letter
+     separator, so we do too.
+
+     On other platforms, we need the slash to indicate an
+     absolute pathname.  On Windows, absolute paths start
+     with a drive letter.
+  */
+  actual_path = real_path;
+  if((actual_path[0] == '/') &&
+      actual_path[1] &&
+     (actual_path[2] == ':' || actual_path[2] == '|')) {
+    actual_path[2] = ':';
+    actual_path++;
+  }
+
+  /* change path separators from '/' to '\\' for DOS, Windows and OS/2 */
+  for(i=0; actual_path[i] != '\0'; ++i)
+    if(actual_path[i] == '/')
+      actual_path[i] = '\\';
+
+  fd = open_readonly(actual_path, O_RDONLY|O_BINARY);
+  file->path = actual_path;
+#else
+  fd = open_readonly(real_path, O_RDONLY);
+  file->path = real_path;
+#endif
+  file->freepath = real_path; /* free this when done */
+
+  file->fd = fd;
+  if(!data->set.upload && (fd == -1)) {
+    failf(data, "Couldn't open file %s", data->state.path);
+    file_done(conn, CURLE_FILE_COULDNT_READ_FILE, FALSE);
+    return CURLE_FILE_COULDNT_READ_FILE;
+  }
+  *done = TRUE;
+
+  return CURLE_OK;
+}
+
+static CURLcode file_done(struct connectdata *conn,
+                               CURLcode status, bool premature)
+{
+  struct FILEPROTO *file = conn->data->state.proto.file;
+  (void)status; /* not used */
+  (void)premature; /* not used */
+
+  if(file) {
+    Curl_safefree(file->freepath);
+    file->path = NULL;
+    if(file->fd != -1)
+      close(file->fd);
+    file->fd = -1;
+  }
+
+  return CURLE_OK;
+}
+
+static CURLcode file_disconnect(struct connectdata *conn,
+                                bool dead_connection)
+{
+  struct FILEPROTO *file = conn->data->state.proto.file;
+  (void)dead_connection; /* not used */
+
+  if(file) {
+    Curl_safefree(file->freepath);
+    file->path = NULL;
+    if(file->fd != -1)
+      close(file->fd);
+    file->fd = -1;
+  }
+
+  return CURLE_OK;
+}
+
+#ifdef DOS_FILESYSTEM
+#define DIRSEP '\\'
+#else
+#define DIRSEP '/'
+#endif
+
+static CURLcode file_upload(struct connectdata *conn)
+{
+  struct FILEPROTO *file = conn->data->state.proto.file;
+  const char *dir = strchr(file->path, DIRSEP);
+  int fd;
+  int mode;
+  CURLcode res=CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  char *buf = data->state.buffer;
+  size_t nread;
+  size_t nwrite;
+  curl_off_t bytecount = 0;
+  struct timeval now = Curl_tvnow();
+  struct_stat file_stat;
+  const char* buf2;
+
+  /*
+   * Since FILE: doesn't do the full init, we need to provide some extra
+   * assignments here.
+   */
+  conn->fread_func = data->set.fread_func;
+  conn->fread_in = data->set.in;
+  conn->data->req.upload_fromhere = buf;
+
+  if(!dir)
+    return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
+
+  if(!dir[1])
+    return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
+
+#ifdef O_BINARY
+#define MODE_DEFAULT O_WRONLY|O_CREAT|O_BINARY
+#else
+#define MODE_DEFAULT O_WRONLY|O_CREAT
+#endif
+
+  if(data->state.resume_from)
+    mode = MODE_DEFAULT|O_APPEND;
+  else
+    mode = MODE_DEFAULT|O_TRUNC;
+
+  fd = open(file->path, mode, conn->data->set.new_file_perms);
+  if(fd < 0) {
+    failf(data, "Can't open %s for writing", file->path);
+    return CURLE_WRITE_ERROR;
+  }
+
+  if(-1 != data->set.infilesize)
+    /* known size of data to "upload" */
+    Curl_pgrsSetUploadSize(data, data->set.infilesize);
+
+  /* treat the negative resume offset value as the case of "-" */
+  if(data->state.resume_from < 0) {
+    if(fstat(fd, &file_stat)) {
+      close(fd);
+      failf(data, "Can't get the size of %s", file->path);
+      return CURLE_WRITE_ERROR;
+    }
+    else
+      data->state.resume_from = (curl_off_t)file_stat.st_size;
+  }
+
+  while(res == CURLE_OK) {
+    int readcount;
+    res = Curl_fillreadbuffer(conn, BUFSIZE, &readcount);
+    if(res)
+      break;
+
+    if(readcount <= 0)  /* fix questionable compare error. curlvms */
+      break;
+
+    nread = (size_t)readcount;
+
+    /*skip bytes before resume point*/
+    if(data->state.resume_from) {
+      if((curl_off_t)nread <= data->state.resume_from ) {
+        data->state.resume_from -= nread;
+        nread = 0;
+        buf2 = buf;
+      }
+      else {
+        buf2 = buf + data->state.resume_from;
+        nread -= (size_t)data->state.resume_from;
+        data->state.resume_from = 0;
+      }
+    }
+    else
+      buf2 = buf;
+
+    /* write the data to the target */
+    nwrite = write(fd, buf2, nread);
+    if(nwrite != nread) {
+      res = CURLE_SEND_ERROR;
+      break;
+    }
+
+    bytecount += nread;
+
+    Curl_pgrsSetUploadCounter(data, bytecount);
+
+    if(Curl_pgrsUpdate(conn))
+      res = CURLE_ABORTED_BY_CALLBACK;
+    else
+      res = Curl_speedcheck(data, now);
+  }
+  if(!res && Curl_pgrsUpdate(conn))
+    res = CURLE_ABORTED_BY_CALLBACK;
+
+  close(fd);
+
+  return res;
+}
+
+/*
+ * file_do() is the protocol-specific function for the do-phase, separated
+ * from the connect-phase above. Other protocols merely setup the transfer in
+ * the do-phase, to have it done in the main transfer loop but since some
+ * platforms we support don't allow select()ing etc on file handles (as
+ * opposed to sockets) we instead perform the whole do-operation in this
+ * function.
+ */
+static CURLcode file_do(struct connectdata *conn, bool *done)
+{
+  /* This implementation ignores the host name in conformance with
+     RFC 1738. Only local files (reachable via the standard file system)
+     are supported. This means that files on remotely mounted directories
+     (via NFS, Samba, NT sharing) can be accessed through a file:// URL
+  */
+  CURLcode res = CURLE_OK;
+  struct_stat statbuf; /* struct_stat instead of struct stat just to allow the
+                          Windows version to have a different struct without
+                          having to redefine the simple word 'stat' */
+  curl_off_t expected_size=0;
+  bool fstated=FALSE;
+  ssize_t nread;
+  struct SessionHandle *data = conn->data;
+  char *buf = data->state.buffer;
+  curl_off_t bytecount = 0;
+  int fd;
+  struct timeval now = Curl_tvnow();
+
+  *done = TRUE; /* unconditionally */
+
+  Curl_initinfo(data);
+  Curl_pgrsStartNow(data);
+
+  if(data->set.upload)
+    return file_upload(conn);
+
+  /* get the fd from the connection phase */
+  fd = conn->data->state.proto.file->fd;
+
+  /* VMS: This only works reliable for STREAMLF files */
+  if(-1 != fstat(fd, &statbuf)) {
+    /* we could stat it, then read out the size */
+    expected_size = statbuf.st_size;
+    /* and store the modification time */
+    data->info.filetime = (long)statbuf.st_mtime;
+    fstated = TRUE;
+  }
+
+  if(fstated && !data->state.range && data->set.timecondition) {
+    if(!Curl_meets_timecondition(data, (time_t)data->info.filetime)) {
+      *done = TRUE;
+      return CURLE_OK;
+    }
+  }
+
+  /* If we have selected NOBODY and HEADER, it means that we only want file
+     information. Which for FILE can't be much more than the file size and
+     date. */
+  if(data->set.opt_no_body && data->set.include_header && fstated) {
+    CURLcode result;
+    snprintf(buf, sizeof(data->state.buffer),
+             "Content-Length: %" FORMAT_OFF_T "\r\n", expected_size);
+    result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
+    if(result)
+      return result;
+
+    result = Curl_client_write(conn, CLIENTWRITE_BOTH,
+                               (char *)"Accept-ranges: bytes\r\n", 0);
+    if(result)
+      return result;
+
+    if(fstated) {
+      time_t filetime = (time_t)statbuf.st_mtime;
+      struct tm buffer;
+      const struct tm *tm = &buffer;
+      result = Curl_gmtime(filetime, &buffer);
+      if(result)
+        return result;
+
+      /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
+      snprintf(buf, BUFSIZE-1,
+               "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
+               Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
+               tm->tm_mday,
+               Curl_month[tm->tm_mon],
+               tm->tm_year + 1900,
+               tm->tm_hour,
+               tm->tm_min,
+               tm->tm_sec);
+      result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
+    }
+    /* if we fstat()ed the file, set the file size to make it available post-
+       transfer */
+    if(fstated)
+      Curl_pgrsSetDownloadSize(data, expected_size);
+    return result;
+  }
+
+  /* Check whether file range has been specified */
+  file_range(conn);
+
+  /* Adjust the start offset in case we want to get the N last bytes
+   * of the stream iff the filesize could be determined */
+  if(data->state.resume_from < 0) {
+    if(!fstated) {
+      failf(data, "Can't get the size of file.");
+      return CURLE_READ_ERROR;
+    }
+    else
+      data->state.resume_from += (curl_off_t)statbuf.st_size;
+  }
+
+  if(data->state.resume_from <= expected_size)
+    expected_size -= data->state.resume_from;
+  else {
+    failf(data, "failed to resume file:// transfer");
+    return CURLE_BAD_DOWNLOAD_RESUME;
+  }
+
+  /* A high water mark has been specified so we obey... */
+  if(data->req.maxdownload > 0)
+    expected_size = data->req.maxdownload;
+
+  if(fstated && (expected_size == 0))
+    return CURLE_OK;
+
+  /* The following is a shortcut implementation of file reading
+     this is both more efficient than the former call to download() and
+     it avoids problems with select() and recv() on file descriptors
+     in Winsock */
+  if(fstated)
+    Curl_pgrsSetDownloadSize(data, expected_size);
+
+  if(data->state.resume_from) {
+    if(data->state.resume_from !=
+       lseek(fd, data->state.resume_from, SEEK_SET))
+      return CURLE_BAD_DOWNLOAD_RESUME;
+  }
+
+  Curl_pgrsTime(data, TIMER_STARTTRANSFER);
+
+  while(res == CURLE_OK) {
+    /* Don't fill a whole buffer if we want less than all data */
+    size_t bytestoread =
+      (expected_size < CURL_OFF_T_C(BUFSIZE) - CURL_OFF_T_C(1)) ?
+      curlx_sotouz(expected_size) : BUFSIZE - 1;
+
+    nread = read(fd, buf, bytestoread);
+
+    if(nread > 0)
+      buf[nread] = 0;
+
+    if(nread <= 0 || expected_size == 0)
+      break;
+
+    bytecount += nread;
+    expected_size -= nread;
+
+    res = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread);
+    if(res)
+      return res;
+
+    Curl_pgrsSetDownloadCounter(data, bytecount);
+
+    if(Curl_pgrsUpdate(conn))
+      res = CURLE_ABORTED_BY_CALLBACK;
+    else
+      res = Curl_speedcheck(data, now);
+  }
+  if(Curl_pgrsUpdate(conn))
+    res = CURLE_ABORTED_BY_CALLBACK;
+
+  return res;
+}
+
+#endif
diff --git a/lib/curl_fileinfo.c b/lib/curl_fileinfo.c
new file mode 100644 (file)
index 0000000..433c709
--- /dev/null
@@ -0,0 +1,54 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2010-2011, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include "curl_strdup.h"
+#include "curl_fileinfo.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+struct curl_fileinfo *Curl_fileinfo_alloc(void)
+{
+  struct curl_fileinfo *tmp = malloc(sizeof(struct curl_fileinfo));
+  if(!tmp)
+    return NULL;
+  memset(tmp, 0, sizeof(struct curl_fileinfo));
+  return tmp;
+}
+
+void Curl_fileinfo_dtor(void *user, void *element)
+{
+  struct curl_fileinfo *finfo = element;
+  (void) user;
+  if(!finfo)
+    return;
+
+  Curl_safefree(finfo->b_data);
+
+  free(finfo);
+}
diff --git a/lib/curl_formdata.c b/lib/curl_formdata.c
new file mode 100644 (file)
index 0000000..c7d85c4
--- /dev/null
@@ -0,0 +1,1494 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+/* Length of the random boundary string. */
+#define BOUNDARY_LENGTH 40
+
+#if !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY)
+
+#if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
+#include <libgen.h>
+#endif
+
+#include "curl_urldata.h" /* for struct SessionHandle */
+#include "curl_formdata.h"
+#include "curl_rand.h"
+#include "curl_strequal.h"
+#include "curl_memory.h"
+#include "curl_sendf.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+#endif  /* !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY) */
+
+#ifndef CURL_DISABLE_HTTP
+
+#ifndef HAVE_BASENAME
+static char *Curl_basename(char *path);
+#define basename(x)  Curl_basename((x))
+#endif
+
+static size_t readfromfile(struct Form *form, char *buffer, size_t size);
+
+/* What kind of Content-Type to use on un-specified files with unrecognized
+   extensions. */
+#define HTTPPOST_CONTENTTYPE_DEFAULT "application/octet-stream"
+
+#define FORM_FILE_SEPARATOR ','
+#define FORM_TYPE_SEPARATOR ';'
+
+/***************************************************************************
+ *
+ * AddHttpPost()
+ *
+ * Adds a HttpPost structure to the list, if parent_post is given becomes
+ * a subpost of parent_post instead of a direct list element.
+ *
+ * Returns newly allocated HttpPost on success and NULL if malloc failed.
+ *
+ ***************************************************************************/
+static struct curl_httppost *
+AddHttpPost(char *name, size_t namelength,
+            char *value, size_t contentslength,
+            char *buffer, size_t bufferlength,
+            char *contenttype,
+            long flags,
+            struct curl_slist* contentHeader,
+            char *showfilename, char *userp,
+            struct curl_httppost *parent_post,
+            struct curl_httppost **httppost,
+            struct curl_httppost **last_post)
+{
+  struct curl_httppost *post;
+  post = calloc(1, sizeof(struct curl_httppost));
+  if(post) {
+    post->name = name;
+    post->namelength = (long)(name?(namelength?namelength:strlen(name)):0);
+    post->contents = value;
+    post->contentslength = (long)contentslength;
+    post->buffer = buffer;
+    post->bufferlength = (long)bufferlength;
+    post->contenttype = contenttype;
+    post->contentheader = contentHeader;
+    post->showfilename = showfilename;
+    post->userp = userp,
+    post->flags = flags;
+  }
+  else
+    return NULL;
+
+  if(parent_post) {
+    /* now, point our 'more' to the original 'more' */
+    post->more = parent_post->more;
+
+    /* then move the original 'more' to point to ourselves */
+    parent_post->more = post;
+  }
+  else {
+    /* make the previous point to this */
+    if(*last_post)
+      (*last_post)->next = post;
+    else
+      (*httppost) = post;
+
+    (*last_post) = post;
+  }
+  return post;
+}
+
+/***************************************************************************
+ *
+ * AddFormInfo()
+ *
+ * Adds a FormInfo structure to the list presented by parent_form_info.
+ *
+ * Returns newly allocated FormInfo on success and NULL if malloc failed/
+ * parent_form_info is NULL.
+ *
+ ***************************************************************************/
+static FormInfo * AddFormInfo(char *value,
+                              char *contenttype,
+                              FormInfo *parent_form_info)
+{
+  FormInfo *form_info;
+  form_info = calloc(1, sizeof(struct FormInfo));
+  if(form_info) {
+    if(value)
+      form_info->value = value;
+    if(contenttype)
+      form_info->contenttype = contenttype;
+    form_info->flags = HTTPPOST_FILENAME;
+  }
+  else
+    return NULL;
+
+  if(parent_form_info) {
+    /* now, point our 'more' to the original 'more' */
+    form_info->more = parent_form_info->more;
+
+    /* then move the original 'more' to point to ourselves */
+    parent_form_info->more = form_info;
+  }
+
+  return form_info;
+}
+
+/***************************************************************************
+ *
+ * ContentTypeForFilename()
+ *
+ * Provides content type for filename if one of the known types (else
+ * (either the prevtype or the default is returned).
+ *
+ * Returns some valid contenttype for filename.
+ *
+ ***************************************************************************/
+static const char * ContentTypeForFilename (const char *filename,
+                                            const char *prevtype)
+{
+  const char *contenttype = NULL;
+  unsigned int i;
+  /*
+   * No type was specified, we scan through a few well-known
+   * extensions and pick the first we match!
+   */
+  struct ContentType {
+    char extension[6];
+    const char *type;
+  };
+  static const struct ContentType ctts[]={
+    {".gif",  "image/gif"},
+    {".jpg",  "image/jpeg"},
+    {".jpeg", "image/jpeg"},
+    {".txt",  "text/plain"},
+    {".html", "text/html"},
+    {".xml", "application/xml"}
+  };
+
+  if(prevtype)
+    /* default to the previously set/used! */
+    contenttype = prevtype;
+  else
+    contenttype = HTTPPOST_CONTENTTYPE_DEFAULT;
+
+  if(filename) { /* in case a NULL was passed in */
+    for(i=0; i<sizeof(ctts)/sizeof(ctts[0]); i++) {
+      if(strlen(filename) >= strlen(ctts[i].extension)) {
+        if(strequal(filename +
+                    strlen(filename) - strlen(ctts[i].extension),
+                    ctts[i].extension)) {
+          contenttype = ctts[i].type;
+          break;
+        }
+      }
+    }
+  }
+  /* we have a contenttype by now */
+  return contenttype;
+}
+
+/***************************************************************************
+ *
+ * memdup()
+ *
+ * Copies the 'source' data to a newly allocated buffer buffer (that is
+ * returned). Uses buffer_length if not null, else uses strlen to determine
+ * the length of the buffer to be copied
+ *
+ * Returns the new pointer or NULL on failure.
+ *
+ ***************************************************************************/
+static char *memdup(const char *src, size_t buffer_length)
+{
+  size_t length;
+  bool add = FALSE;
+  char *buffer;
+
+  if(buffer_length)
+    length = buffer_length;
+  else if(src) {
+    length = strlen(src);
+    add = TRUE;
+  }
+  else
+    /* no length and a NULL src pointer! */
+    return strdup("");
+
+  buffer = malloc(length+add);
+  if(!buffer)
+    return NULL; /* fail */
+
+  memcpy(buffer, src, length);
+
+  /* if length unknown do null termination */
+  if(add)
+    buffer[length] = '\0';
+
+  return buffer;
+}
+
+/***************************************************************************
+ *
+ * FormAdd()
+ *
+ * Stores a formpost parameter and builds the appropriate linked list.
+ *
+ * Has two principal functionalities: using files and byte arrays as
+ * post parts. Byte arrays are either copied or just the pointer is stored
+ * (as the user requests) while for files only the filename and not the
+ * content is stored.
+ *
+ * While you may have only one byte array for each name, multiple filenames
+ * are allowed (and because of this feature CURLFORM_END is needed after
+ * using CURLFORM_FILE).
+ *
+ * Examples:
+ *
+ * Simple name/value pair with copied contents:
+ * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
+ * CURLFORM_COPYCONTENTS, "value", CURLFORM_END);
+ *
+ * name/value pair where only the content pointer is remembered:
+ * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
+ * CURLFORM_PTRCONTENTS, ptr, CURLFORM_CONTENTSLENGTH, 10, CURLFORM_END);
+ * (if CURLFORM_CONTENTSLENGTH is missing strlen () is used)
+ *
+ * storing a filename (CONTENTTYPE is optional!):
+ * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
+ * CURLFORM_FILE, "filename1", CURLFORM_CONTENTTYPE, "plain/text",
+ * CURLFORM_END);
+ *
+ * storing multiple filenames:
+ * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
+ * CURLFORM_FILE, "filename1", CURLFORM_FILE, "filename2", CURLFORM_END);
+ *
+ * Returns:
+ * CURL_FORMADD_OK             on success
+ * CURL_FORMADD_MEMORY         if the FormInfo allocation fails
+ * CURL_FORMADD_OPTION_TWICE   if one option is given twice for one Form
+ * CURL_FORMADD_NULL           if a null pointer was given for a char
+ * CURL_FORMADD_MEMORY         if the allocation of a FormInfo struct failed
+ * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used
+ * CURL_FORMADD_INCOMPLETE     if the some FormInfo is not complete (or error)
+ * CURL_FORMADD_MEMORY         if a HttpPost struct cannot be allocated
+ * CURL_FORMADD_MEMORY         if some allocation for string copying failed.
+ * CURL_FORMADD_ILLEGAL_ARRAY  if an illegal option is used in an array
+ *
+ ***************************************************************************/
+
+static
+CURLFORMcode FormAdd(struct curl_httppost **httppost,
+                     struct curl_httppost **last_post,
+                     va_list params)
+{
+  FormInfo *first_form, *current_form, *form = NULL;
+  CURLFORMcode return_value = CURL_FORMADD_OK;
+  const char *prevtype = NULL;
+  struct curl_httppost *post = NULL;
+  CURLformoption option;
+  struct curl_forms *forms = NULL;
+  char *array_value=NULL; /* value read from an array */
+
+  /* This is a state variable, that if TRUE means that we're parsing an
+     array that we got passed to us. If FALSE we're parsing the input
+     va_list arguments. */
+  bool array_state = FALSE;
+
+  /*
+   * We need to allocate the first struct to fill in.
+   */
+  first_form = calloc(1, sizeof(struct FormInfo));
+  if(!first_form)
+    return CURL_FORMADD_MEMORY;
+
+  current_form = first_form;
+
+  /*
+   * Loop through all the options set. Break if we have an error to report.
+   */
+  while(return_value == CURL_FORMADD_OK) {
+
+    /* first see if we have more parts of the array param */
+    if(array_state && forms) {
+      /* get the upcoming option from the given array */
+      option = forms->option;
+      array_value = (char *)forms->value;
+
+      forms++; /* advance this to next entry */
+      if(CURLFORM_END == option) {
+        /* end of array state */
+        array_state = FALSE;
+        continue;
+      }
+    }
+    else {
+      /* This is not array-state, get next option */
+      option = va_arg(params, CURLformoption);
+      if(CURLFORM_END == option)
+        break;
+    }
+
+    switch (option) {
+    case CURLFORM_ARRAY:
+      if(array_state)
+        /* we don't support an array from within an array */
+        return_value = CURL_FORMADD_ILLEGAL_ARRAY;
+      else {
+        forms = va_arg(params, struct curl_forms *);
+        if(forms)
+          array_state = TRUE;
+        else
+          return_value = CURL_FORMADD_NULL;
+      }
+      break;
+
+      /*
+       * Set the Name property.
+       */
+    case CURLFORM_PTRNAME:
+#ifdef CURL_DOES_CONVERSIONS
+      /* Treat CURLFORM_PTR like CURLFORM_COPYNAME so that libcurl will copy
+       * the data in all cases so that we'll have safe memory for the eventual
+       * conversion.
+       */
+#else
+      current_form->flags |= HTTPPOST_PTRNAME; /* fall through */
+#endif
+    case CURLFORM_COPYNAME:
+      if(current_form->name)
+        return_value = CURL_FORMADD_OPTION_TWICE;
+      else {
+        char *name = array_state?
+          array_value:va_arg(params, char *);
+        if(name)
+          current_form->name = name; /* store for the moment */
+        else
+          return_value = CURL_FORMADD_NULL;
+      }
+      break;
+    case CURLFORM_NAMELENGTH:
+      if(current_form->namelength)
+        return_value = CURL_FORMADD_OPTION_TWICE;
+      else
+        current_form->namelength =
+          array_state?(size_t)array_value:(size_t)va_arg(params, long);
+      break;
+
+      /*
+       * Set the contents property.
+       */
+    case CURLFORM_PTRCONTENTS:
+      current_form->flags |= HTTPPOST_PTRCONTENTS; /* fall through */
+    case CURLFORM_COPYCONTENTS:
+      if(current_form->value)
+        return_value = CURL_FORMADD_OPTION_TWICE;
+      else {
+        char *value =
+          array_state?array_value:va_arg(params, char *);
+        if(value)
+          current_form->value = value; /* store for the moment */
+        else
+          return_value = CURL_FORMADD_NULL;
+      }
+      break;
+    case CURLFORM_CONTENTSLENGTH:
+      if(current_form->contentslength)
+        return_value = CURL_FORMADD_OPTION_TWICE;
+      else
+        current_form->contentslength =
+          array_state?(size_t)array_value:(size_t)va_arg(params, long);
+      break;
+
+      /* Get contents from a given file name */
+    case CURLFORM_FILECONTENT:
+      if(current_form->flags != 0)
+        return_value = CURL_FORMADD_OPTION_TWICE;
+      else {
+        const char *filename = array_state?
+          array_value:va_arg(params, char *);
+        if(filename) {
+          current_form->value = strdup(filename);
+          if(!current_form->value)
+            return_value = CURL_FORMADD_MEMORY;
+          else {
+            current_form->flags |= HTTPPOST_READFILE;
+            current_form->value_alloc = TRUE;
+          }
+        }
+        else
+          return_value = CURL_FORMADD_NULL;
+      }
+      break;
+
+      /* We upload a file */
+    case CURLFORM_FILE:
+      {
+        const char *filename = array_state?array_value:
+          va_arg(params, char *);
+
+        if(current_form->value) {
+          if(current_form->flags & HTTPPOST_FILENAME) {
+            if(filename) {
+              char *fname = strdup(filename);
+              if(!fname)
+                return_value = CURL_FORMADD_MEMORY;
+              else {
+                form = AddFormInfo(fname, NULL, current_form);
+                if(!form) {
+                  Curl_safefree(fname);
+                  return_value = CURL_FORMADD_MEMORY;
+                }
+                else {
+                  form->value_alloc = TRUE;
+                  current_form = form;
+                  form = NULL;
+                }
+              }
+            }
+            else
+              return_value = CURL_FORMADD_NULL;
+          }
+          else
+            return_value = CURL_FORMADD_OPTION_TWICE;
+        }
+        else {
+          if(filename) {
+            current_form->value = strdup(filename);
+            if(!current_form->value)
+              return_value = CURL_FORMADD_MEMORY;
+            else {
+              current_form->flags |= HTTPPOST_FILENAME;
+              current_form->value_alloc = TRUE;
+            }
+          }
+          else
+            return_value = CURL_FORMADD_NULL;
+        }
+        break;
+      }
+
+    case CURLFORM_BUFFERPTR:
+      current_form->flags |= HTTPPOST_PTRBUFFER|HTTPPOST_BUFFER;
+      if(current_form->buffer)
+        return_value = CURL_FORMADD_OPTION_TWICE;
+      else {
+        char *buffer =
+          array_state?array_value:va_arg(params, char *);
+        if(buffer) {
+          current_form->buffer = buffer; /* store for the moment */
+          current_form->value = buffer; /* make it non-NULL to be accepted
+                                           as fine */
+        }
+        else
+          return_value = CURL_FORMADD_NULL;
+      }
+      break;
+
+    case CURLFORM_BUFFERLENGTH:
+      if(current_form->bufferlength)
+        return_value = CURL_FORMADD_OPTION_TWICE;
+      else
+        current_form->bufferlength =
+          array_state?(size_t)array_value:(size_t)va_arg(params, long);
+      break;
+
+    case CURLFORM_STREAM:
+      current_form->flags |= HTTPPOST_CALLBACK;
+      if(current_form->userp)
+        return_value = CURL_FORMADD_OPTION_TWICE;
+      else {
+        char *userp =
+          array_state?array_value:va_arg(params, char *);
+        if(userp) {
+          current_form->userp = userp;
+          current_form->value = userp; /* this isn't strictly true but we
+                                          derive a value from this later on
+                                          and we need this non-NULL to be
+                                          accepted as a fine form part */
+        }
+        else
+          return_value = CURL_FORMADD_NULL;
+      }
+      break;
+
+    case CURLFORM_CONTENTTYPE:
+      {
+        const char *contenttype =
+          array_state?array_value:va_arg(params, char *);
+        if(current_form->contenttype) {
+          if(current_form->flags & HTTPPOST_FILENAME) {
+            if(contenttype) {
+              char *type = strdup(contenttype);
+              if(!type)
+                return_value = CURL_FORMADD_MEMORY;
+              else {
+                form = AddFormInfo(NULL, type, current_form);
+                if(!form) {
+                  Curl_safefree(type);
+                  return_value = CURL_FORMADD_MEMORY;
+                }
+                else {
+                  form->contenttype_alloc = TRUE;
+                  current_form = form;
+                  form = NULL;
+                }
+              }
+            }
+            else
+              return_value = CURL_FORMADD_NULL;
+          }
+          else
+            return_value = CURL_FORMADD_OPTION_TWICE;
+        }
+        else {
+          if(contenttype) {
+            current_form->contenttype = strdup(contenttype);
+            if(!current_form->contenttype)
+              return_value = CURL_FORMADD_MEMORY;
+            else
+              current_form->contenttype_alloc = TRUE;
+          }
+          else
+            return_value = CURL_FORMADD_NULL;
+        }
+        break;
+      }
+    case CURLFORM_CONTENTHEADER:
+      {
+        /* this "cast increases required alignment of target type" but
+           we consider it OK anyway */
+        struct curl_slist* list = array_state?
+          (struct curl_slist*)array_value:
+          va_arg(params, struct curl_slist*);
+
+        if(current_form->contentheader)
+          return_value = CURL_FORMADD_OPTION_TWICE;
+        else
+          current_form->contentheader = list;
+
+        break;
+      }
+    case CURLFORM_FILENAME:
+    case CURLFORM_BUFFER:
+      {
+        const char *filename = array_state?array_value:
+          va_arg(params, char *);
+        if(current_form->showfilename)
+          return_value = CURL_FORMADD_OPTION_TWICE;
+        else {
+          current_form->showfilename = strdup(filename);
+          if(!current_form->showfilename)
+            return_value = CURL_FORMADD_MEMORY;
+          else
+            current_form->showfilename_alloc = TRUE;
+        }
+        break;
+      }
+    default:
+      return_value = CURL_FORMADD_UNKNOWN_OPTION;
+      break;
+    }
+  }
+
+  if(CURL_FORMADD_OK != return_value) {
+    /* On error, free allocated fields for all nodes of the FormInfo linked
+       list without deallocating nodes. List nodes are deallocated later on */
+    FormInfo *ptr;
+    for(ptr = first_form; ptr != NULL; ptr = ptr->more) {
+      if(ptr->name_alloc) {
+        Curl_safefree(ptr->name);
+        ptr->name_alloc = FALSE;
+      }
+      if(ptr->value_alloc) {
+        Curl_safefree(ptr->value);
+        ptr->value_alloc = FALSE;
+      }
+      if(ptr->contenttype_alloc) {
+        Curl_safefree(ptr->contenttype);
+        ptr->contenttype_alloc = FALSE;
+      }
+      if(ptr->showfilename_alloc) {
+        Curl_safefree(ptr->showfilename);
+        ptr->showfilename_alloc = FALSE;
+      }
+    }
+  }
+
+  if(CURL_FORMADD_OK == return_value) {
+    /* go through the list, check for completeness and if everything is
+     * alright add the HttpPost item otherwise set return_value accordingly */
+
+    post = NULL;
+    for(form = first_form;
+        form != NULL;
+        form = form->more) {
+      if(((!form->name || !form->value) && !post) ||
+         ( (form->contentslength) &&
+           (form->flags & HTTPPOST_FILENAME) ) ||
+         ( (form->flags & HTTPPOST_FILENAME) &&
+           (form->flags & HTTPPOST_PTRCONTENTS) ) ||
+
+         ( (!form->buffer) &&
+           (form->flags & HTTPPOST_BUFFER) &&
+           (form->flags & HTTPPOST_PTRBUFFER) ) ||
+
+         ( (form->flags & HTTPPOST_READFILE) &&
+           (form->flags & HTTPPOST_PTRCONTENTS) )
+        ) {
+        return_value = CURL_FORMADD_INCOMPLETE;
+        break;
+      }
+      else {
+        if(((form->flags & HTTPPOST_FILENAME) ||
+            (form->flags & HTTPPOST_BUFFER)) &&
+           !form->contenttype ) {
+          /* our contenttype is missing */
+          form->contenttype
+            = strdup(ContentTypeForFilename(form->value, prevtype));
+          if(!form->contenttype) {
+            return_value = CURL_FORMADD_MEMORY;
+            break;
+          }
+          form->contenttype_alloc = TRUE;
+        }
+        if(!(form->flags & HTTPPOST_PTRNAME) &&
+           (form == first_form) ) {
+          /* Note that there's small risk that form->name is NULL here if the
+             app passed in a bad combo, so we better check for that first. */
+          if(form->name)
+            /* copy name (without strdup; possibly contains null characters) */
+            form->name = memdup(form->name, form->namelength);
+          if(!form->name) {
+            return_value = CURL_FORMADD_MEMORY;
+            break;
+          }
+          form->name_alloc = TRUE;
+        }
+        if(!(form->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE |
+                            HTTPPOST_PTRCONTENTS | HTTPPOST_PTRBUFFER |
+                            HTTPPOST_CALLBACK)) ) {
+          /* copy value (without strdup; possibly contains null characters) */
+          form->value = memdup(form->value, form->contentslength);
+          if(!form->value) {
+            return_value = CURL_FORMADD_MEMORY;
+            break;
+          }
+          form->value_alloc = TRUE;
+        }
+        post = AddHttpPost(form->name, form->namelength,
+                           form->value, form->contentslength,
+                           form->buffer, form->bufferlength,
+                           form->contenttype, form->flags,
+                           form->contentheader, form->showfilename,
+                           form->userp,
+                           post, httppost,
+                           last_post);
+
+        if(!post) {
+          return_value = CURL_FORMADD_MEMORY;
+          break;
+        }
+
+        if(form->contenttype)
+          prevtype = form->contenttype;
+      }
+    }
+    if(CURL_FORMADD_OK != return_value) {
+      /* On error, free allocated fields for nodes of the FormInfo linked
+         list which are not already owned by the httppost linked list
+         without deallocating nodes. List nodes are deallocated later on */
+      FormInfo *ptr;
+      for(ptr = form; ptr != NULL; ptr = ptr->more) {
+        if(ptr->name_alloc) {
+          Curl_safefree(ptr->name);
+          ptr->name_alloc = FALSE;
+        }
+        if(ptr->value_alloc) {
+          Curl_safefree(ptr->value);
+          ptr->value_alloc = FALSE;
+        }
+        if(ptr->contenttype_alloc) {
+          Curl_safefree(ptr->contenttype);
+          ptr->contenttype_alloc = FALSE;
+        }
+        if(ptr->showfilename_alloc) {
+          Curl_safefree(ptr->showfilename);
+          ptr->showfilename_alloc = FALSE;
+        }
+      }
+    }
+  }
+
+  /* Always deallocate FormInfo linked list nodes without touching node
+     fields given that these have either been deallocated or are owned
+     now by the httppost linked list */
+  while(first_form) {
+    FormInfo *ptr = first_form->more;
+    Curl_safefree(first_form);
+    first_form = ptr;
+  }
+
+  return return_value;
+}
+
+/*
+ * curl_formadd() is a public API to add a section to the multipart formpost.
+ *
+ * @unittest: 1308
+ */
+
+CURLFORMcode curl_formadd(struct curl_httppost **httppost,
+                          struct curl_httppost **last_post,
+                          ...)
+{
+  va_list arg;
+  CURLFORMcode result;
+  va_start(arg, last_post);
+  result = FormAdd(httppost, last_post, arg);
+  va_end(arg);
+  return result;
+}
+
+/*
+ * AddFormData() adds a chunk of data to the FormData linked list.
+ *
+ * size is incremented by the chunk length, unless it is NULL
+ */
+static CURLcode AddFormData(struct FormData **formp,
+                            enum formtype type,
+                            const void *line,
+                            size_t length,
+                            curl_off_t *size)
+{
+  struct FormData *newform = malloc(sizeof(struct FormData));
+  if(!newform)
+    return CURLE_OUT_OF_MEMORY;
+  newform->next = NULL;
+
+  if(type <= FORM_CONTENT) {
+    /* we make it easier for plain strings: */
+    if(!length)
+      length = strlen((char *)line);
+
+    newform->line = malloc(length+1);
+    if(!newform->line) {
+      free(newform);
+      return CURLE_OUT_OF_MEMORY;
+    }
+    memcpy(newform->line, line, length);
+    newform->length = length;
+    newform->line[length]=0; /* zero terminate for easier debugging */
+  }
+  else
+    /* For callbacks and files we don't have any actual data so we just keep a
+       pointer to whatever this points to */
+    newform->line = (char *)line;
+
+  newform->type = type;
+
+  if(*formp) {
+    (*formp)->next = newform;
+    *formp = newform;
+  }
+  else
+    *formp = newform;
+
+  if(size) {
+    if(type != FORM_FILE)
+      /* for static content as well as callback data we add the size given
+         as input argument */
+      *size += length;
+    else {
+      /* Since this is a file to be uploaded here, add the size of the actual
+         file */
+      if(!strequal("-", newform->line)) {
+        struct_stat file;
+        if(!stat(newform->line, &file)) {
+          *size += file.st_size;
+        }
+      }
+    }
+  }
+  return CURLE_OK;
+}
+
+/*
+ * AddFormDataf() adds printf()-style formatted data to the formdata chain.
+ */
+
+static CURLcode AddFormDataf(struct FormData **formp,
+                             curl_off_t *size,
+                             const char *fmt, ...)
+{
+  char s[4096];
+  va_list ap;
+  va_start(ap, fmt);
+  vsnprintf(s, sizeof(s), fmt, ap);
+  va_end(ap);
+
+  return AddFormData(formp, FORM_DATA, s, 0, size);
+}
+
+/*
+ * Curl_formclean() is used from curl_http.c, this cleans a built FormData
+ * linked list
+ */
+void Curl_formclean(struct FormData **form_ptr)
+{
+  struct FormData *next, *form;
+
+  form = *form_ptr;
+  if(!form)
+    return;
+
+  do {
+    next=form->next;  /* the following form line */
+    if(form->type <= FORM_CONTENT)
+      free(form->line); /* free the line */
+    free(form);       /* free the struct */
+
+  } while((form = next) != NULL); /* continue */
+
+  *form_ptr = NULL;
+}
+
+/*
+ * curl_formget()
+ * Serialize a curl_httppost struct.
+ * Returns 0 on success.
+ *
+ * @unittest: 1308
+ */
+int curl_formget(struct curl_httppost *form, void *arg,
+                 curl_formget_callback append)
+{
+  CURLcode rc;
+  curl_off_t size;
+  struct FormData *data, *ptr;
+
+  rc = Curl_getformdata(NULL, &data, form, NULL, &size);
+  if(rc != CURLE_OK)
+    return (int)rc;
+
+  for(ptr = data; ptr; ptr = ptr->next) {
+    if((ptr->type == FORM_FILE) || (ptr->type == FORM_CALLBACK)) {
+      char buffer[8192];
+      size_t nread;
+      struct Form temp;
+
+      Curl_FormInit(&temp, ptr);
+
+      do {
+        nread = readfromfile(&temp, buffer, sizeof(buffer));
+        if((nread == (size_t) -1) ||
+           (nread > sizeof(buffer)) ||
+           (nread != append(arg, buffer, nread))) {
+          if(temp.fp)
+            fclose(temp.fp);
+          Curl_formclean(&data);
+          return -1;
+        }
+      } while(nread);
+    }
+    else {
+      if(ptr->length != append(arg, ptr->line, ptr->length)) {
+        Curl_formclean(&data);
+        return -1;
+      }
+    }
+  }
+  Curl_formclean(&data);
+  return 0;
+}
+
+/*
+ * curl_formfree() is an external function to free up a whole form post
+ * chain
+ */
+void curl_formfree(struct curl_httppost *form)
+{
+  struct curl_httppost *next;
+
+  if(!form)
+    /* no form to free, just get out of this */
+    return;
+
+  do {
+    next=form->next;  /* the following form line */
+
+    /* recurse to sub-contents */
+    if(form->more)
+      curl_formfree(form->more);
+
+    if(!(form->flags & HTTPPOST_PTRNAME) && form->name)
+      free(form->name); /* free the name */
+    if(!(form->flags &
+         (HTTPPOST_PTRCONTENTS|HTTPPOST_BUFFER|HTTPPOST_CALLBACK)) &&
+       form->contents)
+      free(form->contents); /* free the contents */
+    if(form->contenttype)
+      free(form->contenttype); /* free the content type */
+    if(form->showfilename)
+      free(form->showfilename); /* free the faked file name */
+    free(form);       /* free the struct */
+
+  } while((form = next) != NULL); /* continue */
+}
+
+#ifndef HAVE_BASENAME
+/*
+  (Quote from The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004
+  Edition)
+
+  The basename() function shall take the pathname pointed to by path and
+  return a pointer to the final component of the pathname, deleting any
+  trailing '/' characters.
+
+  If the string pointed to by path consists entirely of the '/' character,
+  basename() shall return a pointer to the string "/". If the string pointed
+  to by path is exactly "//", it is implementation-defined whether '/' or "//"
+  is returned.
+
+  If path is a null pointer or points to an empty string, basename() shall
+  return a pointer to the string ".".
+
+  The basename() function may modify the string pointed to by path, and may
+  return a pointer to static storage that may then be overwritten by a
+  subsequent call to basename().
+
+  The basename() function need not be reentrant. A function that is not
+  required to be reentrant is not required to be thread-safe.
+
+*/
+static char *Curl_basename(char *path)
+{
+  /* Ignore all the details above for now and make a quick and simple
+     implementaion here */
+  char *s1;
+  char *s2;
+
+  s1=strrchr(path, '/');
+  s2=strrchr(path, '\\');
+
+  if(s1 && s2) {
+    path = (s1 > s2? s1 : s2)+1;
+  }
+  else if(s1)
+    path = s1 + 1;
+  else if(s2)
+    path = s2 + 1;
+
+  return path;
+}
+#endif
+
+static char *strippath(const char *fullfile)
+{
+  char *filename;
+  char *base;
+  filename = strdup(fullfile); /* duplicate since basename() may ruin the
+                                  buffer it works on */
+  if(!filename)
+    return NULL;
+  base = strdup(basename(filename));
+
+  free(filename); /* free temporary buffer */
+
+  return base; /* returns an allocated string or NULL ! */
+}
+
+/*
+ * Curl_getformdata() converts a linked list of "meta data" into a complete
+ * (possibly huge) multipart formdata. The input list is in 'post', while the
+ * output resulting linked lists gets stored in '*finalform'. *sizep will get
+ * the total size of the whole POST.
+ * A multipart/form_data content-type is built, unless a custom content-type
+ * is passed in 'custom_content_type'.
+ *
+ * This function will not do a failf() for the potential memory failures but
+ * should for all other errors it spots. Just note that this function MAY get
+ * a NULL pointer in the 'data' argument.
+ */
+
+CURLcode Curl_getformdata(struct SessionHandle *data,
+                          struct FormData **finalform,
+                          struct curl_httppost *post,
+                          const char *custom_content_type,
+                          curl_off_t *sizep)
+{
+  struct FormData *form = NULL;
+  struct FormData *firstform;
+  struct curl_httppost *file;
+  CURLcode result = CURLE_OK;
+
+  curl_off_t size = 0; /* support potentially ENORMOUS formposts */
+  char *boundary;
+  char *fileboundary = NULL;
+  struct curl_slist* curList;
+
+  *finalform = NULL; /* default form is empty */
+
+  if(!post)
+    return result; /* no input => no output! */
+
+  boundary = Curl_FormBoundary();
+  if(!boundary)
+    return CURLE_OUT_OF_MEMORY;
+
+  /* Make the first line of the output */
+  result = AddFormDataf(&form, NULL,
+                        "%s; boundary=%s\r\n",
+                        custom_content_type?custom_content_type:
+                        "Content-Type: multipart/form-data",
+                        boundary);
+
+  if(result) {
+    Curl_safefree(boundary);
+    return result;
+  }
+  /* we DO NOT include that line in the total size of the POST, since it'll be
+     part of the header! */
+
+  firstform = form;
+
+  do {
+
+    if(size) {
+      result = AddFormDataf(&form, &size, "\r\n");
+      if(result)
+        break;
+    }
+
+    /* boundary */
+    result = AddFormDataf(&form, &size, "--%s\r\n", boundary);
+    if(result)
+      break;
+
+    /* Maybe later this should be disabled when a custom_content_type is
+       passed, since Content-Disposition is not meaningful for all multipart
+       types.
+    */
+    result = AddFormDataf(&form, &size,
+                          "Content-Disposition: form-data; name=\"");
+    if(result)
+      break;
+
+    result = AddFormData(&form, FORM_DATA, post->name, post->namelength,
+                         &size);
+    if(result)
+      break;
+
+    result = AddFormDataf(&form, &size, "\"");
+    if(result)
+      break;
+
+    if(post->more) {
+      /* If used, this is a link to more file names, we must then do
+         the magic to include several files with the same field name */
+
+      Curl_safefree(fileboundary);
+      fileboundary = Curl_FormBoundary();
+      if(!fileboundary) {
+        result = CURLE_OUT_OF_MEMORY;
+        break;
+      }
+
+      result = AddFormDataf(&form, &size,
+                            "\r\nContent-Type: multipart/mixed,"
+                            " boundary=%s\r\n",
+                            fileboundary);
+      if(result)
+        break;
+    }
+
+    file = post;
+
+    do {
+
+      /* If 'showfilename' is set, that is a faked name passed on to us
+         to use to in the formpost. If that is not set, the actually used
+         local file name should be added. */
+
+      if(post->more) {
+        /* if multiple-file */
+        char *filebasename = NULL;
+        if(!file->showfilename) {
+          filebasename = strippath(file->contents);
+          if(!filebasename) {
+            result = CURLE_OUT_OF_MEMORY;
+            break;
+          }
+        }
+
+        result = AddFormDataf(&form, &size,
+                              "\r\n--%s\r\nContent-Disposition: "
+                              "attachment; filename=\"%s\"",
+                              fileboundary,
+                              (file->showfilename?file->showfilename:
+                               filebasename));
+        Curl_safefree(filebasename);
+        if(result)
+          break;
+      }
+      else if(post->flags & (HTTPPOST_FILENAME|HTTPPOST_BUFFER|
+                             HTTPPOST_CALLBACK)) {
+        /* it should be noted that for the HTTPPOST_FILENAME and
+           HTTPPOST_CALLBACK cases the ->showfilename struct member is always
+           assigned at this point */
+        if(post->showfilename || (post->flags & HTTPPOST_FILENAME)) {
+          char *filebasename=
+            (!post->showfilename)?strippath(post->contents):NULL;
+
+          result = AddFormDataf(&form, &size,
+                                "; filename=\"%s\"",
+                                (post->showfilename?post->showfilename:
+                                 filebasename));
+          Curl_safefree(filebasename);
+        }
+
+        if(result)
+          break;
+      }
+
+      if(file->contenttype) {
+        /* we have a specified type */
+        result = AddFormDataf(&form, &size,
+                              "\r\nContent-Type: %s",
+                              file->contenttype);
+        if(result)
+          break;
+      }
+
+      curList = file->contentheader;
+      while(curList) {
+        /* Process the additional headers specified for this form */
+        result = AddFormDataf( &form, &size, "\r\n%s", curList->data );
+        if(result)
+          break;
+        curList = curList->next;
+      }
+      if(result)
+        break;
+
+      result = AddFormDataf(&form, &size, "\r\n\r\n");
+      if(result)
+        break;
+
+      if((post->flags & HTTPPOST_FILENAME) ||
+         (post->flags & HTTPPOST_READFILE)) {
+        /* we should include the contents from the specified file */
+        FILE *fileread;
+
+        fileread = strequal("-", file->contents)?
+          stdin:fopen(file->contents, "rb"); /* binary read for win32  */
+
+        /*
+         * VMS: This only allows for stream files on VMS.  Stream files are
+         * OK, as are FIXED & VAR files WITHOUT implied CC For implied CC,
+         * every record needs to have a \n appended & 1 added to SIZE
+         */
+
+        if(fileread) {
+          if(fileread != stdin) {
+            /* close the file */
+            fclose(fileread);
+            /* add the file name only - for later reading from this */
+            result = AddFormData(&form, FORM_FILE, file->contents, 0, &size);
+          }
+          else {
+            /* When uploading from stdin, we can't know the size of the file,
+             * thus must read the full file as before. We *could* use chunked
+             * transfer-encoding, but that only works for HTTP 1.1 and we
+             * can't be sure we work with such a server.
+             */
+            size_t nread;
+            char buffer[512];
+            while((nread = fread(buffer, 1, sizeof(buffer), fileread)) != 0) {
+              result = AddFormData(&form, FORM_CONTENT, buffer, nread, &size);
+              if(result)
+                break;
+            }
+          }
+        }
+        else {
+          if(data)
+            failf(data, "couldn't open file \"%s\"", file->contents);
+          *finalform = NULL;
+          result = CURLE_READ_ERROR;
+        }
+      }
+      else if(post->flags & HTTPPOST_BUFFER)
+        /* include contents of buffer */
+        result = AddFormData(&form, FORM_CONTENT, post->buffer,
+                             post->bufferlength, &size);
+      else if(post->flags & HTTPPOST_CALLBACK)
+        /* the contents should be read with the callback and the size
+           is set with the contentslength */
+        result = AddFormData(&form, FORM_CALLBACK, post->userp,
+                             post->contentslength, &size);
+      else
+        /* include the contents we got */
+        result = AddFormData(&form, FORM_CONTENT, post->contents,
+                             post->contentslength, &size);
+
+      file = file->more;
+    } while(file && !result); /* for each specified file for this field */
+
+    if(result)
+      break;
+
+    if(post->more) {
+      /* this was a multiple-file inclusion, make a termination file
+         boundary: */
+      result = AddFormDataf(&form, &size,
+                           "\r\n--%s--",
+                           fileboundary);
+      if(result)
+        break;
+    }
+
+  } while((post = post->next) != NULL); /* for each field */
+
+  /* end-boundary for everything */
+  if(CURLE_OK == result)
+    result = AddFormDataf(&form, &size,
+                          "\r\n--%s--\r\n",
+                          boundary);
+
+  if(result) {
+    Curl_formclean(&firstform);
+    Curl_safefree(fileboundary);
+    Curl_safefree(boundary);
+    return result;
+  }
+
+  *sizep = size;
+
+  Curl_safefree(fileboundary);
+  Curl_safefree(boundary);
+
+  *finalform = firstform;
+
+  return result;
+}
+
+/*
+ * Curl_FormInit() inits the struct 'form' points to with the 'formdata'
+ * and resets the 'sent' counter.
+ */
+int Curl_FormInit(struct Form *form, struct FormData *formdata )
+{
+  if(!formdata)
+    return 1; /* error */
+
+  form->data = formdata;
+  form->sent = 0;
+  form->fp = NULL;
+  form->fread_func = ZERO_NULL;
+
+  return 0;
+}
+
+/*
+ * readfromfile()
+ *
+ * The read callback that this function may use can return a value larger than
+ * 'size' (which then this function returns) that indicates a problem and it
+ * must be properly dealt with
+ */
+static size_t readfromfile(struct Form *form, char *buffer,
+                           size_t size)
+{
+  size_t nread;
+  bool callback = (form->data->type == FORM_CALLBACK)?TRUE:FALSE;
+
+  if(callback) {
+    if(form->fread_func == ZERO_NULL)
+      return 0;
+    else
+      nread = form->fread_func(buffer, 1, size, form->data->line);
+  }
+  else {
+    if(!form->fp) {
+      /* this file hasn't yet been opened */
+      form->fp = fopen(form->data->line, "rb"); /* b is for binary */
+      if(!form->fp)
+        return (size_t)-1; /* failure */
+    }
+    nread = fread(buffer, 1, size, form->fp);
+  }
+  if(!nread) {
+    /* this is the last chunk from the file, move on */
+    if(form->fp) {
+      fclose(form->fp);
+      form->fp = NULL;
+    }
+    form->data = form->data->next;
+  }
+
+  return nread;
+}
+
+/*
+ * Curl_FormReader() is the fread() emulation function that will be used to
+ * deliver the formdata to the transfer loop and then sent away to the peer.
+ */
+size_t Curl_FormReader(char *buffer,
+                       size_t size,
+                       size_t nitems,
+                       FILE *mydata)
+{
+  struct Form *form;
+  size_t wantedsize;
+  size_t gotsize = 0;
+
+  form=(struct Form *)mydata;
+
+  wantedsize = size * nitems;
+
+  if(!form->data)
+    return 0; /* nothing, error, empty */
+
+  if((form->data->type == FORM_FILE) ||
+     (form->data->type == FORM_CALLBACK)) {
+    gotsize = readfromfile(form, buffer, wantedsize);
+
+    if(gotsize)
+      /* If positive or -1, return. If zero, continue! */
+      return gotsize;
+  }
+  do {
+
+    if((form->data->length - form->sent ) > wantedsize - gotsize) {
+
+      memcpy(buffer + gotsize , form->data->line + form->sent,
+             wantedsize - gotsize);
+
+      form->sent += wantedsize-gotsize;
+
+      return wantedsize;
+    }
+
+    memcpy(buffer+gotsize,
+           form->data->line + form->sent,
+           (form->data->length - form->sent) );
+    gotsize += form->data->length - form->sent;
+
+    form->sent = 0;
+
+    form->data = form->data->next; /* advance */
+
+  } while(form->data && (form->data->type < FORM_CALLBACK));
+  /* If we got an empty line and we have more data, we proceed to the next
+     line immediately to avoid returning zero before we've reached the end. */
+
+  return gotsize;
+}
+
+/*
+ * Curl_formpostheader() returns the first line of the formpost, the
+ * request-header part (which is not part of the request-body like the rest of
+ * the post).
+ */
+char *Curl_formpostheader(void *formp, size_t *len)
+{
+  char *header;
+  struct Form *form=(struct Form *)formp;
+
+  if(!form->data)
+    return 0; /* nothing, ERROR! */
+
+  header = form->data->line;
+  *len = form->data->length;
+
+  form->data = form->data->next; /* advance */
+
+  return header;
+}
+
+#else  /* CURL_DISABLE_HTTP */
+CURLFORMcode curl_formadd(struct curl_httppost **httppost,
+                          struct curl_httppost **last_post,
+                          ...)
+{
+  (void)httppost;
+  (void)last_post;
+  return CURL_FORMADD_DISABLED;
+}
+
+int curl_formget(struct curl_httppost *form, void *arg,
+                 curl_formget_callback append)
+{
+  (void) form;
+  (void) arg;
+  (void) append;
+  return CURL_FORMADD_DISABLED;
+}
+
+void curl_formfree(struct curl_httppost *form)
+{
+  (void)form;
+  /* does nothing HTTP is disabled */
+}
+
+#endif  /* CURL_DISABLE_HTTP */
+
+#if !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY)
+
+/*
+ * Curl_FormBoundary() creates a suitable boundary string and returns an
+ * allocated one. This is also used by SSL-code so it must be present even
+ * if HTTP is disabled!
+ */
+char *Curl_FormBoundary(void)
+{
+  char *retstring;
+  size_t i;
+
+  static const char table16[]="0123456789abcdef";
+
+  retstring = malloc(BOUNDARY_LENGTH+1);
+
+  if(!retstring)
+    return NULL; /* failed */
+
+  strcpy(retstring, "----------------------------");
+
+  for(i=strlen(retstring); i<BOUNDARY_LENGTH; i++)
+    retstring[i] = table16[Curl_rand()%16];
+
+  /* 28 dashes and 12 hexadecimal digits makes 12^16 (184884258895036416)
+     combinations */
+  retstring[BOUNDARY_LENGTH]=0; /* zero terminate */
+
+  return retstring;
+}
+
+#endif  /* !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY) */
diff --git a/lib/curl_ftp.c b/lib/curl_ftp.c
new file mode 100644 (file)
index 0000000..653e30b
--- /dev/null
@@ -0,0 +1,4596 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_FTP
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_UTSNAME_H
+#include <sys/utsname.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
+#undef in_addr_t
+#define in_addr_t unsigned long
+#endif
+
+#include <curl/curl.h>
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_if2ip.h"
+#include "curl_hostip.h"
+#include "curl_progress.h"
+#include "curl_transfer.h"
+#include "curl_escape.h"
+#include "curl_http.h" /* for HTTP proxy tunnel stuff */
+#include "curl_socks.h"
+#include "curl_ftp.h"
+#include "curl_fileinfo.h"
+#include "curl_ftplistparser.h"
+
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+#include "curl_krb4.h"
+#endif
+
+#include "curl_strtoofft.h"
+#include "curl_strequal.h"
+#include "curl_sslgen.h"
+#include "curl_connect.h"
+#include "curl_strerror.h"
+#include "curl_inet_ntop.h"
+#include "curl_inet_pton.h"
+#include "curl_select.h"
+#include "curl_parsedate.h" /* for the week day and month names */
+#include "curl_sockaddr.h" /* required for Curl_sockaddr_storage */
+#include "curl_multiif.h"
+#include "curl_url.h"
+#include "curl_rawstr.h"
+#include "curl_speedcheck.h"
+#include "curl_warnless.h"
+#include "curl_http_proxy.h"
+#include "curl_non_ascii.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+#ifndef NI_MAXHOST
+#define NI_MAXHOST 1025
+#endif
+#ifndef INET_ADDRSTRLEN
+#define INET_ADDRSTRLEN 16
+#endif
+
+#ifdef CURL_DISABLE_VERBOSE_STRINGS
+#define ftp_pasv_verbose(a,b,c,d)  Curl_nop_stmt
+#endif
+
+/* Local API functions */
+static void state(struct connectdata *conn,
+                  ftpstate newstate);
+static CURLcode ftp_sendquote(struct connectdata *conn,
+                              struct curl_slist *quote);
+static CURLcode ftp_quit(struct connectdata *conn);
+static CURLcode ftp_parse_url_path(struct connectdata *conn);
+static CURLcode ftp_regular_transfer(struct connectdata *conn, bool *done);
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static void ftp_pasv_verbose(struct connectdata *conn,
+                             Curl_addrinfo *ai,
+                             char *newhost, /* ascii version */
+                             int port);
+#endif
+static CURLcode ftp_state_post_rest(struct connectdata *conn);
+static CURLcode ftp_state_post_cwd(struct connectdata *conn);
+static CURLcode ftp_state_quote(struct connectdata *conn,
+                                bool init, ftpstate instate);
+static CURLcode ftp_nb_type(struct connectdata *conn,
+                            bool ascii, ftpstate newstate);
+static int ftp_need_type(struct connectdata *conn,
+                         bool ascii);
+static CURLcode ftp_do(struct connectdata *conn, bool *done);
+static CURLcode ftp_done(struct connectdata *conn,
+                         CURLcode, bool premature);
+static CURLcode ftp_connect(struct connectdata *conn, bool *done);
+static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection);
+static CURLcode ftp_do_more(struct connectdata *conn, bool *completed);
+static CURLcode ftp_multi_statemach(struct connectdata *conn, bool *done);
+static int ftp_getsock(struct connectdata *conn, curl_socket_t *socks,
+                       int numsocks);
+static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks,
+                              int numsocks);
+static CURLcode ftp_doing(struct connectdata *conn,
+                          bool *dophase_done);
+static CURLcode ftp_setup_connection(struct connectdata * conn);
+
+static CURLcode init_wc_data(struct connectdata *conn);
+static CURLcode wc_statemach(struct connectdata *conn);
+
+static void wc_data_dtor(void *ptr);
+
+static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
+                                         curl_off_t filesize);
+
+static CURLcode ftp_readresp(curl_socket_t sockfd,
+                             struct pingpong *pp,
+                             int *ftpcode,
+                             size_t *size);
+
+/* easy-to-use macro: */
+#define FTPSENDF(x,y,z)    if((result = Curl_ftpsendf(x,y,z)) != CURLE_OK) \
+                              return result
+#define PPSENDF(x,y,z)  if((result = Curl_pp_sendf(x,y,z)) != CURLE_OK) \
+                              return result
+
+
+/*
+ * FTP protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_ftp = {
+  "FTP",                           /* scheme */
+  ftp_setup_connection,            /* setup_connection */
+  ftp_do,                          /* do_it */
+  ftp_done,                        /* done */
+  ftp_do_more,                     /* do_more */
+  ftp_connect,                     /* connect_it */
+  ftp_multi_statemach,             /* connecting */
+  ftp_doing,                       /* doing */
+  ftp_getsock,                     /* proto_getsock */
+  ftp_getsock,                     /* doing_getsock */
+  ftp_domore_getsock,              /* domore_getsock */
+  ZERO_NULL,                       /* perform_getsock */
+  ftp_disconnect,                  /* disconnect */
+  ZERO_NULL,                       /* readwrite */
+  PORT_FTP,                        /* defport */
+  CURLPROTO_FTP,                   /* protocol */
+  PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD
+  | PROTOPT_NOURLQUERY /* flags */
+};
+
+
+#ifdef USE_SSL
+/*
+ * FTPS protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_ftps = {
+  "FTPS",                          /* scheme */
+  ftp_setup_connection,            /* setup_connection */
+  ftp_do,                          /* do_it */
+  ftp_done,                        /* done */
+  ftp_do_more,                     /* do_more */
+  ftp_connect,                     /* connect_it */
+  ftp_multi_statemach,             /* connecting */
+  ftp_doing,                       /* doing */
+  ftp_getsock,                     /* proto_getsock */
+  ftp_getsock,                     /* doing_getsock */
+  ftp_domore_getsock,              /* domore_getsock */
+  ZERO_NULL,                       /* perform_getsock */
+  ftp_disconnect,                  /* disconnect */
+  ZERO_NULL,                       /* readwrite */
+  PORT_FTPS,                       /* defport */
+  CURLPROTO_FTP | CURLPROTO_FTPS,  /* protocol */
+  PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION |
+  PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY /* flags */
+};
+#endif
+
+#ifndef CURL_DISABLE_HTTP
+/*
+ * HTTP-proxyed FTP protocol handler.
+ */
+
+static const struct Curl_handler Curl_handler_ftp_proxy = {
+  "FTP",                                /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  Curl_http,                            /* do_it */
+  Curl_http_done,                       /* done */
+  ZERO_NULL,                            /* do_more */
+  ZERO_NULL,                            /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  PORT_FTP,                             /* defport */
+  CURLPROTO_HTTP,                       /* protocol */
+  PROTOPT_NONE                          /* flags */
+};
+
+
+#ifdef USE_SSL
+/*
+ * HTTP-proxyed FTPS protocol handler.
+ */
+
+static const struct Curl_handler Curl_handler_ftps_proxy = {
+  "FTPS",                               /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  Curl_http,                            /* do_it */
+  Curl_http_done,                       /* done */
+  ZERO_NULL,                            /* do_more */
+  ZERO_NULL,                            /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  PORT_FTPS,                            /* defport */
+  CURLPROTO_HTTP,                       /* protocol */
+  PROTOPT_NONE                          /* flags */
+};
+#endif
+#endif
+
+
+/*
+ * NOTE: back in the old days, we added code in the FTP code that made NOBODY
+ * requests on files respond with headers passed to the client/stdout that
+ * looked like HTTP ones.
+ *
+ * This approach is not very elegant, it causes confusion and is error-prone.
+ * It is subject for removal at the next (or at least a future) soname bump.
+ * Until then you can test the effects of the removal by undefining the
+ * following define named CURL_FTP_HTTPSTYLE_HEAD.
+ */
+#define CURL_FTP_HTTPSTYLE_HEAD 1
+
+static void freedirs(struct ftp_conn *ftpc)
+{
+  int i;
+  if(ftpc->dirs) {
+    for(i=0; i < ftpc->dirdepth; i++) {
+      if(ftpc->dirs[i]) {
+        free(ftpc->dirs[i]);
+        ftpc->dirs[i]=NULL;
+      }
+    }
+    free(ftpc->dirs);
+    ftpc->dirs = NULL;
+    ftpc->dirdepth = 0;
+  }
+  if(ftpc->file) {
+    free(ftpc->file);
+    ftpc->file = NULL;
+  }
+}
+
+/* Returns non-zero if the given string contains CR (\r) or LF (\n),
+   which are not allowed within RFC 959 <string>.
+   Note: The input string is in the client's encoding which might
+   not be ASCII, so escape sequences \r & \n must be used instead
+   of hex values 0x0d & 0x0a.
+*/
+static bool isBadFtpString(const char *string)
+{
+  return ((NULL != strchr(string, '\r')) ||
+          (NULL != strchr(string, '\n'))) ? TRUE : FALSE;
+}
+
+/***********************************************************************
+ *
+ * AcceptServerConnect()
+ *
+ * After connection request is received from the server this function is
+ * called to accept the connection and close the listening socket
+ *
+ */
+static CURLcode AcceptServerConnect(struct connectdata *conn)
+{
+  struct SessionHandle *data = conn->data;
+  curl_socket_t sock = conn->sock[SECONDARYSOCKET];
+  curl_socket_t s = CURL_SOCKET_BAD;
+#ifdef ENABLE_IPV6
+  struct Curl_sockaddr_storage add;
+#else
+  struct sockaddr_in add;
+#endif
+  curl_socklen_t size = (curl_socklen_t) sizeof(add);
+
+  if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) {
+    size = sizeof(add);
+
+    s=accept(sock, (struct sockaddr *) &add, &size);
+  }
+  Curl_closesocket(conn, sock); /* close the first socket */
+
+  if(CURL_SOCKET_BAD == s) {
+    failf(data, "Error accept()ing server connect");
+    return CURLE_FTP_PORT_FAILED;
+  }
+  infof(data, "Connection accepted from server\n");
+
+  conn->sock[SECONDARYSOCKET] = s;
+  curlx_nonblock(s, TRUE); /* enable non-blocking */
+  conn->sock_accepted[SECONDARYSOCKET] = TRUE;
+
+  if(data->set.fsockopt) {
+    int error = 0;
+
+    /* activate callback for setting socket options */
+    error = data->set.fsockopt(data->set.sockopt_client,
+                               s,
+                               CURLSOCKTYPE_ACCEPT);
+
+    if(error) {
+      Curl_closesocket(conn, s); /* close the socket and bail out */
+      conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
+      return CURLE_ABORTED_BY_CALLBACK;
+    }
+  }
+
+  return CURLE_OK;
+
+}
+
+/*
+ * ftp_timeleft_accept() returns the amount of milliseconds left allowed for
+ * waiting server to connect. If the value is negative, the timeout time has
+ * already elapsed.
+ *
+ * The start time is stored in progress.t_acceptdata - as set with
+ * Curl_pgrsTime(..., TIMER_STARTACCEPT);
+ *
+ */
+static long ftp_timeleft_accept(struct SessionHandle *data)
+{
+  long timeout_ms = DEFAULT_ACCEPT_TIMEOUT;
+  long other;
+  struct timeval now;
+
+  if(data->set.accepttimeout > 0)
+    timeout_ms = data->set.accepttimeout;
+
+  now = Curl_tvnow();
+
+  /* check if the generic timeout possibly is set shorter */
+  other =  Curl_timeleft(data, &now, FALSE);
+  if(other && (other < timeout_ms))
+    /* note that this also works fine for when other happens to be negative
+       due to it already having elapsed */
+    timeout_ms = other;
+  else {
+    /* subtract elapsed time */
+    timeout_ms -= Curl_tvdiff(now, data->progress.t_acceptdata);
+    if(!timeout_ms)
+      /* avoid returning 0 as that means no timeout! */
+      return -1;
+  }
+
+  return timeout_ms;
+}
+
+
+/***********************************************************************
+ *
+ * ReceivedServerConnect()
+ *
+ * After allowing server to connect to us from data port, this function
+ * checks both data connection for connection establishment and ctrl
+ * connection for a negative response regarding a failure in connecting
+ *
+ */
+static CURLcode ReceivedServerConnect(struct connectdata* conn, bool* received)
+{
+  struct SessionHandle *data = conn->data;
+  curl_socket_t ctrl_sock = conn->sock[FIRSTSOCKET];
+  curl_socket_t data_sock = conn->sock[SECONDARYSOCKET];
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+  struct pingpong *pp = &ftpc->pp;
+  int result;
+  long timeout_ms;
+  ssize_t nread;
+  int ftpcode;
+
+  *received = FALSE;
+
+  timeout_ms = ftp_timeleft_accept(data);
+  infof(data, "Checking for server connect\n");
+  if(timeout_ms < 0) {
+    /* if a timeout was already reached, bail out */
+    failf(data, "Accept timeout occurred while waiting server connect");
+    return CURLE_FTP_ACCEPT_TIMEOUT;
+  }
+
+  /* First check whether there is a cached response from server */
+  if(pp->cache_size && pp->cache && pp->cache[0] > '3') {
+    /* Data connection could not be established, let's return */
+    infof(data, "There is negative response in cache while serv connect\n");
+    Curl_GetFTPResponse(&nread, conn, &ftpcode);
+    return CURLE_FTP_ACCEPT_FAILED;
+  }
+
+  result = Curl_socket_check(ctrl_sock, data_sock, CURL_SOCKET_BAD, 0);
+
+  /* see if the connection request is already here */
+  switch (result) {
+  case -1: /* error */
+    /* let's die here */
+    failf(data, "Error while waiting for server connect");
+    return CURLE_FTP_ACCEPT_FAILED;
+  case 0:  /* Server connect is not received yet */
+    break; /* loop */
+  default:
+
+    if(result & CURL_CSELECT_IN2) {
+      infof(data, "Ready to accept data connection from server\n");
+      *received = TRUE;
+    }
+    else if(result & CURL_CSELECT_IN) {
+      infof(data, "Ctrl conn has data while waiting for data conn\n");
+      Curl_GetFTPResponse(&nread, conn, &ftpcode);
+
+      if(ftpcode/100 > 3)
+        return CURLE_FTP_ACCEPT_FAILED;
+
+      return CURLE_FTP_WEIRD_SERVER_REPLY;
+    }
+
+    break;
+  } /* switch() */
+
+  return CURLE_OK;
+}
+
+
+/***********************************************************************
+ *
+ * InitiateTransfer()
+ *
+ * After connection from server is accepted this function is called to
+ * setup transfer parameters and initiate the data transfer.
+ *
+ */
+static CURLcode InitiateTransfer(struct connectdata *conn)
+{
+  struct SessionHandle *data = conn->data;
+  struct FTP *ftp = data->state.proto.ftp;
+  CURLcode result = CURLE_OK;
+
+  if(conn->ssl[SECONDARYSOCKET].use) {
+    /* since we only have a plaintext TCP connection here, we must now
+     * do the TLS stuff */
+    infof(data, "Doing the SSL/TLS handshake on the data stream\n");
+    result = Curl_ssl_connect(conn, SECONDARYSOCKET);
+    if(result)
+      return result;
+  }
+
+  if(conn->proto.ftpc.state_saved == FTP_STOR) {
+    *(ftp->bytecountp)=0;
+
+    /* When we know we're uploading a specified file, we can get the file
+       size prior to the actual upload. */
+
+    Curl_pgrsSetUploadSize(data, data->set.infilesize);
+
+    /* set the SO_SNDBUF for the secondary socket for those who need it */
+    Curl_sndbufset(conn->sock[SECONDARYSOCKET]);
+
+    Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
+                        SECONDARYSOCKET, ftp->bytecountp);
+  }
+  else {
+    /* FTP download: */
+    Curl_setup_transfer(conn, SECONDARYSOCKET,
+        conn->proto.ftpc.retr_size_saved, FALSE,
+        ftp->bytecountp, -1, NULL); /* no upload here */
+  }
+
+  conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
+  state(conn, FTP_STOP);
+
+  return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * AllowServerConnect()
+ *
+ * When we've issue the PORT command, we have told the server to connect
+ * to us. This function
+ *   - will sit and wait here until the server has connected for easy interface
+ *   - will check whether data connection is established if so it is accepted
+ *   for multi interface
+ *
+ */
+static CURLcode AllowServerConnect(struct connectdata *conn, bool *connected)
+{
+  struct SessionHandle *data = conn->data;
+  long timeout_ms;
+  long interval_ms;
+  CURLcode ret = CURLE_OK;
+
+  *connected = FALSE;
+  infof(data, "Preparing for accepting server on data port\n");
+
+  /* Save the time we start accepting server connect */
+  Curl_pgrsTime(data, TIMER_STARTACCEPT);
+
+  for(;;) {
+    timeout_ms = ftp_timeleft_accept(data);
+    if(timeout_ms < 0) {
+      /* if a timeout was already reached, bail out */
+      failf(data, "Accept timeout occurred while waiting server connect");
+      return CURLE_FTP_ACCEPT_TIMEOUT;
+    }
+
+    /* see if the connection request is already here */
+    ret = ReceivedServerConnect(conn, connected);
+    if(ret)
+      return ret;
+
+    if(*connected) {
+      ret = AcceptServerConnect(conn);
+      if(ret)
+        return ret;
+
+      ret = InitiateTransfer(conn);
+      if(ret)
+        return ret;
+
+      break; /* connection is accepted, break the loop */
+    }
+    else {
+      if(data->state.used_interface == Curl_if_easy) {
+        interval_ms = 1000;
+        if(timeout_ms < interval_ms)
+          interval_ms = timeout_ms;
+
+        /* sleep for 1 second and then continue */
+        Curl_socket_ready(CURL_SOCKET_BAD, CURL_SOCKET_BAD, interval_ms);
+      }
+      else {
+        /* Add timeout to multi handle and break out of the loop */
+        if(ret == CURLE_OK && *connected == FALSE) {
+          if(data->set.accepttimeout > 0)
+            Curl_expire(data, data->set.accepttimeout);
+          else
+            Curl_expire(data, DEFAULT_ACCEPT_TIMEOUT);
+        }
+
+        break; /* connection was not accepted immediately */
+      }
+    }
+  }
+
+  return ret;
+}
+
+/* macro to check for a three-digit ftp status code at the start of the
+   given string */
+#define STATUSCODE(line) (ISDIGIT(line[0]) && ISDIGIT(line[1]) &&       \
+                          ISDIGIT(line[2]))
+
+/* macro to check for the last line in an FTP server response */
+#define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3]))
+
+static int ftp_endofresp(struct pingpong *pp,
+                         int *code)
+{
+  char *line = pp->linestart_resp;
+  size_t len = pp->nread_resp;
+
+  if((len > 3) && LASTLINE(line)) {
+    *code = curlx_sltosi(strtol(line, NULL, 10));
+    return 1;
+  }
+  return 0;
+}
+
+static CURLcode ftp_readresp(curl_socket_t sockfd,
+                             struct pingpong *pp,
+                             int *ftpcode, /* return the ftp-code if done */
+                             size_t *size) /* size of the response */
+{
+  struct connectdata *conn = pp->conn;
+  struct SessionHandle *data = conn->data;
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+  char * const buf = data->state.buffer;
+#endif
+  CURLcode result = CURLE_OK;
+  int code;
+
+  result = Curl_pp_readresp(sockfd, pp, &code, size);
+
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+  /* handle the security-oriented responses 6xx ***/
+  /* FIXME: some errorchecking perhaps... ***/
+  switch(code) {
+  case 631:
+    code = Curl_sec_read_msg(conn, buf, PROT_SAFE);
+    break;
+  case 632:
+    code = Curl_sec_read_msg(conn, buf, PROT_PRIVATE);
+    break;
+  case 633:
+    code = Curl_sec_read_msg(conn, buf, PROT_CONFIDENTIAL);
+    break;
+  default:
+    /* normal ftp stuff we pass through! */
+    break;
+  }
+#endif
+
+  /* store the latest code for later retrieval */
+  data->info.httpcode=code;
+
+  if(ftpcode)
+    *ftpcode = code;
+
+  if(421 == code) {
+    /* 421 means "Service not available, closing control connection." and FTP
+     * servers use it to signal that idle session timeout has been exceeded.
+     * If we ignored the response, it could end up hanging in some cases.
+     *
+     * This response code can come at any point so having it treated
+     * generically is a good idea.
+     */
+    infof(data, "We got a 421 - timeout!\n");
+    state(conn, FTP_STOP);
+    return CURLE_OPERATION_TIMEDOUT;
+  }
+
+  return result;
+}
+
+/* --- parse FTP server responses --- */
+
+/*
+ * Curl_GetFTPResponse() is a BLOCKING function to read the full response
+ * from a server after a command.
+ *
+ */
+
+CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
+                             struct connectdata *conn,
+                             int *ftpcode) /* return the ftp-code */
+{
+  /*
+   * We cannot read just one byte per read() and then go back to select() as
+   * the OpenSSL read() doesn't grok that properly.
+   *
+   * Alas, read as much as possible, split up into lines, use the ending
+   * line in a response or continue reading.  */
+
+  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
+  long timeout;              /* timeout in milliseconds */
+  long interval_ms;
+  struct SessionHandle *data = conn->data;
+  CURLcode result = CURLE_OK;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+  struct pingpong *pp = &ftpc->pp;
+  size_t nread;
+  int cache_skip=0;
+  int value_to_be_ignored=0;
+
+  if(ftpcode)
+    *ftpcode = 0; /* 0 for errors */
+  else
+    /* make the pointer point to something for the rest of this function */
+    ftpcode = &value_to_be_ignored;
+
+  *nreadp=0;
+
+  while(!*ftpcode && !result) {
+    /* check and reset timeout value every lap */
+    timeout = Curl_pp_state_timeout(pp);
+
+    if(timeout <=0 ) {
+      failf(data, "FTP response timeout");
+      return CURLE_OPERATION_TIMEDOUT; /* already too little time */
+    }
+
+    interval_ms = 1000;  /* use 1 second timeout intervals */
+    if(timeout < interval_ms)
+      interval_ms = timeout;
+
+    /*
+     * Since this function is blocking, we need to wait here for input on the
+     * connection and only then we call the response reading function. We do
+     * timeout at least every second to make the timeout check run.
+     *
+     * A caution here is that the ftp_readresp() function has a cache that may
+     * contain pieces of a response from the previous invoke and we need to
+     * make sure we don't just wait for input while there is unhandled data in
+     * that cache. But also, if the cache is there, we call ftp_readresp() and
+     * the cache wasn't good enough to continue we must not just busy-loop
+     * around this function.
+     *
+     */
+
+    if(pp->cache && (cache_skip < 2)) {
+      /*
+       * There's a cache left since before. We then skipping the wait for
+       * socket action, unless this is the same cache like the previous round
+       * as then the cache was deemed not enough to act on and we then need to
+       * wait for more data anyway.
+       */
+    }
+    else {
+      switch (Curl_socket_ready(sockfd, CURL_SOCKET_BAD, interval_ms)) {
+      case -1: /* select() error, stop reading */
+        failf(data, "FTP response aborted due to select/poll error: %d",
+              SOCKERRNO);
+        return CURLE_RECV_ERROR;
+
+      case 0: /* timeout */
+        if(Curl_pgrsUpdate(conn))
+          return CURLE_ABORTED_BY_CALLBACK;
+        continue; /* just continue in our loop for the timeout duration */
+
+      default: /* for clarity */
+        break;
+      }
+    }
+    result = ftp_readresp(sockfd, pp, ftpcode, &nread);
+    if(result)
+      break;
+
+    if(!nread && pp->cache)
+      /* bump cache skip counter as on repeated skips we must wait for more
+         data */
+      cache_skip++;
+    else
+      /* when we got data or there is no cache left, we reset the cache skip
+         counter */
+      cache_skip=0;
+
+    *nreadp += nread;
+
+  } /* while there's buffer left and loop is requested */
+
+  pp->pending_resp = FALSE;
+
+  return result;
+}
+
+/* This is the ONLY way to change FTP state! */
+static void state(struct connectdata *conn,
+                  ftpstate newstate)
+{
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+  /* for debug purposes */
+  static const char * const names[]={
+    "STOP",
+    "WAIT220",
+    "AUTH",
+    "USER",
+    "PASS",
+    "ACCT",
+    "PBSZ",
+    "PROT",
+    "CCC",
+    "PWD",
+    "SYST",
+    "NAMEFMT",
+    "QUOTE",
+    "RETR_PREQUOTE",
+    "STOR_PREQUOTE",
+    "POSTQUOTE",
+    "CWD",
+    "MKD",
+    "MDTM",
+    "TYPE",
+    "LIST_TYPE",
+    "RETR_TYPE",
+    "STOR_TYPE",
+    "SIZE",
+    "RETR_SIZE",
+    "STOR_SIZE",
+    "REST",
+    "RETR_REST",
+    "PORT",
+    "PRET",
+    "PASV",
+    "LIST",
+    "RETR",
+    "STOR",
+    "QUIT"
+  };
+#endif
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+  if(ftpc->state != newstate)
+    infof(conn->data, "FTP %p state change from %s to %s\n",
+          ftpc, names[ftpc->state], names[newstate]);
+#endif
+  ftpc->state = newstate;
+}
+
+static CURLcode ftp_state_user(struct connectdata *conn)
+{
+  CURLcode result;
+  struct FTP *ftp = conn->data->state.proto.ftp;
+  /* send USER */
+  PPSENDF(&conn->proto.ftpc.pp, "USER %s", ftp->user?ftp->user:"");
+
+  state(conn, FTP_USER);
+  conn->data->state.ftp_trying_alternative = FALSE;
+
+  return CURLE_OK;
+}
+
+static CURLcode ftp_state_pwd(struct connectdata *conn)
+{
+  CURLcode result;
+
+  /* send PWD to discover our entry point */
+  PPSENDF(&conn->proto.ftpc.pp, "PWD", NULL);
+  state(conn, FTP_PWD);
+
+  return CURLE_OK;
+}
+
+/* For the FTP "protocol connect" and "doing" phases only */
+static int ftp_getsock(struct connectdata *conn,
+                       curl_socket_t *socks,
+                       int numsocks)
+{
+  return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks);
+}
+
+/* For the FTP "DO_MORE" phase only */
+static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks,
+                              int numsocks)
+{
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+  if(!numsocks)
+    return GETSOCK_BLANK;
+
+  /* When in DO_MORE state, we could be either waiting for us to connect to a
+     remote site, or we could wait for that site to connect to us. Or just
+     handle ordinary commands.
+
+     When waiting for a connect, we will be in FTP_STOP state and then we wait
+     for the secondary socket to become writeable. If we're in another state,
+     we're still handling commands on the control (primary) connection.
+
+  */
+
+  switch(ftpc->state) {
+  case FTP_STOP:
+    break;
+  default:
+    return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks);
+  }
+
+  socks[0] = conn->sock[SECONDARYSOCKET];
+  if(ftpc->wait_data_conn) {
+    socks[1] = conn->sock[FIRSTSOCKET];
+    return GETSOCK_READSOCK(0) | GETSOCK_READSOCK(1);
+  }
+
+  return GETSOCK_READSOCK(0);
+}
+
+/* This is called after the FTP_QUOTE state is passed.
+
+   ftp_state_cwd() sends the range of CWD commands to the server to change to
+   the correct directory. It may also need to send MKD commands to create
+   missing ones, if that option is enabled.
+*/
+static CURLcode ftp_state_cwd(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+  if(ftpc->cwddone)
+    /* already done and fine */
+    result = ftp_state_post_cwd(conn);
+  else {
+    ftpc->count2 = 0; /* count2 counts failed CWDs */
+
+    /* count3 is set to allow a MKD to fail once. In the case when first CWD
+       fails and then MKD fails (due to another session raced it to create the
+       dir) this then allows for a second try to CWD to it */
+    ftpc->count3 = (conn->data->set.ftp_create_missing_dirs==2)?1:0;
+
+    if(conn->bits.reuse && ftpc->entrypath) {
+      /* This is a re-used connection. Since we change directory to where the
+         transfer is taking place, we must first get back to the original dir
+         where we ended up after login: */
+      ftpc->count1 = 0; /* we count this as the first path, then we add one
+                          for all upcoming ones in the ftp->dirs[] array */
+      PPSENDF(&conn->proto.ftpc.pp, "CWD %s", ftpc->entrypath);
+      state(conn, FTP_CWD);
+    }
+    else {
+      if(ftpc->dirdepth) {
+        ftpc->count1 = 1;
+        /* issue the first CWD, the rest is sent when the CWD responses are
+           received... */
+        PPSENDF(&conn->proto.ftpc.pp, "CWD %s", ftpc->dirs[ftpc->count1 -1]);
+        state(conn, FTP_CWD);
+      }
+      else {
+        /* No CWD necessary */
+        result = ftp_state_post_cwd(conn);
+      }
+    }
+  }
+  return result;
+}
+
+typedef enum {
+  EPRT,
+  PORT,
+  DONE
+} ftpport;
+
+static CURLcode ftp_state_use_port(struct connectdata *conn,
+                                   ftpport fcmd) /* start with this */
+
+{
+  CURLcode result = CURLE_OK;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+  struct SessionHandle *data=conn->data;
+  curl_socket_t portsock= CURL_SOCKET_BAD;
+  char myhost[256] = "";
+
+  struct Curl_sockaddr_storage ss;
+  Curl_addrinfo *res, *ai;
+  curl_socklen_t sslen;
+  char hbuf[NI_MAXHOST];
+  struct sockaddr *sa=(struct sockaddr *)&ss;
+  struct sockaddr_in * const sa4 = (void *)sa;
+#ifdef ENABLE_IPV6
+  struct sockaddr_in6 * const sa6 = (void *)sa;
+#endif
+  char tmp[1024];
+  static const char mode[][5] = { "EPRT", "PORT" };
+  int rc;
+  int error;
+  char *host = NULL;
+  char *string_ftpport = data->set.str[STRING_FTPPORT];
+  struct Curl_dns_entry *h=NULL;
+  unsigned short port_min = 0;
+  unsigned short port_max = 0;
+  unsigned short port;
+  bool possibly_non_local = TRUE;
+
+  char *addr = NULL;
+
+  /* Step 1, figure out what is requested,
+   * accepted format :
+   * (ipv4|ipv6|domain|interface)?(:port(-range)?)?
+   */
+
+  if(data->set.str[STRING_FTPPORT] &&
+     (strlen(data->set.str[STRING_FTPPORT]) > 1)) {
+
+#ifdef ENABLE_IPV6
+    size_t addrlen = INET6_ADDRSTRLEN > strlen(string_ftpport) ?
+      INET6_ADDRSTRLEN : strlen(string_ftpport);
+#else
+    size_t addrlen = INET_ADDRSTRLEN > strlen(string_ftpport) ?
+      INET_ADDRSTRLEN : strlen(string_ftpport);
+#endif
+    char *ip_start = string_ftpport;
+    char *ip_end = NULL;
+    char *port_start = NULL;
+    char *port_sep = NULL;
+
+    addr = calloc(addrlen+1, 1);
+    if(!addr)
+      return CURLE_OUT_OF_MEMORY;
+
+#ifdef ENABLE_IPV6
+    if(*string_ftpport == '[') {
+      /* [ipv6]:port(-range) */
+      ip_start = string_ftpport + 1;
+      if((ip_end = strchr(string_ftpport, ']')) != NULL )
+        strncpy(addr, ip_start, ip_end - ip_start);
+    }
+    else
+#endif
+      if(*string_ftpport == ':') {
+        /* :port */
+        ip_end = string_ftpport;
+    }
+    else if((ip_end = strchr(string_ftpport, ':')) != NULL) {
+        /* either ipv6 or (ipv4|domain|interface):port(-range) */
+#ifdef ENABLE_IPV6
+      if(Curl_inet_pton(AF_INET6, string_ftpport, sa6) == 1) {
+        /* ipv6 */
+        port_min = port_max = 0;
+        strcpy(addr, string_ftpport);
+        ip_end = NULL; /* this got no port ! */
+      }
+      else
+#endif
+        /* (ipv4|domain|interface):port(-range) */
+        strncpy(addr, string_ftpport, ip_end - ip_start );
+    }
+    else
+      /* ipv4|interface */
+      strcpy(addr, string_ftpport);
+
+    /* parse the port */
+    if(ip_end != NULL) {
+      if((port_start = strchr(ip_end, ':')) != NULL) {
+        port_min = curlx_ultous(strtoul(port_start+1, NULL, 10));
+        if((port_sep = strchr(port_start, '-')) != NULL) {
+          port_max = curlx_ultous(strtoul(port_sep + 1, NULL, 10));
+        }
+        else
+          port_max = port_min;
+      }
+    }
+
+    /* correct errors like:
+     *  :1234-1230
+     *  :-4711 , in this case port_min is (unsigned)-1,
+     *           therefore port_min > port_max for all cases
+     *           but port_max = (unsigned)-1
+     */
+    if(port_min > port_max )
+      port_min = port_max = 0;
+
+
+    if(*addr != '\0') {
+      /* attempt to get the address of the given interface name */
+      if(!Curl_if2ip(conn->ip_addr->ai_family, addr,
+                     hbuf, sizeof(hbuf)))
+        /* not an interface, use the given string as host name instead */
+        host = addr;
+      else
+        host = hbuf; /* use the hbuf for host name */
+    }
+    else
+      /* there was only a port(-range) given, default the host */
+      host = NULL;
+  } /* data->set.ftpport */
+
+  if(!host) {
+    /* not an interface and not a host name, get default by extracting
+       the IP from the control connection */
+
+    sslen = sizeof(ss);
+    if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
+      failf(data, "getsockname() failed: %s",
+          Curl_strerror(conn, SOCKERRNO) );
+      Curl_safefree(addr);
+      return CURLE_FTP_PORT_FAILED;
+    }
+    switch(sa->sa_family) {
+#ifdef ENABLE_IPV6
+    case AF_INET6:
+      Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
+      break;
+#endif
+    default:
+      Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf));
+      break;
+    }
+    host = hbuf; /* use this host name */
+    possibly_non_local = FALSE; /* we know it is local now */
+  }
+
+  /* resolv ip/host to ip */
+  rc = Curl_resolv(conn, host, 0, &h);
+  if(rc == CURLRESOLV_PENDING)
+    (void)Curl_resolver_wait_resolv(conn, &h);
+  if(h) {
+    res = h->addr;
+    /* when we return from this function, we can forget about this entry
+       to we can unlock it now already */
+    Curl_resolv_unlock(data, h);
+  } /* (h) */
+  else
+    res = NULL; /* failure! */
+
+  if(res == NULL) {
+    failf(data, "failed to resolve the address provided to PORT: %s", host);
+    Curl_safefree(addr);
+    return CURLE_FTP_PORT_FAILED;
+  }
+
+  Curl_safefree(addr);
+  host = NULL;
+
+  /* step 2, create a socket for the requested address */
+
+  portsock = CURL_SOCKET_BAD;
+  error = 0;
+  for(ai = res; ai; ai = ai->ai_next) {
+    result = Curl_socket(conn, ai, NULL, &portsock);
+    if(result) {
+      error = SOCKERRNO;
+      continue;
+    }
+    break;
+  }
+  if(!ai) {
+    failf(data, "socket failure: %s", Curl_strerror(conn, error));
+    return CURLE_FTP_PORT_FAILED;
+  }
+
+  /* step 3, bind to a suitable local address */
+
+  memcpy(sa, ai->ai_addr, ai->ai_addrlen);
+  sslen = ai->ai_addrlen;
+
+  for(port = port_min; port <= port_max;) {
+    if(sa->sa_family == AF_INET)
+      sa4->sin_port = htons(port);
+#ifdef ENABLE_IPV6
+    else
+      sa6->sin6_port = htons(port);
+#endif
+    /* Try binding the given address. */
+    if(bind(portsock, sa, sslen) ) {
+      /* It failed. */
+      error = SOCKERRNO;
+      if(possibly_non_local && (error == EADDRNOTAVAIL)) {
+        /* The requested bind address is not local.  Use the address used for
+         * the control connection instead and restart the port loop
+         */
+
+        infof(data, "bind(port=%hu) on non-local address failed: %s\n", port,
+              Curl_strerror(conn, error) );
+
+        sslen = sizeof(ss);
+        if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
+          failf(data, "getsockname() failed: %s",
+                Curl_strerror(conn, SOCKERRNO) );
+          Curl_closesocket(conn, portsock);
+          return CURLE_FTP_PORT_FAILED;
+        }
+        port = port_min;
+        possibly_non_local = FALSE; /* don't try this again */
+        continue;
+      }
+      else if(error != EADDRINUSE && error != EACCES) {
+        failf(data, "bind(port=%hu) failed: %s", port,
+              Curl_strerror(conn, error) );
+        Curl_closesocket(conn, portsock);
+        return CURLE_FTP_PORT_FAILED;
+      }
+    }
+    else
+      break;
+
+    port++;
+  }
+
+  /* maybe all ports were in use already*/
+  if(port > port_max) {
+    failf(data, "bind() failed, we ran out of ports!");
+    Curl_closesocket(conn, portsock);
+    return CURLE_FTP_PORT_FAILED;
+  }
+
+  /* get the name again after the bind() so that we can extract the
+     port number it uses now */
+  sslen = sizeof(ss);
+  if(getsockname(portsock, (struct sockaddr *)sa, &sslen)) {
+    failf(data, "getsockname() failed: %s",
+          Curl_strerror(conn, SOCKERRNO) );
+    Curl_closesocket(conn, portsock);
+    return CURLE_FTP_PORT_FAILED;
+  }
+
+  /* step 4, listen on the socket */
+
+  if(listen(portsock, 1)) {
+    failf(data, "socket failure: %s", Curl_strerror(conn, SOCKERRNO));
+    Curl_closesocket(conn, portsock);
+    return CURLE_FTP_PORT_FAILED;
+  }
+
+  /* step 5, send the proper FTP command */
+
+  /* get a plain printable version of the numerical address to work with
+     below */
+  Curl_printable_address(ai, myhost, sizeof(myhost));
+
+#ifdef ENABLE_IPV6
+  if(!conn->bits.ftp_use_eprt && conn->bits.ipv6)
+    /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
+       request and enable EPRT again! */
+    conn->bits.ftp_use_eprt = TRUE;
+#endif
+
+  for(; fcmd != DONE; fcmd++) {
+
+    if(!conn->bits.ftp_use_eprt && (EPRT == fcmd))
+      /* if disabled, goto next */
+      continue;
+
+    if((PORT == fcmd) && sa->sa_family != AF_INET)
+      /* PORT is ipv4 only */
+      continue;
+
+    switch (sa->sa_family) {
+    case AF_INET:
+      port = ntohs(sa4->sin_port);
+      break;
+#ifdef ENABLE_IPV6
+    case AF_INET6:
+      port = ntohs(sa6->sin6_port);
+      break;
+#endif
+    default:
+      continue; /* might as well skip this */
+    }
+
+    if(EPRT == fcmd) {
+      /*
+       * Two fine examples from RFC2428;
+       *
+       * EPRT |1|132.235.1.2|6275|
+       *
+       * EPRT |2|1080::8:800:200C:417A|5282|
+       */
+
+      result = Curl_pp_sendf(&ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd],
+                             sa->sa_family == AF_INET?1:2,
+                             myhost, port);
+      if(result) {
+        failf(data, "Failure sending EPRT command: %s",
+              curl_easy_strerror(result));
+        Curl_closesocket(conn, portsock);
+        /* don't retry using PORT */
+        ftpc->count1 = PORT;
+        /* bail out */
+        state(conn, FTP_STOP);
+        return result;
+      }
+      break;
+    }
+    else if(PORT == fcmd) {
+      char *source = myhost;
+      char *dest = tmp;
+
+      /* translate x.x.x.x to x,x,x,x */
+      while(source && *source) {
+        if(*source == '.')
+          *dest=',';
+        else
+          *dest = *source;
+        dest++;
+        source++;
+      }
+      *dest = 0;
+      snprintf(dest, 20, ",%d,%d", (int)(port>>8), (int)(port&0xff));
+
+      result = Curl_pp_sendf(&ftpc->pp, "%s %s", mode[fcmd], tmp);
+      if(result) {
+        failf(data, "Failure sending PORT command: %s",
+              curl_easy_strerror(result));
+        Curl_closesocket(conn, portsock);
+        /* bail out */
+        state(conn, FTP_STOP);
+        return result;
+      }
+      break;
+    }
+  }
+
+  /* store which command was sent */
+  ftpc->count1 = fcmd;
+
+  /* we set the secondary socket variable to this for now, it is only so that
+     the cleanup function will close it in case we fail before the true
+     secondary stuff is made */
+  if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
+    Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
+  conn->sock[SECONDARYSOCKET] = portsock;
+
+  /* this tcpconnect assignment below is a hackish work-around to make the
+     multi interface with active FTP work - as it will not wait for a
+     (passive) connect in Curl_is_connected().
+
+     The *proper* fix is to make sure that the active connection from the
+     server is done in a non-blocking way. Currently, it is still BLOCKING.
+  */
+  conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE;
+
+  state(conn, FTP_PORT);
+  return result;
+}
+
+static CURLcode ftp_state_use_pasv(struct connectdata *conn)
+{
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+  CURLcode result = CURLE_OK;
+  /*
+    Here's the excecutive summary on what to do:
+
+    PASV is RFC959, expect:
+    227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
+
+    LPSV is RFC1639, expect:
+    228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
+
+    EPSV is RFC2428, expect:
+    229 Entering Extended Passive Mode (|||port|)
+
+  */
+
+  static const char mode[][5] = { "EPSV", "PASV" };
+  int modeoff;
+
+#ifdef PF_INET6
+  if(!conn->bits.ftp_use_epsv && conn->bits.ipv6)
+    /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the
+       request and enable EPSV again! */
+    conn->bits.ftp_use_epsv = TRUE;
+#endif
+
+  modeoff = conn->bits.ftp_use_epsv?0:1;
+
+  PPSENDF(&ftpc->pp, "%s", mode[modeoff]);
+
+  ftpc->count1 = modeoff;
+  state(conn, FTP_PASV);
+  infof(conn->data, "Connect data stream passively\n");
+
+  return result;
+}
+
+/* REST is the last command in the chain of commands when a "head"-like
+   request is made. Thus, if an actual transfer is to be made this is where
+   we take off for real. */
+static CURLcode ftp_state_post_rest(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+  struct FTP *ftp = conn->data->state.proto.ftp;
+  struct SessionHandle *data = conn->data;
+
+  if(ftp->transfer != FTPTRANSFER_BODY) {
+    /* doesn't transfer any data */
+
+    /* still possibly do PRE QUOTE jobs */
+    state(conn, FTP_RETR_PREQUOTE);
+    result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE);
+  }
+  else if(data->set.ftp_use_port) {
+    /* We have chosen to use the PORT (or similar) command */
+    result = ftp_state_use_port(conn, EPRT);
+  }
+  else {
+    /* We have chosen (this is default) to use the PASV (or similar) command */
+    if(data->set.ftp_use_pret) {
+      /* The user has requested that we send a PRET command
+         to prepare the server for the upcoming PASV */
+      if(!conn->proto.ftpc.file) {
+        PPSENDF(&conn->proto.ftpc.pp, "PRET %s",
+                data->set.str[STRING_CUSTOMREQUEST]?
+                data->set.str[STRING_CUSTOMREQUEST]:
+                (data->set.ftp_list_only?"NLST":"LIST"));
+      }
+      else if(data->set.upload) {
+        PPSENDF(&conn->proto.ftpc.pp, "PRET STOR %s", conn->proto.ftpc.file);
+      }
+      else {
+        PPSENDF(&conn->proto.ftpc.pp, "PRET RETR %s", conn->proto.ftpc.file);
+      }
+      state(conn, FTP_PRET);
+    }
+    else {
+      result = ftp_state_use_pasv(conn);
+    }
+  }
+  return result;
+}
+
+static CURLcode ftp_state_post_size(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+  struct FTP *ftp = conn->data->state.proto.ftp;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+  if((ftp->transfer != FTPTRANSFER_BODY) && ftpc->file) {
+    /* if a "head"-like request is being made (on a file) */
+
+    /* Determine if server can respond to REST command and therefore
+       whether it supports range */
+    PPSENDF(&conn->proto.ftpc.pp, "REST %d", 0);
+
+    state(conn, FTP_REST);
+  }
+  else
+    result = ftp_state_post_rest(conn);
+
+  return result;
+}
+
+static CURLcode ftp_state_post_type(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+  struct FTP *ftp = conn->data->state.proto.ftp;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+  if((ftp->transfer == FTPTRANSFER_INFO) && ftpc->file) {
+    /* if a "head"-like request is being made (on a file) */
+
+    /* we know ftpc->file is a valid pointer to a file name */
+    PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
+
+    state(conn, FTP_SIZE);
+  }
+  else
+    result = ftp_state_post_size(conn);
+
+  return result;
+}
+
+static CURLcode ftp_state_post_listtype(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+
+  /* If this output is to be machine-parsed, the NLST command might be better
+     to use, since the LIST command output is not specified or standard in any
+     way. It has turned out that the NLST list output is not the same on all
+     servers either... */
+
+  /*
+     if FTPFILE_NOCWD was specified, we are currently in
+     the user's home directory, so we should add the path
+     as argument for the LIST / NLST / or custom command.
+     Whether the server will support this, is uncertain.
+
+     The other ftp_filemethods will CWD into dir/dir/ first and
+     then just do LIST (in that case: nothing to do here)
+  */
+  char *cmd,*lstArg,*slashPos;
+
+  lstArg = NULL;
+  if((data->set.ftp_filemethod == FTPFILE_NOCWD) &&
+     data->state.path &&
+     data->state.path[0] &&
+     strchr(data->state.path,'/')) {
+
+    lstArg = strdup(data->state.path);
+    if(!lstArg)
+      return CURLE_OUT_OF_MEMORY;
+
+    /* Check if path does not end with /, as then we cut off the file part */
+    if(lstArg[strlen(lstArg) - 1] != '/')  {
+
+      /* chop off the file part if format is dir/dir/file */
+      slashPos = strrchr(lstArg,'/');
+      if(slashPos)
+        *(slashPos+1) = '\0';
+    }
+  }
+
+  cmd = aprintf( "%s%s%s",
+                 data->set.str[STRING_CUSTOMREQUEST]?
+                 data->set.str[STRING_CUSTOMREQUEST]:
+                 (data->set.ftp_list_only?"NLST":"LIST"),
+                 lstArg? " ": "",
+                 lstArg? lstArg: "" );
+
+  if(!cmd) {
+    if(lstArg)
+      free(lstArg);
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  result = Curl_pp_sendf(&conn->proto.ftpc.pp, "%s", cmd);
+
+  if(lstArg)
+    free(lstArg);
+
+  free(cmd);
+
+  if(result != CURLE_OK)
+    return result;
+
+  state(conn, FTP_LIST);
+
+  return result;
+}
+
+static CURLcode ftp_state_post_retrtype(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+
+  /* We've sent the TYPE, now we must send the list of prequote strings */
+
+  result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE);
+
+  return result;
+}
+
+static CURLcode ftp_state_post_stortype(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+
+  /* We've sent the TYPE, now we must send the list of prequote strings */
+
+  result = ftp_state_quote(conn, TRUE, FTP_STOR_PREQUOTE);
+
+  return result;
+}
+
+static CURLcode ftp_state_post_mdtm(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+  struct FTP *ftp = conn->data->state.proto.ftp;
+  struct SessionHandle *data = conn->data;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+  /* If we have selected NOBODY and HEADER, it means that we only want file
+     information. Which in FTP can't be much more than the file size and
+     date. */
+  if(data->set.opt_no_body && ftpc->file &&
+     ftp_need_type(conn, data->set.prefer_ascii)) {
+    /* The SIZE command is _not_ RFC 959 specified, and therefor many servers
+       may not support it! It is however the only way we have to get a file's
+       size! */
+
+    ftp->transfer = FTPTRANSFER_INFO;
+    /* this means no actual transfer will be made */
+
+    /* Some servers return different sizes for different modes, and thus we
+       must set the proper type before we check the size */
+    result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_TYPE);
+    if(result)
+      return result;
+  }
+  else
+    result = ftp_state_post_type(conn);
+
+  return result;
+}
+
+/* This is called after the CWD commands have been done in the beginning of
+   the DO phase */
+static CURLcode ftp_state_post_cwd(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+  /* Requested time of file or time-depended transfer? */
+  if((data->set.get_filetime || data->set.timecondition) && ftpc->file) {
+
+    /* we have requested to get the modified-time of the file, this is a white
+       spot as the MDTM is not mentioned in RFC959 */
+    PPSENDF(&ftpc->pp, "MDTM %s", ftpc->file);
+
+    state(conn, FTP_MDTM);
+  }
+  else
+    result = ftp_state_post_mdtm(conn);
+
+  return result;
+}
+
+
+/* This is called after the TYPE and possible quote commands have been sent */
+static CURLcode ftp_state_ul_setup(struct connectdata *conn,
+                                   bool sizechecked)
+{
+  CURLcode result = CURLE_OK;
+  struct FTP *ftp = conn->data->state.proto.ftp;
+  struct SessionHandle *data = conn->data;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+  int seekerr = CURL_SEEKFUNC_OK;
+
+  if((data->state.resume_from && !sizechecked) ||
+     ((data->state.resume_from > 0) && sizechecked)) {
+    /* we're about to continue the uploading of a file */
+    /* 1. get already existing file's size. We use the SIZE command for this
+       which may not exist in the server!  The SIZE command is not in
+       RFC959. */
+
+    /* 2. This used to set REST. But since we can do append, we
+       don't another ftp command. We just skip the source file
+       offset and then we APPEND the rest on the file instead */
+
+    /* 3. pass file-size number of bytes in the source file */
+    /* 4. lower the infilesize counter */
+    /* => transfer as usual */
+
+    if(data->state.resume_from < 0 ) {
+      /* Got no given size to start from, figure it out */
+      PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
+      state(conn, FTP_STOR_SIZE);
+      return result;
+    }
+
+    /* enable append */
+    data->set.ftp_append = TRUE;
+
+    /* Let's read off the proper amount of bytes from the input. */
+    if(conn->seek_func) {
+      seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
+                                SEEK_SET);
+    }
+
+    if(seekerr != CURL_SEEKFUNC_OK) {
+      if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
+        failf(data, "Could not seek stream");
+        return CURLE_FTP_COULDNT_USE_REST;
+      }
+      /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
+      else {
+        curl_off_t passed=0;
+        do {
+          size_t readthisamountnow =
+            (data->state.resume_from - passed > CURL_OFF_T_C(BUFSIZE)) ?
+            BUFSIZE : curlx_sotouz(data->state.resume_from - passed);
+
+          size_t actuallyread =
+            conn->fread_func(data->state.buffer, 1, readthisamountnow,
+                             conn->fread_in);
+
+          passed += actuallyread;
+          if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
+            /* this checks for greater-than only to make sure that the
+               CURL_READFUNC_ABORT return code still aborts */
+            failf(data, "Failed to read data");
+            return CURLE_FTP_COULDNT_USE_REST;
+          }
+        } while(passed < data->state.resume_from);
+      }
+    }
+    /* now, decrease the size of the read */
+    if(data->set.infilesize>0) {
+      data->set.infilesize -= data->state.resume_from;
+
+      if(data->set.infilesize <= 0) {
+        infof(data, "File already completely uploaded\n");
+
+        /* no data to transfer */
+        Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+
+        /* Set ->transfer so that we won't get any error in
+         * ftp_done() because we didn't transfer anything! */
+        ftp->transfer = FTPTRANSFER_NONE;
+
+        state(conn, FTP_STOP);
+        return CURLE_OK;
+      }
+    }
+    /* we've passed, proceed as normal */
+  } /* resume_from */
+
+  PPSENDF(&ftpc->pp, data->set.ftp_append?"APPE %s":"STOR %s",
+          ftpc->file);
+
+  state(conn, FTP_STOR);
+
+  return result;
+}
+
+static CURLcode ftp_state_quote(struct connectdata *conn,
+                                bool init,
+                                ftpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  struct FTP *ftp = data->state.proto.ftp;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+  bool quote=FALSE;
+  struct curl_slist *item;
+
+  switch(instate) {
+  case FTP_QUOTE:
+  default:
+    item = data->set.quote;
+    break;
+  case FTP_RETR_PREQUOTE:
+  case FTP_STOR_PREQUOTE:
+    item = data->set.prequote;
+    break;
+  case FTP_POSTQUOTE:
+    item = data->set.postquote;
+    break;
+  }
+
+  /*
+   * This state uses:
+   * 'count1' to iterate over the commands to send
+   * 'count2' to store wether to allow commands to fail
+   */
+
+  if(init)
+    ftpc->count1 = 0;
+  else
+    ftpc->count1++;
+
+  if(item) {
+    int i = 0;
+
+    /* Skip count1 items in the linked list */
+    while((i< ftpc->count1) && item) {
+      item = item->next;
+      i++;
+    }
+    if(item) {
+      char *cmd = item->data;
+      if(cmd[0] == '*') {
+        cmd++;
+        ftpc->count2 = 1; /* the sent command is allowed to fail */
+      }
+      else
+        ftpc->count2 = 0; /* failure means cancel operation */
+
+      PPSENDF(&ftpc->pp, "%s", cmd);
+      state(conn, instate);
+      quote = TRUE;
+    }
+  }
+
+  if(!quote) {
+    /* No more quote to send, continue to ... */
+    switch(instate) {
+    case FTP_QUOTE:
+    default:
+      result = ftp_state_cwd(conn);
+      break;
+    case FTP_RETR_PREQUOTE:
+      if(ftp->transfer != FTPTRANSFER_BODY)
+        state(conn, FTP_STOP);
+      else {
+        if(ftpc->known_filesize != -1) {
+          Curl_pgrsSetDownloadSize(data, ftpc->known_filesize);
+          result = ftp_state_post_retr_size(conn, ftpc->known_filesize);
+        }
+        else {
+          PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
+          state(conn, FTP_RETR_SIZE);
+        }
+      }
+      break;
+    case FTP_STOR_PREQUOTE:
+      result = ftp_state_ul_setup(conn, FALSE);
+      break;
+    case FTP_POSTQUOTE:
+      break;
+    }
+  }
+
+  return result;
+}
+
+/* called from ftp_state_pasv_resp to switch to PASV in case of EPSV
+   problems */
+static CURLcode ftp_epsv_disable(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+  infof(conn->data, "got positive EPSV response, but can't connect. "
+        "Disabling EPSV\n");
+  /* disable it for next transfer */
+  conn->bits.ftp_use_epsv = FALSE;
+  conn->data->state.errorbuf = FALSE; /* allow error message to get
+                                         rewritten */
+  PPSENDF(&conn->proto.ftpc.pp, "PASV", NULL);
+  conn->proto.ftpc.count1++;
+  /* remain in the FTP_PASV state */
+  return result;
+}
+
+static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
+                                    int ftpcode)
+{
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+  CURLcode result;
+  struct SessionHandle *data=conn->data;
+  Curl_addrinfo *conninfo;
+  struct Curl_dns_entry *addr=NULL;
+  int rc;
+  unsigned short connectport; /* the local port connect() should use! */
+  unsigned short newport=0; /* remote port */
+  bool connected;
+
+  /* newhost must be able to hold a full IP-style address in ASCII, which
+     in the IPv6 case means 5*8-1 = 39 letters */
+#define NEWHOST_BUFSIZE 48
+  char newhost[NEWHOST_BUFSIZE];
+  char *str=&data->state.buffer[4];  /* start on the first letter */
+
+  if((ftpc->count1 == 0) &&
+     (ftpcode == 229)) {
+    /* positive EPSV response */
+    char *ptr = strchr(str, '(');
+    if(ptr) {
+      unsigned int num;
+      char separator[4];
+      ptr++;
+      if(5  == sscanf(ptr, "%c%c%c%u%c",
+                      &separator[0],
+                      &separator[1],
+                      &separator[2],
+                      &num,
+                      &separator[3])) {
+        const char sep1 = separator[0];
+        int i;
+
+        /* The four separators should be identical, or else this is an oddly
+           formatted reply and we bail out immediately. */
+        for(i=1; i<4; i++) {
+          if(separator[i] != sep1) {
+            ptr=NULL; /* set to NULL to signal error */
+            break;
+          }
+        }
+        if(ptr) {
+          newport = (unsigned short)(num & 0xffff);
+
+          if(conn->bits.tunnel_proxy ||
+             conn->proxytype == CURLPROXY_SOCKS5 ||
+             conn->proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
+             conn->proxytype == CURLPROXY_SOCKS4 ||
+             conn->proxytype == CURLPROXY_SOCKS4A)
+            /* proxy tunnel -> use other host info because ip_addr_str is the
+               proxy address not the ftp host */
+            snprintf(newhost, sizeof(newhost), "%s", conn->host.name);
+          else
+            /* use the same IP we are already connected to */
+            snprintf(newhost, NEWHOST_BUFSIZE, "%s", conn->ip_addr_str);
+        }
+      }
+      else
+        ptr=NULL;
+    }
+    if(!ptr) {
+      failf(data, "Weirdly formatted EPSV reply");
+      return CURLE_FTP_WEIRD_PASV_REPLY;
+    }
+  }
+  else if((ftpc->count1 == 1) &&
+          (ftpcode == 227)) {
+    /* positive PASV response */
+    int ip[4];
+    int port[2];
+
+    /*
+     * Scan for a sequence of six comma-separated numbers and use them as
+     * IP+port indicators.
+     *
+     * Found reply-strings include:
+     * "227 Entering Passive Mode (127,0,0,1,4,51)"
+     * "227 Data transfer will passively listen to 127,0,0,1,4,51"
+     * "227 Entering passive mode. 127,0,0,1,4,51"
+     */
+    while(*str) {
+      if(6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
+                      &ip[0], &ip[1], &ip[2], &ip[3],
+                      &port[0], &port[1]))
+        break;
+      str++;
+    }
+
+    if(!*str) {
+      failf(data, "Couldn't interpret the 227-response");
+      return CURLE_FTP_WEIRD_227_FORMAT;
+    }
+
+    /* we got OK from server */
+    if(data->set.ftp_skip_ip) {
+      /* told to ignore the remotely given IP but instead use the one we used
+         for the control connection */
+      infof(data, "Skips %d.%d.%d.%d for data connection, uses %s instead\n",
+            ip[0], ip[1], ip[2], ip[3],
+            conn->ip_addr_str);
+      if(conn->bits.tunnel_proxy ||
+         conn->proxytype == CURLPROXY_SOCKS5 ||
+         conn->proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
+         conn->proxytype == CURLPROXY_SOCKS4 ||
+         conn->proxytype == CURLPROXY_SOCKS4A)
+        /* proxy tunnel -> use other host info because ip_addr_str is the
+           proxy address not the ftp host */
+        snprintf(newhost, sizeof(newhost), "%s", conn->host.name);
+      else
+        snprintf(newhost, sizeof(newhost), "%s", conn->ip_addr_str);
+    }
+    else
+      snprintf(newhost, sizeof(newhost),
+               "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
+    newport = (unsigned short)(((port[0]<<8) + port[1]) & 0xffff);
+  }
+  else if(ftpc->count1 == 0) {
+    /* EPSV failed, move on to PASV */
+
+    /* disable it for next transfer */
+    conn->bits.ftp_use_epsv = FALSE;
+    infof(data, "disabling EPSV usage\n");
+
+    PPSENDF(&ftpc->pp, "PASV", NULL);
+    ftpc->count1++;
+    /* remain in the FTP_PASV state */
+    return result;
+  }
+  else {
+    failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
+    return CURLE_FTP_WEIRD_PASV_REPLY;
+  }
+
+  if(data->set.str[STRING_PROXY] && *data->set.str[STRING_PROXY]) {
+    /*
+     * This is a tunnel through a http proxy and we need to connect to the
+     * proxy again here.
+     *
+     * We don't want to rely on a former host lookup that might've expired
+     * now, instead we remake the lookup here and now!
+     */
+    rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &addr);
+    if(rc == CURLRESOLV_PENDING)
+      /* BLOCKING, ignores the return code but 'addr' will be NULL in
+         case of failure */
+      (void)Curl_resolver_wait_resolv(conn, &addr);
+
+    connectport =
+      (unsigned short)conn->port; /* we connect to the proxy's port */
+
+    if(!addr) {
+      failf(data, "Can't resolve proxy host %s:%hu",
+            conn->proxy.name, connectport);
+      return CURLE_FTP_CANT_GET_HOST;
+    }
+  }
+  else {
+    /* normal, direct, ftp connection */
+    rc = Curl_resolv(conn, newhost, newport, &addr);
+    if(rc == CURLRESOLV_PENDING)
+      /* BLOCKING */
+      (void)Curl_resolver_wait_resolv(conn, &addr);
+
+    connectport = newport; /* we connect to the remote port */
+
+    if(!addr) {
+      failf(data, "Can't resolve new host %s:%hu", newhost, connectport);
+      return CURLE_FTP_CANT_GET_HOST;
+    }
+  }
+
+  result = Curl_connecthost(conn,
+                            addr,
+                            &conn->sock[SECONDARYSOCKET],
+                            &conninfo,
+                            &connected);
+
+  Curl_resolv_unlock(data, addr); /* we're done using this address */
+
+  if(result) {
+    if(ftpc->count1 == 0 && ftpcode == 229)
+      return ftp_epsv_disable(conn);
+
+    return result;
+  }
+
+  conn->bits.tcpconnect[SECONDARYSOCKET] = connected;
+
+  /*
+   * When this is used from the multi interface, this might've returned with
+   * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
+   * connect to connect and we should not be "hanging" here waiting.
+   */
+
+  if(data->set.verbose)
+    /* this just dumps information about this second connection */
+    ftp_pasv_verbose(conn, conninfo, newhost, connectport);
+
+  switch(conn->proxytype) {
+    /* FIX: this MUST wait for a proper connect first if 'connected' is
+     * FALSE */
+  case CURLPROXY_SOCKS5:
+  case CURLPROXY_SOCKS5_HOSTNAME:
+    result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, newhost, newport,
+                         SECONDARYSOCKET, conn);
+    break;
+  case CURLPROXY_SOCKS4:
+    result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
+                         SECONDARYSOCKET, conn, FALSE);
+    break;
+  case CURLPROXY_SOCKS4A:
+    result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
+                         SECONDARYSOCKET, conn, TRUE);
+    break;
+  case CURLPROXY_HTTP:
+  case CURLPROXY_HTTP_1_0:
+    /* do nothing here. handled later. */
+    break;
+  default:
+    failf(data, "unknown proxytype option given");
+    result = CURLE_COULDNT_CONNECT;
+    break;
+  }
+
+  if(result) {
+    if(ftpc->count1 == 0 && ftpcode == 229)
+      return ftp_epsv_disable(conn);
+    return result;
+  }
+
+  if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
+    /* FIX: this MUST wait for a proper connect first if 'connected' is
+     * FALSE */
+
+    /* BLOCKING */
+    /* We want "seamless" FTP operations through HTTP proxy tunnel */
+
+    /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
+     * conn->proto.http; we want FTP through HTTP and we have to change the
+     * member temporarily for connecting to the HTTP proxy. After
+     * Curl_proxyCONNECT we have to set back the member to the original struct
+     * FTP pointer
+     */
+    struct HTTP http_proxy;
+    struct FTP *ftp_save = data->state.proto.ftp;
+    memset(&http_proxy, 0, sizeof(http_proxy));
+    data->state.proto.http = &http_proxy;
+
+    result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport);
+
+    data->state.proto.ftp = ftp_save;
+
+    if(result)
+      return result;
+  }
+
+  conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE;
+
+  state(conn, FTP_STOP); /* this phase is completed */
+
+  return result;
+}
+
+static CURLcode ftp_state_port_resp(struct connectdata *conn,
+                                    int ftpcode)
+{
+  struct SessionHandle *data = conn->data;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+  ftpport fcmd = (ftpport)ftpc->count1;
+  CURLcode result = CURLE_OK;
+
+  if(ftpcode != 200) {
+    /* the command failed */
+
+    if(EPRT == fcmd) {
+      infof(data, "disabling EPRT usage\n");
+      conn->bits.ftp_use_eprt = FALSE;
+    }
+    fcmd++;
+
+    if(fcmd == DONE) {
+      failf(data, "Failed to do PORT");
+      result = CURLE_FTP_PORT_FAILED;
+    }
+    else
+      /* try next */
+      result = ftp_state_use_port(conn, fcmd);
+  }
+  else {
+    infof(data, "Connect data stream actively\n");
+    state(conn, FTP_STOP); /* end of DO phase */
+  }
+
+  return result;
+}
+
+static CURLcode ftp_state_mdtm_resp(struct connectdata *conn,
+                                    int ftpcode)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data=conn->data;
+  struct FTP *ftp = data->state.proto.ftp;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+  switch(ftpcode) {
+  case 213:
+    {
+      /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
+         last .sss part is optional and means fractions of a second */
+      int year, month, day, hour, minute, second;
+      char *buf = data->state.buffer;
+      if(6 == sscanf(buf+4, "%04d%02d%02d%02d%02d%02d",
+                     &year, &month, &day, &hour, &minute, &second)) {
+        /* we have a time, reformat it */
+        time_t secs=time(NULL);
+        /* using the good old yacc/bison yuck */
+        snprintf(buf, sizeof(conn->data->state.buffer),
+                 "%04d%02d%02d %02d:%02d:%02d GMT",
+                 year, month, day, hour, minute, second);
+        /* now, convert this into a time() value: */
+        data->info.filetime = (long)curl_getdate(buf, &secs);
+      }
+
+#ifdef CURL_FTP_HTTPSTYLE_HEAD
+      /* If we asked for a time of the file and we actually got one as well,
+         we "emulate" a HTTP-style header in our output. */
+
+      if(data->set.opt_no_body &&
+         ftpc->file &&
+         data->set.get_filetime &&
+         (data->info.filetime>=0) ) {
+        time_t filetime = (time_t)data->info.filetime;
+        struct tm buffer;
+        const struct tm *tm = &buffer;
+
+        result = Curl_gmtime(filetime, &buffer);
+        if(result)
+          return result;
+
+        /* format: "Tue, 15 Nov 1994 12:45:26" */
+        snprintf(buf, BUFSIZE-1,
+                 "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
+                 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
+                 tm->tm_mday,
+                 Curl_month[tm->tm_mon],
+                 tm->tm_year + 1900,
+                 tm->tm_hour,
+                 tm->tm_min,
+                 tm->tm_sec);
+        result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
+        if(result)
+          return result;
+      } /* end of a ridiculous amount of conditionals */
+#endif
+    }
+    break;
+  default:
+    infof(data, "unsupported MDTM reply format\n");
+    break;
+  case 550: /* "No such file or directory" */
+    failf(data, "Given file does not exist");
+    result = CURLE_FTP_COULDNT_RETR_FILE;
+    break;
+  }
+
+  if(data->set.timecondition) {
+    if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
+      switch(data->set.timecondition) {
+      case CURL_TIMECOND_IFMODSINCE:
+      default:
+        if(data->info.filetime <= data->set.timevalue) {
+          infof(data, "The requested document is not new enough\n");
+          ftp->transfer = FTPTRANSFER_NONE; /* mark to not transfer data */
+          data->info.timecond = TRUE;
+          state(conn, FTP_STOP);
+          return CURLE_OK;
+        }
+        break;
+      case CURL_TIMECOND_IFUNMODSINCE:
+        if(data->info.filetime > data->set.timevalue) {
+          infof(data, "The requested document is not old enough\n");
+          ftp->transfer = FTPTRANSFER_NONE; /* mark to not transfer data */
+          data->info.timecond = TRUE;
+          state(conn, FTP_STOP);
+          return CURLE_OK;
+        }
+        break;
+      } /* switch */
+    }
+    else {
+      infof(data, "Skipping time comparison\n");
+    }
+  }
+
+  if(!result)
+    result = ftp_state_post_mdtm(conn);
+
+  return result;
+}
+
+static CURLcode ftp_state_type_resp(struct connectdata *conn,
+                                    int ftpcode,
+                                    ftpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data=conn->data;
+
+  if(ftpcode/100 != 2) {
+    /* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a
+       successful 'TYPE I'. While that is not as RFC959 says, it is still a
+       positive response code and we allow that. */
+    failf(data, "Couldn't set desired mode");
+    return CURLE_FTP_COULDNT_SET_TYPE;
+  }
+  if(ftpcode != 200)
+    infof(data, "Got a %03d response code instead of the assumed 200\n",
+          ftpcode);
+
+  if(instate == FTP_TYPE)
+    result = ftp_state_post_type(conn);
+  else if(instate == FTP_LIST_TYPE)
+    result = ftp_state_post_listtype(conn);
+  else if(instate == FTP_RETR_TYPE)
+    result = ftp_state_post_retrtype(conn);
+  else if(instate == FTP_STOR_TYPE)
+    result = ftp_state_post_stortype(conn);
+
+  return result;
+}
+
+static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
+                                         curl_off_t filesize)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data=conn->data;
+  struct FTP *ftp = data->state.proto.ftp;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+  if(data->set.max_filesize && (filesize > data->set.max_filesize)) {
+    failf(data, "Maximum file size exceeded");
+    return CURLE_FILESIZE_EXCEEDED;
+  }
+  ftp->downloadsize = filesize;
+
+  if(data->state.resume_from) {
+    /* We always (attempt to) get the size of downloads, so it is done before
+       this even when not doing resumes. */
+    if(filesize == -1) {
+      infof(data, "ftp server doesn't support SIZE\n");
+      /* We couldn't get the size and therefore we can't know if there really
+         is a part of the file left to get, although the server will just
+         close the connection when we start the connection so it won't cause
+         us any harm, just not make us exit as nicely. */
+    }
+    else {
+      /* We got a file size report, so we check that there actually is a
+         part of the file left to get, or else we go home.  */
+      if(data->state.resume_from< 0) {
+        /* We're supposed to download the last abs(from) bytes */
+        if(filesize < -data->state.resume_from) {
+          failf(data, "Offset (%" FORMAT_OFF_T
+                ") was beyond file size (%" FORMAT_OFF_T ")",
+                data->state.resume_from, filesize);
+          return CURLE_BAD_DOWNLOAD_RESUME;
+        }
+        /* convert to size to download */
+        ftp->downloadsize = -data->state.resume_from;
+        /* download from where? */
+        data->state.resume_from = filesize - ftp->downloadsize;
+      }
+      else {
+        if(filesize < data->state.resume_from) {
+          failf(data, "Offset (%" FORMAT_OFF_T
+                ") was beyond file size (%" FORMAT_OFF_T ")",
+                data->state.resume_from, filesize);
+          return CURLE_BAD_DOWNLOAD_RESUME;
+        }
+        /* Now store the number of bytes we are expected to download */
+        ftp->downloadsize = filesize-data->state.resume_from;
+      }
+    }
+
+    if(ftp->downloadsize == 0) {
+      /* no data to transfer */
+      Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+      infof(data, "File already completely downloaded\n");
+
+      /* Set ->transfer so that we won't get any error in ftp_done()
+       * because we didn't transfer the any file */
+      ftp->transfer = FTPTRANSFER_NONE;
+      state(conn, FTP_STOP);
+      return CURLE_OK;
+    }
+
+    /* Set resume file transfer offset */
+    infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T
+          "\n", data->state.resume_from);
+
+    PPSENDF(&ftpc->pp, "REST %" FORMAT_OFF_T, data->state.resume_from);
+
+    state(conn, FTP_RETR_REST);
+
+  }
+  else {
+    /* no resume */
+    PPSENDF(&ftpc->pp, "RETR %s", ftpc->file);
+    state(conn, FTP_RETR);
+  }
+
+  return result;
+}
+
+static CURLcode ftp_state_size_resp(struct connectdata *conn,
+                                    int ftpcode,
+                                    ftpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data=conn->data;
+  curl_off_t filesize;
+  char *buf = data->state.buffer;
+
+  /* get the size from the ascii string: */
+  filesize = (ftpcode == 213)?curlx_strtoofft(buf+4, NULL, 0):-1;
+
+  if(instate == FTP_SIZE) {
+#ifdef CURL_FTP_HTTPSTYLE_HEAD
+    if(-1 != filesize) {
+      snprintf(buf, sizeof(data->state.buffer),
+               "Content-Length: %" FORMAT_OFF_T "\r\n", filesize);
+      result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
+      if(result)
+        return result;
+    }
+#endif
+    Curl_pgrsSetDownloadSize(data, filesize);
+    result = ftp_state_post_size(conn);
+  }
+  else if(instate == FTP_RETR_SIZE) {
+    Curl_pgrsSetDownloadSize(data, filesize);
+    result = ftp_state_post_retr_size(conn, filesize);
+  }
+  else if(instate == FTP_STOR_SIZE) {
+    data->state.resume_from = filesize;
+    result = ftp_state_ul_setup(conn, TRUE);
+  }
+
+  return result;
+}
+
+static CURLcode ftp_state_rest_resp(struct connectdata *conn,
+                                    int ftpcode,
+                                    ftpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+  switch(instate) {
+  case FTP_REST:
+  default:
+#ifdef CURL_FTP_HTTPSTYLE_HEAD
+    if(ftpcode == 350) {
+      char buffer[24]= { "Accept-ranges: bytes\r\n" };
+      result = Curl_client_write(conn, CLIENTWRITE_BOTH, buffer, 0);
+      if(result)
+        return result;
+    }
+#endif
+    result = ftp_state_post_rest(conn);
+    break;
+
+  case FTP_RETR_REST:
+    if(ftpcode != 350) {
+      failf(conn->data, "Couldn't use REST");
+      result = CURLE_FTP_COULDNT_USE_REST;
+    }
+    else {
+      PPSENDF(&ftpc->pp, "RETR %s", ftpc->file);
+      state(conn, FTP_RETR);
+    }
+    break;
+  }
+
+  return result;
+}
+
+static CURLcode ftp_state_stor_resp(struct connectdata *conn,
+                                    int ftpcode, ftpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+
+  if(ftpcode>=400) {
+    failf(data, "Failed FTP upload: %0d", ftpcode);
+    state(conn, FTP_STOP);
+    /* oops, we never close the sockets! */
+    return CURLE_UPLOAD_FAILED;
+  }
+
+  conn->proto.ftpc.state_saved = instate;
+
+  /* PORT means we are now awaiting the server to connect to us. */
+  if(data->set.ftp_use_port) {
+    bool connected;
+
+    result = AllowServerConnect(conn, &connected);
+    if(result)
+      return result;
+
+    if(!connected) {
+      struct ftp_conn *ftpc = &conn->proto.ftpc;
+      infof(data, "Data conn was not available immediately\n");
+      ftpc->wait_data_conn = TRUE;
+    }
+
+    return CURLE_OK;
+  }
+  else
+    return InitiateTransfer(conn);
+}
+
+/* for LIST and RETR responses */
+static CURLcode ftp_state_get_resp(struct connectdata *conn,
+                                    int ftpcode,
+                                    ftpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  struct FTP *ftp = data->state.proto.ftp;
+  char *buf = data->state.buffer;
+
+  if((ftpcode == 150) || (ftpcode == 125)) {
+
+    /*
+      A;
+      150 Opening BINARY mode data connection for /etc/passwd (2241
+      bytes).  (ok, the file is being transferred)
+
+      B:
+      150 Opening ASCII mode data connection for /bin/ls
+
+      C:
+      150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
+
+      D:
+      150 Opening ASCII mode data connection for [file] (0.0.0.0,0) (545 bytes)
+
+      E:
+      125 Data connection already open; Transfer starting. */
+
+    curl_off_t size=-1; /* default unknown size */
+
+
+    /*
+     * It appears that there are FTP-servers that return size 0 for files when
+     * SIZE is used on the file while being in BINARY mode. To work around
+     * that (stupid) behavior, we attempt to parse the RETR response even if
+     * the SIZE returned size zero.
+     *
+     * Debugging help from Salvatore Sorrentino on February 26, 2003.
+     */
+
+    if((instate != FTP_LIST) &&
+       !data->set.prefer_ascii &&
+       (ftp->downloadsize < 1)) {
+      /*
+       * It seems directory listings either don't show the size or very
+       * often uses size 0 anyway. ASCII transfers may very well turn out
+       * that the transferred amount of data is not the same as this line
+       * tells, why using this number in those cases only confuses us.
+       *
+       * Example D above makes this parsing a little tricky */
+      char *bytes;
+      bytes=strstr(buf, " bytes");
+      if(bytes--) {
+        long in=(long)(bytes-buf);
+        /* this is a hint there is size information in there! ;-) */
+        while(--in) {
+          /* scan for the left parenthesis and break there */
+          if('(' == *bytes)
+            break;
+          /* skip only digits */
+          if(!ISDIGIT(*bytes)) {
+            bytes=NULL;
+            break;
+          }
+          /* one more estep backwards */
+          bytes--;
+        }
+        /* if we have nothing but digits: */
+        if(bytes++) {
+          /* get the number! */
+          size = curlx_strtoofft(bytes, NULL, 0);
+        }
+      }
+    }
+    else if(ftp->downloadsize > -1)
+      size = ftp->downloadsize;
+
+    if(size > data->req.maxdownload && data->req.maxdownload > 0)
+      size = data->req.size = data->req.maxdownload;
+    else if((instate != FTP_LIST) && (data->set.prefer_ascii))
+      size = -1; /* kludge for servers that understate ASCII mode file size */
+
+    infof(data, "Maxdownload = %" FORMAT_OFF_T "\n", data->req.maxdownload);
+
+    if(instate != FTP_LIST)
+      infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size);
+
+    /* FTP download: */
+    conn->proto.ftpc.state_saved = instate;
+    conn->proto.ftpc.retr_size_saved = size;
+
+    if(data->set.ftp_use_port) {
+      bool connected;
+
+      result = AllowServerConnect(conn, &connected);
+      if(result)
+        return result;
+
+      if(!connected) {
+        struct ftp_conn *ftpc = &conn->proto.ftpc;
+        infof(data, "Data conn was not available immediately\n");
+        state(conn, FTP_STOP);
+        ftpc->wait_data_conn = TRUE;
+      }
+    }
+    else
+      return InitiateTransfer(conn);
+  }
+  else {
+    if((instate == FTP_LIST) && (ftpcode == 450)) {
+      /* simply no matching files in the dir listing */
+      ftp->transfer = FTPTRANSFER_NONE; /* don't download anything */
+      state(conn, FTP_STOP); /* this phase is over */
+    }
+    else {
+      failf(data, "RETR response: %03d", ftpcode);
+      return instate == FTP_RETR && ftpcode == 550?
+        CURLE_REMOTE_FILE_NOT_FOUND:
+        CURLE_FTP_COULDNT_RETR_FILE;
+    }
+  }
+
+  return result;
+}
+
+/* after USER, PASS and ACCT */
+static CURLcode ftp_state_loggedin(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+
+#ifdef HAVE_KRB4
+  if(conn->data->set.krb) {
+    /* We may need to issue a KAUTH here to have access to the files
+     * do it if user supplied a password
+     */
+    if(conn->passwd && *conn->passwd) {
+      /* BLOCKING */
+      result = Curl_krb_kauth(conn);
+      if(result)
+        return result;
+    }
+  }
+#endif
+  if(conn->ssl[FIRSTSOCKET].use) {
+    /* PBSZ = PROTECTION BUFFER SIZE.
+
+    The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
+
+    Specifically, the PROT command MUST be preceded by a PBSZ
+    command and a PBSZ command MUST be preceded by a successful
+    security data exchange (the TLS negotiation in this case)
+
+    ... (and on page 8):
+
+    Thus the PBSZ command must still be issued, but must have a
+    parameter of '0' to indicate that no buffering is taking place
+    and the data connection should not be encapsulated.
+    */
+    PPSENDF(&conn->proto.ftpc.pp, "PBSZ %d", 0);
+    state(conn, FTP_PBSZ);
+  }
+  else {
+    result = ftp_state_pwd(conn);
+  }
+  return result;
+}
+
+/* for USER and PASS responses */
+static CURLcode ftp_state_user_resp(struct connectdata *conn,
+                                    int ftpcode,
+                                    ftpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  struct FTP *ftp = data->state.proto.ftp;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+  (void)instate; /* no use for this yet */
+
+  /* some need password anyway, and others just return 2xx ignored */
+  if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
+    /* 331 Password required for ...
+       (the server requires to send the user's password too) */
+    PPSENDF(&ftpc->pp, "PASS %s", ftp->passwd?ftp->passwd:"");
+    state(conn, FTP_PASS);
+  }
+  else if(ftpcode/100 == 2) {
+    /* 230 User ... logged in.
+       (the user logged in with or without password) */
+    result = ftp_state_loggedin(conn);
+  }
+  else if(ftpcode == 332) {
+    if(data->set.str[STRING_FTP_ACCOUNT]) {
+      PPSENDF(&ftpc->pp, "ACCT %s", data->set.str[STRING_FTP_ACCOUNT]);
+      state(conn, FTP_ACCT);
+    }
+    else {
+      failf(data, "ACCT requested but none available");
+      result = CURLE_LOGIN_DENIED;
+    }
+  }
+  else {
+    /* All other response codes, like:
+
+    530 User ... access denied
+    (the server denies to log the specified user) */
+
+    if(conn->data->set.str[STRING_FTP_ALTERNATIVE_TO_USER] &&
+        !conn->data->state.ftp_trying_alternative) {
+      /* Ok, USER failed.  Let's try the supplied command. */
+      PPSENDF(&conn->proto.ftpc.pp, "%s",
+              conn->data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
+      conn->data->state.ftp_trying_alternative = TRUE;
+      state(conn, FTP_USER);
+      result = CURLE_OK;
+    }
+    else {
+      failf(data, "Access denied: %03d", ftpcode);
+      result = CURLE_LOGIN_DENIED;
+    }
+  }
+  return result;
+}
+
+/* for ACCT response */
+static CURLcode ftp_state_acct_resp(struct connectdata *conn,
+                                    int ftpcode)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  if(ftpcode != 230) {
+    failf(data, "ACCT rejected by server: %03d", ftpcode);
+    result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */
+  }
+  else
+    result = ftp_state_loggedin(conn);
+
+  return result;
+}
+
+
+static CURLcode ftp_statemach_act(struct connectdata *conn)
+{
+  CURLcode result;
+  curl_socket_t sock = conn->sock[FIRSTSOCKET];
+  struct SessionHandle *data=conn->data;
+  int ftpcode;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+  struct pingpong *pp = &ftpc->pp;
+  static const char ftpauth[][4]  = { "SSL", "TLS" };
+  size_t nread = 0;
+
+  if(pp->sendleft)
+    return Curl_pp_flushsend(pp);
+
+  result = ftp_readresp(sock, pp, &ftpcode, &nread);
+  if(result)
+    return result;
+
+  if(ftpcode) {
+    /* we have now received a full FTP server response */
+    switch(ftpc->state) {
+    case FTP_WAIT220:
+      if(ftpcode != 220) {
+        failf(data, "Got a %03d ftp-server response when 220 was expected",
+              ftpcode);
+        return CURLE_FTP_WEIRD_SERVER_REPLY;
+      }
+
+      /* We have received a 220 response fine, now we proceed. */
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+      if(data->set.krb) {
+        /* If not anonymous login, try a secure login. Note that this
+           procedure is still BLOCKING. */
+
+        Curl_sec_request_prot(conn, "private");
+        /* We set private first as default, in case the line below fails to
+           set a valid level */
+        Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]);
+
+        if(Curl_sec_login(conn) != CURLE_OK)
+          infof(data, "Logging in with password in cleartext!\n");
+        else
+          infof(data, "Authentication successful\n");
+      }
+#endif
+
+      if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
+        /* We don't have a SSL/TLS connection yet, but FTPS is
+           requested. Try a FTPS connection now */
+
+        ftpc->count3=0;
+        switch(data->set.ftpsslauth) {
+        case CURLFTPAUTH_DEFAULT:
+        case CURLFTPAUTH_SSL:
+          ftpc->count2 = 1; /* add one to get next */
+          ftpc->count1 = 0;
+          break;
+        case CURLFTPAUTH_TLS:
+          ftpc->count2 = -1; /* subtract one to get next */
+          ftpc->count1 = 1;
+          break;
+        default:
+          failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d",
+                (int)data->set.ftpsslauth);
+          return CURLE_UNKNOWN_OPTION; /* we don't know what to do */
+        }
+        PPSENDF(&ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
+        state(conn, FTP_AUTH);
+      }
+      else {
+        result = ftp_state_user(conn);
+        if(result)
+          return result;
+      }
+
+      break;
+
+    case FTP_AUTH:
+      /* we have gotten the response to a previous AUTH command */
+
+      /* RFC2228 (page 5) says:
+       *
+       * If the server is willing to accept the named security mechanism,
+       * and does not require any security data, it must respond with
+       * reply code 234/334.
+       */
+
+      if((ftpcode == 234) || (ftpcode == 334)) {
+        /* Curl_ssl_connect is BLOCKING */
+        result = Curl_ssl_connect(conn, FIRSTSOCKET);
+        if(CURLE_OK == result) {
+          conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */
+          result = ftp_state_user(conn);
+        }
+      }
+      else if(ftpc->count3 < 1) {
+        ftpc->count3++;
+        ftpc->count1 += ftpc->count2; /* get next attempt */
+        result = Curl_pp_sendf(&ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
+        /* remain in this same state */
+      }
+      else {
+        if(data->set.use_ssl > CURLUSESSL_TRY)
+          /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */
+          result = CURLE_USE_SSL_FAILED;
+        else
+          /* ignore the failure and continue */
+          result = ftp_state_user(conn);
+      }
+
+      if(result)
+        return result;
+      break;
+
+    case FTP_USER:
+    case FTP_PASS:
+      result = ftp_state_user_resp(conn, ftpcode, ftpc->state);
+      break;
+
+    case FTP_ACCT:
+      result = ftp_state_acct_resp(conn, ftpcode);
+      break;
+
+    case FTP_PBSZ:
+      PPSENDF(&ftpc->pp, "PROT %c",
+              data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
+      state(conn, FTP_PROT);
+
+      break;
+
+    case FTP_PROT:
+      if(ftpcode/100 == 2)
+        /* We have enabled SSL for the data connection! */
+        conn->ssl[SECONDARYSOCKET].use =
+          (data->set.use_ssl != CURLUSESSL_CONTROL) ? TRUE : FALSE;
+      /* FTP servers typically responds with 500 if they decide to reject
+         our 'P' request */
+      else if(data->set.use_ssl > CURLUSESSL_CONTROL)
+        /* we failed and bails out */
+        return CURLE_USE_SSL_FAILED;
+
+      if(data->set.ftp_ccc) {
+        /* CCC - Clear Command Channel
+         */
+        PPSENDF(&ftpc->pp, "CCC", NULL);
+        state(conn, FTP_CCC);
+      }
+      else {
+        result = ftp_state_pwd(conn);
+        if(result)
+          return result;
+      }
+      break;
+
+    case FTP_CCC:
+      if(ftpcode < 500) {
+        /* First shut down the SSL layer (note: this call will block) */
+        result = Curl_ssl_shutdown(conn, FIRSTSOCKET);
+
+        if(result) {
+          failf(conn->data, "Failed to clear the command channel (CCC)");
+          return result;
+        }
+      }
+
+      /* Then continue as normal */
+      result = ftp_state_pwd(conn);
+      if(result)
+        return result;
+      break;
+
+    case FTP_PWD:
+      if(ftpcode == 257) {
+        char *ptr=&data->state.buffer[4];  /* start on the first letter */
+        char *dir;
+        char *store;
+
+        dir = malloc(nread + 1);
+        if(!dir)
+          return CURLE_OUT_OF_MEMORY;
+
+        /* Reply format is like
+           257<space>"<directory-name>"<space><commentary> and the RFC959
+           says
+
+           The directory name can contain any character; embedded
+           double-quotes should be escaped by double-quotes (the
+           "quote-doubling" convention).
+        */
+        if('\"' == *ptr) {
+          /* it started good */
+          ptr++;
+          for(store = dir; *ptr;) {
+            if('\"' == *ptr) {
+              if('\"' == ptr[1]) {
+                /* "quote-doubling" */
+                *store = ptr[1];
+                ptr++;
+              }
+              else {
+                /* end of path */
+                *store = '\0'; /* zero terminate */
+                break; /* get out of this loop */
+              }
+            }
+            else
+              *store = *ptr;
+            store++;
+            ptr++;
+          }
+
+          /* If the path name does not look like an absolute path (i.e.: it
+             does not start with a '/'), we probably need some server-dependent
+             adjustments. For example, this is the case when connecting to
+             an OS400 FTP server: this server supports two name syntaxes,
+             the default one being incompatible with standard pathes. In
+             addition, this server switches automatically to the regular path
+             syntax when one is encountered in a command: this results in
+             having an entrypath in the wrong syntax when later used in CWD.
+               The method used here is to check the server OS: we do it only
+             if the path name looks strange to minimize overhead on other
+             systems. */
+
+          if(!ftpc->server_os && dir[0] != '/') {
+
+            result = Curl_pp_sendf(&ftpc->pp, "SYST", NULL);
+            if(result != CURLE_OK) {
+              free(dir);
+              return result;
+            }
+            Curl_safefree(ftpc->entrypath);
+            ftpc->entrypath = dir; /* remember this */
+            infof(data, "Entry path is '%s'\n", ftpc->entrypath);
+            /* also save it where getinfo can access it: */
+            data->state.most_recent_ftp_entrypath = ftpc->entrypath;
+            state(conn, FTP_SYST);
+            break;
+          }
+
+          Curl_safefree(ftpc->entrypath);
+          ftpc->entrypath = dir; /* remember this */
+          infof(data, "Entry path is '%s'\n", ftpc->entrypath);
+          /* also save it where getinfo can access it: */
+          data->state.most_recent_ftp_entrypath = ftpc->entrypath;
+        }
+        else {
+          /* couldn't get the path */
+          free(dir);
+          infof(data, "Failed to figure out path\n");
+        }
+      }
+      state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
+      DEBUGF(infof(data, "protocol connect phase DONE\n"));
+      break;
+
+    case FTP_SYST:
+      if(ftpcode == 215) {
+        char *ptr=&data->state.buffer[4];  /* start on the first letter */
+        char *os;
+        char *store;
+
+        os = malloc(nread + 1);
+        if(!os)
+          return CURLE_OUT_OF_MEMORY;
+
+        /* Reply format is like
+           215<space><OS-name><space><commentary>
+        */
+        while(*ptr == ' ')
+          ptr++;
+        for(store = os; *ptr && *ptr != ' ';)
+          *store++ = *ptr++;
+        *store = '\0'; /* zero terminate */
+
+        /* Check for special servers here. */
+
+        if(strequal(os, "OS/400")) {
+          /* Force OS400 name format 1. */
+          result = Curl_pp_sendf(&ftpc->pp, "SITE NAMEFMT 1", NULL);
+          if(result != CURLE_OK) {
+            free(os);
+            return result;
+          }
+          /* remember target server OS */
+          Curl_safefree(ftpc->server_os);
+          ftpc->server_os = os;
+          state(conn, FTP_NAMEFMT);
+          break;
+        }
+        else {
+          /* Nothing special for the target server. */
+          /* remember target server OS */
+          Curl_safefree(ftpc->server_os);
+          ftpc->server_os = os;
+        }
+      }
+      else {
+        /* Cannot identify server OS. Continue anyway and cross fingers. */
+      }
+
+      state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
+      DEBUGF(infof(data, "protocol connect phase DONE\n"));
+      break;
+
+    case FTP_NAMEFMT:
+      if(ftpcode == 250) {
+        /* Name format change successful: reload initial path. */
+        ftp_state_pwd(conn);
+        break;
+      }
+
+      state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
+      DEBUGF(infof(data, "protocol connect phase DONE\n"));
+      break;
+
+    case FTP_QUOTE:
+    case FTP_POSTQUOTE:
+    case FTP_RETR_PREQUOTE:
+    case FTP_STOR_PREQUOTE:
+      if((ftpcode >= 400) && !ftpc->count2) {
+        /* failure response code, and not allowed to fail */
+        failf(conn->data, "QUOT command failed with %03d", ftpcode);
+        return CURLE_QUOTE_ERROR;
+      }
+      result = ftp_state_quote(conn, FALSE, ftpc->state);
+      if(result)
+        return result;
+
+      break;
+
+    case FTP_CWD:
+      if(ftpcode/100 != 2) {
+        /* failure to CWD there */
+        if(conn->data->set.ftp_create_missing_dirs &&
+           ftpc->count1 && !ftpc->count2) {
+          /* try making it */
+          ftpc->count2++; /* counter to prevent CWD-MKD loops */
+          PPSENDF(&ftpc->pp, "MKD %s", ftpc->dirs[ftpc->count1 - 1]);
+          state(conn, FTP_MKD);
+        }
+        else {
+          /* return failure */
+          failf(data, "Server denied you to change to the given directory");
+          ftpc->cwdfail = TRUE; /* don't remember this path as we failed
+                                   to enter it */
+          return CURLE_REMOTE_ACCESS_DENIED;
+        }
+      }
+      else {
+        /* success */
+        ftpc->count2=0;
+        if(++ftpc->count1 <= ftpc->dirdepth) {
+          /* send next CWD */
+          PPSENDF(&ftpc->pp, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
+        }
+        else {
+          result = ftp_state_post_cwd(conn);
+          if(result)
+            return result;
+        }
+      }
+      break;
+
+    case FTP_MKD:
+      if((ftpcode/100 != 2) && !ftpc->count3--) {
+        /* failure to MKD the dir */
+        failf(data, "Failed to MKD dir: %03d", ftpcode);
+        return CURLE_REMOTE_ACCESS_DENIED;
+      }
+      state(conn, FTP_CWD);
+      /* send CWD */
+      PPSENDF(&ftpc->pp, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
+      break;
+
+    case FTP_MDTM:
+      result = ftp_state_mdtm_resp(conn, ftpcode);
+      break;
+
+    case FTP_TYPE:
+    case FTP_LIST_TYPE:
+    case FTP_RETR_TYPE:
+    case FTP_STOR_TYPE:
+      result = ftp_state_type_resp(conn, ftpcode, ftpc->state);
+      break;
+
+    case FTP_SIZE:
+    case FTP_RETR_SIZE:
+    case FTP_STOR_SIZE:
+      result = ftp_state_size_resp(conn, ftpcode, ftpc->state);
+      break;
+
+    case FTP_REST:
+    case FTP_RETR_REST:
+      result = ftp_state_rest_resp(conn, ftpcode, ftpc->state);
+      break;
+
+    case FTP_PRET:
+      if(ftpcode != 200) {
+        /* there only is this one standard OK return code. */
+        failf(data, "PRET command not accepted: %03d", ftpcode);
+        return CURLE_FTP_PRET_FAILED;
+      }
+      result = ftp_state_use_pasv(conn);
+      break;
+
+    case FTP_PASV:
+      result = ftp_state_pasv_resp(conn, ftpcode);
+      break;
+
+    case FTP_PORT:
+      result = ftp_state_port_resp(conn, ftpcode);
+      break;
+
+    case FTP_LIST:
+    case FTP_RETR:
+      result = ftp_state_get_resp(conn, ftpcode, ftpc->state);
+      break;
+
+    case FTP_STOR:
+      result = ftp_state_stor_resp(conn, ftpcode, ftpc->state);
+      break;
+
+    case FTP_QUIT:
+      /* fallthrough, just stop! */
+    default:
+      /* internal error */
+      state(conn, FTP_STOP);
+      break;
+    }
+  } /* if(ftpcode) */
+
+  return result;
+}
+
+
+/* called repeatedly until done from curl_multi.c */
+static CURLcode ftp_multi_statemach(struct connectdata *conn,
+                                    bool *done)
+{
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+  CURLcode result = Curl_pp_multi_statemach(&ftpc->pp);
+
+  /* Check for the state outside of the Curl_socket_ready() return code checks
+     since at times we are in fact already in this state when this function
+     gets called. */
+  *done = (ftpc->state == FTP_STOP) ? TRUE : FALSE;
+
+  return result;
+}
+
+static CURLcode ftp_easy_statemach(struct connectdata *conn)
+{
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+  struct pingpong *pp = &ftpc->pp;
+  CURLcode result = CURLE_OK;
+
+  while(ftpc->state != FTP_STOP) {
+    result = Curl_pp_easy_statemach(pp);
+    if(result)
+      break;
+  }
+
+  return result;
+}
+
+/*
+ * Allocate and initialize the struct FTP for the current SessionHandle.  If
+ * need be.
+ */
+
+#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \
+    defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__)
+  /* workaround icc 9.1 optimizer issue */
+#pragma optimize("", off)
+#endif
+
+static CURLcode ftp_init(struct connectdata *conn)
+{
+  struct FTP *ftp;
+
+  if(NULL == conn->data->state.proto.ftp) {
+    conn->data->state.proto.ftp = malloc(sizeof(struct FTP));
+    if(NULL == conn->data->state.proto.ftp)
+      return CURLE_OUT_OF_MEMORY;
+  }
+
+  ftp = conn->data->state.proto.ftp;
+
+  /* get some initial data into the ftp struct */
+  ftp->bytecountp = &conn->data->req.bytecount;
+  ftp->transfer = FTPTRANSFER_BODY;
+  ftp->downloadsize = 0;
+
+  /* No need to duplicate user+password, the connectdata struct won't change
+     during a session, but we re-init them here since on subsequent inits
+     since the conn struct may have changed or been replaced.
+  */
+  ftp->user = conn->user;
+  ftp->passwd = conn->passwd;
+  if(isBadFtpString(ftp->user))
+    return CURLE_URL_MALFORMAT;
+  if(isBadFtpString(ftp->passwd))
+    return CURLE_URL_MALFORMAT;
+
+  conn->proto.ftpc.known_filesize = -1; /* unknown size for now */
+
+  return CURLE_OK;
+}
+
+#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \
+    defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__)
+  /* workaround icc 9.1 optimizer issue */
+#pragma optimize("", on)
+#endif
+
+/*
+ * ftp_connect() should do everything that is to be considered a part of
+ * the connection phase.
+ *
+ * The variable 'done' points to will be TRUE if the protocol-layer connect
+ * phase is done when this function returns, or FALSE is not. When called as
+ * a part of the easy interface, it will always be TRUE.
+ */
+static CURLcode ftp_connect(struct connectdata *conn,
+                                 bool *done) /* see description above */
+{
+  CURLcode result;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+  struct SessionHandle *data=conn->data;
+  struct pingpong *pp = &ftpc->pp;
+
+  *done = FALSE; /* default to not done yet */
+
+  /* If there already is a protocol-specific struct allocated for this
+     sessionhandle, deal with it */
+  Curl_reset_reqproto(conn);
+
+  result = ftp_init(conn);
+  if(CURLE_OK != result)
+    return result;
+
+  /* We always support persistent connections on ftp */
+  conn->bits.close = FALSE;
+
+  pp->response_time = RESP_TIMEOUT; /* set default response time-out */
+  pp->statemach_act = ftp_statemach_act;
+  pp->endofresp = ftp_endofresp;
+  pp->conn = conn;
+
+  if(conn->handler->flags & PROTOPT_SSL) {
+    /* BLOCKING */
+    result = Curl_ssl_connect(conn, FIRSTSOCKET);
+    if(result)
+      return result;
+  }
+
+  Curl_pp_init(pp); /* init the generic pingpong data */
+
+  /* When we connect, we start in the state where we await the 220
+     response */
+  state(conn, FTP_WAIT220);
+
+  if(data->state.used_interface == Curl_if_multi)
+    result = ftp_multi_statemach(conn, done);
+  else {
+    result = ftp_easy_statemach(conn);
+    if(!result)
+      *done = TRUE;
+  }
+
+  return result;
+}
+
+/***********************************************************************
+ *
+ * ftp_done()
+ *
+ * The DONE function. This does what needs to be done after a single DO has
+ * performed.
+ *
+ * Input argument is already checked for validity.
+ */
+static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
+                              bool premature)
+{
+  struct SessionHandle *data = conn->data;
+  struct FTP *ftp = data->state.proto.ftp;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+  struct pingpong *pp = &ftpc->pp;
+  ssize_t nread;
+  int ftpcode;
+  CURLcode result = CURLE_OK;
+  bool was_ctl_valid = ftpc->ctl_valid;
+  char *path;
+  const char *path_to_use = data->state.path;
+
+  if(!ftp)
+    /* When the easy handle is removed from the multi while libcurl is still
+     * trying to resolve the host name, it seems that the ftp struct is not
+     * yet initialized, but the removal action calls Curl_done() which calls
+     * this function. So we simply return success if no ftp pointer is set.
+     */
+    return CURLE_OK;
+
+  switch(status) {
+  case CURLE_BAD_DOWNLOAD_RESUME:
+  case CURLE_FTP_WEIRD_PASV_REPLY:
+  case CURLE_FTP_PORT_FAILED:
+  case CURLE_FTP_ACCEPT_FAILED:
+  case CURLE_FTP_ACCEPT_TIMEOUT:
+  case CURLE_FTP_COULDNT_SET_TYPE:
+  case CURLE_FTP_COULDNT_RETR_FILE:
+  case CURLE_PARTIAL_FILE:
+  case CURLE_UPLOAD_FAILED:
+  case CURLE_REMOTE_ACCESS_DENIED:
+  case CURLE_FILESIZE_EXCEEDED:
+  case CURLE_REMOTE_FILE_NOT_FOUND:
+  case CURLE_WRITE_ERROR:
+    /* the connection stays alive fine even though this happened */
+    /* fall-through */
+  case CURLE_OK: /* doesn't affect the control connection's status */
+    if(!premature) {
+      ftpc->ctl_valid = was_ctl_valid;
+      break;
+    }
+    /* until we cope better with prematurely ended requests, let them
+     * fallback as if in complete failure */
+  default:       /* by default, an error means the control connection is
+                    wedged and should not be used anymore */
+    ftpc->ctl_valid = FALSE;
+    ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the
+                             current path, as this connection is going */
+    conn->bits.close = TRUE; /* marked for closure */
+    result = status;      /* use the already set error code */
+    break;
+  }
+
+  /* now store a copy of the directory we are in */
+  if(ftpc->prevpath)
+    free(ftpc->prevpath);
+
+  if(data->set.wildcardmatch) {
+    if(data->set.chunk_end && ftpc->file) {
+      data->set.chunk_end(data->wildcard.customptr);
+    }
+    ftpc->known_filesize = -1;
+  }
+
+  /* get the "raw" path */
+  path = curl_easy_unescape(data, path_to_use, 0, NULL);
+  if(!path) {
+    /* out of memory, but we can limp along anyway (and should try to
+     * since we may already be in the out of memory cleanup path) */
+    if(!result)
+      result = CURLE_OUT_OF_MEMORY;
+    ftpc->ctl_valid = FALSE; /* mark control connection as bad */
+    conn->bits.close = TRUE; /* mark for connection closure */
+    ftpc->prevpath = NULL; /* no path remembering */
+  }
+  else {
+    size_t flen = ftpc->file?strlen(ftpc->file):0; /* file is "raw" already */
+    size_t dlen = strlen(path)-flen;
+    if(!ftpc->cwdfail) {
+      if(dlen && (data->set.ftp_filemethod != FTPFILE_NOCWD)) {
+        ftpc->prevpath = path;
+        if(flen)
+          /* if 'path' is not the whole string */
+          ftpc->prevpath[dlen]=0; /* terminate */
+      }
+      else {
+        /* we never changed dir */
+        ftpc->prevpath=strdup("");
+        free(path);
+      }
+      if(ftpc->prevpath)
+        infof(data, "Remembering we are in dir \"%s\"\n", ftpc->prevpath);
+    }
+    else {
+      ftpc->prevpath = NULL; /* no path */
+      free(path);
+    }
+  }
+  /* free the dir tree and file parts */
+  freedirs(ftpc);
+
+  /* shut down the socket to inform the server we're done */
+
+#ifdef _WIN32_WCE
+  shutdown(conn->sock[SECONDARYSOCKET],2);  /* SD_BOTH */
+#endif
+
+  if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
+    if(!result && ftpc->dont_check && data->req.maxdownload > 0)
+      /* partial download completed */
+      result = Curl_pp_sendf(pp, "ABOR");
+      if(result) {
+        failf(data, "Failure sending ABOR command: %s",
+              curl_easy_strerror(result));
+        ftpc->ctl_valid = FALSE; /* mark control connection as bad */
+        conn->bits.close = TRUE; /* mark for connection closure */
+      }
+
+    if(conn->ssl[SECONDARYSOCKET].use) {
+      /* The secondary socket is using SSL so we must close down that part
+         first before we close the socket for real */
+      Curl_ssl_close(conn, SECONDARYSOCKET);
+
+      /* Note that we keep "use" set to TRUE since that (next) connection is
+         still requested to use SSL */
+    }
+    if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) {
+      Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
+      conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
+      conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE;
+    }
+  }
+
+  if(!result && (ftp->transfer == FTPTRANSFER_BODY) && ftpc->ctl_valid &&
+     pp->pending_resp && !premature) {
+    /*
+     * Let's see what the server says about the transfer we just performed,
+     * but lower the timeout as sometimes this connection has died while the
+     * data has been transferred. This happens when doing through NATs etc that
+     * abandon old silent connections.
+     */
+    long old_time = pp->response_time;
+
+    pp->response_time = 60*1000; /* give it only a minute for now */
+    pp->response = Curl_tvnow(); /* timeout relative now */
+
+    result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
+
+    pp->response_time = old_time; /* set this back to previous value */
+
+    if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
+      failf(data, "control connection looks dead");
+      ftpc->ctl_valid = FALSE; /* mark control connection as bad */
+      conn->bits.close = TRUE; /* mark for closure */
+    }
+
+    if(result)
+      return result;
+
+    if(ftpc->dont_check && data->req.maxdownload > 0) {
+      /* we have just sent ABOR and there is no reliable way to check if it was
+       * successful or not; we have to close the connection now */
+      infof(data, "partial download completed, closing connection\n");
+      conn->bits.close = TRUE; /* mark for closure */
+      return result;
+    }
+
+    if(!ftpc->dont_check) {
+      /* 226 Transfer complete, 250 Requested file action okay, completed. */
+      if((ftpcode != 226) && (ftpcode != 250)) {
+        failf(data, "server did not report OK, got %d", ftpcode);
+        result = CURLE_PARTIAL_FILE;
+      }
+    }
+  }
+
+  if(result || premature)
+    /* the response code from the transfer showed an error already so no
+       use checking further */
+    ;
+  else if(data->set.upload) {
+    if((-1 != data->set.infilesize) &&
+       (data->set.infilesize != *ftp->bytecountp) &&
+       !data->set.crlf &&
+       (ftp->transfer == FTPTRANSFER_BODY)) {
+      failf(data, "Uploaded unaligned file size (%" FORMAT_OFF_T
+            " out of %" FORMAT_OFF_T " bytes)",
+            *ftp->bytecountp, data->set.infilesize);
+      result = CURLE_PARTIAL_FILE;
+    }
+  }
+  else {
+    if((-1 != data->req.size) &&
+       (data->req.size != *ftp->bytecountp) &&
+#ifdef CURL_DO_LINEEND_CONV
+       /* Most FTP servers don't adjust their file SIZE response for CRLFs, so
+        * we'll check to see if the discrepancy can be explained by the number
+        * of CRLFs we've changed to LFs.
+        */
+       ((data->req.size + data->state.crlf_conversions) !=
+        *ftp->bytecountp) &&
+#endif /* CURL_DO_LINEEND_CONV */
+       (data->req.maxdownload != *ftp->bytecountp)) {
+      failf(data, "Received only partial file: %" FORMAT_OFF_T " bytes",
+            *ftp->bytecountp);
+      result = CURLE_PARTIAL_FILE;
+    }
+    else if(!ftpc->dont_check &&
+            !*ftp->bytecountp &&
+            (data->req.size>0)) {
+      failf(data, "No data was received!");
+      result = CURLE_FTP_COULDNT_RETR_FILE;
+    }
+  }
+
+  /* clear these for next connection */
+  ftp->transfer = FTPTRANSFER_BODY;
+  ftpc->dont_check = FALSE;
+
+  /* Send any post-transfer QUOTE strings? */
+  if(!status && !result && !premature && data->set.postquote)
+    result = ftp_sendquote(conn, data->set.postquote);
+
+  return result;
+}
+
+/***********************************************************************
+ *
+ * ftp_sendquote()
+ *
+ * Where a 'quote' means a list of custom commands to send to the server.
+ * The quote list is passed as an argument.
+ *
+ * BLOCKING
+ */
+
+static
+CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
+{
+  struct curl_slist *item;
+  ssize_t nread;
+  int ftpcode;
+  CURLcode result;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+  struct pingpong *pp = &ftpc->pp;
+
+  item = quote;
+  while(item) {
+    if(item->data) {
+      char *cmd = item->data;
+      bool acceptfail = FALSE;
+
+      /* if a command starts with an asterisk, which a legal FTP command never
+         can, the command will be allowed to fail without it causing any
+         aborts or cancels etc. It will cause libcurl to act as if the command
+         is successful, whatever the server reponds. */
+
+      if(cmd[0] == '*') {
+        cmd++;
+        acceptfail = TRUE;
+      }
+
+      FTPSENDF(conn, "%s", cmd);
+
+      pp->response = Curl_tvnow(); /* timeout relative now */
+
+      result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
+      if(result)
+        return result;
+
+      if(!acceptfail && (ftpcode >= 400)) {
+        failf(conn->data, "QUOT string not accepted: %s", cmd);
+        return CURLE_QUOTE_ERROR;
+      }
+    }
+
+    item = item->next;
+  }
+
+  return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * ftp_need_type()
+ *
+ * Returns TRUE if we in the current situation should send TYPE
+ */
+static int ftp_need_type(struct connectdata *conn,
+                         bool ascii_wanted)
+{
+  return conn->proto.ftpc.transfertype != (ascii_wanted?'A':'I');
+}
+
+/***********************************************************************
+ *
+ * ftp_nb_type()
+ *
+ * Set TYPE. We only deal with ASCII or BINARY so this function
+ * sets one of them.
+ * If the transfer type is not sent, simulate on OK response in newstate
+ */
+static CURLcode ftp_nb_type(struct connectdata *conn,
+                            bool ascii, ftpstate newstate)
+{
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+  CURLcode result;
+  char want = (char)(ascii?'A':'I');
+
+  if(ftpc->transfertype == want) {
+    state(conn, newstate);
+    return ftp_state_type_resp(conn, 200, newstate);
+  }
+
+  PPSENDF(&ftpc->pp, "TYPE %c", want);
+  state(conn, newstate);
+
+  /* keep track of our current transfer type */
+  ftpc->transfertype = want;
+  return CURLE_OK;
+}
+
+/***************************************************************************
+ *
+ * ftp_pasv_verbose()
+ *
+ * This function only outputs some informationals about this second connection
+ * when we've issued a PASV command before and thus we have connected to a
+ * possibly new IP address.
+ *
+ */
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static void
+ftp_pasv_verbose(struct connectdata *conn,
+                 Curl_addrinfo *ai,
+                 char *newhost, /* ascii version */
+                 int port)
+{
+  char buf[256];
+  Curl_printable_address(ai, buf, sizeof(buf));
+  infof(conn->data, "Connecting to %s (%s) port %d\n", newhost, buf, port);
+}
+#endif
+
+/*
+  Check if this is a range download, and if so, set the internal variables
+  properly.
+ */
+
+static CURLcode ftp_range(struct connectdata *conn)
+{
+  curl_off_t from, to;
+  char *ptr;
+  char *ptr2;
+  struct SessionHandle *data = conn->data;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+  if(data->state.use_range && data->state.range) {
+    from=curlx_strtoofft(data->state.range, &ptr, 0);
+    while(*ptr && (ISSPACE(*ptr) || (*ptr=='-')))
+      ptr++;
+    to=curlx_strtoofft(ptr, &ptr2, 0);
+    if(ptr == ptr2) {
+      /* we didn't get any digit */
+      to=-1;
+    }
+    if((-1 == to) && (from>=0)) {
+      /* X - */
+      data->state.resume_from = from;
+      DEBUGF(infof(conn->data, "FTP RANGE %" FORMAT_OFF_T " to end of file\n",
+                   from));
+    }
+    else if(from < 0) {
+      /* -Y */
+      data->req.maxdownload = -from;
+      data->state.resume_from = from;
+      DEBUGF(infof(conn->data, "FTP RANGE the last %" FORMAT_OFF_T " bytes\n",
+                   -from));
+    }
+    else {
+      /* X-Y */
+      data->req.maxdownload = (to-from)+1; /* include last byte */
+      data->state.resume_from = from;
+      DEBUGF(infof(conn->data, "FTP RANGE from %" FORMAT_OFF_T
+                   " getting %" FORMAT_OFF_T " bytes\n",
+                   from, data->req.maxdownload));
+    }
+    DEBUGF(infof(conn->data, "range-download from %" FORMAT_OFF_T
+                 " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
+                 from, to, data->req.maxdownload));
+    ftpc->dont_check = TRUE; /* dont check for successful transfer */
+  }
+  else
+    data->req.maxdownload = -1;
+  return CURLE_OK;
+}
+
+
+/*
+ * ftp_do_more()
+ *
+ * This function shall be called when the second FTP (data) connection is
+ * connected.
+ */
+
+static CURLcode ftp_do_more(struct connectdata *conn, bool *complete)
+{
+  struct SessionHandle *data=conn->data;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+  CURLcode result = CURLE_OK;
+  bool connected = FALSE;
+
+  /* the ftp struct is inited in ftp_connect() */
+  struct FTP *ftp = data->state.proto.ftp;
+
+  *complete = FALSE;
+
+  /* if the second connection isn't done yet, wait for it */
+  if(!conn->bits.tcpconnect[SECONDARYSOCKET]) {
+    result = Curl_is_connected(conn, SECONDARYSOCKET, &connected);
+
+    /* Ready to do more? */
+    if(connected) {
+      DEBUGF(infof(data, "DO-MORE connected phase starts\n"));
+    }
+    else
+      return result;
+  }
+
+  if((data->state.used_interface == Curl_if_multi) &&
+     ftpc->state) {
+    /* multi interface and already in a state so skip the intial commands.
+       They are only done to kickstart the do_more state */
+    result = ftp_multi_statemach(conn, complete);
+
+    /* if we got an error or if we don't wait for a data connection return
+       immediately */
+    if(result || (ftpc->wait_data_conn != TRUE))
+      return result;
+  }
+
+  if(ftp->transfer <= FTPTRANSFER_INFO) {
+    /* a transfer is about to take place, or if not a file name was given
+       so we'll do a SIZE on it later and then we need the right TYPE first */
+
+    if(ftpc->wait_data_conn == TRUE) {
+      bool serv_conned;
+
+      result = ReceivedServerConnect(conn, &serv_conned);
+      if(result)
+        return result; /* Failed to accept data connection */
+
+      if(serv_conned) {
+        /* It looks data connection is established */
+        result = AcceptServerConnect(conn);
+        ftpc->wait_data_conn = FALSE;
+        if(!result)
+          result = InitiateTransfer(conn);
+
+        if(result)
+          return result;
+      }
+    }
+    else if(data->set.upload) {
+      result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_STOR_TYPE);
+      if(result)
+        return result;
+    }
+    else {
+      /* download */
+      ftp->downloadsize = -1; /* unknown as of yet */
+
+      result = ftp_range(conn);
+      if(result)
+        ;
+      else if(data->set.ftp_list_only || !ftpc->file) {
+        /* The specified path ends with a slash, and therefore we think this
+           is a directory that is requested, use LIST. But before that we
+           need to set ASCII transfer mode. */
+
+        /* But only if a body transfer was requested. */
+        if(ftp->transfer == FTPTRANSFER_BODY) {
+          result = ftp_nb_type(conn, TRUE, FTP_LIST_TYPE);
+          if(result)
+            return result;
+        }
+        /* otherwise just fall through */
+      }
+      else {
+        result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_RETR_TYPE);
+        if(result)
+          return result;
+      }
+    }
+    if(data->state.used_interface == Curl_if_multi) {
+      result = ftp_multi_statemach(conn, complete);
+
+      return result;
+    }
+    else
+      result = ftp_easy_statemach(conn);
+  }
+
+  if((result == CURLE_OK) && (ftp->transfer != FTPTRANSFER_BODY))
+    /* no data to transfer. FIX: it feels like a kludge to have this here
+       too! */
+    Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+
+  if(!ftpc->wait_data_conn) {
+    /* no waiting for the data connection so this is now complete */
+    *complete = TRUE;
+    DEBUGF(infof(data, "DO-MORE phase ends with %d\n", (int)result));
+  }
+
+  return result;
+}
+
+
+
+/***********************************************************************
+ *
+ * ftp_perform()
+ *
+ * This is the actual DO function for FTP. Get a file/directory according to
+ * the options previously setup.
+ */
+
+static
+CURLcode ftp_perform(struct connectdata *conn,
+                     bool *connected,  /* connect status after PASV / PORT */
+                     bool *dophase_done)
+{
+  /* this is FTP and no proxy */
+  CURLcode result=CURLE_OK;
+
+  DEBUGF(infof(conn->data, "DO phase starts\n"));
+
+  if(conn->data->set.opt_no_body) {
+    /* requested no body means no transfer... */
+    struct FTP *ftp = conn->data->state.proto.ftp;
+    ftp->transfer = FTPTRANSFER_INFO;
+  }
+
+
+  *dophase_done = FALSE; /* not done yet */
+
+  /* start the first command in the DO phase */
+  result = ftp_state_quote(conn, TRUE, FTP_QUOTE);
+  if(result)
+    return result;
+
+  /* run the state-machine */
+  if(conn->data->state.used_interface == Curl_if_multi)
+    result = ftp_multi_statemach(conn, dophase_done);
+  else {
+    result = ftp_easy_statemach(conn);
+    *dophase_done = TRUE; /* with the easy interface we are done here */
+  }
+  *connected = conn->bits.tcpconnect[FIRSTSOCKET];
+
+  if(*dophase_done)
+    DEBUGF(infof(conn->data, "DO phase is complete\n"));
+
+  return result;
+}
+
+static void wc_data_dtor(void *ptr)
+{
+  struct ftp_wc_tmpdata *tmp = ptr;
+  if(tmp)
+    Curl_ftp_parselist_data_free(&tmp->parser);
+  Curl_safefree(tmp);
+}
+
+static CURLcode init_wc_data(struct connectdata *conn)
+{
+  char *last_slash;
+  char *path = conn->data->state.path;
+  struct WildcardData *wildcard = &(conn->data->wildcard);
+  CURLcode ret = CURLE_OK;
+  struct ftp_wc_tmpdata *ftp_tmp;
+
+  last_slash = strrchr(conn->data->state.path, '/');
+  if(last_slash) {
+    last_slash++;
+    if(last_slash[0] == '\0') {
+      wildcard->state = CURLWC_CLEAN;
+      ret = ftp_parse_url_path(conn);
+      return ret;
+    }
+    else {
+      wildcard->pattern = strdup(last_slash);
+      if(!wildcard->pattern)
+        return CURLE_OUT_OF_MEMORY;
+      last_slash[0] = '\0'; /* cut file from path */
+    }
+  }
+  else { /* there is only 'wildcard pattern' or nothing */
+    if(path[0]) {
+      wildcard->pattern = strdup(path);
+      if(!wildcard->pattern)
+        return CURLE_OUT_OF_MEMORY;
+      path[0] = '\0';
+    }
+    else { /* only list */
+      wildcard->state = CURLWC_CLEAN;
+      ret = ftp_parse_url_path(conn);
+      return ret;
+    }
+  }
+
+  /* program continues only if URL is not ending with slash, allocate needed
+     resources for wildcard transfer */
+
+  /* allocate ftp protocol specific temporary wildcard data */
+  ftp_tmp = calloc(1, sizeof(struct ftp_wc_tmpdata));
+  if(!ftp_tmp) {
+    Curl_safefree(wildcard->pattern);
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  /* INITIALIZE parselist structure */
+  ftp_tmp->parser = Curl_ftp_parselist_data_alloc();
+  if(!ftp_tmp->parser) {
+    Curl_safefree(wildcard->pattern);
+    Curl_safefree(ftp_tmp);
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  wildcard->tmp = ftp_tmp; /* put it to the WildcardData tmp pointer */
+  wildcard->tmp_dtor = wc_data_dtor;
+
+  /* wildcard does not support NOCWD option (assert it?) */
+  if(conn->data->set.ftp_filemethod == FTPFILE_NOCWD)
+    conn->data->set.ftp_filemethod = FTPFILE_MULTICWD;
+
+  /* try to parse ftp url */
+  ret = ftp_parse_url_path(conn);
+  if(ret) {
+    Curl_safefree(wildcard->pattern);
+    wildcard->tmp_dtor(wildcard->tmp);
+    wildcard->tmp_dtor = ZERO_NULL;
+    wildcard->tmp = NULL;
+    return ret;
+  }
+
+  wildcard->path = strdup(conn->data->state.path);
+  if(!wildcard->path) {
+    Curl_safefree(wildcard->pattern);
+    wildcard->tmp_dtor(wildcard->tmp);
+    wildcard->tmp_dtor = ZERO_NULL;
+    wildcard->tmp = NULL;
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  /* backup old write_function */
+  ftp_tmp->backup.write_function = conn->data->set.fwrite_func;
+  /* parsing write function */
+  conn->data->set.fwrite_func = Curl_ftp_parselist;
+  /* backup old file descriptor */
+  ftp_tmp->backup.file_descriptor = conn->data->set.out;
+  /* let the writefunc callback know what curl pointer is working with */
+  conn->data->set.out = conn;
+
+  infof(conn->data, "Wildcard - Parsing started\n");
+  return CURLE_OK;
+}
+
+/* This is called recursively */
+static CURLcode wc_statemach(struct connectdata *conn)
+{
+  struct WildcardData * const wildcard = &(conn->data->wildcard);
+  CURLcode ret = CURLE_OK;
+
+  switch (wildcard->state) {
+  case CURLWC_INIT:
+    ret = init_wc_data(conn);
+    if(wildcard->state == CURLWC_CLEAN)
+      /* only listing! */
+      break;
+    else
+      wildcard->state = ret ? CURLWC_ERROR : CURLWC_MATCHING;
+    break;
+
+  case CURLWC_MATCHING: {
+    /* In this state is LIST response successfully parsed, so lets restore
+       previous WRITEFUNCTION callback and WRITEDATA pointer */
+    struct ftp_wc_tmpdata *ftp_tmp = wildcard->tmp;
+    conn->data->set.fwrite_func = ftp_tmp->backup.write_function;
+    conn->data->set.out = ftp_tmp->backup.file_descriptor;
+    ftp_tmp->backup.write_function = ZERO_NULL;
+    ftp_tmp->backup.file_descriptor = NULL;
+    wildcard->state = CURLWC_DOWNLOADING;
+
+    if(Curl_ftp_parselist_geterror(ftp_tmp->parser)) {
+      /* error found in LIST parsing */
+      wildcard->state = CURLWC_CLEAN;
+      return wc_statemach(conn);
+    }
+    else if(wildcard->filelist->size == 0) {
+      /* no corresponding file */
+      wildcard->state = CURLWC_CLEAN;
+      return CURLE_REMOTE_FILE_NOT_FOUND;
+    }
+    return wc_statemach(conn);
+  }
+
+  case CURLWC_DOWNLOADING: {
+    /* filelist has at least one file, lets get first one */
+    struct ftp_conn *ftpc = &conn->proto.ftpc;
+    struct curl_fileinfo *finfo = wildcard->filelist->head->ptr;
+    char *tmp_path = malloc(strlen(conn->data->state.path) +
+                      strlen(finfo->filename) + 1);
+    if(!tmp_path) {
+      return CURLE_OUT_OF_MEMORY;
+    }
+
+    tmp_path[0] = 0;
+    /* make full path to matched file */
+    strcat(tmp_path, wildcard->path);
+    strcat(tmp_path, finfo->filename);
+    /* switch default "state.pathbuffer" and tmp_path, good to see
+       ftp_parse_url_path function to understand this trick */
+    Curl_safefree(conn->data->state.pathbuffer);
+    conn->data->state.pathbuffer = tmp_path;
+    conn->data->state.path = tmp_path;
+
+    infof(conn->data, "Wildcard - START of \"%s\"\n", finfo->filename);
+    if(conn->data->set.chunk_bgn) {
+      long userresponse = conn->data->set.chunk_bgn(
+          finfo, wildcard->customptr, (int)wildcard->filelist->size);
+      switch(userresponse) {
+      case CURL_CHUNK_BGN_FUNC_SKIP:
+        infof(conn->data, "Wildcard - \"%s\" skipped by user\n",
+              finfo->filename);
+        wildcard->state = CURLWC_SKIP;
+        return wc_statemach(conn);
+      case CURL_CHUNK_BGN_FUNC_FAIL:
+        return CURLE_CHUNK_FAILED;
+      }
+    }
+
+    if(finfo->filetype != CURLFILETYPE_FILE) {
+      wildcard->state = CURLWC_SKIP;
+      return wc_statemach(conn);
+    }
+
+    if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
+      ftpc->known_filesize = finfo->size;
+
+    ret = ftp_parse_url_path(conn);
+    if(ret) {
+      return ret;
+    }
+
+    /* we don't need the Curl_fileinfo of first file anymore */
+    Curl_llist_remove(wildcard->filelist, wildcard->filelist->head, NULL);
+
+    if(wildcard->filelist->size == 0) { /* remains only one file to down. */
+      wildcard->state = CURLWC_CLEAN;
+      /* after that will be ftp_do called once again and no transfer
+         will be done because of CURLWC_CLEAN state */
+      return CURLE_OK;
+    }
+  } break;
+
+  case CURLWC_SKIP: {
+    if(conn->data->set.chunk_end)
+      conn->data->set.chunk_end(conn->data->wildcard.customptr);
+    Curl_llist_remove(wildcard->filelist, wildcard->filelist->head, NULL);
+    wildcard->state = (wildcard->filelist->size == 0) ?
+                      CURLWC_CLEAN : CURLWC_DOWNLOADING;
+    return wc_statemach(conn);
+  }
+
+  case CURLWC_CLEAN: {
+    struct ftp_wc_tmpdata *ftp_tmp = wildcard->tmp;
+    ret = CURLE_OK;
+    if(ftp_tmp) {
+      ret = Curl_ftp_parselist_geterror(ftp_tmp->parser);
+    }
+    wildcard->state = ret ? CURLWC_ERROR : CURLWC_DONE;
+  } break;
+
+  case CURLWC_DONE:
+  case CURLWC_ERROR:
+    break;
+  }
+
+  return ret;
+}
+
+/***********************************************************************
+ *
+ * ftp_do()
+ *
+ * This function is registered as 'curl_do' function. It decodes the path
+ * parts etc as a wrapper to the actual DO function (ftp_perform).
+ *
+ * The input argument is already checked for validity.
+ */
+static CURLcode ftp_do(struct connectdata *conn, bool *done)
+{
+  CURLcode retcode = CURLE_OK;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+  *done = FALSE; /* default to false */
+  ftpc->wait_data_conn = FALSE; /* default to no such wait */
+
+  /*
+    Since connections can be re-used between SessionHandles, this might be a
+    connection already existing but on a fresh SessionHandle struct so we must
+    make sure we have a good 'struct FTP' to play with. For new connections,
+    the struct FTP is allocated and setup in the ftp_connect() function.
+  */
+  Curl_reset_reqproto(conn);
+  retcode = ftp_init(conn);
+  if(retcode)
+    return retcode;
+
+  if(conn->data->set.wildcardmatch) {
+    retcode = wc_statemach(conn);
+    if(conn->data->wildcard.state == CURLWC_SKIP ||
+      conn->data->wildcard.state == CURLWC_DONE) {
+      /* do not call ftp_regular_transfer */
+      return CURLE_OK;
+    }
+    if(retcode) /* error, loop or skipping the file */
+      return retcode;
+  }
+  else { /* no wildcard FSM needed */
+    retcode = ftp_parse_url_path(conn);
+    if(retcode)
+      return retcode;
+  }
+
+  retcode = ftp_regular_transfer(conn, done);
+
+  return retcode;
+}
+
+
+CURLcode Curl_ftpsendf(struct connectdata *conn,
+                       const char *fmt, ...)
+{
+  ssize_t bytes_written;
+#define SBUF_SIZE 1024
+  char s[SBUF_SIZE];
+  size_t write_len;
+  char *sptr=s;
+  CURLcode res = CURLE_OK;
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+  enum protection_level data_sec = conn->data_prot;
+#endif
+
+  va_list ap;
+  va_start(ap, fmt);
+  vsnprintf(s, SBUF_SIZE-3, fmt, ap);
+  va_end(ap);
+
+  strcat(s, "\r\n"); /* append a trailing CRLF */
+
+  bytes_written=0;
+  write_len = strlen(s);
+
+  res = Curl_convert_to_network(conn->data, s, write_len);
+  /* Curl_convert_to_network calls failf if unsuccessful */
+  if(res)
+    return(res);
+
+  for(;;) {
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+    conn->data_prot = PROT_CMD;
+#endif
+    res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
+                     &bytes_written);
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+    DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
+    conn->data_prot = data_sec;
+#endif
+
+    if(CURLE_OK != res)
+      break;
+
+    if(conn->data->set.verbose)
+      Curl_debug(conn->data, CURLINFO_HEADER_OUT,
+                 sptr, (size_t)bytes_written, conn);
+
+    if(bytes_written != (ssize_t)write_len) {
+      write_len -= bytes_written;
+      sptr += bytes_written;
+    }
+    else
+      break;
+  }
+
+  return res;
+}
+
+/***********************************************************************
+ *
+ * ftp_quit()
+ *
+ * This should be called before calling sclose() on an ftp control connection
+ * (not data connections). We should then wait for the response from the
+ * server before returning. The calling code should then try to close the
+ * connection.
+ *
+ */
+static CURLcode ftp_quit(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+
+  if(conn->proto.ftpc.ctl_valid) {
+    result = Curl_pp_sendf(&conn->proto.ftpc.pp, "QUIT", NULL);
+    if(result) {
+      failf(conn->data, "Failure sending QUIT command: %s",
+            curl_easy_strerror(result));
+      conn->proto.ftpc.ctl_valid = FALSE; /* mark control connection as bad */
+      conn->bits.close = TRUE; /* mark for connection closure */
+      state(conn, FTP_STOP);
+      return result;
+    }
+
+    state(conn, FTP_QUIT);
+
+    result = ftp_easy_statemach(conn);
+  }
+
+  return result;
+}
+
+/***********************************************************************
+ *
+ * ftp_disconnect()
+ *
+ * Disconnect from an FTP server. Cleanup protocol-specific per-connection
+ * resources. BLOCKING.
+ */
+static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection)
+{
+  struct ftp_conn *ftpc= &conn->proto.ftpc;
+  struct pingpong *pp = &ftpc->pp;
+
+  /* We cannot send quit unconditionally. If this connection is stale or
+     bad in any way, sending quit and waiting around here will make the
+     disconnect wait in vain and cause more problems than we need to.
+
+     ftp_quit() will check the state of ftp->ctl_valid. If it's ok it
+     will try to send the QUIT command, otherwise it will just return.
+  */
+  if(dead_connection)
+    ftpc->ctl_valid = FALSE;
+
+  /* The FTP session may or may not have been allocated/setup at this point! */
+  (void)ftp_quit(conn); /* ignore errors on the QUIT */
+
+  if(ftpc->entrypath) {
+    struct SessionHandle *data = conn->data;
+    if(data->state.most_recent_ftp_entrypath == ftpc->entrypath) {
+      data->state.most_recent_ftp_entrypath = NULL;
+    }
+    free(ftpc->entrypath);
+    ftpc->entrypath = NULL;
+  }
+
+  freedirs(ftpc);
+  if(ftpc->prevpath) {
+    free(ftpc->prevpath);
+    ftpc->prevpath = NULL;
+  }
+  if(ftpc->server_os) {
+    free(ftpc->server_os);
+    ftpc->server_os = NULL;
+  }
+
+  Curl_pp_disconnect(pp);
+
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+  Curl_sec_end(conn);
+#endif
+
+  return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * ftp_parse_url_path()
+ *
+ * Parse the URL path into separate path components.
+ *
+ */
+static
+CURLcode ftp_parse_url_path(struct connectdata *conn)
+{
+  struct SessionHandle *data = conn->data;
+  /* the ftp struct is already inited in ftp_connect() */
+  struct FTP *ftp = data->state.proto.ftp;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+  const char *slash_pos;  /* position of the first '/' char in curpos */
+  const char *path_to_use = data->state.path;
+  const char *cur_pos;
+  const char *filename = NULL;
+
+  cur_pos = path_to_use; /* current position in path. point at the begin
+                            of next path component */
+
+  ftpc->ctl_valid = FALSE;
+  ftpc->cwdfail = FALSE;
+
+  switch(data->set.ftp_filemethod) {
+  case FTPFILE_NOCWD:
+    /* fastest, but less standard-compliant */
+
+    /*
+      The best time to check whether the path is a file or directory is right
+      here. so:
+
+      the first condition in the if() right here, is there just in case
+      someone decides to set path to NULL one day
+   */
+    if(data->state.path &&
+       data->state.path[0] &&
+       (data->state.path[strlen(data->state.path) - 1] != '/') )
+      filename = data->state.path;  /* this is a full file path */
+      /*
+        ftpc->file is not used anywhere other than for operations on a file.
+        In other words, never for directory operations.
+        So we can safely leave filename as NULL here and use it as a
+        argument in dir/file decisions.
+      */
+    break;
+
+  case FTPFILE_SINGLECWD:
+    /* get the last slash */
+    if(!path_to_use[0]) {
+      /* no dir, no file */
+      ftpc->dirdepth = 0;
+      break;
+    }
+    slash_pos=strrchr(cur_pos, '/');
+    if(slash_pos || !*cur_pos) {
+      ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0]));
+      if(!ftpc->dirs)
+        return CURLE_OUT_OF_MEMORY;
+
+      ftpc->dirs[0] = curl_easy_unescape(conn->data, slash_pos ? cur_pos : "/",
+                                         slash_pos ?
+                                         curlx_sztosi(slash_pos-cur_pos) : 1,
+                                         NULL);
+      if(!ftpc->dirs[0]) {
+        freedirs(ftpc);
+        return CURLE_OUT_OF_MEMORY;
+      }
+      ftpc->dirdepth = 1; /* we consider it to be a single dir */
+      filename = slash_pos ? slash_pos+1 : cur_pos; /* rest is file name */
+    }
+    else
+      filename = cur_pos;  /* this is a file name only */
+    break;
+
+  default: /* allow pretty much anything */
+  case FTPFILE_MULTICWD:
+    ftpc->dirdepth = 0;
+    ftpc->diralloc = 5; /* default dir depth to allocate */
+    ftpc->dirs = calloc(ftpc->diralloc, sizeof(ftpc->dirs[0]));
+    if(!ftpc->dirs)
+      return CURLE_OUT_OF_MEMORY;
+
+    /* we have a special case for listing the root dir only */
+    if(strequal(path_to_use, "/")) {
+      cur_pos++; /* make it point to the zero byte */
+      ftpc->dirs[0] = strdup("/");
+      ftpc->dirdepth++;
+    }
+    else {
+      /* parse the URL path into separate path components */
+      while((slash_pos = strchr(cur_pos, '/')) != NULL) {
+        /* 1 or 0 pointer offset to indicate absolute directory */
+        ssize_t absolute_dir = ((cur_pos - data->state.path > 0) &&
+                                (ftpc->dirdepth == 0))?1:0;
+
+        /* seek out the next path component */
+        if(slash_pos-cur_pos) {
+          /* we skip empty path components, like "x//y" since the FTP command
+             CWD requires a parameter and a non-existent parameter a) doesn't
+             work on many servers and b) has no effect on the others. */
+          int len = curlx_sztosi(slash_pos - cur_pos + absolute_dir);
+          ftpc->dirs[ftpc->dirdepth] =
+            curl_easy_unescape(conn->data, cur_pos - absolute_dir, len, NULL);
+          if(!ftpc->dirs[ftpc->dirdepth]) { /* run out of memory ... */
+            failf(data, "no memory");
+            freedirs(ftpc);
+            return CURLE_OUT_OF_MEMORY;
+          }
+          if(isBadFtpString(ftpc->dirs[ftpc->dirdepth])) {
+            free(ftpc->dirs[ftpc->dirdepth]);
+            freedirs(ftpc);
+            return CURLE_URL_MALFORMAT;
+          }
+        }
+        else {
+          cur_pos = slash_pos + 1; /* jump to the rest of the string */
+          continue;
+        }
+
+        cur_pos = slash_pos + 1; /* jump to the rest of the string */
+        if(++ftpc->dirdepth >= ftpc->diralloc) {
+          /* enlarge array */
+          char **bigger;
+          ftpc->diralloc *= 2; /* double the size each time */
+          bigger = realloc(ftpc->dirs, ftpc->diralloc * sizeof(ftpc->dirs[0]));
+          if(!bigger) {
+            freedirs(ftpc);
+            return CURLE_OUT_OF_MEMORY;
+          }
+          ftpc->dirs = bigger;
+        }
+      }
+    }
+    filename = cur_pos;  /* the rest is the file name */
+    break;
+  } /* switch */
+
+  if(filename && *filename) {
+    ftpc->file = curl_easy_unescape(conn->data, filename, 0, NULL);
+    if(NULL == ftpc->file) {
+      freedirs(ftpc);
+      failf(data, "no memory");
+      return CURLE_OUT_OF_MEMORY;
+    }
+    if(isBadFtpString(ftpc->file)) {
+      freedirs(ftpc);
+      return CURLE_URL_MALFORMAT;
+    }
+  }
+  else
+    ftpc->file=NULL; /* instead of point to a zero byte, we make it a NULL
+                       pointer */
+
+  if(data->set.upload && !ftpc->file && (ftp->transfer == FTPTRANSFER_BODY)) {
+    /* We need a file name when uploading. Return error! */
+    failf(data, "Uploading to a URL without a file name!");
+    return CURLE_URL_MALFORMAT;
+  }
+
+  ftpc->cwddone = FALSE; /* default to not done */
+
+  if(ftpc->prevpath) {
+    /* prevpath is "raw" so we convert the input path before we compare the
+       strings */
+    int dlen;
+    char *path = curl_easy_unescape(conn->data, data->state.path, 0, &dlen);
+    if(!path) {
+      freedirs(ftpc);
+      return CURLE_OUT_OF_MEMORY;
+    }
+
+    dlen -= ftpc->file?curlx_uztosi(strlen(ftpc->file)):0;
+    if((dlen == curlx_uztosi(strlen(ftpc->prevpath))) &&
+       strnequal(path, ftpc->prevpath, dlen)) {
+      infof(data, "Request has same path as previous transfer\n");
+      ftpc->cwddone = TRUE;
+    }
+    free(path);
+  }
+
+  return CURLE_OK;
+}
+
+/* call this when the DO phase has completed */
+static CURLcode ftp_dophase_done(struct connectdata *conn,
+                                 bool connected)
+{
+  struct FTP *ftp = conn->data->state.proto.ftp;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+  if(connected) {
+    bool completed;
+    CURLcode result = ftp_do_more(conn, &completed);
+
+    if(result) {
+      if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
+        /* close the second socket if it was created already */
+        Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
+        conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
+      }
+      return result;
+    }
+  }
+
+  if(ftp->transfer != FTPTRANSFER_BODY)
+    /* no data to transfer */
+    Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+  else if(!connected)
+    /* since we didn't connect now, we want do_more to get called */
+    conn->bits.do_more = TRUE;
+
+  ftpc->ctl_valid = TRUE; /* seems good */
+
+  return CURLE_OK;
+}
+
+/* called from curl_multi.c while DOing */
+static CURLcode ftp_doing(struct connectdata *conn,
+                          bool *dophase_done)
+{
+  CURLcode result = ftp_multi_statemach(conn, dophase_done);
+
+  if(result)
+    DEBUGF(infof(conn->data, "DO phase failed\n"));
+  else if(*dophase_done) {
+    result = ftp_dophase_done(conn, FALSE /* not connected */);
+
+    DEBUGF(infof(conn->data, "DO phase is complete\n"));
+  }
+  return result;
+}
+
+/***********************************************************************
+ *
+ * ftp_regular_transfer()
+ *
+ * The input argument is already checked for validity.
+ *
+ * Performs all commands done before a regular transfer between a local and a
+ * remote host.
+ *
+ * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
+ * ftp_done() function without finding any major problem.
+ */
+static
+CURLcode ftp_regular_transfer(struct connectdata *conn,
+                              bool *dophase_done)
+{
+  CURLcode result=CURLE_OK;
+  bool connected=FALSE;
+  struct SessionHandle *data = conn->data;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+  data->req.size = -1; /* make sure this is unknown at this point */
+
+  Curl_pgrsSetUploadCounter(data, 0);
+  Curl_pgrsSetDownloadCounter(data, 0);
+  Curl_pgrsSetUploadSize(data, 0);
+  Curl_pgrsSetDownloadSize(data, 0);
+
+  ftpc->ctl_valid = TRUE; /* starts good */
+
+  result = ftp_perform(conn,
+                       &connected, /* have we connected after PASV/PORT */
+                       dophase_done); /* all commands in the DO-phase done? */
+
+  if(CURLE_OK == result) {
+
+    if(!*dophase_done)
+      /* the DO phase has not completed yet */
+      return CURLE_OK;
+
+    result = ftp_dophase_done(conn, connected);
+    if(result)
+      return result;
+  }
+  else
+    freedirs(ftpc);
+
+  return result;
+}
+
+static CURLcode ftp_setup_connection(struct connectdata * conn)
+{
+  struct SessionHandle *data = conn->data;
+  char * type;
+  char command;
+
+  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 */
+#ifndef CURL_DISABLE_HTTP
+    if(conn->handler == &Curl_handler_ftp)
+      conn->handler = &Curl_handler_ftp_proxy;
+    else {
+#ifdef USE_SSL
+      conn->handler = &Curl_handler_ftps_proxy;
+#else
+      failf(data, "FTPS not supported!");
+      return CURLE_UNSUPPORTED_PROTOCOL;
+#endif
+    }
+    /*
+     * We explicitly mark this connection as persistent here as we're doing
+     * FTP over HTTP and thus we accidentally avoid setting this value
+     * otherwise.
+     */
+    conn->bits.close = FALSE;
+#else
+    failf(data, "FTP over http proxy requires HTTP support built-in!");
+    return CURLE_UNSUPPORTED_PROTOCOL;
+#endif
+  }
+
+  data->state.path++;   /* don't include the initial slash */
+  data->state.slash_removed = TRUE; /* we've skipped the slash */
+
+  /* FTP URLs support an extension like ";type=<typecode>" that
+   * we'll try to get now! */
+  type = strstr(data->state.path, ";type=");
+
+  if(!type)
+    type = strstr(conn->host.rawalloc, ";type=");
+
+  if(type) {
+    *type = 0;                     /* it was in the middle of the hostname */
+    command = Curl_raw_toupper(type[6]);
+    conn->bits.type_set = TRUE;
+
+    switch (command) {
+    case 'A': /* ASCII mode */
+      data->set.prefer_ascii = TRUE;
+      break;
+
+    case 'D': /* directory mode */
+      data->set.ftp_list_only = TRUE;
+      break;
+
+    case 'I': /* binary mode */
+    default:
+      /* switch off ASCII */
+      data->set.prefer_ascii = FALSE;
+      break;
+    }
+  }
+
+  return CURLE_OK;
+}
+
+#endif /* CURL_DISABLE_FTP */
diff --git a/lib/curl_ftplistparser.c b/lib/curl_ftplistparser.c
new file mode 100644 (file)
index 0000000..a1a7d51
--- /dev/null
@@ -0,0 +1,1050 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/**
+ * Now implemented:
+ *
+ * 1) UNIX version 1
+ * drwxr-xr-x 1 user01 ftp  512 Jan 29 23:32 prog
+ * 2) UNIX version 2
+ * drwxr-xr-x 1 user01 ftp  512 Jan 29 1997  prog
+ * 3) UNIX version 3
+ * drwxr-xr-x 1      1   1  512 Jan 29 23:32 prog
+ * 4) UNIX symlink
+ * lrwxr-xr-x 1 user01 ftp  512 Jan 29 23:32 prog -> prog2000
+ * 5) DOS style
+ * 01-29-97 11:32PM <DIR> prog
+ */
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_FTP
+
+#include <curl/curl.h>
+
+#include "curl_urldata.h"
+#include "curl_fileinfo.h"
+#include "curl_llist.h"
+#include "curl_strtoofft.h"
+#include "curl_rawstr.h"
+#include "curl_ftp.h"
+#include "curl_ftplistparser.h"
+#include "curl_fnmatch.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/* allocs buffer which will contain one line of LIST command response */
+#define FTP_BUFFER_ALLOCSIZE 160
+
+typedef enum {
+  PL_UNIX_TOTALSIZE = 0,
+  PL_UNIX_FILETYPE,
+  PL_UNIX_PERMISSION,
+  PL_UNIX_HLINKS,
+  PL_UNIX_USER,
+  PL_UNIX_GROUP,
+  PL_UNIX_SIZE,
+  PL_UNIX_TIME,
+  PL_UNIX_FILENAME,
+  PL_UNIX_SYMLINK
+} pl_unix_mainstate;
+
+typedef union {
+  enum {
+    PL_UNIX_TOTALSIZE_INIT = 0,
+    PL_UNIX_TOTALSIZE_READING
+  } total_dirsize;
+
+  enum {
+    PL_UNIX_HLINKS_PRESPACE = 0,
+    PL_UNIX_HLINKS_NUMBER
+  } hlinks;
+
+  enum {
+    PL_UNIX_USER_PRESPACE = 0,
+    PL_UNIX_USER_PARSING
+  } user;
+
+  enum {
+    PL_UNIX_GROUP_PRESPACE = 0,
+    PL_UNIX_GROUP_NAME
+  } group;
+
+  enum {
+    PL_UNIX_SIZE_PRESPACE = 0,
+    PL_UNIX_SIZE_NUMBER
+  } size;
+
+  enum {
+    PL_UNIX_TIME_PREPART1 = 0,
+    PL_UNIX_TIME_PART1,
+    PL_UNIX_TIME_PREPART2,
+    PL_UNIX_TIME_PART2,
+    PL_UNIX_TIME_PREPART3,
+    PL_UNIX_TIME_PART3
+  } time;
+
+  enum {
+    PL_UNIX_FILENAME_PRESPACE = 0,
+    PL_UNIX_FILENAME_NAME,
+    PL_UNIX_FILENAME_WINDOWSEOL
+  } filename;
+
+  enum {
+    PL_UNIX_SYMLINK_PRESPACE = 0,
+    PL_UNIX_SYMLINK_NAME,
+    PL_UNIX_SYMLINK_PRETARGET1,
+    PL_UNIX_SYMLINK_PRETARGET2,
+    PL_UNIX_SYMLINK_PRETARGET3,
+    PL_UNIX_SYMLINK_PRETARGET4,
+    PL_UNIX_SYMLINK_TARGET,
+    PL_UNIX_SYMLINK_WINDOWSEOL
+  } symlink;
+} pl_unix_substate;
+
+typedef enum {
+  PL_WINNT_DATE = 0,
+  PL_WINNT_TIME,
+  PL_WINNT_DIRORSIZE,
+  PL_WINNT_FILENAME
+} pl_winNT_mainstate;
+
+typedef union {
+  enum {
+    PL_WINNT_TIME_PRESPACE = 0,
+    PL_WINNT_TIME_TIME
+  } time;
+  enum {
+    PL_WINNT_DIRORSIZE_PRESPACE = 0,
+    PL_WINNT_DIRORSIZE_CONTENT
+  } dirorsize;
+  enum {
+    PL_WINNT_FILENAME_PRESPACE = 0,
+    PL_WINNT_FILENAME_CONTENT,
+    PL_WINNT_FILENAME_WINEOL
+  } filename;
+} pl_winNT_substate;
+
+/* This struct is used in wildcard downloading - for parsing LIST response */
+struct ftp_parselist_data {
+  enum {
+    OS_TYPE_UNKNOWN = 0,
+    OS_TYPE_UNIX,
+    OS_TYPE_WIN_NT
+  } os_type;
+
+  union {
+    struct {
+      pl_unix_mainstate main;
+      pl_unix_substate sub;
+    } UNIX;
+
+    struct {
+      pl_winNT_mainstate main;
+      pl_winNT_substate sub;
+    } NT;
+  } state;
+
+  CURLcode error;
+  struct curl_fileinfo *file_data;
+  unsigned int item_length;
+  size_t item_offset;
+  struct {
+    size_t filename;
+    size_t user;
+    size_t group;
+    size_t time;
+    size_t perm;
+    size_t symlink_target;
+  } offsets;
+};
+
+struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void)
+{
+  return calloc(1, sizeof(struct ftp_parselist_data));
+}
+
+
+void Curl_ftp_parselist_data_free(struct ftp_parselist_data **pl_data)
+{
+  if(*pl_data)
+    free(*pl_data);
+  *pl_data = NULL;
+}
+
+
+CURLcode Curl_ftp_parselist_geterror(struct ftp_parselist_data *pl_data)
+{
+  return pl_data->error;
+}
+
+
+#define FTP_LP_MALFORMATED_PERM 0x01000000
+
+static int ftp_pl_get_permission(const char *str)
+{
+  int permissions = 0;
+  /* USER */
+  if(str[0] == 'r')
+    permissions |= 1 << 8;
+  else if(str[0] != '-')
+    permissions |= FTP_LP_MALFORMATED_PERM;
+  if(str[1] == 'w')
+    permissions |= 1 << 7;
+  else if(str[1] != '-')
+    permissions |= FTP_LP_MALFORMATED_PERM;
+
+  if(str[2] == 'x')
+    permissions |= 1 << 6;
+  else if(str[2] == 's') {
+    permissions |= 1 << 6;
+    permissions |= 1 << 11;
+  }
+  else if(str[2] == 'S')
+    permissions |= 1 << 11;
+  else if(str[2] != '-')
+    permissions |= FTP_LP_MALFORMATED_PERM;
+  /* GROUP */
+  if(str[3] == 'r')
+    permissions |= 1 << 5;
+  else if(str[3] != '-')
+    permissions |= FTP_LP_MALFORMATED_PERM;
+  if(str[4] == 'w')
+    permissions |= 1 << 4;
+  else if(str[4] != '-')
+    permissions |= FTP_LP_MALFORMATED_PERM;
+  if(str[5] == 'x')
+    permissions |= 1 << 3;
+  else if(str[5] == 's') {
+    permissions |= 1 << 3;
+    permissions |= 1 << 10;
+  }
+  else if(str[5] == 'S')
+    permissions |= 1 << 10;
+  else if(str[5] != '-')
+    permissions |= FTP_LP_MALFORMATED_PERM;
+  /* others */
+  if(str[6] == 'r')
+    permissions |= 1 << 2;
+  else if(str[6] != '-')
+    permissions |= FTP_LP_MALFORMATED_PERM;
+  if(str[7] == 'w')
+    permissions |= 1 << 1;
+  else if(str[7] != '-')
+      permissions |= FTP_LP_MALFORMATED_PERM;
+  if(str[8] == 'x')
+    permissions |= 1;
+  else if(str[8] == 't') {
+    permissions |= 1;
+    permissions |= 1 << 9;
+  }
+  else if(str[8] == 'T')
+    permissions |= 1 << 9;
+  else if(str[8] != '-')
+    permissions |= FTP_LP_MALFORMATED_PERM;
+
+  return permissions;
+}
+
+static void PL_ERROR(struct connectdata *conn, CURLcode err)
+{
+  struct ftp_wc_tmpdata *tmpdata = conn->data->wildcard.tmp;
+  struct ftp_parselist_data *parser = tmpdata->parser;
+  if(parser->file_data)
+    Curl_fileinfo_dtor(NULL, parser->file_data);
+  parser->file_data = NULL;
+  parser->error = err;
+}
+
+static bool ftp_pl_gettime(struct ftp_parselist_data *parser, char *string)
+{
+  (void)parser;
+  (void)string;
+  /* TODO
+   * There could be possible parse timestamp from server. Leaving unimplemented
+   * for now.
+   * If you want implement this, please add CURLFINFOFLAG_KNOWN_TIME flag to
+   * parser->file_data->flags
+   *
+   * Ftp servers are giving usually these formats:
+   *  Apr 11  1998 (unknown time.. set it to 00:00:00?)
+   *  Apr 11 12:21 (unknown year -> set it to NOW() time?)
+   *  08-05-09  02:49PM  (ms-dos format)
+   *  20100421092538 -> for MLST/MLSD response
+   */
+
+  return FALSE;
+}
+
+static CURLcode ftp_pl_insert_finfo(struct connectdata *conn,
+                                    struct curl_fileinfo *finfo)
+{
+  curl_fnmatch_callback compare;
+  struct WildcardData *wc = &conn->data->wildcard;
+  struct ftp_wc_tmpdata *tmpdata = wc->tmp;
+  struct curl_llist *llist = wc->filelist;
+  struct ftp_parselist_data *parser = tmpdata->parser;
+  bool add = TRUE;
+
+  /* move finfo pointers to b_data */
+  char *str = finfo->b_data;
+  finfo->filename       = str + parser->offsets.filename;
+  finfo->strings.group  = parser->offsets.group ?
+                          str + parser->offsets.group : NULL;
+  finfo->strings.perm   = parser->offsets.perm ?
+                          str + parser->offsets.perm : NULL;
+  finfo->strings.target = parser->offsets.symlink_target ?
+                          str + parser->offsets.symlink_target : NULL;
+  finfo->strings.time   = str + parser->offsets.time;
+  finfo->strings.user   = parser->offsets.user ?
+                          str + parser->offsets.user : NULL;
+
+  /* get correct fnmatch callback */
+  compare = conn->data->set.fnmatch;
+  if(!compare)
+    compare = Curl_fnmatch;
+
+  /* filter pattern-corresponding filenames */
+  if(compare(conn->data->set.fnmatch_data, wc->pattern,
+             finfo->filename) == 0) {
+    /* discard symlink which is containing multiple " -> " */
+    if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target &&
+       (strstr(finfo->strings.target, " -> "))) {
+      add = FALSE;
+    }
+  }
+  else {
+    add = FALSE;
+  }
+
+  if(add) {
+    if(!Curl_llist_insert_next(llist, llist->tail, finfo)) {
+      Curl_fileinfo_dtor(NULL, finfo);
+      tmpdata->parser->file_data = NULL;
+      return CURLE_OUT_OF_MEMORY;
+    }
+  }
+  else {
+    Curl_fileinfo_dtor(NULL, finfo);
+  }
+
+  tmpdata->parser->file_data = NULL;
+  return CURLE_OK;
+}
+
+size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
+                          void *connptr)
+{
+  size_t bufflen = size*nmemb;
+  struct connectdata *conn = (struct connectdata *)connptr;
+  struct ftp_wc_tmpdata *tmpdata = conn->data->wildcard.tmp;
+  struct ftp_parselist_data *parser = tmpdata->parser;
+  struct curl_fileinfo *finfo;
+  unsigned long i = 0;
+  CURLcode rc;
+
+  if(parser->error) { /* error in previous call */
+    /* scenario:
+     * 1. call => OK..
+     * 2. call => OUT_OF_MEMORY (or other error)
+     * 3. (last) call => is skipped RIGHT HERE and the error is hadled later
+     *    in wc_statemach()
+     */
+    return bufflen;
+  }
+
+  if(parser->os_type == OS_TYPE_UNKNOWN && bufflen > 0) {
+    /* considering info about FILE response format */
+    parser->os_type = (buffer[0] >= '0' && buffer[0] <= '9') ?
+                       OS_TYPE_WIN_NT : OS_TYPE_UNIX;
+  }
+
+  while(i < bufflen) { /* FSM */
+
+    char c = buffer[i];
+    if(!parser->file_data) { /* tmp file data is not allocated yet */
+      parser->file_data = Curl_fileinfo_alloc();
+      if(!parser->file_data) {
+        parser->error = CURLE_OUT_OF_MEMORY;
+        return bufflen;
+      }
+      parser->file_data->b_data = malloc(FTP_BUFFER_ALLOCSIZE);
+      if(!parser->file_data->b_data) {
+        PL_ERROR(conn, CURLE_OUT_OF_MEMORY);
+        return bufflen;
+      }
+      parser->file_data->b_size = FTP_BUFFER_ALLOCSIZE;
+      parser->item_offset = 0;
+      parser->item_length = 0;
+    }
+
+    finfo = parser->file_data;
+    finfo->b_data[finfo->b_used++] = c;
+
+    if(finfo->b_used >= finfo->b_size - 1) {
+      /* if it is important, extend buffer space for file data */
+      char *tmp = realloc(finfo->b_data,
+                          finfo->b_size + FTP_BUFFER_ALLOCSIZE);
+      if(tmp) {
+        finfo->b_size += FTP_BUFFER_ALLOCSIZE;
+        finfo->b_data = tmp;
+      }
+      else {
+        Curl_fileinfo_dtor(NULL, parser->file_data);
+        parser->file_data = NULL;
+        parser->error = CURLE_OUT_OF_MEMORY;
+        PL_ERROR(conn, CURLE_OUT_OF_MEMORY);
+        return bufflen;
+      }
+    }
+
+    switch (parser->os_type) {
+    case OS_TYPE_UNIX:
+      switch (parser->state.UNIX.main) {
+      case PL_UNIX_TOTALSIZE:
+        switch(parser->state.UNIX.sub.total_dirsize) {
+        case PL_UNIX_TOTALSIZE_INIT:
+          if(c == 't') {
+            parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING;
+            parser->item_length++;
+          }
+          else {
+            parser->state.UNIX.main = PL_UNIX_FILETYPE;
+            /* start FSM again not considering size of directory */
+            finfo->b_used = 0;
+            i--;
+          }
+          break;
+        case PL_UNIX_TOTALSIZE_READING:
+          parser->item_length++;
+          if(c == '\r') {
+            parser->item_length--;
+            finfo->b_used--;
+          }
+          else if(c == '\n') {
+            finfo->b_data[parser->item_length - 1] = 0;
+            if(strncmp("total ", finfo->b_data, 6) == 0) {
+              char *endptr = finfo->b_data+6;
+              /* here we can deal with directory size */
+              while(ISSPACE(*endptr))
+                endptr++;
+              if(*endptr != 0) {
+                PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+                return bufflen;
+              }
+              else {
+                parser->state.UNIX.main = PL_UNIX_FILETYPE;
+                finfo->b_used = 0;
+              }
+            }
+            else {
+              PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+              return bufflen;
+            }
+          }
+          break;
+        }
+        break;
+      case PL_UNIX_FILETYPE:
+        switch (c) {
+        case '-':
+          finfo->filetype = CURLFILETYPE_FILE;
+          break;
+        case 'd':
+          finfo->filetype = CURLFILETYPE_DIRECTORY;
+          break;
+        case 'l':
+          finfo->filetype = CURLFILETYPE_SYMLINK;
+          break;
+        case 'p':
+          finfo->filetype = CURLFILETYPE_NAMEDPIPE;
+          break;
+        case 's':
+          finfo->filetype = CURLFILETYPE_SOCKET;
+          break;
+        case 'c':
+          finfo->filetype = CURLFILETYPE_DEVICE_CHAR;
+          break;
+        case 'b':
+          finfo->filetype = CURLFILETYPE_DEVICE_BLOCK;
+          break;
+        case 'D':
+          finfo->filetype = CURLFILETYPE_DOOR;
+          break;
+        default:
+          PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+          return bufflen;
+        }
+        parser->state.UNIX.main = PL_UNIX_PERMISSION;
+        parser->item_length = 0;
+        parser->item_offset = 1;
+        break;
+      case PL_UNIX_PERMISSION:
+        parser->item_length++;
+        if(parser->item_length <= 9) {
+          if(!strchr("rwx-tTsS", c)) {
+            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+            return bufflen;
+          }
+        }
+        else if(parser->item_length == 10) {
+          unsigned int perm;
+          if(c != ' ') {
+            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+            return bufflen;
+          }
+          finfo->b_data[10] = 0; /* terminate permissions */
+          perm = ftp_pl_get_permission(finfo->b_data + parser->item_offset);
+          if(perm & FTP_LP_MALFORMATED_PERM) {
+            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+            return bufflen;
+          }
+          parser->file_data->flags |= CURLFINFOFLAG_KNOWN_PERM;
+          parser->file_data->perm = perm;
+          parser->offsets.perm = parser->item_offset;
+
+          parser->item_length = 0;
+          parser->state.UNIX.main = PL_UNIX_HLINKS;
+          parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE;
+        }
+        break;
+      case PL_UNIX_HLINKS:
+        switch(parser->state.UNIX.sub.hlinks) {
+        case PL_UNIX_HLINKS_PRESPACE:
+          if(c != ' ') {
+            if(c >= '0' && c <= '9') {
+              parser->item_offset = finfo->b_used - 1;
+              parser->item_length = 1;
+              parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER;
+            }
+            else {
+              PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+              return bufflen;
+            }
+          }
+          break;
+        case PL_UNIX_HLINKS_NUMBER:
+          parser->item_length ++;
+          if(c == ' ') {
+            char *p;
+            long int hlinks;
+            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
+            hlinks = strtol(finfo->b_data + parser->item_offset, &p, 10);
+            if(p[0] == '\0' && hlinks != LONG_MAX && hlinks != LONG_MIN) {
+              parser->file_data->flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT;
+              parser->file_data->hardlinks = hlinks;
+            }
+            parser->item_length = 0;
+            parser->item_offset = 0;
+            parser->state.UNIX.main = PL_UNIX_USER;
+            parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE;
+          }
+          else if(c < '0' || c > '9') {
+            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+            return bufflen;
+          }
+          break;
+        }
+        break;
+      case PL_UNIX_USER:
+        switch(parser->state.UNIX.sub.user) {
+        case PL_UNIX_USER_PRESPACE:
+          if(c != ' ') {
+            parser->item_offset = finfo->b_used - 1;
+            parser->item_length = 1;
+            parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING;
+          }
+          break;
+        case PL_UNIX_USER_PARSING:
+          parser->item_length++;
+          if(c == ' ') {
+            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
+            parser->offsets.user = parser->item_offset;
+            parser->state.UNIX.main = PL_UNIX_GROUP;
+            parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE;
+            parser->item_offset = 0;
+            parser->item_length = 0;
+          }
+          break;
+        }
+        break;
+      case PL_UNIX_GROUP:
+        switch(parser->state.UNIX.sub.group) {
+        case PL_UNIX_GROUP_PRESPACE:
+          if(c != ' ') {
+            parser->item_offset = finfo->b_used - 1;
+            parser->item_length = 1;
+            parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME;
+          }
+          break;
+        case PL_UNIX_GROUP_NAME:
+          parser->item_length++;
+          if(c == ' ') {
+            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
+            parser->offsets.group = parser->item_offset;
+            parser->state.UNIX.main = PL_UNIX_SIZE;
+            parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE;
+            parser->item_offset = 0;
+            parser->item_length = 0;
+          }
+          break;
+        }
+        break;
+      case PL_UNIX_SIZE:
+        switch(parser->state.UNIX.sub.size) {
+        case PL_UNIX_SIZE_PRESPACE:
+          if(c != ' ') {
+            if(c >= '0' && c <= '9') {
+              parser->item_offset = finfo->b_used - 1;
+              parser->item_length = 1;
+              parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER;
+            }
+            else {
+              PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+              return bufflen;
+            }
+          }
+          break;
+        case PL_UNIX_SIZE_NUMBER:
+          parser->item_length++;
+          if(c == ' ') {
+            char *p;
+            curl_off_t fsize;
+            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
+            fsize = curlx_strtoofft(finfo->b_data+parser->item_offset, &p, 10);
+            if(p[0] == '\0' && fsize != CURL_OFF_T_MAX &&
+                               fsize != CURL_OFF_T_MIN) {
+              parser->file_data->flags |= CURLFINFOFLAG_KNOWN_SIZE;
+              parser->file_data->size = fsize;
+            }
+            parser->item_length = 0;
+            parser->item_offset = 0;
+            parser->state.UNIX.main = PL_UNIX_TIME;
+            parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1;
+          }
+          else if(!ISDIGIT(c)) {
+            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+            return bufflen;
+          }
+          break;
+        }
+        break;
+      case PL_UNIX_TIME:
+        switch(parser->state.UNIX.sub.time) {
+        case PL_UNIX_TIME_PREPART1:
+          if(c != ' ') {
+            if(ISALNUM(c)) {
+              parser->item_offset = finfo->b_used -1;
+              parser->item_length = 1;
+              parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1;
+            }
+            else {
+              PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+              return bufflen;
+            }
+          }
+          break;
+        case PL_UNIX_TIME_PART1:
+          parser->item_length++;
+          if(c == ' ') {
+            parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2;
+          }
+          else if(!ISALNUM(c) && c != '.') {
+            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+            return bufflen;
+          }
+          break;
+        case PL_UNIX_TIME_PREPART2:
+          parser->item_length++;
+          if(c != ' ') {
+            if(ISALNUM(c)) {
+              parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2;
+            }
+            else {
+              PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+              return bufflen;
+            }
+          }
+          break;
+        case PL_UNIX_TIME_PART2:
+          parser->item_length++;
+          if(c == ' ') {
+            parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3;
+          }
+          else if(!ISALNUM(c) && c != '.') {
+            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+            return bufflen;
+          }
+          break;
+        case PL_UNIX_TIME_PREPART3:
+          parser->item_length++;
+          if(c != ' ') {
+            if(ISALNUM(c)) {
+              parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3;
+            }
+            else {
+              PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+              return bufflen;
+            }
+          }
+          break;
+        case PL_UNIX_TIME_PART3:
+          parser->item_length++;
+          if(c == ' ') {
+            finfo->b_data[parser->item_offset + parser->item_length -1] = 0;
+            parser->offsets.time = parser->item_offset;
+            if(ftp_pl_gettime(parser, finfo->b_data + parser->item_offset)) {
+              parser->file_data->flags |= CURLFINFOFLAG_KNOWN_TIME;
+            }
+            if(finfo->filetype == CURLFILETYPE_SYMLINK) {
+              parser->state.UNIX.main = PL_UNIX_SYMLINK;
+              parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE;
+            }
+            else {
+              parser->state.UNIX.main = PL_UNIX_FILENAME;
+              parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE;
+            }
+          }
+          else if(!ISALNUM(c) && c != '.' && c != ':') {
+            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+            return bufflen;
+          }
+          break;
+        }
+        break;
+      case PL_UNIX_FILENAME:
+        switch(parser->state.UNIX.sub.filename) {
+        case PL_UNIX_FILENAME_PRESPACE:
+          if(c != ' ') {
+            parser->item_offset = finfo->b_used - 1;
+            parser->item_length = 1;
+            parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME;
+          }
+          break;
+        case PL_UNIX_FILENAME_NAME:
+          parser->item_length++;
+          if(c == '\r') {
+            parser->item_length--;
+            parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL;
+          }
+          else if(c == '\n') {
+            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
+            parser->offsets.filename = parser->item_offset;
+            parser->state.UNIX.main = PL_UNIX_FILETYPE;
+            rc = ftp_pl_insert_finfo(conn, finfo);
+            if(rc) {
+              PL_ERROR(conn, rc);
+              return bufflen;
+            }
+          }
+          break;
+        case PL_UNIX_FILENAME_WINDOWSEOL:
+          if(c == '\n') {
+            finfo->b_data[parser->item_offset + parser->item_length] = 0;
+            parser->offsets.filename = parser->item_offset;
+            parser->state.UNIX.main = PL_UNIX_FILETYPE;
+            rc = ftp_pl_insert_finfo(conn, finfo);
+            if(rc) {
+              PL_ERROR(conn, rc);
+              return bufflen;
+            }
+          }
+          else {
+            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+            return bufflen;
+          }
+          break;
+        }
+        break;
+      case PL_UNIX_SYMLINK:
+        switch(parser->state.UNIX.sub.symlink) {
+        case PL_UNIX_SYMLINK_PRESPACE:
+          if(c != ' ') {
+            parser->item_offset = finfo->b_used - 1;
+            parser->item_length = 1;
+            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
+          }
+          break;
+        case PL_UNIX_SYMLINK_NAME:
+          parser->item_length++;
+          if(c == ' ') {
+            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1;
+          }
+          else if(c == '\r' || c == '\n') {
+            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+            return bufflen;
+          }
+          break;
+        case PL_UNIX_SYMLINK_PRETARGET1:
+          parser->item_length++;
+          if(c == '-') {
+            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2;
+          }
+          else if(c == '\r' || c == '\n') {
+            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+            return bufflen;
+          }
+          else {
+            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
+          }
+          break;
+        case PL_UNIX_SYMLINK_PRETARGET2:
+          parser->item_length++;
+          if(c == '>') {
+            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3;
+          }
+          else if(c == '\r' || c == '\n') {
+            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+            return bufflen;
+          }
+          else {
+            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
+          }
+          break;
+        case PL_UNIX_SYMLINK_PRETARGET3:
+          parser->item_length++;
+          if(c == ' ') {
+            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4;
+            /* now place where is symlink following */
+            finfo->b_data[parser->item_offset + parser->item_length - 4] = 0;
+            parser->offsets.filename = parser->item_offset;
+            parser->item_length = 0;
+            parser->item_offset = 0;
+          }
+          else if(c == '\r' || c == '\n') {
+            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+            return bufflen;
+          }
+          else {
+            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
+          }
+          break;
+        case PL_UNIX_SYMLINK_PRETARGET4:
+          if(c != '\r' && c != '\n') {
+            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET;
+            parser->item_offset = finfo->b_used - 1;
+            parser->item_length = 1;
+          }
+          else {
+            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+            return bufflen;
+          }
+          break;
+        case PL_UNIX_SYMLINK_TARGET:
+          parser->item_length ++;
+          if(c == '\r') {
+            parser->item_length --;
+            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL;
+          }
+          else if(c == '\n') {
+            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
+            parser->offsets.symlink_target = parser->item_offset;
+            rc = ftp_pl_insert_finfo(conn, finfo);
+            if(rc) {
+              PL_ERROR(conn, rc);
+              return bufflen;
+            }
+            parser->state.UNIX.main = PL_UNIX_FILETYPE;
+          }
+          break;
+        case PL_UNIX_SYMLINK_WINDOWSEOL:
+          if(c == '\n') {
+            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
+            parser->offsets.symlink_target = parser->item_offset;
+            rc = ftp_pl_insert_finfo(conn, finfo);
+            if(rc) {
+              PL_ERROR(conn, rc);
+              return bufflen;
+            }
+            parser->state.UNIX.main = PL_UNIX_FILETYPE;
+          }
+          else {
+            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+            return bufflen;
+          }
+          break;
+        }
+        break;
+      }
+      break;
+    case OS_TYPE_WIN_NT:
+      switch(parser->state.NT.main) {
+      case PL_WINNT_DATE:
+        parser->item_length++;
+        if(parser->item_length < 9) {
+          if(!strchr("0123456789-", c)) { /* only simple control */
+            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+            return bufflen;
+          }
+        }
+        else if(parser->item_length == 9) {
+          if(c == ' ') {
+            parser->state.NT.main = PL_WINNT_TIME;
+            parser->state.NT.sub.time = PL_WINNT_TIME_PRESPACE;
+          }
+          else {
+            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+            return bufflen;
+          }
+        }
+        else {
+          PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+          return bufflen;
+        }
+        break;
+      case PL_WINNT_TIME:
+        parser->item_length++;
+        switch(parser->state.NT.sub.time) {
+        case PL_WINNT_TIME_PRESPACE:
+          if(!ISSPACE(c)) {
+            parser->state.NT.sub.time = PL_WINNT_TIME_TIME;
+          }
+          break;
+        case PL_WINNT_TIME_TIME:
+          if(c == ' ') {
+            parser->offsets.time = parser->item_offset;
+            finfo->b_data[parser->item_offset + parser->item_length -1] = 0;
+            parser->state.NT.main = PL_WINNT_DIRORSIZE;
+            parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE;
+            parser->item_length = 0;
+          }
+          else if(!strchr("APM0123456789:", c)) {
+            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+            return bufflen;
+          }
+          break;
+        }
+        break;
+      case PL_WINNT_DIRORSIZE:
+        switch(parser->state.NT.sub.dirorsize) {
+        case PL_WINNT_DIRORSIZE_PRESPACE:
+          if(c == ' ') {
+
+          }
+          else {
+            parser->item_offset = finfo->b_used - 1;
+            parser->item_length = 1;
+            parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT;
+          }
+          break;
+        case PL_WINNT_DIRORSIZE_CONTENT:
+          parser->item_length ++;
+          if(c == ' ') {
+            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
+            if(strcmp("<DIR>", finfo->b_data + parser->item_offset) == 0) {
+              finfo->filetype = CURLFILETYPE_DIRECTORY;
+              finfo->size = 0;
+            }
+            else {
+              char *endptr;
+              finfo->size = curlx_strtoofft(finfo->b_data +
+                                            parser->item_offset,
+                                            &endptr, 10);
+              if(!*endptr) {
+                if(finfo->size == CURL_OFF_T_MAX ||
+                   finfo->size == CURL_OFF_T_MIN) {
+                  if(errno == ERANGE) {
+                    PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+                    return bufflen;
+                  }
+                }
+              }
+              else {
+                PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+                return bufflen;
+              }
+              /* correct file type */
+              parser->file_data->filetype = CURLFILETYPE_FILE;
+            }
+
+            parser->file_data->flags |= CURLFINFOFLAG_KNOWN_SIZE;
+            parser->item_length = 0;
+            parser->state.NT.main = PL_WINNT_FILENAME;
+            parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
+          }
+          break;
+        }
+        break;
+      case PL_WINNT_FILENAME:
+        switch (parser->state.NT.sub.filename) {
+        case PL_WINNT_FILENAME_PRESPACE:
+          if(c != ' ') {
+            parser->item_offset = finfo->b_used -1;
+            parser->item_length = 1;
+            parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT;
+          }
+          break;
+        case PL_WINNT_FILENAME_CONTENT:
+          parser->item_length++;
+          if(c == '\r') {
+            parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL;
+            finfo->b_data[finfo->b_used - 1] = 0;
+          }
+          else if(c == '\n') {
+            parser->offsets.filename = parser->item_offset;
+            finfo->b_data[finfo->b_used - 1] = 0;
+            parser->offsets.filename = parser->item_offset;
+            rc = ftp_pl_insert_finfo(conn, finfo);
+            if(rc) {
+              PL_ERROR(conn, rc);
+              return bufflen;
+            }
+            parser->state.NT.main = PL_WINNT_DATE;
+            parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
+          }
+          break;
+        case PL_WINNT_FILENAME_WINEOL:
+          if(c == '\n') {
+            parser->offsets.filename = parser->item_offset;
+            rc = ftp_pl_insert_finfo(conn, finfo);
+            if(rc) {
+              PL_ERROR(conn, rc);
+              return bufflen;
+            }
+            parser->state.NT.main = PL_WINNT_DATE;
+            parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
+          }
+          else {
+            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
+            return bufflen;
+          }
+          break;
+        }
+        break;
+      }
+      break;
+    default:
+      return bufflen+1;
+    }
+
+    i++;
+  }
+
+  return bufflen;
+}
+
+#endif /* CURL_DISABLE_FTP */
diff --git a/lib/curl_getenv.c b/lib/curl_getenv.c
new file mode 100644 (file)
index 0000000..cf8b036
--- /dev/null
@@ -0,0 +1,61 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef __VMS
+#include <unixlib.h>
+#endif
+
+#include <curl/curl.h>
+#include "curl_memory.h"
+
+#include "curl_memdebug.h"
+
+static
+char *GetEnv(const char *variable)
+{
+#ifdef _WIN32_WCE
+  return NULL;
+#else
+#ifdef WIN32
+  char env[MAX_PATH]; /* MAX_PATH is from windef.h */
+  char *temp = getenv(variable);
+  env[0] = '\0';
+  if(temp != NULL)
+    ExpandEnvironmentStringsA(temp, env, sizeof(env));
+  return (env[0] != '\0')?strdup(env):NULL;
+#else
+  char *env = getenv(variable);
+#ifdef __VMS
+  if(env && strcmp("HOME",variable) == 0)
+    env = decc_translate_vms(env);
+#endif
+  return (env && env[0])?strdup(env):NULL;
+#endif
+#endif
+}
+
+char *curl_getenv(const char *v)
+{
+  return GetEnv(v);
+}
diff --git a/lib/curl_getinfo.c b/lib/curl_getinfo.c
new file mode 100644 (file)
index 0000000..0404c28
--- /dev/null
@@ -0,0 +1,330 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+#include "curl_urldata.h"
+#include "curl_getinfo.h"
+
+#include "curl_memory.h"
+#include "curl_sslgen.h"
+#include "curl_connect.h" /* Curl_getconnectinfo() */
+#include "curl_progress.h"
+
+/* Make this the last #include */
+#include "curl_memdebug.h"
+
+/*
+ * This is supposed to be called in the beginning of a perform() session
+ * and should reset all session-info variables
+ */
+CURLcode Curl_initinfo(struct SessionHandle *data)
+{
+  struct Progress *pro = &data->progress;
+  struct PureInfo *info =&data->info;
+
+  pro->t_nslookup = 0;
+  pro->t_connect = 0;
+  pro->t_appconnect = 0;
+  pro->t_pretransfer = 0;
+  pro->t_starttransfer = 0;
+  pro->timespent = 0;
+  pro->t_redirect = 0;
+
+  info->httpcode = 0;
+  info->httpversion=0;
+  info->filetime=-1; /* -1 is an illegal time and thus means unknown */
+
+  if(info->contenttype)
+    free(info->contenttype);
+  info->contenttype = NULL;
+
+  info->header_size = 0;
+  info->request_size = 0;
+  info->numconnects = 0;
+
+  info->conn_primary_ip[0] = '\0';
+  info->conn_local_ip[0] = '\0';
+  info->conn_primary_port = 0;
+  info->conn_local_port = 0;
+
+  return CURLE_OK;
+}
+
+static CURLcode getinfo_char(struct SessionHandle *data, CURLINFO info,
+                             char **param_charp)
+{
+  switch(info) {
+  case CURLINFO_EFFECTIVE_URL:
+    *param_charp = data->change.url?data->change.url:(char *)"";
+    break;
+  case CURLINFO_CONTENT_TYPE:
+    *param_charp = data->info.contenttype;
+    break;
+  case CURLINFO_PRIVATE:
+    *param_charp = (char *) data->set.private_data;
+    break;
+  case CURLINFO_FTP_ENTRY_PATH:
+    /* Return the entrypath string from the most recent connection.
+       This pointer was copied from the connectdata structure by FTP.
+       The actual string may be free()ed by subsequent libcurl calls so
+       it must be copied to a safer area before the next libcurl call.
+       Callers must never free it themselves. */
+    *param_charp = data->state.most_recent_ftp_entrypath;
+    break;
+  case CURLINFO_REDIRECT_URL:
+    /* Return the URL this request would have been redirected to if that
+       option had been enabled! */
+    *param_charp = data->info.wouldredirect;
+    break;
+  case CURLINFO_PRIMARY_IP:
+    /* Return the ip address of the most recent (primary) connection */
+    *param_charp = data->info.conn_primary_ip;
+    break;
+  case CURLINFO_LOCAL_IP:
+    /* Return the source/local ip address of the most recent (primary)
+       connection */
+    *param_charp = data->info.conn_local_ip;
+    break;
+  case CURLINFO_RTSP_SESSION_ID:
+    *param_charp = data->set.str[STRING_RTSP_SESSION_ID];
+    break;
+
+  default:
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  }
+  return CURLE_OK;
+}
+
+static CURLcode getinfo_long(struct SessionHandle *data, CURLINFO info,
+                             long *param_longp)
+{
+  curl_socket_t sockfd;
+
+  union {
+    unsigned long *to_ulong;
+    long          *to_long;
+  } lptr;
+
+  switch(info) {
+  case CURLINFO_RESPONSE_CODE:
+    *param_longp = data->info.httpcode;
+    break;
+  case CURLINFO_HTTP_CONNECTCODE:
+    *param_longp = data->info.httpproxycode;
+    break;
+  case CURLINFO_FILETIME:
+    *param_longp = data->info.filetime;
+    break;
+  case CURLINFO_HEADER_SIZE:
+    *param_longp = data->info.header_size;
+    break;
+  case CURLINFO_REQUEST_SIZE:
+    *param_longp = data->info.request_size;
+    break;
+  case CURLINFO_SSL_VERIFYRESULT:
+    *param_longp = data->set.ssl.certverifyresult;
+    break;
+  case CURLINFO_REDIRECT_COUNT:
+    *param_longp = data->set.followlocation;
+    break;
+  case CURLINFO_HTTPAUTH_AVAIL:
+    lptr.to_long = param_longp;
+    *lptr.to_ulong = data->info.httpauthavail;
+    break;
+  case CURLINFO_PROXYAUTH_AVAIL:
+    lptr.to_long = param_longp;
+    *lptr.to_ulong = data->info.proxyauthavail;
+    break;
+  case CURLINFO_OS_ERRNO:
+    *param_longp = data->state.os_errno;
+    break;
+  case CURLINFO_NUM_CONNECTS:
+    *param_longp = data->info.numconnects;
+    break;
+  case CURLINFO_LASTSOCKET:
+    sockfd = Curl_getconnectinfo(data, NULL);
+
+    /* note: this is not a good conversion for systems with 64 bit sockets and
+       32 bit longs */
+    if(sockfd != CURL_SOCKET_BAD)
+      *param_longp = (long)sockfd;
+    else
+      /* this interface is documented to return -1 in case of badness, which
+         may not be the same as the CURL_SOCKET_BAD value */
+      *param_longp = -1;
+    break;
+  case CURLINFO_PRIMARY_PORT:
+    /* Return the (remote) port of the most recent (primary) connection */
+    *param_longp = data->info.conn_primary_port;
+    break;
+  case CURLINFO_LOCAL_PORT:
+    /* Return the local port of the most recent (primary) connection */
+    *param_longp = data->info.conn_local_port;
+    break;
+  case CURLINFO_CONDITION_UNMET:
+    /* return if the condition prevented the document to get transferred */
+    *param_longp = data->info.timecond;
+    break;
+  case CURLINFO_RTSP_CLIENT_CSEQ:
+    *param_longp = data->state.rtsp_next_client_CSeq;
+    break;
+  case CURLINFO_RTSP_SERVER_CSEQ:
+    *param_longp = data->state.rtsp_next_server_CSeq;
+    break;
+  case CURLINFO_RTSP_CSEQ_RECV:
+    *param_longp = data->state.rtsp_CSeq_recv;
+    break;
+
+  default:
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  }
+  return CURLE_OK;
+}
+
+static CURLcode getinfo_double(struct SessionHandle *data, CURLINFO info,
+                               double *param_doublep)
+{
+  switch(info) {
+  case CURLINFO_TOTAL_TIME:
+    *param_doublep = data->progress.timespent;
+    break;
+  case CURLINFO_NAMELOOKUP_TIME:
+    *param_doublep = data->progress.t_nslookup;
+    break;
+  case CURLINFO_CONNECT_TIME:
+    *param_doublep = data->progress.t_connect;
+    break;
+  case CURLINFO_APPCONNECT_TIME:
+    *param_doublep = data->progress.t_appconnect;
+    break;
+  case CURLINFO_PRETRANSFER_TIME:
+    *param_doublep =  data->progress.t_pretransfer;
+    break;
+  case CURLINFO_STARTTRANSFER_TIME:
+    *param_doublep = data->progress.t_starttransfer;
+    break;
+  case CURLINFO_SIZE_UPLOAD:
+    *param_doublep =  (double)data->progress.uploaded;
+    break;
+  case CURLINFO_SIZE_DOWNLOAD:
+    *param_doublep = (double)data->progress.downloaded;
+    break;
+  case CURLINFO_SPEED_DOWNLOAD:
+    *param_doublep =  (double)data->progress.dlspeed;
+    break;
+  case CURLINFO_SPEED_UPLOAD:
+    *param_doublep = (double)data->progress.ulspeed;
+    break;
+  case CURLINFO_CONTENT_LENGTH_DOWNLOAD:
+    *param_doublep = (data->progress.flags & PGRS_DL_SIZE_KNOWN)?
+      (double)data->progress.size_dl:-1;
+    break;
+  case CURLINFO_CONTENT_LENGTH_UPLOAD:
+    *param_doublep = (data->progress.flags & PGRS_UL_SIZE_KNOWN)?
+      (double)data->progress.size_ul:-1;
+    break;
+  case CURLINFO_REDIRECT_TIME:
+    *param_doublep =  data->progress.t_redirect;
+    break;
+
+  default:
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  }
+  return CURLE_OK;
+}
+
+static CURLcode getinfo_slist(struct SessionHandle *data, CURLINFO info,
+                              struct curl_slist **param_slistp)
+{
+  union {
+    struct curl_certinfo * to_certinfo;
+    struct curl_slist    * to_slist;
+  } ptr;
+
+  switch(info) {
+  case CURLINFO_SSL_ENGINES:
+    *param_slistp = Curl_ssl_engines_list(data);
+    break;
+  case CURLINFO_COOKIELIST:
+    *param_slistp = Curl_cookie_list(data);
+    break;
+  case CURLINFO_CERTINFO:
+    /* Return the a pointer to the certinfo struct. Not really an slist
+       pointer but we can pretend it is here */
+    ptr.to_certinfo = &data->info.certs;
+    *param_slistp = ptr.to_slist;
+    break;
+
+  default:
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  }
+  return CURLE_OK;
+}
+
+CURLcode Curl_getinfo(struct SessionHandle *data, CURLINFO info, ...)
+{
+  va_list arg;
+  long *param_longp=NULL;
+  double *param_doublep=NULL;
+  char **param_charp=NULL;
+  struct curl_slist **param_slistp=NULL;
+  int type;
+  /* default return code is to error out! */
+  CURLcode ret = CURLE_BAD_FUNCTION_ARGUMENT;
+
+  if(!data)
+    return ret;
+
+  va_start(arg, info);
+
+  type = CURLINFO_TYPEMASK & (int)info;
+  switch(type) {
+  case CURLINFO_STRING:
+    param_charp = va_arg(arg, char **);
+    if(NULL != param_charp)
+      ret = getinfo_char(data, info, param_charp);
+    break;
+  case CURLINFO_LONG:
+    param_longp = va_arg(arg, long *);
+    if(NULL != param_longp)
+      ret = getinfo_long(data, info, param_longp);
+    break;
+  case CURLINFO_DOUBLE:
+    param_doublep = va_arg(arg, double *);
+    if(NULL != param_doublep)
+      ret = getinfo_double(data, info, param_doublep);
+    break;
+  case CURLINFO_SLIST:
+    param_slistp = va_arg(arg, struct curl_slist **);
+    if(NULL != param_slistp)
+      ret = getinfo_slist(data, info, param_slistp);
+    break;
+  default:
+    break;
+  }
+
+  va_end(arg);
+  return ret;
+}
diff --git a/lib/curl_gopher.c b/lib/curl_gopher.c
new file mode 100644 (file)
index 0000000..80fc18e
--- /dev/null
@@ -0,0 +1,169 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_GOPHER
+
+#include "curl_urldata.h"
+#include <curl/curl.h>
+#include "curl_transfer.h"
+#include "curl_sendf.h"
+
+#include "curl_progress.h"
+#include "curl_strequal.h"
+#include "curl_gopher.h"
+#include "curl_rawstr.h"
+#include "curl_select.h"
+#include "curl_url.h"
+#include "curl_warnless.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/*
+ * Forward declarations.
+ */
+
+static CURLcode gopher_do(struct connectdata *conn, bool *done);
+
+/*
+ * Gopher protocol handler.
+ * This is also a nice simple template to build off for simple
+ * connect-command-download protocols.
+ */
+
+const struct Curl_handler Curl_handler_gopher = {
+  "GOPHER",                             /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  gopher_do,                            /* do_it */
+  ZERO_NULL,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  ZERO_NULL,                            /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  PORT_GOPHER,                          /* defport */
+  CURLPROTO_GOPHER,                     /* protocol */
+  PROTOPT_NONE                          /* flags */
+};
+
+static CURLcode gopher_do(struct connectdata *conn, bool *done)
+{
+  CURLcode result=CURLE_OK;
+  struct SessionHandle *data=conn->data;
+  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
+
+  curl_off_t *bytecount = &data->req.bytecount;
+  char *path = data->state.path;
+  char *sel;
+  char *sel_org = NULL;
+  ssize_t amount, k;
+
+  *done = TRUE; /* unconditionally */
+
+  /* Create selector. Degenerate cases: / and /1 => convert to "" */
+  if(strlen(path) <= 2)
+    sel = (char *)"";
+  else {
+    char *newp;
+    size_t j, i;
+    int len;
+
+    /* Otherwise, drop / and the first character (i.e., item type) ... */
+    newp = path;
+    newp+=2;
+
+    /* ... then turn ? into TAB for search servers, Veronica, etc. ... */
+    j = strlen(newp);
+    for(i=0; i<j; i++)
+      if(newp[i] == '?')
+        newp[i] = '\x09';
+
+    /* ... and finally unescape */
+    sel = curl_easy_unescape(data, newp, 0, &len);
+    if(!sel)
+      return CURLE_OUT_OF_MEMORY;
+    sel_org = sel;
+  }
+
+  /* We use Curl_write instead of Curl_sendf to make sure the entire buffer is
+     sent, which could be sizeable with long selectors. */
+  k = curlx_uztosz(strlen(sel));
+
+  for(;;) {
+    result = Curl_write(conn, sockfd, sel, k, &amount);
+    if(CURLE_OK == result) { /* Which may not have written it all! */
+      result = Curl_client_write(conn, CLIENTWRITE_HEADER, sel, amount);
+      if(result) {
+        Curl_safefree(sel_org);
+        return result;
+      }
+      k -= amount;
+      sel += amount;
+      if(k < 1)
+        break; /* but it did write it all */
+    }
+    else {
+      failf(data, "Failed sending Gopher request");
+      Curl_safefree(sel_org);
+      return result;
+    }
+    /* Don't busyloop. The entire loop thing is a work-around as it causes a
+       BLOCKING behavior which is a NO-NO. This function should rather be
+       split up in a do and a doing piece where the pieces that aren't
+       possible to send now will be sent in the doing function repeatedly
+       until the entire request is sent.
+
+       Wait a while for the socket to be writable. Note that this doesn't
+       acknowledge the timeout.
+    */
+    Curl_socket_ready(CURL_SOCKET_BAD, sockfd, 100);
+  }
+
+  Curl_safefree(sel_org);
+
+  /* We can use Curl_sendf to send the terminal \r\n relatively safely and
+     save allocing another string/doing another _write loop. */
+  result = Curl_sendf(sockfd, conn, "\r\n");
+  if(result != CURLE_OK) {
+    failf(data, "Failed sending Gopher request");
+    return result;
+  }
+  result = Curl_client_write(conn, CLIENTWRITE_HEADER, (char *)"\r\n", 2);
+  if(result)
+    return result;
+
+  Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount,
+                      -1, NULL); /* no upload */
+  return CURLE_OK;
+}
+#endif /*CURL_DISABLE_GOPHER*/
diff --git a/lib/curl_gtls.c b/lib/curl_gtls.c
new file mode 100644 (file)
index 0000000..5c9d165
--- /dev/null
@@ -0,0 +1,1118 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/*
+ * Source file for all GnuTLS-specific code for the TLS/SSL layer. No code
+ * but curl_sslgen.c should ever call or use these functions.
+ *
+ * Note: don't use the GnuTLS' *_t variable type names in this source code,
+ * since they were not present in 1.0.X.
+ */
+
+#include "curl_setup.h"
+
+#ifdef USE_GNUTLS
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#ifdef USE_GNUTLS_NETTLE
+#include <gnutls/crypto.h>
+#include <nettle/md5.h>
+#else
+#include <gcrypt.h>
+#endif
+
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_inet_pton.h"
+#include "curl_gtls.h"
+#include "curl_sslgen.h"
+#include "curl_parsedate.h"
+#include "curl_connect.h" /* for the connect timeout */
+#include "curl_select.h"
+#include "curl_rawstr.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/*
+ Some hackish cast macros based on:
+ http://library.gnome.org/devel/glib/unstable/glib-Type-Conversion-Macros.html
+*/
+#ifndef GNUTLS_POINTER_TO_INT_CAST
+#define GNUTLS_POINTER_TO_INT_CAST(p) ((int) (long) (p))
+#endif
+#ifndef GNUTLS_INT_TO_POINTER_CAST
+#define GNUTLS_INT_TO_POINTER_CAST(i) ((void*) (long) (i))
+#endif
+
+/* Enable GnuTLS debugging by defining GTLSDEBUG */
+/*#define GTLSDEBUG */
+
+#ifdef GTLSDEBUG
+static void tls_log_func(int level, const char *str)
+{
+    fprintf(stderr, "|<%d>| %s", level, str);
+}
+#endif
+static bool gtls_inited = FALSE;
+
+#if defined(GNUTLS_VERSION_NUMBER)
+#  if (GNUTLS_VERSION_NUMBER >= 0x020c00)
+#    undef gnutls_transport_set_lowat
+#    define gnutls_transport_set_lowat(A,B) Curl_nop_stmt
+#    define USE_GNUTLS_PRIORITY_SET_DIRECT 1
+#  endif
+#  if (GNUTLS_VERSION_NUMBER >= 0x020c03)
+#    define GNUTLS_MAPS_WINSOCK_ERRORS 1
+#  endif
+#endif
+
+/*
+ * Custom push and pull callback functions used by GNU TLS to read and write
+ * to the socket.  These functions are simple wrappers to send() and recv()
+ * (although here using sread/swrite macros as defined by curl_setup_once.h).
+ * We use custom functions rather than the GNU TLS defaults because it allows
+ * us to get specific about the fourth "flags" argument, and to use arbitrary
+ * private data with gnutls_transport_set_ptr if we wish.
+ *
+ * When these custom push and pull callbacks fail, GNU TLS checks its own
+ * session-specific error variable, and when not set also its own global
+ * errno variable, in order to take appropriate action. GNU TLS does not
+ * require that the transport is actually a socket. This implies that for
+ * Windows builds these callbacks should ideally set the session-specific
+ * error variable using function gnutls_transport_set_errno or as a last
+ * resort global errno variable using gnutls_transport_set_global_errno,
+ * with a transport agnostic error value. This implies that some winsock
+ * error translation must take place in these callbacks.
+ *
+ * Paragraph above applies to GNU TLS versions older than 2.12.3, since
+ * this version GNU TLS does its own internal winsock error translation
+ * using system_errno() function.
+ */
+
+#if defined(USE_WINSOCK) && !defined(GNUTLS_MAPS_WINSOCK_ERRORS)
+#  define gtls_EINTR  4
+#  define gtls_EIO    5
+#  define gtls_EAGAIN 11
+static int gtls_mapped_sockerrno(void)
+{
+  switch(SOCKERRNO) {
+  case WSAEWOULDBLOCK:
+    return gtls_EAGAIN;
+  case WSAEINTR:
+    return gtls_EINTR;
+  default:
+    break;
+  }
+  return gtls_EIO;
+}
+#endif
+
+static ssize_t Curl_gtls_push(void *s, const void *buf, size_t len)
+{
+  ssize_t ret = swrite(GNUTLS_POINTER_TO_INT_CAST(s), buf, len);
+#if defined(USE_WINSOCK) && !defined(GNUTLS_MAPS_WINSOCK_ERRORS)
+  if(ret < 0)
+    gnutls_transport_set_global_errno(gtls_mapped_sockerrno());
+#endif
+  return ret;
+}
+
+static ssize_t Curl_gtls_pull(void *s, void *buf, size_t len)
+{
+  ssize_t ret = sread(GNUTLS_POINTER_TO_INT_CAST(s), buf, len);
+#if defined(USE_WINSOCK) && !defined(GNUTLS_MAPS_WINSOCK_ERRORS)
+  if(ret < 0)
+    gnutls_transport_set_global_errno(gtls_mapped_sockerrno());
+#endif
+  return ret;
+}
+
+/* Curl_gtls_init()
+ *
+ * Global GnuTLS init, called from Curl_ssl_init(). This calls functions that
+ * are not thread-safe and thus this function itself is not thread-safe and
+ * must only be called from within curl_global_init() to keep the thread
+ * situation under control!
+ */
+int Curl_gtls_init(void)
+{
+  int ret = 1;
+  if(!gtls_inited) {
+    ret = gnutls_global_init()?0:1;
+#ifdef GTLSDEBUG
+    gnutls_global_set_log_function(tls_log_func);
+    gnutls_global_set_log_level(2);
+#endif
+    gtls_inited = TRUE;
+  }
+  return ret;
+}
+
+int Curl_gtls_cleanup(void)
+{
+  if(gtls_inited) {
+    gnutls_global_deinit();
+    gtls_inited = FALSE;
+  }
+  return 1;
+}
+
+static void showtime(struct SessionHandle *data,
+                     const char *text,
+                     time_t stamp)
+{
+  struct tm buffer;
+  const struct tm *tm = &buffer;
+  CURLcode result = Curl_gmtime(stamp, &buffer);
+  if(result)
+    return;
+
+  snprintf(data->state.buffer,
+           BUFSIZE,
+           "\t %s: %s, %02d %s %4d %02d:%02d:%02d GMT\n",
+           text,
+           Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
+           tm->tm_mday,
+           Curl_month[tm->tm_mon],
+           tm->tm_year + 1900,
+           tm->tm_hour,
+           tm->tm_min,
+           tm->tm_sec);
+  infof(data, "%s\n", data->state.buffer);
+}
+
+static gnutls_datum load_file (const char *file)
+{
+  FILE *f;
+  gnutls_datum loaded_file = { NULL, 0 };
+  long filelen;
+  void *ptr;
+
+  if(!(f = fopen(file, "r")))
+    return loaded_file;
+  if(fseek(f, 0, SEEK_END) != 0
+     || (filelen = ftell(f)) < 0
+     || fseek(f, 0, SEEK_SET) != 0
+     || !(ptr = malloc((size_t)filelen)))
+    goto out;
+  if(fread(ptr, 1, (size_t)filelen, f) < (size_t)filelen) {
+    free(ptr);
+    goto out;
+  }
+
+  loaded_file.data = ptr;
+  loaded_file.size = (unsigned int)filelen;
+out:
+  fclose(f);
+  return loaded_file;
+}
+
+static void unload_file(gnutls_datum data) {
+  free(data.data);
+}
+
+
+/* this function does a SSL/TLS (re-)handshake */
+static CURLcode handshake(struct connectdata *conn,
+                          int sockindex,
+                          bool duringconnect,
+                          bool nonblocking)
+{
+  struct SessionHandle *data = conn->data;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  gnutls_session session = conn->ssl[sockindex].session;
+  curl_socket_t sockfd = conn->sock[sockindex];
+  long timeout_ms;
+  int rc;
+  int what;
+
+  for(;;) {
+    /* check allowed time left */
+    timeout_ms = Curl_timeleft(data, NULL, duringconnect);
+
+    if(timeout_ms < 0) {
+      /* no need to continue if time already is up */
+      failf(data, "SSL connection timeout");
+      return CURLE_OPERATION_TIMEDOUT;
+    }
+
+    /* if ssl is expecting something, check if it's available. */
+    if(connssl->connecting_state == ssl_connect_2_reading
+       || connssl->connecting_state == ssl_connect_2_writing) {
+
+      curl_socket_t writefd = ssl_connect_2_writing==
+        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+      curl_socket_t readfd = ssl_connect_2_reading==
+        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+
+      what = Curl_socket_ready(readfd, writefd,
+                               nonblocking?0:
+                               timeout_ms?timeout_ms:1000);
+      if(what < 0) {
+        /* fatal error */
+        failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+        return CURLE_SSL_CONNECT_ERROR;
+      }
+      else if(0 == what) {
+        if(nonblocking)
+          return CURLE_OK;
+        else if(timeout_ms) {
+          /* timeout */
+          failf(data, "SSL connection timeout at %ld", timeout_ms);
+          return CURLE_OPERATION_TIMEDOUT;
+        }
+      }
+      /* socket is readable or writable */
+    }
+
+    rc = gnutls_handshake(session);
+
+    if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) {
+      connssl->connecting_state =
+        gnutls_record_get_direction(session)?
+        ssl_connect_2_writing:ssl_connect_2_reading;
+      continue;
+      if(nonblocking)
+        return CURLE_OK;
+    }
+    else if((rc < 0) && !gnutls_error_is_fatal(rc)) {
+      const char *strerr = NULL;
+
+      if(rc == GNUTLS_E_WARNING_ALERT_RECEIVED) {
+        int alert = gnutls_alert_get(session);
+        strerr = gnutls_alert_get_name(alert);
+      }
+
+      if(strerr == NULL)
+        strerr = gnutls_strerror(rc);
+
+      failf(data, "gnutls_handshake() warning: %s", strerr);
+    }
+    else if(rc < 0) {
+      const char *strerr = NULL;
+
+      if(rc == GNUTLS_E_FATAL_ALERT_RECEIVED) {
+        int alert = gnutls_alert_get(session);
+        strerr = gnutls_alert_get_name(alert);
+      }
+
+      if(strerr == NULL)
+        strerr = gnutls_strerror(rc);
+
+      failf(data, "gnutls_handshake() failed: %s", strerr);
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+
+    /* Reset our connect state machine */
+    connssl->connecting_state = ssl_connect_1;
+    return CURLE_OK;
+  }
+}
+
+static gnutls_x509_crt_fmt do_file_type(const char *type)
+{
+  if(!type || !type[0])
+    return GNUTLS_X509_FMT_PEM;
+  if(Curl_raw_equal(type, "PEM"))
+    return GNUTLS_X509_FMT_PEM;
+  if(Curl_raw_equal(type, "DER"))
+    return GNUTLS_X509_FMT_DER;
+  return -1;
+}
+
+static CURLcode
+gtls_connect_step1(struct connectdata *conn,
+                   int sockindex)
+{
+#ifndef USE_GNUTLS_PRIORITY_SET_DIRECT
+  static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 };
+#endif
+  struct SessionHandle *data = conn->data;
+  gnutls_session session;
+  int rc;
+  void *ssl_sessionid;
+  size_t ssl_idsize;
+  bool sni = TRUE; /* default is SNI enabled */
+#ifdef ENABLE_IPV6
+  struct in6_addr addr;
+#else
+  struct in_addr addr;
+#endif
+
+  if(conn->ssl[sockindex].state == ssl_connection_complete)
+    /* to make us tolerant against being called more than once for the
+       same connection */
+    return CURLE_OK;
+
+  if(!gtls_inited)
+    Curl_gtls_init();
+
+  /* GnuTLS only supports SSLv3 and TLSv1 */
+  if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) {
+    failf(data, "GnuTLS does not support SSLv2");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+  else if(data->set.ssl.version == CURL_SSLVERSION_SSLv3)
+    sni = FALSE; /* SSLv3 has no SNI */
+
+  /* allocate a cred struct */
+  rc = gnutls_certificate_allocate_credentials(&conn->ssl[sockindex].cred);
+  if(rc != GNUTLS_E_SUCCESS) {
+    failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc));
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+#ifdef USE_TLS_SRP
+  if(data->set.ssl.authtype == CURL_TLSAUTH_SRP) {
+    infof(data, "Using TLS-SRP username: %s\n", data->set.ssl.username);
+
+    rc = gnutls_srp_allocate_client_credentials(
+           &conn->ssl[sockindex].srp_client_cred);
+    if(rc != GNUTLS_E_SUCCESS) {
+      failf(data, "gnutls_srp_allocate_client_cred() failed: %s",
+            gnutls_strerror(rc));
+      return CURLE_OUT_OF_MEMORY;
+    }
+
+    rc = gnutls_srp_set_client_credentials(conn->ssl[sockindex].
+                                           srp_client_cred,
+                                           data->set.ssl.username,
+                                           data->set.ssl.password);
+    if(rc != GNUTLS_E_SUCCESS) {
+      failf(data, "gnutls_srp_set_client_cred() failed: %s",
+            gnutls_strerror(rc));
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    }
+  }
+#endif
+
+  if(data->set.ssl.CAfile) {
+    /* set the trusted CA cert bundle file */
+    gnutls_certificate_set_verify_flags(conn->ssl[sockindex].cred,
+                                        GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
+
+    rc = gnutls_certificate_set_x509_trust_file(conn->ssl[sockindex].cred,
+                                                data->set.ssl.CAfile,
+                                                GNUTLS_X509_FMT_PEM);
+    if(rc < 0) {
+      infof(data, "error reading ca cert file %s (%s)\n",
+            data->set.ssl.CAfile, gnutls_strerror(rc));
+      if(data->set.ssl.verifypeer)
+        return CURLE_SSL_CACERT_BADFILE;
+    }
+    else
+      infof(data, "found %d certificates in %s\n",
+            rc, data->set.ssl.CAfile);
+  }
+
+  if(data->set.ssl.CRLfile) {
+    /* set the CRL list file */
+    rc = gnutls_certificate_set_x509_crl_file(conn->ssl[sockindex].cred,
+                                              data->set.ssl.CRLfile,
+                                              GNUTLS_X509_FMT_PEM);
+    if(rc < 0) {
+      failf(data, "error reading crl file %s (%s)",
+            data->set.ssl.CRLfile, gnutls_strerror(rc));
+      return CURLE_SSL_CRL_BADFILE;
+    }
+    else
+      infof(data, "found %d CRL in %s\n",
+            rc, data->set.ssl.CRLfile);
+  }
+
+  /* Initialize TLS session as a client */
+  rc = gnutls_init(&conn->ssl[sockindex].session, GNUTLS_CLIENT);
+  if(rc != GNUTLS_E_SUCCESS) {
+    failf(data, "gnutls_init() failed: %d", rc);
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  /* convenient assign */
+  session = conn->ssl[sockindex].session;
+
+  if((0 == Curl_inet_pton(AF_INET, conn->host.name, &addr)) &&
+#ifdef ENABLE_IPV6
+     (0 == Curl_inet_pton(AF_INET6, conn->host.name, &addr)) &&
+#endif
+     sni &&
+     (gnutls_server_name_set(session, GNUTLS_NAME_DNS, conn->host.name,
+                             strlen(conn->host.name)) < 0))
+    infof(data, "WARNING: failed to configure server name indication (SNI) "
+          "TLS extension\n");
+
+  /* Use default priorities */
+  rc = gnutls_set_default_priority(session);
+  if(rc != GNUTLS_E_SUCCESS)
+    return CURLE_SSL_CONNECT_ERROR;
+
+  if(data->set.ssl.version == CURL_SSLVERSION_SSLv3) {
+#ifndef USE_GNUTLS_PRIORITY_SET_DIRECT
+    static const int protocol_priority[] = { GNUTLS_SSL3, 0 };
+    rc = gnutls_protocol_set_priority(session, protocol_priority);
+#else
+    const char *err;
+    /* the combination of the cipher ARCFOUR with SSL 3.0 and TLS 1.0 is not
+       vulnerable to attacks such as the BEAST, why this code now explicitly
+       asks for that
+    */
+    rc = gnutls_priority_set_direct(session,
+                                    "NORMAL:-VERS-TLS-ALL:+VERS-SSL3.0:"
+                                    "-CIPHER-ALL:+ARCFOUR-128",
+                                    &err);
+#endif
+    if(rc != GNUTLS_E_SUCCESS)
+      return CURLE_SSL_CONNECT_ERROR;
+  }
+
+#ifndef USE_GNUTLS_PRIORITY_SET_DIRECT
+  /* Sets the priority on the certificate types supported by gnutls. Priority
+     is higher for types specified before others. After specifying the types
+     you want, you must append a 0. */
+  rc = gnutls_certificate_type_set_priority(session, cert_type_priority);
+  if(rc != GNUTLS_E_SUCCESS)
+    return CURLE_SSL_CONNECT_ERROR;
+#endif
+
+  if(data->set.str[STRING_CERT]) {
+    if(gnutls_certificate_set_x509_key_file(
+         conn->ssl[sockindex].cred,
+         data->set.str[STRING_CERT],
+         data->set.str[STRING_KEY] ?
+         data->set.str[STRING_KEY] : data->set.str[STRING_CERT],
+         do_file_type(data->set.str[STRING_CERT_TYPE]) ) !=
+       GNUTLS_E_SUCCESS) {
+      failf(data, "error reading X.509 key or certificate file");
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+  }
+
+#ifdef USE_TLS_SRP
+  /* put the credentials to the current session */
+  if(data->set.ssl.authtype == CURL_TLSAUTH_SRP) {
+    rc = gnutls_credentials_set(session, GNUTLS_CRD_SRP,
+                                conn->ssl[sockindex].srp_client_cred);
+    if(rc != GNUTLS_E_SUCCESS)
+      failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc));
+  }
+  else
+#endif
+    rc = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
+                                conn->ssl[sockindex].cred);
+
+  /* set the connection handle (file descriptor for the socket) */
+  gnutls_transport_set_ptr(session,
+                           GNUTLS_INT_TO_POINTER_CAST(conn->sock[sockindex]));
+
+  /* register callback functions to send and receive data. */
+  gnutls_transport_set_push_function(session, Curl_gtls_push);
+  gnutls_transport_set_pull_function(session, Curl_gtls_pull);
+
+  /* lowat must be set to zero when using custom push and pull functions. */
+  gnutls_transport_set_lowat(session, 0);
+
+  /* This might be a reconnect, so we check for a session ID in the cache
+     to speed up things */
+
+  if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, &ssl_idsize)) {
+    /* we got a session id, use it! */
+    gnutls_session_set_data(session, ssl_sessionid, ssl_idsize);
+
+    /* Informational message */
+    infof (data, "SSL re-using session ID\n");
+  }
+
+  return CURLE_OK;
+}
+
+static Curl_recv gtls_recv;
+static Curl_send gtls_send;
+
+static CURLcode
+gtls_connect_step3(struct connectdata *conn,
+                   int sockindex)
+{
+  unsigned int cert_list_size;
+  const gnutls_datum *chainp;
+  unsigned int verify_status;
+  gnutls_x509_crt x509_cert,x509_issuer;
+  gnutls_datum issuerp;
+  char certbuf[256]; /* big enough? */
+  size_t size;
+  unsigned int algo;
+  unsigned int bits;
+  time_t certclock;
+  const char *ptr;
+  struct SessionHandle *data = conn->data;
+  gnutls_session session = conn->ssl[sockindex].session;
+  int rc;
+  int incache;
+  void *ssl_sessionid;
+  CURLcode result = CURLE_OK;
+
+  /* This function will return the peer's raw certificate (chain) as sent by
+     the peer. These certificates are in raw format (DER encoded for
+     X.509). In case of a X.509 then a certificate list may be present. The
+     first certificate in the list is the peer's certificate, following the
+     issuer's certificate, then the issuer's issuer etc. */
+
+  chainp = gnutls_certificate_get_peers(session, &cert_list_size);
+  if(!chainp) {
+    if(data->set.ssl.verifypeer ||
+       data->set.ssl.verifyhost ||
+       data->set.ssl.issuercert) {
+#ifdef USE_TLS_SRP
+      if(data->set.ssl.authtype == CURL_TLSAUTH_SRP
+         && data->set.ssl.username != NULL
+         && !data->set.ssl.verifypeer
+         && gnutls_cipher_get(session)) {
+        /* no peer cert, but auth is ok if we have SRP user and cipher and no
+           peer verify */
+      }
+      else {
+#endif
+        failf(data, "failed to get server cert");
+        return CURLE_PEER_FAILED_VERIFICATION;
+#ifdef USE_TLS_SRP
+      }
+#endif
+    }
+    infof(data, "\t common name: WARNING couldn't obtain\n");
+  }
+
+  if(data->set.ssl.verifypeer) {
+    /* This function will try to verify the peer's certificate and return its
+       status (trusted, invalid etc.). The value of status should be one or
+       more of the gnutls_certificate_status_t enumerated elements bitwise
+       or'd. To avoid denial of service attacks some default upper limits
+       regarding the certificate key size and chain size are set. To override
+       them use gnutls_certificate_set_verify_limits(). */
+
+    rc = gnutls_certificate_verify_peers2(session, &verify_status);
+    if(rc < 0) {
+      failf(data, "server cert verify failed: %d", rc);
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+
+    /* verify_status is a bitmask of gnutls_certificate_status bits */
+    if(verify_status & GNUTLS_CERT_INVALID) {
+      if(data->set.ssl.verifypeer) {
+        failf(data, "server certificate verification failed. CAfile: %s "
+              "CRLfile: %s", data->set.ssl.CAfile?data->set.ssl.CAfile:"none",
+              data->set.ssl.CRLfile?data->set.ssl.CRLfile:"none");
+        return CURLE_SSL_CACERT;
+      }
+      else
+        infof(data, "\t server certificate verification FAILED\n");
+    }
+    else
+      infof(data, "\t server certificate verification OK\n");
+  }
+  else {
+    infof(data, "\t server certificate verification SKIPPED\n");
+    goto after_server_cert_verification;
+  }
+
+  /* initialize an X.509 certificate structure. */
+  gnutls_x509_crt_init(&x509_cert);
+
+  /* convert the given DER or PEM encoded Certificate to the native
+     gnutls_x509_crt_t format */
+  gnutls_x509_crt_import(x509_cert, chainp, GNUTLS_X509_FMT_DER);
+
+  if(data->set.ssl.issuercert) {
+    gnutls_x509_crt_init(&x509_issuer);
+    issuerp = load_file(data->set.ssl.issuercert);
+    gnutls_x509_crt_import(x509_issuer, &issuerp, GNUTLS_X509_FMT_PEM);
+    rc = gnutls_x509_crt_check_issuer(x509_cert,x509_issuer);
+    unload_file(issuerp);
+    if(rc <= 0) {
+      failf(data, "server certificate issuer check failed (IssuerCert: %s)",
+            data->set.ssl.issuercert?data->set.ssl.issuercert:"none");
+      return CURLE_SSL_ISSUER_ERROR;
+    }
+    infof(data,"\t server certificate issuer check OK (Issuer Cert: %s)\n",
+          data->set.ssl.issuercert?data->set.ssl.issuercert:"none");
+  }
+
+  size=sizeof(certbuf);
+  rc = gnutls_x509_crt_get_dn_by_oid(x509_cert, GNUTLS_OID_X520_COMMON_NAME,
+                                     0, /* the first and only one */
+                                     FALSE,
+                                     certbuf,
+                                     &size);
+  if(rc) {
+    infof(data, "error fetching CN from cert:%s\n",
+          gnutls_strerror(rc));
+  }
+
+  /* This function will check if the given certificate's subject matches the
+     given hostname. This is a basic implementation of the matching described
+     in RFC2818 (HTTPS), which takes into account wildcards, and the subject
+     alternative name PKIX extension. Returns non zero on success, and zero on
+     failure. */
+  rc = gnutls_x509_crt_check_hostname(x509_cert, conn->host.name);
+
+  if(!rc) {
+    if(data->set.ssl.verifyhost) {
+      failf(data, "SSL: certificate subject name (%s) does not match "
+            "target host name '%s'", certbuf, conn->host.dispname);
+      gnutls_x509_crt_deinit(x509_cert);
+      return CURLE_PEER_FAILED_VERIFICATION;
+    }
+    else
+      infof(data, "\t common name: %s (does not match '%s')\n",
+            certbuf, conn->host.dispname);
+  }
+  else
+    infof(data, "\t common name: %s (matched)\n", certbuf);
+
+  /* Check for time-based validity */
+  certclock = gnutls_x509_crt_get_expiration_time(x509_cert);
+
+  if(certclock == (time_t)-1) {
+    failf(data, "server cert expiration date verify failed");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  if(certclock < time(NULL)) {
+    if(data->set.ssl.verifypeer) {
+      failf(data, "server certificate expiration date has passed.");
+      return CURLE_PEER_FAILED_VERIFICATION;
+    }
+    else
+      infof(data, "\t server certificate expiration date FAILED\n");
+  }
+  else
+    infof(data, "\t server certificate expiration date OK\n");
+
+  certclock = gnutls_x509_crt_get_activation_time(x509_cert);
+
+  if(certclock == (time_t)-1) {
+    failf(data, "server cert activation date verify failed");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  if(certclock > time(NULL)) {
+    if(data->set.ssl.verifypeer) {
+      failf(data, "server certificate not activated yet.");
+      return CURLE_PEER_FAILED_VERIFICATION;
+    }
+    else
+      infof(data, "\t server certificate activation date FAILED\n");
+  }
+  else
+    infof(data, "\t server certificate activation date OK\n");
+
+  /* Show:
+
+  - ciphers used
+  - subject
+  - start date
+  - expire date
+  - common name
+  - issuer
+
+  */
+
+  /* public key algorithm's parameters */
+  algo = gnutls_x509_crt_get_pk_algorithm(x509_cert, &bits);
+  infof(data, "\t certificate public key: %s\n",
+        gnutls_pk_algorithm_get_name(algo));
+
+  /* version of the X.509 certificate. */
+  infof(data, "\t certificate version: #%d\n",
+        gnutls_x509_crt_get_version(x509_cert));
+
+
+  size = sizeof(certbuf);
+  gnutls_x509_crt_get_dn(x509_cert, certbuf, &size);
+  infof(data, "\t subject: %s\n", certbuf);
+
+  certclock = gnutls_x509_crt_get_activation_time(x509_cert);
+  showtime(data, "start date", certclock);
+
+  certclock = gnutls_x509_crt_get_expiration_time(x509_cert);
+  showtime(data, "expire date", certclock);
+
+  size = sizeof(certbuf);
+  gnutls_x509_crt_get_issuer_dn(x509_cert, certbuf, &size);
+  infof(data, "\t issuer: %s\n", certbuf);
+
+  gnutls_x509_crt_deinit(x509_cert);
+
+after_server_cert_verification:
+
+  /* compression algorithm (if any) */
+  ptr = gnutls_compression_get_name(gnutls_compression_get(session));
+  /* the *_get_name() says "NULL" if GNUTLS_COMP_NULL is returned */
+  infof(data, "\t compression: %s\n", ptr);
+
+  /* the name of the cipher used. ie 3DES. */
+  ptr = gnutls_cipher_get_name(gnutls_cipher_get(session));
+  infof(data, "\t cipher: %s\n", ptr);
+
+  /* the MAC algorithms name. ie SHA1 */
+  ptr = gnutls_mac_get_name(gnutls_mac_get(session));
+  infof(data, "\t MAC: %s\n", ptr);
+
+  conn->ssl[sockindex].state = ssl_connection_complete;
+  conn->recv[sockindex] = gtls_recv;
+  conn->send[sockindex] = gtls_send;
+
+  {
+    /* we always unconditionally get the session id here, as even if we
+       already got it from the cache and asked to use it in the connection, it
+       might've been rejected and then a new one is in use now and we need to
+       detect that. */
+    void *connect_sessionid;
+    size_t connect_idsize;
+
+    /* get the session ID data size */
+    gnutls_session_get_data(session, NULL, &connect_idsize);
+    connect_sessionid = malloc(connect_idsize); /* get a buffer for it */
+
+    if(connect_sessionid) {
+      /* extract session ID to the allocated buffer */
+      gnutls_session_get_data(session, connect_sessionid, &connect_idsize);
+
+      incache = !(Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL));
+      if(incache) {
+        /* there was one before in the cache, so instead of risking that the
+           previous one was rejected, we just kill that and store the new */
+        Curl_ssl_delsessionid(conn, ssl_sessionid);
+      }
+
+      /* store this session id */
+      result = Curl_ssl_addsessionid(conn, connect_sessionid, connect_idsize);
+      if(result) {
+        free(connect_sessionid);
+        result = CURLE_OUT_OF_MEMORY;
+      }
+    }
+    else
+      result = CURLE_OUT_OF_MEMORY;
+  }
+
+  return result;
+}
+
+
+/*
+ * This function is called after the TCP connect has completed. Setup the TLS
+ * layer and do all necessary magic.
+ */
+/* We use connssl->connecting_state to keep track of the connection status;
+   there are three states: 'ssl_connect_1' (not started yet or complete),
+   'ssl_connect_2_reading' (waiting for data from server), and
+   'ssl_connect_2_writing' (waiting to be able to write).
+ */
+static CURLcode
+gtls_connect_common(struct connectdata *conn,
+                    int sockindex,
+                    bool nonblocking,
+                    bool *done)
+{
+  int rc;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+
+  /* Initiate the connection, if not already done */
+  if(ssl_connect_1==connssl->connecting_state) {
+    rc = gtls_connect_step1 (conn, sockindex);
+    if(rc)
+      return rc;
+  }
+
+  rc = handshake(conn, sockindex, TRUE, nonblocking);
+  if(rc)
+    /* handshake() sets its own error message with failf() */
+    return rc;
+
+  /* Finish connecting once the handshake is done */
+  if(ssl_connect_1==connssl->connecting_state) {
+    rc = gtls_connect_step3(conn, sockindex);
+    if(rc)
+      return rc;
+  }
+
+  *done = ssl_connect_1==connssl->connecting_state;
+
+  return CURLE_OK;
+}
+
+CURLcode
+Curl_gtls_connect_nonblocking(struct connectdata *conn,
+                              int sockindex,
+                              bool *done)
+{
+  return gtls_connect_common(conn, sockindex, TRUE, done);
+}
+
+CURLcode
+Curl_gtls_connect(struct connectdata *conn,
+                  int sockindex)
+
+{
+  CURLcode retcode;
+  bool done = FALSE;
+
+  retcode = gtls_connect_common(conn, sockindex, FALSE, &done);
+  if(retcode)
+    return retcode;
+
+  DEBUGASSERT(done);
+
+  return CURLE_OK;
+}
+
+static ssize_t gtls_send(struct connectdata *conn,
+                         int sockindex,
+                         const void *mem,
+                         size_t len,
+                         CURLcode *curlcode)
+{
+  ssize_t rc = gnutls_record_send(conn->ssl[sockindex].session, mem, len);
+
+  if(rc < 0 ) {
+    *curlcode = (rc == GNUTLS_E_AGAIN)
+      ? CURLE_AGAIN
+      : CURLE_SEND_ERROR;
+
+    rc = -1;
+  }
+
+  return rc;
+}
+
+void Curl_gtls_close_all(struct SessionHandle *data)
+{
+  /* FIX: make the OpenSSL code more generic and use parts of it here */
+  (void)data;
+}
+
+static void close_one(struct connectdata *conn,
+                      int idx)
+{
+  if(conn->ssl[idx].session) {
+    gnutls_bye(conn->ssl[idx].session, GNUTLS_SHUT_RDWR);
+    gnutls_deinit(conn->ssl[idx].session);
+    conn->ssl[idx].session = NULL;
+  }
+  if(conn->ssl[idx].cred) {
+    gnutls_certificate_free_credentials(conn->ssl[idx].cred);
+    conn->ssl[idx].cred = NULL;
+  }
+#ifdef USE_TLS_SRP
+  if(conn->ssl[idx].srp_client_cred) {
+    gnutls_srp_free_client_credentials(conn->ssl[idx].srp_client_cred);
+    conn->ssl[idx].srp_client_cred = NULL;
+  }
+#endif
+}
+
+void Curl_gtls_close(struct connectdata *conn, int sockindex)
+{
+  close_one(conn, sockindex);
+}
+
+/*
+ * This function is called to shut down the SSL layer but keep the
+ * socket open (CCC - Clear Command Channel)
+ */
+int Curl_gtls_shutdown(struct connectdata *conn, int sockindex)
+{
+  ssize_t result;
+  int retval = 0;
+  struct SessionHandle *data = conn->data;
+  int done = 0;
+  char buf[120];
+
+  /* This has only been tested on the proftpd server, and the mod_tls code
+     sends a close notify alert without waiting for a close notify alert in
+     response. Thus we wait for a close notify alert from the server, but
+     we do not send one. Let's hope other servers do the same... */
+
+  if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE)
+      gnutls_bye(conn->ssl[sockindex].session, GNUTLS_SHUT_WR);
+
+  if(conn->ssl[sockindex].session) {
+    while(!done) {
+      int what = Curl_socket_ready(conn->sock[sockindex],
+                                   CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT);
+      if(what > 0) {
+        /* Something to read, let's do it and hope that it is the close
+           notify alert from the server */
+        result = gnutls_record_recv(conn->ssl[sockindex].session,
+                                    buf, sizeof(buf));
+        switch(result) {
+        case 0:
+          /* This is the expected response. There was no data but only
+             the close notify alert */
+          done = 1;
+          break;
+        case GNUTLS_E_AGAIN:
+        case GNUTLS_E_INTERRUPTED:
+          infof(data, "GNUTLS_E_AGAIN || GNUTLS_E_INTERRUPTED\n");
+          break;
+        default:
+          retval = -1;
+          done = 1;
+          break;
+        }
+      }
+      else if(0 == what) {
+        /* timeout */
+        failf(data, "SSL shutdown timeout");
+        done = 1;
+        break;
+      }
+      else {
+        /* anything that gets here is fatally bad */
+        failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+        retval = -1;
+        done = 1;
+      }
+    }
+    gnutls_deinit(conn->ssl[sockindex].session);
+  }
+  gnutls_certificate_free_credentials(conn->ssl[sockindex].cred);
+
+#ifdef USE_TLS_SRP
+  if(data->set.ssl.authtype == CURL_TLSAUTH_SRP
+     && data->set.ssl.username != NULL)
+    gnutls_srp_free_client_credentials(conn->ssl[sockindex].srp_client_cred);
+#endif
+
+  conn->ssl[sockindex].cred = NULL;
+  conn->ssl[sockindex].session = NULL;
+
+  return retval;
+}
+
+static ssize_t gtls_recv(struct connectdata *conn, /* connection data */
+                         int num,                  /* socketindex */
+                         char *buf,                /* store read data here */
+                         size_t buffersize,        /* max amount to read */
+                         CURLcode *curlcode)
+{
+  ssize_t ret;
+
+  ret = gnutls_record_recv(conn->ssl[num].session, buf, buffersize);
+  if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) {
+    *curlcode = CURLE_AGAIN;
+    return -1;
+  }
+
+  if(ret == GNUTLS_E_REHANDSHAKE) {
+    /* BLOCKING call, this is bad but a work-around for now. Fixing this "the
+       proper way" takes a whole lot of work. */
+    CURLcode rc = handshake(conn, num, FALSE, FALSE);
+    if(rc)
+      /* handshake() writes error message on its own */
+      *curlcode = rc;
+    else
+      *curlcode = CURLE_AGAIN; /* then return as if this was a wouldblock */
+    return -1;
+  }
+
+  if(ret < 0) {
+    failf(conn->data, "GnuTLS recv error (%d): %s",
+          (int)ret, gnutls_strerror((int)ret));
+    *curlcode = CURLE_RECV_ERROR;
+    return -1;
+  }
+
+  return ret;
+}
+
+void Curl_gtls_session_free(void *ptr)
+{
+  free(ptr);
+}
+
+size_t Curl_gtls_version(char *buffer, size_t size)
+{
+  return snprintf(buffer, size, "GnuTLS/%s", gnutls_check_version(NULL));
+}
+
+int Curl_gtls_seed(struct SessionHandle *data)
+{
+  /* we have the "SSL is seeded" boolean static to prevent multiple
+     time-consuming seedings in vain */
+  static bool ssl_seeded = FALSE;
+
+  /* Quickly add a bit of entropy */
+#ifndef USE_GNUTLS_NETTLE
+  gcry_fast_random_poll();
+#endif
+
+  if(!ssl_seeded || data->set.str[STRING_SSL_RANDOM_FILE] ||
+     data->set.str[STRING_SSL_EGDSOCKET]) {
+
+    /* TODO: to a good job seeding the RNG
+       This may involve the gcry_control function and these options:
+       GCRYCTL_SET_RANDOM_SEED_FILE
+       GCRYCTL_SET_RNDEGD_SOCKET
+    */
+    ssl_seeded = TRUE;
+  }
+  return 0;
+}
+
+void Curl_gtls_random(struct SessionHandle *data,
+                      unsigned char *entropy,
+                      size_t length)
+{
+#if defined(USE_GNUTLS_NETTLE)
+  (void)data;
+  gnutls_rnd(GNUTLS_RND_RANDOM, entropy, length);
+#elif defined(USE_GNUTLS)
+  Curl_gtls_seed(data); /* Initiate the seed if not already done */
+  gcry_randomize(entropy, length, GCRY_STRONG_RANDOM);
+#endif
+}
+
+void Curl_gtls_md5sum(unsigned char *tmp, /* input */
+                      size_t tmplen,
+                      unsigned char *md5sum, /* output */
+                      size_t md5len)
+{
+#if defined(USE_GNUTLS_NETTLE)
+  struct md5_ctx MD5pw;
+  md5_init(&MD5pw);
+  md5_update(&MD5pw, tmplen, tmp);
+  md5_digest(&MD5pw, md5len, md5sum);
+#elif defined(USE_GNUTLS)
+  gcry_md_hd_t MD5pw;
+  gcry_md_open(&MD5pw, GCRY_MD_MD5, 0);
+  gcry_md_write(MD5pw, tmp, tmplen);
+  memcpy(md5sum, gcry_md_read (MD5pw, 0), md5len);
+  gcry_md_close(MD5pw);
+#endif
+}
+
+#endif /* USE_GNUTLS */
diff --git a/lib/curl_hash.c b/lib/curl_hash.c
new file mode 100644 (file)
index 0000000..732dbcf
--- /dev/null
@@ -0,0 +1,400 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include "curl_hash.h"
+#include "curl_llist.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+static void
+hash_element_dtor(void *user, void *element)
+{
+  struct curl_hash *h = (struct curl_hash *) user;
+  struct curl_hash_element *e = (struct curl_hash_element *) element;
+
+  Curl_safefree(e->key);
+
+  if(e->ptr) {
+    h->dtor(e->ptr);
+    e->ptr = NULL;
+  }
+
+  e->key_len = 0;
+
+  free(e);
+}
+
+/* return 1 on error, 0 is fine */
+int
+Curl_hash_init(struct curl_hash *h,
+               int slots,
+               hash_function hfunc,
+               comp_function comparator,
+               curl_hash_dtor dtor)
+{
+  int i;
+
+  if(!slots || !hfunc || !comparator ||!dtor) {
+    return 1; /* failure */
+  }
+
+  h->hash_func = hfunc;
+  h->comp_func = comparator;
+  h->dtor = dtor;
+  h->size = 0;
+  h->slots = slots;
+
+  h->table = malloc(slots * sizeof(struct curl_llist *));
+  if(h->table) {
+    for(i = 0; i < slots; ++i) {
+      h->table[i] = Curl_llist_alloc((curl_llist_dtor) hash_element_dtor);
+      if(!h->table[i]) {
+        while(i--) {
+          Curl_llist_destroy(h->table[i], NULL);
+          h->table[i] = NULL;
+        }
+        free(h->table);
+        h->table = NULL;
+        h->slots = 0;
+        return 1; /* failure */
+      }
+    }
+    return 0; /* fine */
+  }
+  else {
+    h->slots = 0;
+    return 1; /* failure */
+  }
+}
+
+struct curl_hash *
+Curl_hash_alloc(int slots,
+                hash_function hfunc,
+                comp_function comparator,
+                curl_hash_dtor dtor)
+{
+  struct curl_hash *h;
+
+  if(!slots || !hfunc || !comparator ||!dtor) {
+    return NULL; /* failure */
+  }
+
+  h = malloc(sizeof(struct curl_hash));
+  if(h) {
+    if(Curl_hash_init(h, slots, hfunc, comparator, dtor)) {
+      /* failure */
+      free(h);
+      h = NULL;
+    }
+  }
+
+  return h;
+}
+
+
+
+static struct curl_hash_element *
+mk_hash_element(const void *key, size_t key_len, const void *p)
+{
+  struct curl_hash_element *he = malloc(sizeof(struct curl_hash_element));
+
+  if(he) {
+    void *dupkey = malloc(key_len);
+    if(dupkey) {
+      /* copy the key */
+      memcpy(dupkey, key, key_len);
+
+      he->key = dupkey;
+      he->key_len = key_len;
+      he->ptr = (void *) p;
+    }
+    else {
+      /* failed to duplicate the key, free memory and fail */
+      free(he);
+      he = NULL;
+    }
+  }
+  return he;
+}
+
+#define FETCH_LIST(x,y,z) x->table[x->hash_func(y, z, x->slots)]
+
+/* Insert the data in the hash. If there already was a match in the hash,
+ * that data is replaced.
+ *
+ * @unittest: 1305
+ */
+void *
+Curl_hash_add(struct curl_hash *h, void *key, size_t key_len, void *p)
+{
+  struct curl_hash_element  *he;
+  struct curl_llist_element *le;
+  struct curl_llist *l = FETCH_LIST (h, key, key_len);
+
+  for(le = l->head; le; le = le->next) {
+    he = (struct curl_hash_element *) le->ptr;
+    if(h->comp_func(he->key, he->key_len, key, key_len)) {
+      Curl_llist_remove(l, le, (void *)h);
+      --h->size;
+      break;
+    }
+  }
+
+  he = mk_hash_element(key, key_len, p);
+  if(he) {
+    if(Curl_llist_insert_next(l, l->tail, he)) {
+      ++h->size;
+      return p; /* return the new entry */
+    }
+    /*
+     * Couldn't insert it, destroy the 'he' element and the key again. We
+     * don't call hash_element_dtor() since that would also call the
+     * "destructor" for the actual data 'p'. When we fail, we shall not touch
+     * that data.
+     */
+    free(he->key);
+    free(he);
+  }
+
+  return NULL; /* failure */
+}
+
+/* remove the identified hash entry, returns non-zero on failure */
+int Curl_hash_delete(struct curl_hash *h, void *key, size_t key_len)
+{
+  struct curl_llist_element *le;
+  struct curl_hash_element  *he;
+  struct curl_llist *l = FETCH_LIST(h, key, key_len);
+
+  for(le = l->head; le; le = le->next) {
+    he = le->ptr;
+    if(h->comp_func(he->key, he->key_len, key, key_len)) {
+      Curl_llist_remove(l, le, (void *) h);
+      --h->size;
+      return 0;
+    }
+  }
+  return 1;
+}
+
+void *
+Curl_hash_pick(struct curl_hash *h, void *key, size_t key_len)
+{
+  struct curl_llist_element *le;
+  struct curl_hash_element  *he;
+  struct curl_llist *l;
+
+  if(h) {
+    l = FETCH_LIST(h, key, key_len);
+    for(le = l->head; le; le = le->next) {
+      he = le->ptr;
+      if(h->comp_func(he->key, he->key_len, key, key_len)) {
+        return he->ptr;
+      }
+    }
+  }
+
+  return NULL;
+}
+
+#if defined(DEBUGBUILD) && defined(AGGRESIVE_TEST)
+void
+Curl_hash_apply(curl_hash *h, void *user,
+                void (*cb)(void *user, void *ptr))
+{
+  struct curl_llist_element  *le;
+  int                  i;
+
+  for(i = 0; i < h->slots; ++i) {
+    for(le = (h->table[i])->head;
+        le;
+        le = le->next) {
+      curl_hash_element *el = le->ptr;
+      cb(user, el->ptr);
+    }
+  }
+}
+#endif
+
+void
+Curl_hash_clean(struct curl_hash *h)
+{
+  int i;
+
+  for(i = 0; i < h->slots; ++i) {
+    Curl_llist_destroy(h->table[i], (void *) h);
+    h->table[i] = NULL;
+  }
+
+  Curl_safefree(h->table);
+  h->size = 0;
+  h->slots = 0;
+}
+
+void
+Curl_hash_clean_with_criterium(struct curl_hash *h, void *user,
+                               int (*comp)(void *, void *))
+{
+  struct curl_llist_element *le;
+  struct curl_llist_element *lnext;
+  struct curl_llist *list;
+  int i;
+
+  if(!h)
+    return;
+
+  for(i = 0; i < h->slots; ++i) {
+    list = h->table[i];
+    le = list->head; /* get first list entry */
+    while(le) {
+      struct curl_hash_element *he = le->ptr;
+      lnext = le->next;
+      /* ask the callback function if we shall remove this entry or not */
+      if(comp(user, he->ptr)) {
+        Curl_llist_remove(list, le, (void *) h);
+        --h->size; /* one less entry in the hash now */
+      }
+      le = lnext;
+    }
+  }
+}
+
+void
+Curl_hash_destroy(struct curl_hash *h)
+{
+  if(!h)
+    return;
+
+  Curl_hash_clean(h);
+
+  free(h);
+}
+
+size_t Curl_hash_str(void* key, size_t key_length, size_t slots_num)
+{
+  const char* key_str = (const char *) key;
+  const char *end = key_str + key_length;
+  unsigned long h = 5381;
+
+  while(key_str < end) {
+    h += h << 5;
+    h ^= (unsigned long) *key_str++;
+  }
+
+  return (h % slots_num);
+}
+
+size_t Curl_str_key_compare(void*k1, size_t key1_len, void*k2, size_t key2_len)
+{
+  char *key1 = (char *)k1;
+  char *key2 = (char *)k2;
+
+  if(key1_len == key2_len &&
+      *key1 == *key2 &&
+      memcmp(key1, key2, key1_len) == 0) {
+    return 1;
+  }
+
+  return 0;
+}
+
+void Curl_hash_start_iterate(struct curl_hash *hash,
+                             struct curl_hash_iterator *iter)
+{
+  iter->hash = hash;
+  iter->slot_index = 0;
+  iter->current_element = NULL;
+}
+
+struct curl_hash_element *
+Curl_hash_next_element(struct curl_hash_iterator *iter)
+{
+  int i;
+  struct curl_hash *h = iter->hash;
+
+  /* Get the next element in the current list, if any */
+  if(iter->current_element)
+    iter->current_element = iter->current_element->next;
+
+  /* If we have reached the end of the list, find the next one */
+  if(!iter->current_element) {
+    for(i = iter->slot_index;i < h->slots;i++) {
+      if(h->table[i]->head) {
+        iter->current_element = h->table[i]->head;
+        iter->slot_index = i+1;
+        break;
+      }
+    }
+  }
+
+  if(iter->current_element) {
+    struct curl_hash_element *he = iter->current_element->ptr;
+    return he;
+  }
+  else {
+    iter->current_element = NULL;
+    return NULL;
+  }
+}
+
+#if 0 /* useful function for debugging hashes and their contents */
+void Curl_hash_print(struct curl_hash *h,
+                     void (*func)(void *))
+{
+  struct curl_hash_iterator iter;
+  struct curl_hash_element *he;
+  int last_index = -1;
+
+  if(!h)
+    return;
+
+  fprintf(stderr, "=Hash dump=\n");
+
+  Curl_hash_start_iterate(h, &iter);
+
+  he = Curl_hash_next_element(&iter);
+  while(he) {
+    if(iter.slot_index != last_index) {
+      fprintf(stderr, "index %d:", iter.slot_index);
+      if(last_index >= 0) {
+        fprintf(stderr, "\n");
+      }
+      last_index = iter.slot_index;
+    }
+
+    if(func)
+      func(he->ptr);
+    else
+      fprintf(stderr, " [%p]", he->ptr);
+
+    he = Curl_hash_next_element(&iter);
+  }
+  fprintf(stderr, "\n");
+}
+#endif
diff --git a/lib/curl_hmac.c b/lib/curl_hmac.c
new file mode 100644 (file)
index 0000000..692d279
--- /dev/null
@@ -0,0 +1,133 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * RFC2104 Keyed-Hashing for Message Authentication
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+
+#include "curl_hmac.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/*
+ * Generic HMAC algorithm.
+ *
+ *   This module computes HMAC digests based on any hash function. Parameters
+ * and computing procedures are set-up dynamically at HMAC computation
+ * context initialisation.
+ */
+
+static const unsigned char hmac_ipad = 0x36;
+static const unsigned char hmac_opad = 0x5C;
+
+
+
+HMAC_context *
+Curl_HMAC_init(const HMAC_params * hashparams,
+               const unsigned char * key,
+               unsigned int keylen)
+{
+  size_t i;
+  HMAC_context * ctxt;
+  unsigned char * hkey;
+  unsigned char b;
+
+  /* Create HMAC context. */
+  i = sizeof *ctxt + 2 * hashparams->hmac_ctxtsize +
+    hashparams->hmac_resultlen;
+  ctxt = malloc(i);
+
+  if(!ctxt)
+    return ctxt;
+
+  ctxt->hmac_hash = hashparams;
+  ctxt->hmac_hashctxt1 = (void *) (ctxt + 1);
+  ctxt->hmac_hashctxt2 = (void *) ((char *) ctxt->hmac_hashctxt1 +
+      hashparams->hmac_ctxtsize);
+
+  /* If the key is too long, replace it by its hash digest. */
+  if(keylen > hashparams->hmac_maxkeylen) {
+    (*hashparams->hmac_hinit)(ctxt->hmac_hashctxt1);
+    (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt1, key, keylen);
+    hkey = (unsigned char *) ctxt->hmac_hashctxt2 + hashparams->hmac_ctxtsize;
+    (*hashparams->hmac_hfinal)(hkey, ctxt->hmac_hashctxt1);
+    key = hkey;
+    keylen = hashparams->hmac_resultlen;
+  }
+
+  /* Prime the two hash contexts with the modified key. */
+  (*hashparams->hmac_hinit)(ctxt->hmac_hashctxt1);
+  (*hashparams->hmac_hinit)(ctxt->hmac_hashctxt2);
+
+  for(i = 0; i < keylen; i++) {
+    b = (unsigned char)(*key ^ hmac_ipad);
+    (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt1, &b, 1);
+    b = (unsigned char)(*key++ ^ hmac_opad);
+    (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt2, &b, 1);
+  }
+
+  for(; i < hashparams->hmac_maxkeylen; i++) {
+    (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt1, &hmac_ipad, 1);
+    (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt2, &hmac_opad, 1);
+  }
+
+  /* Done, return pointer to HMAC context. */
+  return ctxt;
+}
+
+int Curl_HMAC_update(HMAC_context * ctxt,
+                     const unsigned char * data,
+                     unsigned int len)
+{
+  /* Update first hash calculation. */
+  (*ctxt->hmac_hash->hmac_hupdate)(ctxt->hmac_hashctxt1, data, len);
+  return 0;
+}
+
+
+int Curl_HMAC_final(HMAC_context * ctxt, unsigned char * result)
+{
+  const HMAC_params * hashparams = ctxt->hmac_hash;
+
+  /* Do not get result if called with a null parameter: only release
+     storage. */
+
+  if(!result)
+    result = (unsigned char *) ctxt->hmac_hashctxt2 +
+     ctxt->hmac_hash->hmac_ctxtsize;
+
+  (*hashparams->hmac_hfinal)(result, ctxt->hmac_hashctxt1);
+  (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt2,
+   result, hashparams->hmac_resultlen);
+  (*hashparams->hmac_hfinal)(result, ctxt->hmac_hashctxt2);
+  free((char *) ctxt);
+  return 0;
+}
+
+#endif /* CURL_DISABLE_CRYPTO_AUTH */
diff --git a/lib/curl_hostasyn.c b/lib/curl_hostasyn.c
new file mode 100644 (file)
index 0000000..0097b6c
--- /dev/null
@@ -0,0 +1,157 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#ifdef HAVE_PROCESS_H
+#include <process.h>
+#endif
+
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_hostip.h"
+#include "curl_hash.h"
+#include "curl_share.h"
+#include "curl_strerror.h"
+#include "curl_url.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/***********************************************************************
+ * Only for builds using asynchronous name resolves
+ **********************************************************************/
+#ifdef CURLRES_ASYNCH
+
+/*
+ * Curl_addrinfo_callback() gets called by ares, gethostbyname_thread()
+ * or getaddrinfo_thread() when we got the name resolved (or not!).
+ *
+ * If the status argument is CURL_ASYNC_SUCCESS, this function takes
+ * ownership of the Curl_addrinfo passed, storing the resolved data
+ * in the DNS cache.
+ *
+ * The storage operation locks and unlocks the DNS cache.
+ */
+CURLcode Curl_addrinfo_callback(struct connectdata *conn,
+                                int status,
+                                struct Curl_addrinfo *ai)
+{
+  struct Curl_dns_entry *dns = NULL;
+  CURLcode rc = CURLE_OK;
+
+  conn->async.status = status;
+
+  if(CURL_ASYNC_SUCCESS == status) {
+    if(ai) {
+      struct SessionHandle *data = conn->data;
+
+      if(data->share)
+        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+      dns = Curl_cache_addr(data, ai,
+                            conn->async.hostname,
+                            conn->async.port);
+      if(!dns) {
+        /* failed to store, cleanup and return error */
+        Curl_freeaddrinfo(ai);
+        rc = CURLE_OUT_OF_MEMORY;
+      }
+
+      if(data->share)
+        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+    }
+    else {
+      rc = CURLE_OUT_OF_MEMORY;
+    }
+  }
+
+  conn->async.dns = dns;
+
+ /* Set async.done TRUE last in this function since it may be used multi-
+    threaded and once this is TRUE the other thread may read fields from the
+    async struct */
+  conn->async.done = TRUE;
+
+  /* ipv4: The input hostent struct will be freed by ares when we return from
+     this function */
+  return rc;
+}
+
+/* Call this function after Curl_connect() has returned async=TRUE and
+   then a successful name resolve has been received.
+
+   Note: this function disconnects and frees the conn data in case of
+   resolve failure */
+CURLcode Curl_async_resolved(struct connectdata *conn,
+                             bool *protocol_done)
+{
+  CURLcode code;
+
+  if(conn->async.dns) {
+    conn->dns_entry = conn->async.dns;
+    conn->async.dns = NULL;
+  }
+
+  code = Curl_setup_conn(conn, protocol_done);
+
+  if(code)
+    /* We're not allowed to return failure with memory left allocated
+       in the connectdata struct, free those here */
+    Curl_disconnect(conn, FALSE); /* close the connection */
+
+  return code;
+}
+
+/*
+ * Curl_getaddrinfo() is the generic low-level name resolve API within this
+ * source file. There are several versions of this function - for different
+ * name resolve layers (selected at build-time). They all take this same set
+ * of arguments
+ */
+Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
+                                const char *hostname,
+                                int port,
+                                int *waitp)
+{
+  return Curl_resolver_getaddrinfo(conn, hostname, port, waitp);
+}
+
+#endif /* CURLRES_ASYNCH */
diff --git a/lib/curl_hostcheck.c b/lib/curl_hostcheck.c
new file mode 100644 (file)
index 0000000..a5bf8b0
--- /dev/null
@@ -0,0 +1,96 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if defined(USE_SSLEAY) || defined(USE_AXTLS)
+/* these two backends use functions from this file */
+
+#include "curl_hostcheck.h"
+#include "curl_rawstr.h"
+
+/*
+ * Match a hostname against a wildcard pattern.
+ * E.g.
+ *  "foo.host.com" matches "*.host.com".
+ *
+ * We use the matching rule described in RFC6125, section 6.4.3.
+ * http://tools.ietf.org/html/rfc6125#section-6.4.3
+ */
+
+static int hostmatch(const char *hostname, const char *pattern)
+{
+  const char *pattern_label_end, *pattern_wildcard, *hostname_label_end;
+  int wildcard_enabled;
+  size_t prefixlen, suffixlen;
+  pattern_wildcard = strchr(pattern, '*');
+  if(pattern_wildcard == NULL)
+    return Curl_raw_equal(pattern, hostname) ?
+      CURL_HOST_MATCH : CURL_HOST_NOMATCH;
+
+  /* We require at least 2 dots in pattern to avoid too wide wildcard
+     match. */
+  wildcard_enabled = 1;
+  pattern_label_end = strchr(pattern, '.');
+  if(pattern_label_end == NULL || strchr(pattern_label_end+1, '.') == NULL ||
+     pattern_wildcard > pattern_label_end ||
+     Curl_raw_nequal(pattern, "xn--", 4)) {
+    wildcard_enabled = 0;
+  }
+  if(!wildcard_enabled)
+    return Curl_raw_equal(pattern, hostname) ?
+      CURL_HOST_MATCH : CURL_HOST_NOMATCH;
+
+  hostname_label_end = strchr(hostname, '.');
+  if(hostname_label_end == NULL ||
+     !Curl_raw_equal(pattern_label_end, hostname_label_end))
+    return CURL_HOST_NOMATCH;
+
+  /* The wildcard must match at least one character, so the left-most
+     label of the hostname is at least as large as the left-most label
+     of the pattern. */
+  if(hostname_label_end - hostname < pattern_label_end - pattern)
+    return CURL_HOST_NOMATCH;
+
+  prefixlen = pattern_wildcard - pattern;
+  suffixlen = pattern_label_end - (pattern_wildcard+1);
+  return Curl_raw_nequal(pattern, hostname, prefixlen) &&
+    Curl_raw_nequal(pattern_wildcard+1, hostname_label_end - suffixlen,
+                    suffixlen) ?
+    CURL_HOST_MATCH : CURL_HOST_NOMATCH;
+}
+
+int Curl_cert_hostcheck(const char *match_pattern, const char *hostname)
+{
+  if(!match_pattern || !*match_pattern ||
+      !hostname || !*hostname) /* sanity check */
+    return 0;
+
+  if(Curl_raw_equal(hostname, match_pattern)) /* trivial case */
+    return 1;
+
+  if(hostmatch(hostname,match_pattern) == CURL_HOST_MATCH)
+    return 1;
+  return 0;
+}
+
+#endif /* SSLEAY or AXTLS */
diff --git a/lib/curl_hostip.c b/lib/curl_hostip.c
new file mode 100644 (file)
index 0000000..7cc51f8
--- /dev/null
@@ -0,0 +1,820 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#ifdef HAVE_SETJMP_H
+#include <setjmp.h>
+#endif
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#ifdef HAVE_PROCESS_H
+#include <process.h>
+#endif
+
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_hostip.h"
+#include "curl_hash.h"
+#include "curl_share.h"
+#include "curl_strerror.h"
+#include "curl_url.h"
+#include "curl_inet_ntop.h"
+#include "curl_warnless.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+#if defined(CURLRES_SYNCH) && \
+    defined(HAVE_ALARM) && defined(SIGALRM) && defined(HAVE_SIGSETJMP)
+/* alarm-based timeouts can only be used with all the dependencies satisfied */
+#define USE_ALARM_TIMEOUT
+#endif
+
+/*
+ * curl_hostip.c explained
+ * =======================
+ *
+ * The main COMPILE-TIME DEFINES to keep in mind when reading the curl_host*.c
+ * source file are these:
+ *
+ * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
+ * that. The host may not be able to resolve IPv6, but we don't really have to
+ * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4
+ * defined.
+ *
+ * CURLRES_ARES - is defined if libcurl is built to use c-ares for
+ * asynchronous name resolves. This can be Windows or *nix.
+ *
+ * CURLRES_THREADED - is defined if libcurl is built to run under (native)
+ * Windows, and then the name resolve will be done in a new thread, and the
+ * supported API will be the same as for ares-builds.
+ *
+ * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
+ * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
+ * defined.
+ *
+ * The curl_host*.c sources files are split up like this:
+ *
+ * curl_hostip.c   - method-independent resolver and utility functions
+ * curl_hostasyn.c - functions for asynchronous name resolves
+ * curl_hostsyn.c  - functions for synchronous name resolves
+ * curl_hostip4.c  - ipv4-specific functions
+ * curl_hostip6.c  - ipv6-specific functions
+ *
+ * The two asynchronous name resolver backends are implemented in:
+ * curl_asyn_ares.c   - functions for ares-using name resolves
+ * curl_asyn_thread.c - functions for threaded name resolves
+
+ * The curl_hostip.h is the united header file for all this. It defines the
+ * CURLRES_* defines based on the config*.h and curl_setup.h defines.
+ */
+
+/* These two symbols are for the global DNS cache */
+static struct curl_hash hostname_cache;
+static int host_cache_initialized;
+
+static void freednsentry(void *freethis);
+
+/*
+ * Curl_global_host_cache_init() initializes and sets up a global DNS cache.
+ * Global DNS cache is general badness. Do not use. This will be removed in
+ * a future version. Use the share interface instead!
+ *
+ * Returns a struct curl_hash pointer on success, NULL on failure.
+ */
+struct curl_hash *Curl_global_host_cache_init(void)
+{
+  int rc = 0;
+  if(!host_cache_initialized) {
+    rc = Curl_hash_init(&hostname_cache, 7, Curl_hash_str,
+                        Curl_str_key_compare, freednsentry);
+    if(!rc)
+      host_cache_initialized = 1;
+  }
+  return rc?NULL:&hostname_cache;
+}
+
+/*
+ * Destroy and cleanup the global DNS cache
+ */
+void Curl_global_host_cache_dtor(void)
+{
+  if(host_cache_initialized) {
+    Curl_hash_clean(&hostname_cache);
+    host_cache_initialized = 0;
+  }
+}
+
+/*
+ * Return # of adresses in a Curl_addrinfo struct
+ */
+int Curl_num_addresses(const Curl_addrinfo *addr)
+{
+  int i = 0;
+  while(addr) {
+    addr = addr->ai_next;
+    i++;
+  }
+  return i;
+}
+
+/*
+ * Curl_printable_address() returns a printable version of the 1st address
+ * given in the 'ai' argument. The result will be stored in the buf that is
+ * bufsize bytes big.
+ *
+ * If the conversion fails, it returns NULL.
+ */
+const char *
+Curl_printable_address(const Curl_addrinfo *ai, char *buf, size_t bufsize)
+{
+  const struct sockaddr_in *sa4;
+  const struct in_addr *ipaddr4;
+#ifdef ENABLE_IPV6
+  const struct sockaddr_in6 *sa6;
+  const struct in6_addr *ipaddr6;
+#endif
+
+  switch (ai->ai_family) {
+    case AF_INET:
+      sa4 = (const void *)ai->ai_addr;
+      ipaddr4 = &sa4->sin_addr;
+      return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf,
+                            bufsize);
+#ifdef ENABLE_IPV6
+    case AF_INET6:
+      sa6 = (const void *)ai->ai_addr;
+      ipaddr6 = &sa6->sin6_addr;
+      return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf,
+                            bufsize);
+#endif
+    default:
+      break;
+  }
+  return NULL;
+}
+
+/*
+ * Return a hostcache id string for the provided host + port, to be used by
+ * the DNS caching.
+ */
+static char *
+create_hostcache_id(const char *name, int port)
+{
+  /* create and return the new allocated entry */
+  char *id = aprintf("%s:%d", name, port);
+  char *ptr = id;
+  if(ptr) {
+    /* lower case the name part */
+    while(*ptr && (*ptr != ':')) {
+      *ptr = (char)TOLOWER(*ptr);
+      ptr++;
+    }
+  }
+  return id;
+}
+
+struct hostcache_prune_data {
+  long cache_timeout;
+  time_t now;
+};
+
+/*
+ * This function is set as a callback to be called for every entry in the DNS
+ * cache when we want to prune old unused entries.
+ *
+ * Returning non-zero means remove the entry, return 0 to keep it in the
+ * cache.
+ */
+static int
+hostcache_timestamp_remove(void *datap, void *hc)
+{
+  struct hostcache_prune_data *data =
+    (struct hostcache_prune_data *) datap;
+  struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
+
+  return (data->now - c->timestamp >= data->cache_timeout);
+}
+
+/*
+ * Prune the DNS cache. This assumes that a lock has already been taken.
+ */
+static void
+hostcache_prune(struct curl_hash *hostcache, long cache_timeout, time_t now)
+{
+  struct hostcache_prune_data user;
+
+  user.cache_timeout = cache_timeout;
+  user.now = now;
+
+  Curl_hash_clean_with_criterium(hostcache,
+                                 (void *) &user,
+                                 hostcache_timestamp_remove);
+}
+
+/*
+ * Library-wide function for pruning the DNS cache. This function takes and
+ * returns the appropriate locks.
+ */
+void Curl_hostcache_prune(struct SessionHandle *data)
+{
+  time_t now;
+
+  if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
+    /* cache forever means never prune, and NULL hostcache means
+       we can't do it */
+    return;
+
+  if(data->share)
+    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+  time(&now);
+
+  /* Remove outdated and unused entries from the hostcache */
+  hostcache_prune(data->dns.hostcache,
+                  data->set.dns_cache_timeout,
+                  now);
+
+  if(data->share)
+    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+}
+
+/*
+ * Check if the entry should be pruned. Assumes a locked cache.
+ */
+static int
+remove_entry_if_stale(struct SessionHandle *data, struct Curl_dns_entry *dns)
+{
+  struct hostcache_prune_data user;
+
+  if(!dns || (data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
+    /* cache forever means never prune, and NULL hostcache means
+       we can't do it */
+    return 0;
+
+  time(&user.now);
+  user.cache_timeout = data->set.dns_cache_timeout;
+
+  if(!hostcache_timestamp_remove(&user,dns) )
+    return 0;
+
+  Curl_hash_clean_with_criterium(data->dns.hostcache,
+                                 (void *) &user,
+                                 hostcache_timestamp_remove);
+
+  return 1;
+}
+
+
+#ifdef HAVE_SIGSETJMP
+/* Beware this is a global and unique instance. This is used to store the
+   return address that we can jump back to from inside a signal handler. This
+   is not thread-safe stuff. */
+sigjmp_buf curl_jmpenv;
+#endif
+
+
+/*
+ * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
+ *
+ * When calling Curl_resolv() has resulted in a response with a returned
+ * address, we call this function to store the information in the dns
+ * cache etc
+ *
+ * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
+ */
+struct Curl_dns_entry *
+Curl_cache_addr(struct SessionHandle *data,
+                Curl_addrinfo *addr,
+                const char *hostname,
+                int port)
+{
+  char *entry_id;
+  size_t entry_len;
+  struct Curl_dns_entry *dns;
+  struct Curl_dns_entry *dns2;
+
+  /* Create an entry id, based upon the hostname and port */
+  entry_id = create_hostcache_id(hostname, port);
+  /* If we can't create the entry id, fail */
+  if(!entry_id)
+    return NULL;
+  entry_len = strlen(entry_id);
+
+  /* Create a new cache entry */
+  dns = calloc(1, sizeof(struct Curl_dns_entry));
+  if(!dns) {
+    free(entry_id);
+    return NULL;
+  }
+
+  dns->inuse = 0;   /* init to not used */
+  dns->addr = addr; /* this is the address(es) */
+  time(&dns->timestamp);
+  if(dns->timestamp == 0)
+    dns->timestamp = 1;   /* zero indicates that entry isn't in hash table */
+
+  /* Store the resolved data in our DNS cache. */
+  dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len+1,
+                       (void *)dns);
+  if(!dns2) {
+    free(dns);
+    free(entry_id);
+    return NULL;
+  }
+
+  dns = dns2;
+  dns->inuse++;         /* mark entry as in-use */
+
+  /* free the allocated entry_id */
+  free(entry_id);
+
+  return dns;
+}
+
+/*
+ * Curl_resolv() is the main name resolve function within libcurl. It resolves
+ * a name and returns a pointer to the entry in the 'entry' argument (if one
+ * is provided). This function might return immediately if we're using asynch
+ * resolves. See the return codes.
+ *
+ * The cache entry we return will get its 'inuse' counter increased when this
+ * function is used. You MUST call Curl_resolv_unlock() later (when you're
+ * done using this struct) to decrease the counter again.
+ *
+ * In debug mode, we specifically test for an interface name "LocalHost"
+ * and resolve "localhost" instead as a means to permit test cases
+ * to connect to a local test server with any host name.
+ *
+ * Return codes:
+ *
+ * CURLRESOLV_ERROR   (-1) = error, no pointer
+ * CURLRESOLV_RESOLVED (0) = OK, pointer provided
+ * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
+ */
+
+int Curl_resolv(struct connectdata *conn,
+                const char *hostname,
+                int port,
+                struct Curl_dns_entry **entry)
+{
+  char *entry_id = NULL;
+  struct Curl_dns_entry *dns = NULL;
+  size_t entry_len;
+  struct SessionHandle *data = conn->data;
+  CURLcode result;
+  int rc = CURLRESOLV_ERROR; /* default to failure */
+
+  *entry = NULL;
+
+  /* Create an entry id, based upon the hostname and port */
+  entry_id = create_hostcache_id(hostname, port);
+  /* If we can't create the entry id, fail */
+  if(!entry_id)
+    return rc;
+
+  entry_len = strlen(entry_id);
+
+  if(data->share)
+    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+  /* See if its already in our dns cache */
+  dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
+
+  /* free the allocated entry_id again */
+  free(entry_id);
+
+  /* See whether the returned entry is stale. Done before we release lock */
+  if(remove_entry_if_stale(data, dns))
+    dns = NULL; /* the memory deallocation is being handled by the hash */
+
+  if(dns) {
+    dns->inuse++; /* we use it! */
+    rc = CURLRESOLV_RESOLVED;
+  }
+
+  if(data->share)
+    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+
+  if(!dns) {
+    /* The entry was not in the cache. Resolve it to IP address */
+
+    Curl_addrinfo *addr;
+    int respwait;
+
+    /* Check what IP specifics the app has requested and if we can provide it.
+     * If not, bail out. */
+    if(!Curl_ipvalid(conn))
+      return CURLRESOLV_ERROR;
+
+    /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
+       non-zero value indicating that we need to wait for the response to the
+       resolve call */
+    addr = Curl_getaddrinfo(conn,
+#ifdef DEBUGBUILD
+                            (data->set.str[STRING_DEVICE]
+                             && !strcmp(data->set.str[STRING_DEVICE],
+                                        "LocalHost"))?"localhost":
+#endif
+                            hostname, port, &respwait);
+
+    if(!addr) {
+      if(respwait) {
+        /* the response to our resolve call will come asynchronously at
+           a later time, good or bad */
+        /* First, check that we haven't received the info by now */
+        result = Curl_resolver_is_resolved(conn, &dns);
+        if(result) /* error detected */
+          return CURLRESOLV_ERROR;
+        if(dns)
+          rc = CURLRESOLV_RESOLVED; /* pointer provided */
+        else
+          rc = CURLRESOLV_PENDING; /* no info yet */
+      }
+    }
+    else {
+      if(data->share)
+        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+      /* we got a response, store it in the cache */
+      dns = Curl_cache_addr(data, addr, hostname, port);
+
+      if(data->share)
+        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+
+      if(!dns)
+        /* returned failure, bail out nicely */
+        Curl_freeaddrinfo(addr);
+      else
+        rc = CURLRESOLV_RESOLVED;
+    }
+  }
+
+  *entry = dns;
+
+  return rc;
+}
+
+#ifdef USE_ALARM_TIMEOUT
+/*
+ * This signal handler jumps back into the main libcurl code and continues
+ * execution.  This effectively causes the remainder of the application to run
+ * within a signal handler which is nonportable and could lead to problems.
+ */
+static
+RETSIGTYPE alarmfunc(int sig)
+{
+  /* this is for "-ansi -Wall -pedantic" to stop complaining!   (rabe) */
+  (void)sig;
+  siglongjmp(curl_jmpenv, 1);
+  return;
+}
+#endif /* USE_ALARM_TIMEOUT */
+
+/*
+ * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
+ * timeout.  This function might return immediately if we're using asynch
+ * resolves. See the return codes.
+ *
+ * The cache entry we return will get its 'inuse' counter increased when this
+ * function is used. You MUST call Curl_resolv_unlock() later (when you're
+ * done using this struct) to decrease the counter again.
+ *
+ * If built with a synchronous resolver and use of signals is not
+ * disabled by the application, then a nonzero timeout will cause a
+ * timeout after the specified number of milliseconds. Otherwise, timeout
+ * is ignored.
+ *
+ * Return codes:
+ *
+ * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired
+ * CURLRESOLV_ERROR   (-1) = error, no pointer
+ * CURLRESOLV_RESOLVED (0) = OK, pointer provided
+ * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
+ */
+
+int Curl_resolv_timeout(struct connectdata *conn,
+                        const char *hostname,
+                        int port,
+                        struct Curl_dns_entry **entry,
+                        long timeoutms)
+{
+#ifdef USE_ALARM_TIMEOUT
+#ifdef HAVE_SIGACTION
+  struct sigaction keep_sigact;   /* store the old struct here */
+  volatile bool keep_copysig = FALSE; /* wether old sigact has been saved */
+  struct sigaction sigact;
+#else
+#ifdef HAVE_SIGNAL
+  void (*keep_sigact)(int);       /* store the old handler here */
+#endif /* HAVE_SIGNAL */
+#endif /* HAVE_SIGACTION */
+  volatile long timeout;
+  volatile unsigned int prev_alarm = 0;
+  struct SessionHandle *data = conn->data;
+#endif /* USE_ALARM_TIMEOUT */
+  int rc;
+
+  *entry = NULL;
+
+  if(timeoutms < 0)
+    /* got an already expired timeout */
+    return CURLRESOLV_TIMEDOUT;
+
+#ifdef USE_ALARM_TIMEOUT
+  if(data->set.no_signal)
+    /* Ignore the timeout when signals are disabled */
+    timeout = 0;
+  else
+    timeout = timeoutms;
+
+  if(!timeout)
+    /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
+    return Curl_resolv(conn, hostname, port, entry);
+
+  if(timeout < 1000)
+    /* The alarm() function only provides integer second resolution, so if
+       we want to wait less than one second we must bail out already now. */
+    return CURLRESOLV_TIMEDOUT;
+
+  /*************************************************************
+   * Set signal handler to catch SIGALRM
+   * Store the old value to be able to set it back later!
+   *************************************************************/
+#ifdef HAVE_SIGACTION
+  sigaction(SIGALRM, NULL, &sigact);
+  keep_sigact = sigact;
+  keep_copysig = TRUE; /* yes, we have a copy */
+  sigact.sa_handler = alarmfunc;
+#ifdef SA_RESTART
+  /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
+  sigact.sa_flags &= ~SA_RESTART;
+#endif
+  /* now set the new struct */
+  sigaction(SIGALRM, &sigact, NULL);
+#else /* HAVE_SIGACTION */
+  /* no sigaction(), revert to the much lamer signal() */
+#ifdef HAVE_SIGNAL
+  keep_sigact = signal(SIGALRM, alarmfunc);
+#endif
+#endif /* HAVE_SIGACTION */
+
+  /* alarm() makes a signal get sent when the timeout fires off, and that
+     will abort system calls */
+  prev_alarm = alarm(curlx_sltoui(timeout/1000L));
+
+  /* This allows us to time-out from the name resolver, as the timeout
+     will generate a signal and we will siglongjmp() from that here.
+     This technique has problems (see alarmfunc).
+     This should be the last thing we do before calling Curl_resolv(),
+     as otherwise we'd have to worry about variables that get modified
+     before we invoke Curl_resolv() (and thus use "volatile"). */
+  if(sigsetjmp(curl_jmpenv, 1)) {
+    /* this is coming from a siglongjmp() after an alarm signal */
+    failf(data, "name lookup timed out");
+    rc = CURLRESOLV_ERROR;
+    goto clean_up;
+  }
+
+#else
+#ifndef CURLRES_ASYNCH
+  if(timeoutms)
+    infof(conn->data, "timeout on name lookup is not supported\n");
+#else
+  (void)timeoutms; /* timeoutms not used with an async resolver */
+#endif
+#endif /* USE_ALARM_TIMEOUT */
+
+  /* Perform the actual name resolution. This might be interrupted by an
+   * alarm if it takes too long.
+   */
+  rc = Curl_resolv(conn, hostname, port, entry);
+
+#ifdef USE_ALARM_TIMEOUT
+clean_up:
+
+  if(!prev_alarm)
+    /* deactivate a possibly active alarm before uninstalling the handler */
+    alarm(0);
+
+#ifdef HAVE_SIGACTION
+  if(keep_copysig) {
+    /* we got a struct as it looked before, now put that one back nice
+       and clean */
+    sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
+  }
+#else
+#ifdef HAVE_SIGNAL
+  /* restore the previous SIGALRM handler */
+  signal(SIGALRM, keep_sigact);
+#endif
+#endif /* HAVE_SIGACTION */
+
+  /* switch back the alarm() to either zero or to what it was before minus
+     the time we spent until now! */
+  if(prev_alarm) {
+    /* there was an alarm() set before us, now put it back */
+    unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);
+
+    /* the alarm period is counted in even number of seconds */
+    unsigned long alarm_set = prev_alarm - elapsed_ms/1000;
+
+    if(!alarm_set ||
+       ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
+      /* if the alarm time-left reached zero or turned "negative" (counted
+         with unsigned values), we should fire off a SIGALRM here, but we
+         won't, and zero would be to switch it off so we never set it to
+         less than 1! */
+      alarm(1);
+      rc = CURLRESOLV_TIMEDOUT;
+      failf(data, "Previous alarm fired off!");
+    }
+    else
+      alarm((unsigned int)alarm_set);
+  }
+#endif /* USE_ALARM_TIMEOUT */
+
+  return rc;
+}
+
+/*
+ * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
+ * made, the struct may be destroyed due to pruning. It is important that only
+ * one unlock is made for each Curl_resolv() call.
+ */
+void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns)
+{
+  DEBUGASSERT(dns && (dns->inuse>0));
+
+  if(data->share)
+    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+  dns->inuse--;
+  /* only free if nobody is using AND it is not in hostcache (timestamp ==
+     0) */
+  if(dns->inuse == 0 && dns->timestamp == 0) {
+    Curl_freeaddrinfo(dns->addr);
+    free(dns);
+  }
+
+  if(data->share)
+    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+}
+
+/*
+ * File-internal: free a cache dns entry.
+ */
+static void freednsentry(void *freethis)
+{
+  struct Curl_dns_entry *p = (struct Curl_dns_entry *) freethis;
+
+  /* mark the entry as not in hostcache */
+  p->timestamp = 0;
+  if(p->inuse == 0) {
+    Curl_freeaddrinfo(p->addr);
+    free(p);
+  }
+}
+
+/*
+ * Curl_mk_dnscache() creates a new DNS cache and returns the handle for it.
+ */
+struct curl_hash *Curl_mk_dnscache(void)
+{
+  return Curl_hash_alloc(7, Curl_hash_str, Curl_str_key_compare, freednsentry);
+}
+
+static int hostcache_inuse(void *data, void *hc)
+{
+  struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
+
+  if(c->inuse == 1)
+    Curl_resolv_unlock(data, c);
+
+  return 1; /* free all entries */
+}
+
+void Curl_hostcache_clean(struct SessionHandle *data)
+{
+  /* Entries added to the hostcache with the CURLOPT_RESOLVE function are
+   * still present in the cache with the inuse counter set to 1. Detect them
+   * and cleanup!
+   */
+  Curl_hash_clean_with_criterium(data->dns.hostcache, data, hostcache_inuse);
+}
+
+void Curl_hostcache_destroy(struct SessionHandle *data)
+{
+  Curl_hostcache_clean(data);
+  Curl_hash_destroy(data->dns.hostcache);
+  data->dns.hostcachetype = HCACHE_NONE;
+  data->dns.hostcache = NULL;
+}
+
+CURLcode Curl_loadhostpairs(struct SessionHandle *data)
+{
+  struct curl_slist *hostp;
+  char hostname[256];
+  char address[256];
+  int port;
+
+  for(hostp = data->change.resolve; hostp; hostp = hostp->next ) {
+    if(!hostp->data)
+      continue;
+    if(hostp->data[0] == '-') {
+      /* TODO: mark an entry for removal */
+    }
+    else if(3 == sscanf(hostp->data, "%255[^:]:%d:%255s", hostname, &port,
+                        address)) {
+      struct Curl_dns_entry *dns;
+      Curl_addrinfo *addr;
+      char *entry_id;
+      size_t entry_len;
+
+      addr = Curl_str2addr(address, port);
+      if(!addr) {
+        infof(data, "Resolve %s found illegal!\n", hostp->data);
+        continue;
+      }
+
+      /* Create an entry id, based upon the hostname and port */
+      entry_id = create_hostcache_id(hostname, port);
+      /* If we can't create the entry id, fail */
+      if(!entry_id) {
+        Curl_freeaddrinfo(addr);
+        return CURLE_OUT_OF_MEMORY;
+      }
+
+      entry_len = strlen(entry_id);
+
+      if(data->share)
+        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+      /* See if its already in our dns cache */
+      dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
+
+      /* free the allocated entry_id again */
+      free(entry_id);
+
+      if(!dns)
+        /* if not in the cache already, put this host in the cache */
+        dns = Curl_cache_addr(data, addr, hostname, port);
+      else
+        /* this is a duplicate, free it again */
+        Curl_freeaddrinfo(addr);
+
+      if(data->share)
+        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+
+      if(!dns) {
+        Curl_freeaddrinfo(addr);
+        return CURLE_OUT_OF_MEMORY;
+      }
+      infof(data, "Added %s:%d:%s to DNS cache\n",
+            hostname, port, address);
+    }
+  }
+  data->change.resolve = NULL; /* dealt with now */
+
+  return CURLE_OK;
+}
diff --git a/lib/curl_hostip4.c b/lib/curl_hostip4.c
new file mode 100644 (file)
index 0000000..5b64b46
--- /dev/null
@@ -0,0 +1,310 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#ifdef HAVE_PROCESS_H
+#include <process.h>
+#endif
+
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_hostip.h"
+#include "curl_hash.h"
+#include "curl_share.h"
+#include "curl_strerror.h"
+#include "curl_url.h"
+#include "curl_inet_pton.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/***********************************************************************
+ * Only for plain-ipv4 builds
+ **********************************************************************/
+#ifdef CURLRES_IPV4 /* plain ipv4 code coming up */
+/*
+ * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've
+ * been set and returns TRUE if they are OK.
+ */
+bool Curl_ipvalid(struct connectdata *conn)
+{
+  if(conn->ip_version == CURL_IPRESOLVE_V6)
+    /* an ipv6 address was requested and we can't get/use one */
+    return FALSE;
+
+  return TRUE; /* OK, proceed */
+}
+
+#ifdef CURLRES_SYNCH
+
+/*
+ * Curl_getaddrinfo() - the ipv4 synchronous version.
+ *
+ * The original code to this function was from the Dancer source code, written
+ * by Bjorn Reese, it has since been patched and modified considerably.
+ *
+ * gethostbyname_r() is the thread-safe version of the gethostbyname()
+ * function. When we build for plain IPv4, we attempt to use this
+ * function. There are _three_ different gethostbyname_r() versions, and we
+ * detect which one this platform supports in the configure script and set up
+ * the HAVE_GETHOSTBYNAME_R_3, HAVE_GETHOSTBYNAME_R_5 or
+ * HAVE_GETHOSTBYNAME_R_6 defines accordingly. Note that HAVE_GETADDRBYNAME
+ * has the corresponding rules. This is primarily on *nix. Note that some unix
+ * flavours have thread-safe versions of the plain gethostbyname() etc.
+ *
+ */
+Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
+                                const char *hostname,
+                                int port,
+                                int *waitp)
+{
+  Curl_addrinfo *ai = NULL;
+
+#ifdef CURL_DISABLE_VERBOSE_STRINGS
+  (void)conn;
+#endif
+
+  *waitp = 0; /* synchronous response only */
+
+  ai = Curl_ipv4_resolve_r(hostname, port);
+  if(!ai)
+    infof(conn->data, "Curl_ipv4_resolve_r failed for %s\n", hostname);
+
+  return ai;
+}
+#endif /* CURLRES_SYNCH */
+#endif /* CURLRES_IPV4 */
+
+#if defined(CURLRES_IPV4) && !defined(CURLRES_ARES)
+
+/*
+ * Curl_ipv4_resolve_r() - ipv4 threadsafe resolver function.
+ *
+ * This is used for both synchronous and asynchronous resolver builds,
+ * implying that only threadsafe code and function calls may be used.
+ *
+ */
+Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname,
+                                   int port)
+{
+#if !defined(HAVE_GETADDRINFO_THREADSAFE) && defined(HAVE_GETHOSTBYNAME_R_3)
+  int res;
+#endif
+  Curl_addrinfo *ai = NULL;
+  struct hostent *h = NULL;
+  struct in_addr in;
+  struct hostent *buf = NULL;
+
+  if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
+    /* This is a dotted IP address 123.123.123.123-style */
+    return Curl_ip2addr(AF_INET, &in, hostname, port);
+
+#if defined(HAVE_GETADDRINFO_THREADSAFE)
+  else {
+    struct addrinfo hints;
+    char sbuf[NI_MAXSERV];
+    char *sbufptr = NULL;
+
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = PF_INET;
+    hints.ai_socktype = SOCK_STREAM;
+    if(port) {
+      snprintf(sbuf, sizeof(sbuf), "%d", port);
+      sbufptr = sbuf;
+    }
+
+    (void)Curl_getaddrinfo_ex(hostname, sbufptr, &hints, &ai);
+
+#elif defined(HAVE_GETHOSTBYNAME_R)
+  /*
+   * gethostbyname_r() is the preferred resolve function for many platforms.
+   * Since there are three different versions of it, the following code is
+   * somewhat #ifdef-ridden.
+   */
+  else {
+    int h_errnop;
+
+    buf = calloc(1, CURL_HOSTENT_SIZE);
+    if(!buf)
+      return NULL; /* major failure */
+    /*
+     * The clearing of the buffer is a workaround for a gethostbyname_r bug in
+     * qnx nto and it is also _required_ for some of these functions on some
+     * platforms.
+     */
+
+#if defined(HAVE_GETHOSTBYNAME_R_5)
+    /* Solaris, IRIX and more */
+    h = gethostbyname_r(hostname,
+                        (struct hostent *)buf,
+                        (char *)buf + sizeof(struct hostent),
+                        CURL_HOSTENT_SIZE - sizeof(struct hostent),
+                        &h_errnop);
+
+    /* If the buffer is too small, it returns NULL and sets errno to
+     * ERANGE. The errno is thread safe if this is compiled with
+     * -D_REENTRANT as then the 'errno' variable is a macro defined to get
+     * used properly for threads.
+     */
+
+    if(h) {
+      ;
+    }
+    else
+#elif defined(HAVE_GETHOSTBYNAME_R_6)
+    /* Linux */
+
+    (void)gethostbyname_r(hostname,
+                        (struct hostent *)buf,
+                        (char *)buf + sizeof(struct hostent),
+                        CURL_HOSTENT_SIZE - sizeof(struct hostent),
+                        &h, /* DIFFERENCE */
+                        &h_errnop);
+    /* Redhat 8, using glibc 2.2.93 changed the behavior. Now all of a
+     * sudden this function returns EAGAIN if the given buffer size is too
+     * small. Previous versions are known to return ERANGE for the same
+     * problem.
+     *
+     * This wouldn't be such a big problem if older versions wouldn't
+     * sometimes return EAGAIN on a common failure case. Alas, we can't
+     * assume that EAGAIN *or* ERANGE means ERANGE for any given version of
+     * glibc.
+     *
+     * For now, we do that and thus we may call the function repeatedly and
+     * fail for older glibc versions that return EAGAIN, until we run out of
+     * buffer size (step_size grows beyond CURL_HOSTENT_SIZE).
+     *
+     * If anyone has a better fix, please tell us!
+     *
+     * -------------------------------------------------------------------
+     *
+     * On October 23rd 2003, Dan C dug up more details on the mysteries of
+     * gethostbyname_r() in glibc:
+     *
+     * In glibc 2.2.5 the interface is different (this has also been
+     * discovered in glibc 2.1.1-6 as shipped by Redhat 6). What I can't
+     * explain, is that tests performed on glibc 2.2.4-34 and 2.2.4-32
+     * (shipped/upgraded by Redhat 7.2) don't show this behavior!
+     *
+     * In this "buggy" version, the return code is -1 on error and 'errno'
+     * is set to the ERANGE or EAGAIN code. Note that 'errno' is not a
+     * thread-safe variable.
+     */
+
+    if(!h) /* failure */
+#elif defined(HAVE_GETHOSTBYNAME_R_3)
+    /* AIX, Digital Unix/Tru64, HPUX 10, more? */
+
+    /* For AIX 4.3 or later, we don't use gethostbyname_r() at all, because of
+     * the plain fact that it does not return unique full buffers on each
+     * call, but instead several of the pointers in the hostent structs will
+     * point to the same actual data! This have the unfortunate down-side that
+     * our caching system breaks down horribly. Luckily for us though, AIX 4.3
+     * and more recent versions have a "completely thread-safe"[*] libc where
+     * all the data is stored in thread-specific memory areas making calls to
+     * the plain old gethostbyname() work fine even for multi-threaded
+     * programs.
+     *
+     * This AIX 4.3 or later detection is all made in the configure script.
+     *
+     * Troels Walsted Hansen helped us work this out on March 3rd, 2003.
+     *
+     * [*] = much later we've found out that it isn't at all "completely
+     * thread-safe", but at least the gethostbyname() function is.
+     */
+
+    if(CURL_HOSTENT_SIZE >=
+       (sizeof(struct hostent)+sizeof(struct hostent_data))) {
+
+      /* August 22nd, 2000: Albert Chin-A-Young brought an updated version
+       * that should work! September 20: Richard Prescott worked on the buffer
+       * size dilemma.
+       */
+
+      res = gethostbyname_r(hostname,
+                            (struct hostent *)buf,
+                            (struct hostent_data *)((char *)buf +
+                                                    sizeof(struct hostent)));
+      h_errnop = SOCKERRNO; /* we don't deal with this, but set it anyway */
+    }
+    else
+      res = -1; /* failure, too smallish buffer size */
+
+    if(!res) { /* success */
+
+      h = buf; /* result expected in h */
+
+      /* This is the worst kind of the different gethostbyname_r() interfaces.
+       * Since we don't know how big buffer this particular lookup required,
+       * we can't realloc down the huge alloc without doing closer analysis of
+       * the returned data. Thus, we always use CURL_HOSTENT_SIZE for every
+       * name lookup. Fixing this would require an extra malloc() and then
+       * calling Curl_addrinfo_copy() that subsequent realloc()s down the new
+       * memory area to the actually used amount.
+       */
+    }
+    else
+#endif /* HAVE_...BYNAME_R_5 || HAVE_...BYNAME_R_6 || HAVE_...BYNAME_R_3 */
+    {
+      h = NULL; /* set return code to NULL */
+      free(buf);
+    }
+#else /* HAVE_GETADDRINFO_THREADSAFE || HAVE_GETHOSTBYNAME_R */
+    /*
+     * Here is code for platforms that don't have a thread safe
+     * getaddrinfo() nor gethostbyname_r() function or for which
+     * gethostbyname() is the preferred one.
+     */
+  else {
+    h = gethostbyname((void*)hostname);
+#endif /* HAVE_GETADDRINFO_THREADSAFE || HAVE_GETHOSTBYNAME_R */
+  }
+
+  if(h) {
+    ai = Curl_he2ai(h, port);
+
+    if(buf) /* used a *_r() function */
+      free(buf);
+  }
+
+  return ai;
+}
+#endif /* defined(CURLRES_IPV4) && !defined(CURLRES_ARES) */
diff --git a/lib/curl_hostip6.c b/lib/curl_hostip6.c
new file mode 100644 (file)
index 0000000..cfd6081
--- /dev/null
@@ -0,0 +1,224 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#ifdef HAVE_PROCESS_H
+#include <process.h>
+#endif
+
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_hostip.h"
+#include "curl_hash.h"
+#include "curl_share.h"
+#include "curl_strerror.h"
+#include "curl_url.h"
+#include "curl_inet_pton.h"
+#include "curl_connect.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/***********************************************************************
+ * Only for ipv6-enabled builds
+ **********************************************************************/
+#ifdef CURLRES_IPV6
+
+
+#if defined(CURLDEBUG) && defined(HAVE_GETNAMEINFO)
+/* These are strictly for memory tracing and are using the same style as the
+ * family otherwise present in curl_memdebug.c. I put these ones here since
+ * they require a bunch of structs I didn't want to include there.
+ */
+
+/*
+ * For CURLRES_ARS, this should be written using ares_gethostbyaddr()
+ * (ignoring the fact c-ares doesn't return 'serv').
+ */
+
+int curl_dogetnameinfo(GETNAMEINFO_QUAL_ARG1 GETNAMEINFO_TYPE_ARG1 sa,
+                       GETNAMEINFO_TYPE_ARG2 salen,
+                       char *host, GETNAMEINFO_TYPE_ARG46 hostlen,
+                       char *serv, GETNAMEINFO_TYPE_ARG46 servlen,
+                       GETNAMEINFO_TYPE_ARG7 flags,
+                       int line, const char *source)
+{
+  int res = (getnameinfo)(sa, salen,
+                          host, hostlen,
+                          serv, servlen,
+                          flags);
+  if(0 == res)
+    /* success */
+    curl_memlog("GETNAME %s:%d getnameinfo()\n",
+                source, line);
+  else
+    curl_memlog("GETNAME %s:%d getnameinfo() failed = %d\n",
+                source, line, res);
+  return res;
+}
+#endif /* defined(CURLDEBUG) && defined(HAVE_GETNAMEINFO) */
+
+/*
+ * Curl_ipv6works() returns TRUE if ipv6 seems to work.
+ */
+bool Curl_ipv6works(void)
+{
+  /* the nature of most system is that IPv6 status doesn't come and go
+     during a program's lifetime so we only probe the first time and then we
+     have the info kept for fast re-use */
+  static int ipv6_works = -1;
+  if(-1 == ipv6_works) {
+    /* probe to see if we have a working IPv6 stack */
+    curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0);
+    if(s == CURL_SOCKET_BAD)
+      /* an ipv6 address was requested but we can't get/use one */
+      ipv6_works = 0;
+    else {
+      ipv6_works = 1;
+      Curl_closesocket(NULL, s);
+    }
+  }
+  return (ipv6_works>0)?TRUE:FALSE;
+}
+
+/*
+ * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've
+ * been set and returns TRUE if they are OK.
+ */
+bool Curl_ipvalid(struct connectdata *conn)
+{
+  if(conn->ip_version == CURL_IPRESOLVE_V6)
+    return Curl_ipv6works();
+  return TRUE;
+}
+
+#if defined(CURLRES_SYNCH)
+
+#ifdef DEBUG_ADDRINFO
+static void dump_addrinfo(struct connectdata *conn, const Curl_addrinfo *ai)
+{
+  printf("dump_addrinfo:\n");
+  for(; ai; ai = ai->ai_next) {
+    char  buf[INET6_ADDRSTRLEN];
+
+    printf("    fam %2d, CNAME %s, ",
+           ai->ai_family, ai->ai_canonname ? ai->ai_canonname : "<none>");
+    if(Curl_printable_address(ai, buf, sizeof(buf)))
+      printf("%s\n", buf);
+    else
+      printf("failed; %s\n", Curl_strerror(conn, SOCKERRNO));
+  }
+}
+#else
+#define dump_addrinfo(x,y) Curl_nop_stmt
+#endif
+
+/*
+ * Curl_getaddrinfo() when built ipv6-enabled (non-threading and
+ * non-ares version).
+ *
+ * Returns name information about the given hostname and port number. If
+ * successful, the 'addrinfo' is returned and the forth argument will point to
+ * memory we need to free after use. That memory *MUST* be freed with
+ * Curl_freeaddrinfo(), nothing else.
+ */
+Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
+                                const char *hostname,
+                                int port,
+                                int *waitp)
+{
+  struct addrinfo hints;
+  Curl_addrinfo *res;
+  int error;
+  char sbuf[NI_MAXSERV];
+  char *sbufptr = NULL;
+  char addrbuf[128];
+  int pf;
+  struct SessionHandle *data = conn->data;
+
+  *waitp = 0; /* synchronous response only */
+
+  /*
+   * Check if a limited name resolve has been requested.
+   */
+  switch(conn->ip_version) {
+  case CURL_IPRESOLVE_V4:
+    pf = PF_INET;
+    break;
+  case CURL_IPRESOLVE_V6:
+    pf = PF_INET6;
+    break;
+  default:
+    pf = PF_UNSPEC;
+    break;
+  }
+
+  if((pf != PF_INET) && !Curl_ipv6works())
+    /* the stack seems to be a non-ipv6 one */
+    pf = PF_INET;
+
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_family = pf;
+  hints.ai_socktype = conn->socktype;
+
+  if((1 == Curl_inet_pton(AF_INET, hostname, addrbuf)) ||
+     (1 == Curl_inet_pton(AF_INET6, hostname, addrbuf))) {
+    /* the given address is numerical only, prevent a reverse lookup */
+    hints.ai_flags = AI_NUMERICHOST;
+  }
+
+  if(port) {
+    snprintf(sbuf, sizeof(sbuf), "%d", port);
+    sbufptr=sbuf;
+  }
+  error = Curl_getaddrinfo_ex(hostname, sbufptr, &hints, &res);
+  if(error) {
+    infof(data, "getaddrinfo(3) failed for %s:%d\n", hostname, port);
+    return NULL;
+  }
+
+  dump_addrinfo(conn, res);
+
+  return res;
+}
+#endif /* CURLRES_SYNCH */
+#endif /* CURLRES_IPV6 */
+
diff --git a/lib/curl_hostsyn.c b/lib/curl_hostsyn.c
new file mode 100644 (file)
index 0000000..9a26f8d
--- /dev/null
@@ -0,0 +1,75 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#ifdef HAVE_PROCESS_H
+#include <process.h>
+#endif
+
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_hostip.h"
+#include "curl_hash.h"
+#include "curl_share.h"
+#include "curl_strerror.h"
+#include "curl_url.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/***********************************************************************
+ * Only for builds using synchronous name resolves
+ **********************************************************************/
+#ifdef CURLRES_SYNCH
+
+/*
+ * Function provided by the resolver backend to set DNS servers to use.
+ */
+CURLcode Curl_set_dns_servers(struct SessionHandle *data,
+                              char *servers)
+{
+  (void)data;
+  (void)servers;
+  return CURLE_NOT_BUILT_IN;
+
+}
+
+#endif /* truly sync */
diff --git a/lib/curl_http.c b/lib/curl_http.c
new file mode 100644 (file)
index 0000000..420361c
--- /dev/null
@@ -0,0 +1,3506 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_HTTP
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#include "curl_urldata.h"
+#include <curl/curl.h>
+#include "curl_transfer.h"
+#include "curl_sendf.h"
+#include "curl_formdata.h"
+#include "curl_progress.h"
+#include "curl_base64.h"
+#include "curl_cookie.h"
+#include "curl_strequal.h"
+#include "curl_sslgen.h"
+#include "curl_http_digest.h"
+#include "curl_ntlm.h"
+#include "curl_ntlm_wb.h"
+#include "curl_http_negotiate.h"
+#include "curl_url.h"
+#include "curl_share.h"
+#include "curl_hostip.h"
+#include "curl_http.h"
+#include "curl_memory.h"
+#include "curl_select.h"
+#include "curl_parsedate.h" /* for the week day and month names */
+#include "curl_strtoofft.h"
+#include "curl_multiif.h"
+#include "curl_rawstr.h"
+#include "curl_content_encoding.h"
+#include "curl_http_proxy.h"
+#include "curl_warnless.h"
+#include "curl_non_ascii.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/*
+ * Forward declarations.
+ */
+
+static int http_getsock_do(struct connectdata *conn,
+                           curl_socket_t *socks,
+                           int numsocks);
+static int http_should_fail(struct connectdata *conn);
+
+#ifdef USE_SSL
+static CURLcode https_connecting(struct connectdata *conn, bool *done);
+static int https_getsock(struct connectdata *conn,
+                         curl_socket_t *socks,
+                         int numsocks);
+#else
+#define https_connecting(x,y) CURLE_COULDNT_CONNECT
+#endif
+
+/*
+ * HTTP handler interface.
+ */
+const struct Curl_handler Curl_handler_http = {
+  "HTTP",                               /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  Curl_http,                            /* do_it */
+  Curl_http_done,                       /* done */
+  ZERO_NULL,                            /* do_more */
+  Curl_http_connect,                    /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  http_getsock_do,                      /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  PORT_HTTP,                            /* defport */
+  CURLPROTO_HTTP,                       /* protocol */
+  PROTOPT_NONE                          /* flags */
+};
+
+#ifdef USE_SSL
+/*
+ * HTTPS handler interface.
+ */
+const struct Curl_handler Curl_handler_https = {
+  "HTTPS",                              /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  Curl_http,                            /* do_it */
+  Curl_http_done,                       /* done */
+  ZERO_NULL,                            /* do_more */
+  Curl_http_connect,                    /* connect_it */
+  https_connecting,                     /* connecting */
+  ZERO_NULL,                            /* doing */
+  https_getsock,                        /* proto_getsock */
+  http_getsock_do,                      /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  PORT_HTTPS,                           /* defport */
+  CURLPROTO_HTTP | CURLPROTO_HTTPS,     /* protocol */
+  PROTOPT_SSL                           /* flags */
+};
+#endif
+
+
+/*
+ * checkheaders() checks the linked list of custom HTTP headers for a
+ * particular header (prefix).
+ *
+ * Returns a pointer to the first matching header or NULL if none matched.
+ */
+char *Curl_checkheaders(struct SessionHandle *data, const char *thisheader)
+{
+  struct curl_slist *head;
+  size_t thislen = strlen(thisheader);
+
+  for(head = data->set.headers; head; head=head->next) {
+    if(Curl_raw_nequal(head->data, thisheader, thislen))
+      return head->data;
+  }
+  return NULL;
+}
+
+/*
+ * Strip off leading and trailing whitespace from the value in the
+ * given HTTP header line and return a strdupped copy. Returns NULL in
+ * case of allocation failure. Returns an empty string if the header value
+ * consists entirely of whitespace.
+ */
+static char *copy_header_value(const char *h)
+{
+  const char *start;
+  const char *end;
+  char *value;
+  size_t len;
+
+  DEBUGASSERT(h);
+
+  /* Find the end of the header name */
+  while(*h && (*h != ':'))
+    ++h;
+
+  if(*h)
+    /* Skip over colon */
+    ++h;
+
+  /* Find the first non-space letter */
+  start = h;
+  while(*start && ISSPACE(*start))
+    start++;
+
+  /* data is in the host encoding so
+     use '\r' and '\n' instead of 0x0d and 0x0a */
+  end = strchr(start, '\r');
+  if(!end)
+    end = strchr(start, '\n');
+  if(!end)
+    end = strchr(start, '\0');
+  if(!end)
+    return NULL;
+
+  /* skip all trailing space letters */
+  while((end > start) && ISSPACE(*end))
+    end--;
+
+  /* get length of the type */
+  len = end-start+1;
+
+  value = malloc(len + 1);
+  if(!value)
+    return NULL;
+
+  memcpy(value, start, len);
+  value[len] = 0; /* zero terminate */
+
+  return value;
+}
+
+/*
+ * http_output_basic() sets up an Authorization: header (or the proxy version)
+ * for HTTP Basic authentication.
+ *
+ * Returns CURLcode.
+ */
+static CURLcode http_output_basic(struct connectdata *conn, bool proxy)
+{
+  size_t size = 0;
+  char *authorization = NULL;
+  struct SessionHandle *data = conn->data;
+  char **userp;
+  const char *user;
+  const char *pwd;
+  CURLcode error;
+
+  if(proxy) {
+    userp = &conn->allocptr.proxyuserpwd;
+    user = conn->proxyuser;
+    pwd = conn->proxypasswd;
+  }
+  else {
+    userp = &conn->allocptr.userpwd;
+    user = conn->user;
+    pwd = conn->passwd;
+  }
+
+  snprintf(data->state.buffer, sizeof(data->state.buffer), "%s:%s", user, pwd);
+
+  error = Curl_base64_encode(data,
+                             data->state.buffer, strlen(data->state.buffer),
+                             &authorization, &size);
+  if(error)
+    return error;
+
+  if(!authorization)
+    return CURLE_REMOTE_ACCESS_DENIED;
+
+  Curl_safefree(*userp);
+  *userp = aprintf("%sAuthorization: Basic %s\r\n",
+                   proxy?"Proxy-":"",
+                   authorization);
+  free(authorization);
+  if(!*userp)
+    return CURLE_OUT_OF_MEMORY;
+
+  return CURLE_OK;
+}
+
+/* pickoneauth() selects the most favourable authentication method from the
+ * ones available and the ones we want.
+ *
+ * return TRUE if one was picked
+ */
+static bool pickoneauth(struct auth *pick)
+{
+  bool picked;
+  /* only deal with authentication we want */
+  unsigned long avail = pick->avail & pick->want;
+  picked = TRUE;
+
+  /* The order of these checks is highly relevant, as this will be the order
+     of preference in case of the existence of multiple accepted types. */
+  if(avail & CURLAUTH_GSSNEGOTIATE)
+    pick->picked = CURLAUTH_GSSNEGOTIATE;
+  else if(avail & CURLAUTH_DIGEST)
+    pick->picked = CURLAUTH_DIGEST;
+  else if(avail & CURLAUTH_NTLM)
+    pick->picked = CURLAUTH_NTLM;
+  else if(avail & CURLAUTH_NTLM_WB)
+    pick->picked = CURLAUTH_NTLM_WB;
+  else if(avail & CURLAUTH_BASIC)
+    pick->picked = CURLAUTH_BASIC;
+  else {
+    pick->picked = CURLAUTH_PICKNONE; /* we select to use nothing */
+    picked = FALSE;
+  }
+  pick->avail = CURLAUTH_NONE; /* clear it here */
+
+  return picked;
+}
+
+/*
+ * Curl_http_perhapsrewind()
+ *
+ * If we are doing POST or PUT {
+ *   If we have more data to send {
+ *     If we are doing NTLM {
+ *       Keep sending since we must not disconnect
+ *     }
+ *     else {
+ *       If there is more than just a little data left to send, close
+ *       the current connection by force.
+ *     }
+ *   }
+ *   If we have sent any data {
+ *     If we don't have track of all the data {
+ *       call app to tell it to rewind
+ *     }
+ *     else {
+ *       rewind internally so that the operation can restart fine
+ *     }
+ *   }
+ * }
+ */
+static CURLcode http_perhapsrewind(struct connectdata *conn)
+{
+  struct SessionHandle *data = conn->data;
+  struct HTTP *http = data->state.proto.http;
+  curl_off_t bytessent;
+  curl_off_t expectsend = -1; /* default is unknown */
+
+  if(!http)
+    /* If this is still NULL, we have not reach very far and we can safely
+       skip this rewinding stuff */
+    return CURLE_OK;
+
+  switch(data->set.httpreq) {
+  case HTTPREQ_GET:
+  case HTTPREQ_HEAD:
+    return CURLE_OK;
+  default:
+    break;
+  }
+
+  bytessent = http->writebytecount;
+
+  if(conn->bits.authneg)
+    /* This is a state where we are known to be negotiating and we don't send
+       any data then. */
+    expectsend = 0;
+  else {
+    /* figure out how much data we are expected to send */
+    switch(data->set.httpreq) {
+    case HTTPREQ_POST:
+      if(data->set.postfieldsize != -1)
+        expectsend = data->set.postfieldsize;
+      else if(data->set.postfields)
+        expectsend = (curl_off_t)strlen(data->set.postfields);
+      break;
+    case HTTPREQ_PUT:
+      if(data->set.infilesize != -1)
+        expectsend = data->set.infilesize;
+      break;
+    case HTTPREQ_POST_FORM:
+      expectsend = http->postsize;
+      break;
+    default:
+      break;
+    }
+  }
+
+  conn->bits.rewindaftersend = FALSE; /* default */
+
+  if((expectsend == -1) || (expectsend > bytessent)) {
+    /* There is still data left to send */
+    if((data->state.authproxy.picked == CURLAUTH_NTLM) ||
+       (data->state.authhost.picked == CURLAUTH_NTLM) ||
+       (data->state.authproxy.picked == CURLAUTH_NTLM_WB) ||
+       (data->state.authhost.picked == CURLAUTH_NTLM_WB)) {
+      if(((expectsend - bytessent) < 2000) ||
+         (conn->ntlm.state != NTLMSTATE_NONE) ||
+         (conn->proxyntlm.state != NTLMSTATE_NONE)) {
+        /* The NTLM-negotiation has started *OR* there is just a little (<2K)
+           data left to send, keep on sending. */
+
+        /* rewind data when completely done sending! */
+        if(!conn->bits.authneg) {
+          conn->bits.rewindaftersend = TRUE;
+          infof(data, "Rewind stream after send\n");
+        }
+
+        return CURLE_OK;
+      }
+      if(conn->bits.close)
+        /* this is already marked to get closed */
+        return CURLE_OK;
+
+      infof(data, "NTLM send, close instead of sending %" FORMAT_OFF_T
+            " bytes\n", (curl_off_t)(expectsend - bytessent));
+    }
+
+    /* This is not NTLM or many bytes left to send: close
+     */
+    conn->bits.close = TRUE;
+    data->req.size = 0; /* don't download any more than 0 bytes */
+
+    /* There still is data left to send, but this connection is marked for
+       closure so we can safely do the rewind right now */
+  }
+
+  if(bytessent)
+    /* we rewind now at once since if we already sent something */
+    return Curl_readrewind(conn);
+
+  return CURLE_OK;
+}
+
+/*
+ * Curl_http_auth_act() gets called when all HTTP headers have been received
+ * and it checks what authentication methods that are available and decides
+ * which one (if any) to use. It will set 'newurl' if an auth method was
+ * picked.
+ */
+
+CURLcode Curl_http_auth_act(struct connectdata *conn)
+{
+  struct SessionHandle *data = conn->data;
+  bool pickhost = FALSE;
+  bool pickproxy = FALSE;
+  CURLcode code = CURLE_OK;
+
+  if(100 <= data->req.httpcode && 199 >= data->req.httpcode)
+    /* this is a transient response code, ignore */
+    return CURLE_OK;
+
+  if(data->state.authproblem)
+    return data->set.http_fail_on_error?CURLE_HTTP_RETURNED_ERROR:CURLE_OK;
+
+  if(conn->bits.user_passwd &&
+     ((data->req.httpcode == 401) ||
+      (conn->bits.authneg && data->req.httpcode < 300))) {
+    pickhost = pickoneauth(&data->state.authhost);
+    if(!pickhost)
+      data->state.authproblem = TRUE;
+  }
+  if(conn->bits.proxy_user_passwd &&
+     ((data->req.httpcode == 407) ||
+      (conn->bits.authneg && data->req.httpcode < 300))) {
+    pickproxy = pickoneauth(&data->state.authproxy);
+    if(!pickproxy)
+      data->state.authproblem = TRUE;
+  }
+
+  if(pickhost || pickproxy) {
+    /* In case this is GSS auth, the newurl field is already allocated so
+       we must make sure to free it before allocating a new one. As figured
+       out in bug #2284386 */
+    Curl_safefree(data->req.newurl);
+    data->req.newurl = strdup(data->change.url); /* clone URL */
+    if(!data->req.newurl)
+      return CURLE_OUT_OF_MEMORY;
+
+    if((data->set.httpreq != HTTPREQ_GET) &&
+       (data->set.httpreq != HTTPREQ_HEAD) &&
+       !conn->bits.rewindaftersend) {
+      code = http_perhapsrewind(conn);
+      if(code)
+        return code;
+    }
+  }
+
+  else if((data->req.httpcode < 300) &&
+          (!data->state.authhost.done) &&
+          conn->bits.authneg) {
+    /* no (known) authentication available,
+       authentication is not "done" yet and
+       no authentication seems to be required and
+       we didn't try HEAD or GET */
+    if((data->set.httpreq != HTTPREQ_GET) &&
+       (data->set.httpreq != HTTPREQ_HEAD)) {
+      data->req.newurl = strdup(data->change.url); /* clone URL */
+      if(!data->req.newurl)
+        return CURLE_OUT_OF_MEMORY;
+      data->state.authhost.done = TRUE;
+    }
+  }
+  if(http_should_fail(conn)) {
+    failf (data, "The requested URL returned error: %d",
+           data->req.httpcode);
+    code = CURLE_HTTP_RETURNED_ERROR;
+  }
+
+  return code;
+}
+
+
+/*
+ * Output the correct authentication header depending on the auth type
+ * and whether or not it is to a proxy.
+ */
+static CURLcode
+output_auth_headers(struct connectdata *conn,
+                    struct auth *authstatus,
+                    const char *request,
+                    const char *path,
+                    bool proxy)
+{
+  struct SessionHandle *data = conn->data;
+  const char *auth=NULL;
+  CURLcode result = CURLE_OK;
+#ifdef USE_HTTP_NEGOTIATE
+  struct negotiatedata *negdata = proxy?
+    &data->state.proxyneg:&data->state.negotiate;
+#endif
+
+#ifdef CURL_DISABLE_CRYPTO_AUTH
+  (void)request;
+  (void)path;
+#endif
+
+#ifdef USE_HTTP_NEGOTIATE
+  negdata->state = GSS_AUTHNONE;
+  if((authstatus->picked == CURLAUTH_GSSNEGOTIATE) &&
+     negdata->context && !GSS_ERROR(negdata->status)) {
+    auth="GSS-Negotiate";
+    result = Curl_output_negotiate(conn, proxy);
+    if(result)
+      return result;
+    authstatus->done = TRUE;
+    negdata->state = GSS_AUTHSENT;
+  }
+  else
+#endif
+#ifdef USE_NTLM
+  if(authstatus->picked == CURLAUTH_NTLM) {
+    auth="NTLM";
+    result = Curl_output_ntlm(conn, proxy);
+    if(result)
+      return result;
+  }
+  else
+#endif
+#if defined(USE_NTLM) && defined(NTLM_WB_ENABLED)
+  if(authstatus->picked == CURLAUTH_NTLM_WB) {
+    auth="NTLM_WB";
+    result = Curl_output_ntlm_wb(conn, proxy);
+    if(result)
+      return result;
+  }
+  else
+#endif
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+  if(authstatus->picked == CURLAUTH_DIGEST) {
+    auth="Digest";
+    result = Curl_output_digest(conn,
+                                proxy,
+                                (const unsigned char *)request,
+                                (const unsigned char *)path);
+    if(result)
+      return result;
+  }
+  else
+#endif
+  if(authstatus->picked == CURLAUTH_BASIC) {
+    /* Basic */
+    if((proxy && conn->bits.proxy_user_passwd &&
+       !Curl_checkheaders(data, "Proxy-authorization:")) ||
+       (!proxy && conn->bits.user_passwd &&
+       !Curl_checkheaders(data, "Authorization:"))) {
+      auth="Basic";
+      result = http_output_basic(conn, proxy);
+      if(result)
+        return result;
+    }
+    /* NOTE: this function should set 'done' TRUE, as the other auth
+       functions work that way */
+    authstatus->done = TRUE;
+  }
+
+  if(auth) {
+    infof(data, "%s auth using %s with user '%s'\n",
+          proxy?"Proxy":"Server", auth,
+          proxy?(conn->proxyuser?conn->proxyuser:""):
+                (conn->user?conn->user:""));
+    authstatus->multi = (!authstatus->done) ? TRUE : FALSE;
+  }
+  else
+    authstatus->multi = FALSE;
+
+  return CURLE_OK;
+}
+
+/**
+ * Curl_http_output_auth() setups the authentication headers for the
+ * host/proxy and the correct authentication
+ * method. conn->data->state.authdone is set to TRUE when authentication is
+ * done.
+ *
+ * @param conn all information about the current connection
+ * @param request pointer to the request keyword
+ * @param path pointer to the requested path
+ * @param proxytunnel boolean if this is the request setting up a "proxy
+ * tunnel"
+ *
+ * @returns CURLcode
+ */
+CURLcode
+Curl_http_output_auth(struct connectdata *conn,
+                      const char *request,
+                      const char *path,
+                      bool proxytunnel) /* TRUE if this is the request setting
+                                           up the proxy tunnel */
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  struct auth *authhost;
+  struct auth *authproxy;
+
+  DEBUGASSERT(data);
+
+  authhost = &data->state.authhost;
+  authproxy = &data->state.authproxy;
+
+  if((conn->bits.httpproxy && conn->bits.proxy_user_passwd) ||
+     conn->bits.user_passwd)
+    /* continue please */ ;
+  else {
+    authhost->done = TRUE;
+    authproxy->done = TRUE;
+    return CURLE_OK; /* no authentication with no user or password */
+  }
+
+  if(authhost->want && !authhost->picked)
+    /* The app has selected one or more methods, but none has been picked
+       so far by a server round-trip. Then we set the picked one to the
+       want one, and if this is one single bit it'll be used instantly. */
+    authhost->picked = authhost->want;
+
+  if(authproxy->want && !authproxy->picked)
+    /* The app has selected one or more methods, but none has been picked so
+       far by a proxy round-trip. Then we set the picked one to the want one,
+       and if this is one single bit it'll be used instantly. */
+    authproxy->picked = authproxy->want;
+
+#ifndef CURL_DISABLE_PROXY
+  /* Send proxy authentication header if needed */
+  if(conn->bits.httpproxy &&
+      (conn->bits.tunnel_proxy == proxytunnel)) {
+    result = output_auth_headers(conn, authproxy, request, path, TRUE);
+    if(result)
+      return result;
+  }
+  else
+#else
+  (void)proxytunnel;
+#endif /* CURL_DISABLE_PROXY */
+    /* we have no proxy so let's pretend we're done authenticating
+       with it */
+    authproxy->done = TRUE;
+
+  /* To prevent the user+password to get sent to other than the original
+     host due to a location-follow, we do some weirdo checks here */
+  if(!data->state.this_is_a_follow ||
+     conn->bits.netrc ||
+     !data->state.first_host ||
+     data->set.http_disable_hostname_check_before_authentication ||
+     Curl_raw_equal(data->state.first_host, conn->host.name)) {
+    result = output_auth_headers(conn, authhost, request, path, FALSE);
+  }
+  else
+    authhost->done = TRUE;
+
+  return result;
+}
+
+
+/*
+ * Curl_http_input_auth() deals with Proxy-Authenticate: and WWW-Authenticate:
+ * headers. They are dealt with both in the curl_transfer.c main loop and in
+ * the proxy CONNECT loop.
+ */
+
+CURLcode Curl_http_input_auth(struct connectdata *conn,
+                              int httpcode,
+                              const char *header) /* the first non-space */
+{
+  /*
+   * This resource requires authentication
+   */
+  struct SessionHandle *data = conn->data;
+
+  unsigned long *availp;
+  const char *start;
+  struct auth *authp;
+
+  if(httpcode == 407) {
+    start = header+strlen("Proxy-authenticate:");
+    availp = &data->info.proxyauthavail;
+    authp = &data->state.authproxy;
+  }
+  else {
+    start = header+strlen("WWW-Authenticate:");
+    availp = &data->info.httpauthavail;
+    authp = &data->state.authhost;
+  }
+
+  /* pass all white spaces */
+  while(*start && ISSPACE(*start))
+    start++;
+
+  /*
+   * Here we check if we want the specific single authentication (using ==) and
+   * if we do, we initiate usage of it.
+   *
+   * If the provided authentication is wanted as one out of several accepted
+   * types (using &), we OR this authentication type to the authavail
+   * variable.
+   *
+   * Note:
+   *
+   * ->picked is first set to the 'want' value (one or more bits) before the
+   * request is sent, and then it is again set _after_ all response 401/407
+   * headers have been received but then only to a single preferred method
+   * (bit).
+   *
+   */
+
+  while(*start) {
+#ifdef USE_HTTP_NEGOTIATE
+    if(checkprefix("GSS-Negotiate", start) ||
+       checkprefix("Negotiate", start)) {
+      int neg;
+      *availp |= CURLAUTH_GSSNEGOTIATE;
+      authp->avail |= CURLAUTH_GSSNEGOTIATE;
+
+      if(authp->picked == CURLAUTH_GSSNEGOTIATE) {
+        if(data->state.negotiate.state == GSS_AUTHSENT) {
+          /* if we sent GSS authentication in the outgoing request and we get
+             this back, we're in trouble */
+          infof(data, "Authentication problem. Ignoring this.\n");
+          data->state.authproblem = TRUE;
+        }
+        else {
+          neg = Curl_input_negotiate(conn, (bool)(httpcode == 407), start);
+          if(neg == 0) {
+            DEBUGASSERT(!data->req.newurl);
+            data->req.newurl = strdup(data->change.url);
+            if(!data->req.newurl)
+              return CURLE_OUT_OF_MEMORY;
+            data->state.authproblem = FALSE;
+            /* we received GSS auth info and we dealt with it fine */
+            data->state.negotiate.state = GSS_AUTHRECV;
+          }
+          else
+            data->state.authproblem = TRUE;
+        }
+      }
+    }
+    else
+#endif
+#ifdef USE_NTLM
+      /* NTLM support requires the SSL crypto libs */
+      if(checkprefix("NTLM", start)) {
+        *availp |= CURLAUTH_NTLM;
+        authp->avail |= CURLAUTH_NTLM;
+        if(authp->picked == CURLAUTH_NTLM ||
+           authp->picked == CURLAUTH_NTLM_WB) {
+          /* NTLM authentication is picked and activated */
+          CURLcode ntlm =
+            Curl_input_ntlm(conn, (httpcode == 407)?TRUE:FALSE, start);
+          if(CURLE_OK == ntlm) {
+            data->state.authproblem = FALSE;
+#ifdef NTLM_WB_ENABLED
+            if(authp->picked == CURLAUTH_NTLM_WB) {
+              *availp &= ~CURLAUTH_NTLM;
+              authp->avail &= ~CURLAUTH_NTLM;
+              *availp |= CURLAUTH_NTLM_WB;
+              authp->avail |= CURLAUTH_NTLM_WB;
+
+              /* Get the challenge-message which will be passed to
+               * ntlm_auth for generating the type 3 message later */
+              while(*start && ISSPACE(*start))
+                start++;
+              if(checkprefix("NTLM", start)) {
+                start += strlen("NTLM");
+                while(*start && ISSPACE(*start))
+                  start++;
+                if(*start)
+                  if((conn->challenge_header = strdup(start)) == NULL)
+                    return CURLE_OUT_OF_MEMORY;
+              }
+            }
+#endif
+          }
+          else {
+            infof(data, "Authentication problem. Ignoring this.\n");
+            data->state.authproblem = TRUE;
+          }
+        }
+      }
+      else
+#endif
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+        if(checkprefix("Digest", start)) {
+          if((authp->avail & CURLAUTH_DIGEST) != 0) {
+            infof(data, "Ignoring duplicate digest auth header.\n");
+          }
+          else {
+            CURLdigest dig;
+            *availp |= CURLAUTH_DIGEST;
+            authp->avail |= CURLAUTH_DIGEST;
+
+            /* We call this function on input Digest headers even if Digest
+             * authentication isn't activated yet, as we need to store the
+             * incoming data from this header in case we are gonna use
+             * Digest. */
+            dig = Curl_input_digest(conn, (httpcode == 407)?TRUE:FALSE, start);
+
+            if(CURLDIGEST_FINE != dig) {
+              infof(data, "Authentication problem. Ignoring this.\n");
+              data->state.authproblem = TRUE;
+            }
+          }
+        }
+        else
+#endif
+          if(checkprefix("Basic", start)) {
+            *availp |= CURLAUTH_BASIC;
+            authp->avail |= CURLAUTH_BASIC;
+            if(authp->picked == CURLAUTH_BASIC) {
+              /* We asked for Basic authentication but got a 40X back
+                 anyway, which basically means our name+password isn't
+                 valid. */
+              authp->avail = CURLAUTH_NONE;
+              infof(data, "Authentication problem. Ignoring this.\n");
+              data->state.authproblem = TRUE;
+            }
+          }
+
+    /* there may be multiple methods on one line, so keep reading */
+    while(*start && *start != ',') /* read up to the next comma */
+      start++;
+    if(*start == ',') /* if we're on a comma, skip it */
+      start++;
+    while(*start && ISSPACE(*start))
+      start++;
+  }
+  return CURLE_OK;
+}
+
+/**
+ * http_should_fail() determines whether an HTTP response has gotten us
+ * into an error state or not.
+ *
+ * @param conn all information about the current connection
+ *
+ * @retval 0 communications should continue
+ *
+ * @retval 1 communications should not continue
+ */
+static int http_should_fail(struct connectdata *conn)
+{
+  struct SessionHandle *data;
+  int httpcode;
+
+  DEBUGASSERT(conn);
+  data = conn->data;
+  DEBUGASSERT(data);
+
+  httpcode = data->req.httpcode;
+
+  /*
+  ** If we haven't been asked to fail on error,
+  ** don't fail.
+  */
+  if(!data->set.http_fail_on_error)
+    return 0;
+
+  /*
+  ** Any code < 400 is never terminal.
+  */
+  if(httpcode < 400)
+    return 0;
+
+  if(data->state.resume_from &&
+     (data->set.httpreq==HTTPREQ_GET) &&
+     (httpcode == 416)) {
+    /* "Requested Range Not Satisfiable", just proceed and
+       pretend this is no error */
+    return 0;
+  }
+
+  /*
+  ** Any code >= 400 that's not 401 or 407 is always
+  ** a terminal error
+  */
+  if((httpcode != 401) &&
+      (httpcode != 407))
+    return 1;
+
+  /*
+  ** All we have left to deal with is 401 and 407
+  */
+  DEBUGASSERT((httpcode == 401) || (httpcode == 407));
+
+  /*
+  ** Examine the current authentication state to see if this
+  ** is an error.  The idea is for this function to get
+  ** called after processing all the headers in a response
+  ** message.  So, if we've been to asked to authenticate a
+  ** particular stage, and we've done it, we're OK.  But, if
+  ** we're already completely authenticated, it's not OK to
+  ** get another 401 or 407.
+  **
+  ** It is possible for authentication to go stale such that
+  ** the client needs to reauthenticate.  Once that info is
+  ** available, use it here.
+  */
+
+  /*
+  ** Either we're not authenticating, or we're supposed to
+  ** be authenticating something else.  This is an error.
+  */
+  if((httpcode == 401) && !conn->bits.user_passwd)
+    return TRUE;
+  if((httpcode == 407) && !conn->bits.proxy_user_passwd)
+    return TRUE;
+
+  return data->state.authproblem;
+}
+
+/*
+ * readmoredata() is a "fread() emulation" to provide POST and/or request
+ * data. It is used when a huge POST is to be made and the entire chunk wasn't
+ * sent in the first send(). This function will then be called from the
+ * curl_transfer.c loop when more data is to be sent to the peer.
+ *
+ * Returns the amount of bytes it filled the buffer with.
+ */
+static size_t readmoredata(char *buffer,
+                           size_t size,
+                           size_t nitems,
+                           void *userp)
+{
+  struct connectdata *conn = (struct connectdata *)userp;
+  struct HTTP *http = conn->data->state.proto.http;
+  size_t fullsize = size * nitems;
+
+  if(0 == http->postsize)
+    /* nothing to return */
+    return 0;
+
+  /* make sure that a HTTP request is never sent away chunked! */
+  conn->data->req.forbidchunk = (http->sending == HTTPSEND_REQUEST)?TRUE:FALSE;
+
+  if(http->postsize <= (curl_off_t)fullsize) {
+    memcpy(buffer, http->postdata, (size_t)http->postsize);
+    fullsize = (size_t)http->postsize;
+
+    if(http->backup.postsize) {
+      /* move backup data into focus and continue on that */
+      http->postdata = http->backup.postdata;
+      http->postsize = http->backup.postsize;
+      conn->fread_func = http->backup.fread_func;
+      conn->fread_in = http->backup.fread_in;
+
+      http->sending++; /* move one step up */
+
+      http->backup.postsize=0;
+    }
+    else
+      http->postsize = 0;
+
+    return fullsize;
+  }
+
+  memcpy(buffer, http->postdata, fullsize);
+  http->postdata += fullsize;
+  http->postsize -= fullsize;
+
+  return fullsize;
+}
+
+/* ------------------------------------------------------------------------- */
+/* add_buffer functions */
+
+/*
+ * Curl_add_buffer_init() sets up and returns a fine buffer struct
+ */
+Curl_send_buffer *Curl_add_buffer_init(void)
+{
+  return calloc(1, sizeof(Curl_send_buffer));
+}
+
+/*
+ * Curl_add_buffer_send() sends a header buffer and frees all associated
+ * memory.  Body data may be appended to the header data if desired.
+ *
+ * Returns CURLcode
+ */
+CURLcode Curl_add_buffer_send(Curl_send_buffer *in,
+                              struct connectdata *conn,
+
+                               /* add the number of sent bytes to this
+                                  counter */
+                              long *bytes_written,
+
+                               /* how much of the buffer contains body data */
+                              size_t included_body_bytes,
+                              int socketindex)
+
+{
+  ssize_t amount;
+  CURLcode res;
+  char *ptr;
+  size_t size;
+  struct HTTP *http = conn->data->state.proto.http;
+  size_t sendsize;
+  curl_socket_t sockfd;
+  size_t headersize;
+
+  DEBUGASSERT(socketindex <= SECONDARYSOCKET);
+
+  sockfd = conn->sock[socketindex];
+
+  /* The looping below is required since we use non-blocking sockets, but due
+     to the circumstances we will just loop and try again and again etc */
+
+  ptr = in->buffer;
+  size = in->size_used;
+
+  headersize = size - included_body_bytes; /* the initial part that isn't body
+                                              is header */
+
+  DEBUGASSERT(size > included_body_bytes);
+
+  res = Curl_convert_to_network(conn->data, ptr, headersize);
+  /* Curl_convert_to_network calls failf if unsuccessful */
+  if(res) {
+    /* conversion failed, free memory and return to the caller */
+    if(in->buffer)
+      free(in->buffer);
+    free(in);
+    return res;
+  }
+
+  if(conn->handler->flags & PROTOPT_SSL) {
+    /* We never send more than CURL_MAX_WRITE_SIZE bytes in one single chunk
+       when we speak HTTPS, as if only a fraction of it is sent now, this data
+       needs to fit into the normal read-callback buffer later on and that
+       buffer is using this size.
+    */
+
+    sendsize= (size > CURL_MAX_WRITE_SIZE)?CURL_MAX_WRITE_SIZE:size;
+
+    /* OpenSSL is very picky and we must send the SAME buffer pointer to the
+       library when we attempt to re-send this buffer. Sending the same data
+       is not enough, we must use the exact same address. For this reason, we
+       must copy the data to the uploadbuffer first, since that is the buffer
+       we will be using if this send is retried later.
+    */
+    memcpy(conn->data->state.uploadbuffer, ptr, sendsize);
+    ptr = conn->data->state.uploadbuffer;
+  }
+  else
+    sendsize = size;
+
+  res = Curl_write(conn, sockfd, ptr, sendsize, &amount);
+
+  if(CURLE_OK == res) {
+    /*
+     * Note that we may not send the entire chunk at once, and we have a set
+     * number of data bytes at the end of the big buffer (out of which we may
+     * only send away a part).
+     */
+    /* how much of the header that was sent */
+    size_t headlen = (size_t)amount>headersize?headersize:(size_t)amount;
+    size_t bodylen = amount - headlen;
+
+    if(conn->data->set.verbose) {
+      /* this data _may_ contain binary stuff */
+      Curl_debug(conn->data, CURLINFO_HEADER_OUT, ptr, headlen, conn);
+      if(bodylen) {
+        /* there was body data sent beyond the initial header part, pass that
+           on to the debug callback too */
+        Curl_debug(conn->data, CURLINFO_DATA_OUT,
+                   ptr+headlen, bodylen, conn);
+      }
+    }
+    if(bodylen)
+      /* since we sent a piece of the body here, up the byte counter for it
+         accordingly */
+      http->writebytecount += bodylen;
+
+    /* 'amount' can never be a very large value here so typecasting it so a
+       signed 31 bit value should not cause problems even if ssize_t is
+       64bit */
+    *bytes_written += (long)amount;
+
+    if(http) {
+      if((size_t)amount != size) {
+        /* The whole request could not be sent in one system call. We must
+           queue it up and send it later when we get the chance. We must not
+           loop here and wait until it might work again. */
+
+        size -= amount;
+
+        ptr = in->buffer + amount;
+
+        /* backup the currently set pointers */
+        http->backup.fread_func = conn->fread_func;
+        http->backup.fread_in = conn->fread_in;
+        http->backup.postdata = http->postdata;
+        http->backup.postsize = http->postsize;
+
+        /* set the new pointers for the request-sending */
+        conn->fread_func = (curl_read_callback)readmoredata;
+        conn->fread_in = (void *)conn;
+        http->postdata = ptr;
+        http->postsize = (curl_off_t)size;
+
+        http->send_buffer = in;
+        http->sending = HTTPSEND_REQUEST;
+
+        return CURLE_OK;
+      }
+      http->sending = HTTPSEND_BODY;
+      /* the full buffer was sent, clean up and return */
+    }
+    else {
+      if((size_t)amount != size)
+        /* We have no continue-send mechanism now, fail. This can only happen
+           when this function is used from the CONNECT sending function. We
+           currently (stupidly) assume that the whole request is always sent
+           away in the first single chunk.
+
+           This needs FIXing.
+        */
+        return CURLE_SEND_ERROR;
+      else
+        conn->writechannel_inuse = FALSE;
+    }
+  }
+  if(in->buffer)
+    free(in->buffer);
+  free(in);
+
+  return res;
+}
+
+
+/*
+ * add_bufferf() add the formatted input to the buffer.
+ */
+CURLcode Curl_add_bufferf(Curl_send_buffer *in, const char *fmt, ...)
+{
+  char *s;
+  va_list ap;
+  va_start(ap, fmt);
+  s = vaprintf(fmt, ap); /* this allocs a new string to append */
+  va_end(ap);
+
+  if(s) {
+    CURLcode result = Curl_add_buffer(in, s, strlen(s));
+    free(s);
+    return result;
+  }
+  /* If we failed, we cleanup the whole buffer and return error */
+  if(in->buffer)
+    free(in->buffer);
+  free(in);
+  return CURLE_OUT_OF_MEMORY;
+}
+
+/*
+ * add_buffer() appends a memory chunk to the existing buffer
+ */
+CURLcode Curl_add_buffer(Curl_send_buffer *in, const void *inptr, size_t size)
+{
+  char *new_rb;
+  size_t new_size;
+
+  if(~size < in->size_used) {
+    /* If resulting used size of send buffer would wrap size_t, cleanup
+       the whole buffer and return error. Otherwise the required buffer
+       size will fit into a single allocatable memory chunk */
+    Curl_safefree(in->buffer);
+    free(in);
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  if(!in->buffer ||
+     ((in->size_used + size) > (in->size_max - 1))) {
+
+    /* If current buffer size isn't enough to hold the result, use a
+       buffer size that doubles the required size. If this new size
+       would wrap size_t, then just use the largest possible one */
+
+    if((size > (size_t)-1/2) || (in->size_used > (size_t)-1/2) ||
+       (~(size*2) < (in->size_used*2)))
+      new_size = (size_t)-1;
+    else
+      new_size = (in->size_used+size)*2;
+
+    if(in->buffer)
+      /* we have a buffer, enlarge the existing one */
+      new_rb = realloc(in->buffer, new_size);
+    else
+      /* create a new buffer */
+      new_rb = malloc(new_size);
+
+    if(!new_rb) {
+      /* If we failed, we cleanup the whole buffer and return error */
+      Curl_safefree(in->buffer);
+      free(in);
+      return CURLE_OUT_OF_MEMORY;
+    }
+
+    in->buffer = new_rb;
+    in->size_max = new_size;
+  }
+  memcpy(&in->buffer[in->size_used], inptr, size);
+
+  in->size_used += size;
+
+  return CURLE_OK;
+}
+
+/* end of the add_buffer functions */
+/* ------------------------------------------------------------------------- */
+
+
+
+/*
+ * Curl_compareheader()
+ *
+ * Returns TRUE if 'headerline' contains the 'header' with given 'content'.
+ * Pass headers WITH the colon.
+ */
+bool
+Curl_compareheader(const char *headerline, /* line to check */
+                   const char *header,  /* header keyword _with_ colon */
+                   const char *content) /* content string to find */
+{
+  /* RFC2616, section 4.2 says: "Each header field consists of a name followed
+   * by a colon (":") and the field value. Field names are case-insensitive.
+   * The field value MAY be preceded by any amount of LWS, though a single SP
+   * is preferred." */
+
+  size_t hlen = strlen(header);
+  size_t clen;
+  size_t len;
+  const char *start;
+  const char *end;
+
+  if(!Curl_raw_nequal(headerline, header, hlen))
+    return FALSE; /* doesn't start with header */
+
+  /* pass the header */
+  start = &headerline[hlen];
+
+  /* pass all white spaces */
+  while(*start && ISSPACE(*start))
+    start++;
+
+  /* find the end of the header line */
+  end = strchr(start, '\r'); /* lines end with CRLF */
+  if(!end) {
+    /* in case there's a non-standard compliant line here */
+    end = strchr(start, '\n');
+
+    if(!end)
+      /* hm, there's no line ending here, use the zero byte! */
+      end = strchr(start, '\0');
+  }
+
+  len = end-start; /* length of the content part of the input line */
+  clen = strlen(content); /* length of the word to find */
+
+  /* find the content string in the rest of the line */
+  for(;len>=clen;len--, start++) {
+    if(Curl_raw_nequal(start, content, clen))
+      return TRUE; /* match! */
+  }
+
+  return FALSE; /* no match */
+}
+
+/*
+ * Curl_http_connect() performs HTTP stuff to do at connect-time, called from
+ * the generic Curl_connect().
+ */
+CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
+{
+  struct SessionHandle *data;
+  CURLcode result;
+
+  data=conn->data;
+
+  /* We default to persistent connections. We set this already in this connect
+     function to make the re-use checks properly be able to check this bit. */
+  conn->bits.close = FALSE;
+
+  if(data->state.used_interface == Curl_if_multi) {
+    /* when the multi interface is used, the CONNECT procedure might not have
+       been completed */
+    result = Curl_proxy_connect(conn);
+    if(result)
+      return result;
+  }
+
+  if(conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
+    /* nothing else to do except wait right now - we're not done here. */
+    return CURLE_OK;
+
+  if(conn->given->flags & PROTOPT_SSL) {
+    /* perform SSL initialization */
+    if(data->state.used_interface == Curl_if_multi) {
+      result = https_connecting(conn, done);
+      if(result)
+        return result;
+    }
+    else {
+      /* BLOCKING */
+      result = Curl_ssl_connect(conn, FIRSTSOCKET);
+      if(result)
+        return result;
+      *done = TRUE;
+    }
+  }
+  else {
+    *done = TRUE;
+  }
+
+  return CURLE_OK;
+}
+
+/* this returns the socket to wait for in the DO and DOING state for the multi
+   interface and then we're always _sending_ a request and thus we wait for
+   the single socket to become writable only */
+static int http_getsock_do(struct connectdata *conn,
+                           curl_socket_t *socks,
+                           int numsocks)
+{
+  /* write mode */
+  (void)numsocks; /* unused, we trust it to be at least 1 */
+  socks[0] = conn->sock[FIRSTSOCKET];
+  return GETSOCK_WRITESOCK(0);
+}
+
+#ifdef USE_SSL
+static CURLcode https_connecting(struct connectdata *conn, bool *done)
+{
+  CURLcode result;
+  DEBUGASSERT((conn) && (conn->handler->flags & PROTOPT_SSL));
+
+  /* perform SSL initialization for this socket */
+  result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, done);
+  if(result)
+    conn->bits.close = TRUE; /* a failed connection is marked for closure
+                                to prevent (bad) re-use or similar */
+  return result;
+}
+#endif
+
+#if defined(USE_SSLEAY) || defined(USE_GNUTLS) || defined(USE_SCHANNEL) || \
+    defined(USE_DARWINSSL)
+/* This function is for OpenSSL, GnuTLS, darwinssl, and schannel only.
+   It should be made to query the generic SSL layer instead. */
+static int https_getsock(struct connectdata *conn,
+                         curl_socket_t *socks,
+                         int numsocks)
+{
+  if(conn->handler->flags & PROTOPT_SSL) {
+    struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET];
+
+    if(!numsocks)
+      return GETSOCK_BLANK;
+
+    if(connssl->connecting_state == ssl_connect_2_writing) {
+      /* write mode */
+      socks[0] = conn->sock[FIRSTSOCKET];
+      return GETSOCK_WRITESOCK(0);
+    }
+    else if(connssl->connecting_state == ssl_connect_2_reading) {
+      /* read mode */
+      socks[0] = conn->sock[FIRSTSOCKET];
+      return GETSOCK_READSOCK(0);
+    }
+  }
+  return CURLE_OK;
+}
+#else
+#ifdef USE_SSL
+static int https_getsock(struct connectdata *conn,
+                         curl_socket_t *socks,
+                         int numsocks)
+{
+  (void)conn;
+  (void)socks;
+  (void)numsocks;
+  return GETSOCK_BLANK;
+}
+#endif /* USE_SSL */
+#endif /* USE_SSLEAY || USE_GNUTLS || USE_SCHANNEL */
+
+/*
+ * Curl_http_done() gets called from Curl_done() after a single HTTP request
+ * has been performed.
+ */
+
+CURLcode Curl_http_done(struct connectdata *conn,
+                        CURLcode status, bool premature)
+{
+  struct SessionHandle *data = conn->data;
+  struct HTTP *http =data->state.proto.http;
+
+  Curl_unencode_cleanup(conn);
+
+  /* set the proper values (possibly modified on POST) */
+  conn->fread_func = data->set.fread_func; /* restore */
+  conn->fread_in = data->set.in; /* restore */
+  conn->seek_func = data->set.seek_func; /* restore */
+  conn->seek_client = data->set.seek_client; /* restore */
+
+  if(http == NULL)
+    return CURLE_OK;
+
+  if(http->send_buffer) {
+    Curl_send_buffer *buff = http->send_buffer;
+
+    free(buff->buffer);
+    free(buff);
+    http->send_buffer = NULL; /* clear the pointer */
+  }
+
+  if(HTTPREQ_POST_FORM == data->set.httpreq) {
+    data->req.bytecount = http->readbytecount + http->writebytecount;
+
+    Curl_formclean(&http->sendit); /* Now free that whole lot */
+    if(http->form.fp) {
+      /* a file being uploaded was left opened, close it! */
+      fclose(http->form.fp);
+      http->form.fp = NULL;
+    }
+  }
+  else if(HTTPREQ_PUT == data->set.httpreq)
+    data->req.bytecount = http->readbytecount + http->writebytecount;
+
+  if(status != CURLE_OK)
+    return (status);
+
+  if(!premature && /* this check is pointless when DONE is called before the
+                      entire operation is complete */
+     !conn->bits.retry &&
+     ((http->readbytecount +
+       data->req.headerbytecount -
+       data->req.deductheadercount)) <= 0) {
+    /* If this connection isn't simply closed to be retried, AND nothing was
+       read from the HTTP server (that counts), this can't be right so we
+       return an error here */
+    failf(data, "Empty reply from server");
+    return CURLE_GOT_NOTHING;
+  }
+
+  return CURLE_OK;
+}
+
+
+/* Determine if we should use HTTP 1.1 for this request. Reasons to avoid it
+   are if the user specifically requested HTTP 1.0, if the server we are
+   connected to only supports 1.0, or if any server previously contacted to
+   handle this request only supports 1.0. */
+static bool use_http_1_1(const struct SessionHandle *data,
+                         const struct connectdata *conn)
+{
+  return ((data->set.httpversion == CURL_HTTP_VERSION_1_1) ||
+         ((data->set.httpversion != CURL_HTTP_VERSION_1_0) &&
+          ((conn->httpversion == 11) ||
+           ((conn->httpversion != 10) &&
+            (data->state.httpversion != 10))))) ? TRUE : FALSE;
+}
+
+/* check and possibly add an Expect: header */
+static CURLcode expect100(struct SessionHandle *data,
+                          struct connectdata *conn,
+                          Curl_send_buffer *req_buffer)
+{
+  CURLcode result = CURLE_OK;
+  const char *ptr;
+  data->state.expect100header = FALSE; /* default to false unless it is set
+                                          to TRUE below */
+  if(use_http_1_1(data, conn)) {
+    /* if not doing HTTP 1.0 or disabled explicitly, we add a Expect:
+       100-continue to the headers which actually speeds up post operations
+       (as there is one packet coming back from the web server) */
+    ptr = Curl_checkheaders(data, "Expect:");
+    if(ptr) {
+      data->state.expect100header =
+        Curl_compareheader(ptr, "Expect:", "100-continue");
+    }
+    else {
+      result = Curl_add_bufferf(req_buffer,
+                         "Expect: 100-continue\r\n");
+      if(result == CURLE_OK)
+        data->state.expect100header = TRUE;
+    }
+  }
+  return result;
+}
+
+CURLcode Curl_add_custom_headers(struct connectdata *conn,
+                                   Curl_send_buffer *req_buffer)
+{
+  char *ptr;
+  struct curl_slist *headers=conn->data->set.headers;
+
+  while(headers) {
+    ptr = strchr(headers->data, ':');
+    if(ptr) {
+      /* we require a colon for this to be a true header */
+
+      ptr++; /* pass the colon */
+      while(*ptr && ISSPACE(*ptr))
+        ptr++;
+
+      if(*ptr) {
+        /* only send this if the contents was non-blank */
+
+        if(conn->allocptr.host &&
+           /* a Host: header was sent already, don't pass on any custom Host:
+              header as that will produce *two* in the same request! */
+           checkprefix("Host:", headers->data))
+          ;
+        else if(conn->data->set.httpreq == HTTPREQ_POST_FORM &&
+                /* this header (extended by curl_formdata.c) is sent later */
+                checkprefix("Content-Type:", headers->data))
+          ;
+        else if(conn->bits.authneg &&
+                /* while doing auth neg, don't allow the custom length since
+                   we will force length zero then */
+                checkprefix("Content-Length", headers->data))
+          ;
+        else if(conn->allocptr.te &&
+                /* when asking for Transfer-Encoding, don't pass on a custom
+                   Connection: */
+                checkprefix("Connection", headers->data))
+          ;
+        else {
+          CURLcode result = Curl_add_bufferf(req_buffer, "%s\r\n",
+                                             headers->data);
+          if(result)
+            return result;
+        }
+      }
+    }
+    else {
+      ptr = strchr(headers->data, ';');
+      if(ptr) {
+
+        ptr++; /* pass the semicolon */
+        while(*ptr && ISSPACE(*ptr))
+          ptr++;
+
+        if(*ptr) {
+          /* this may be used for something else in the future */
+        }
+        else {
+          if(*(--ptr) == ';') {
+            CURLcode result;
+
+            /* send no-value custom header if terminated by semicolon */
+            *ptr = ':';
+            result = Curl_add_bufferf(req_buffer, "%s\r\n",
+                                             headers->data);
+            if(result)
+              return result;
+          }
+        }
+      }
+    }
+    headers = headers->next;
+  }
+  return CURLE_OK;
+}
+
+CURLcode Curl_add_timecondition(struct SessionHandle *data,
+                                Curl_send_buffer *req_buffer)
+{
+  const struct tm *tm;
+  char *buf = data->state.buffer;
+  CURLcode result = CURLE_OK;
+  struct tm keeptime;
+
+  result = Curl_gmtime(data->set.timevalue, &keeptime);
+  if(result) {
+    failf(data, "Invalid TIMEVALUE");
+    return result;
+  }
+  tm = &keeptime;
+
+  /* The If-Modified-Since header family should have their times set in
+   * GMT as RFC2616 defines: "All HTTP date/time stamps MUST be
+   * represented in Greenwich Mean Time (GMT), without exception. For the
+   * purposes of HTTP, GMT is exactly equal to UTC (Coordinated Universal
+   * Time)." (see page 20 of RFC2616).
+   */
+
+  /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
+  snprintf(buf, BUFSIZE-1,
+           "%s, %02d %s %4d %02d:%02d:%02d GMT",
+           Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
+           tm->tm_mday,
+           Curl_month[tm->tm_mon],
+           tm->tm_year + 1900,
+           tm->tm_hour,
+           tm->tm_min,
+           tm->tm_sec);
+
+  switch(data->set.timecondition) {
+  case CURL_TIMECOND_IFMODSINCE:
+  default:
+    result = Curl_add_bufferf(req_buffer,
+                              "If-Modified-Since: %s\r\n", buf);
+    break;
+  case CURL_TIMECOND_IFUNMODSINCE:
+    result = Curl_add_bufferf(req_buffer,
+                              "If-Unmodified-Since: %s\r\n", buf);
+    break;
+  case CURL_TIMECOND_LASTMOD:
+    result = Curl_add_bufferf(req_buffer,
+                              "Last-Modified: %s\r\n", buf);
+    break;
+  }
+
+  return result;
+}
+
+/*
+ * Curl_http() gets called from the generic Curl_do() function when a HTTP
+ * request is to be performed. This creates and sends a properly constructed
+ * HTTP request.
+ */
+CURLcode Curl_http(struct connectdata *conn, bool *done)
+{
+  struct SessionHandle *data=conn->data;
+  CURLcode result=CURLE_OK;
+  struct HTTP *http;
+  const char *ppath = data->state.path;
+  bool paste_ftp_userpwd = FALSE;
+  char ftp_typecode[sizeof("/;type=?")] = "";
+  const char *host = conn->host.name;
+  const char *te = ""; /* transfer-encoding */
+  const char *ptr;
+  const char *request;
+  Curl_HttpReq httpreq = data->set.httpreq;
+  char *addcookies = NULL;
+  curl_off_t included_body = 0;
+  const char *httpstring;
+  Curl_send_buffer *req_buffer;
+  curl_off_t postsize = 0; /* curl_off_t to handle large file sizes */
+  int seekerr = CURL_SEEKFUNC_OK;
+
+  /* Always consider the DO phase done after this function call, even if there
+     may be parts of the request that is not yet sent, since we can deal with
+     the rest of the request in the PERFORM phase. */
+  *done = TRUE;
+
+  /* If there already is a protocol-specific struct allocated for this
+     sessionhandle, deal with it */
+  Curl_reset_reqproto(conn);
+
+  if(!data->state.proto.http) {
+    /* Only allocate this struct if we don't already have it! */
+
+    http = calloc(1, sizeof(struct HTTP));
+    if(!http)
+      return CURLE_OUT_OF_MEMORY;
+    data->state.proto.http = http;
+  }
+  else
+    http = data->state.proto.http;
+
+  if(!data->state.this_is_a_follow) {
+    /* this is not a followed location, get the original host name */
+    if(data->state.first_host)
+      /* Free to avoid leaking memory on multiple requests*/
+      free(data->state.first_host);
+
+    data->state.first_host = strdup(conn->host.name);
+    if(!data->state.first_host)
+      return CURLE_OUT_OF_MEMORY;
+  }
+  http->writebytecount = http->readbytecount = 0;
+
+  if((conn->handler->protocol&(CURLPROTO_HTTP|CURLPROTO_FTP)) &&
+     data->set.upload) {
+    httpreq = HTTPREQ_PUT;
+  }
+
+  /* Now set the 'request' pointer to the proper request string */
+  if(data->set.str[STRING_CUSTOMREQUEST])
+    request = data->set.str[STRING_CUSTOMREQUEST];
+  else {
+    if(data->set.opt_no_body)
+      request = "HEAD";
+    else {
+      DEBUGASSERT((httpreq > HTTPREQ_NONE) && (httpreq < HTTPREQ_LAST));
+      switch(httpreq) {
+      case HTTPREQ_POST:
+      case HTTPREQ_POST_FORM:
+        request = "POST";
+        break;
+      case HTTPREQ_PUT:
+        request = "PUT";
+        break;
+      default: /* this should never happen */
+      case HTTPREQ_GET:
+        request = "GET";
+        break;
+      case HTTPREQ_HEAD:
+        request = "HEAD";
+        break;
+      }
+    }
+  }
+
+  /* The User-Agent string might have been allocated in curl_url.c already,
+     because it might have been used in the proxy connect, but if we have
+     got a header with the user-agent string specified, we erase the
+     previously made string here. */
+  if(Curl_checkheaders(data, "User-Agent:") && conn->allocptr.uagent) {
+    free(conn->allocptr.uagent);
+    conn->allocptr.uagent=NULL;
+  }
+
+  /* setup the authentication headers */
+  result = Curl_http_output_auth(conn, request, ppath, FALSE);
+  if(result)
+    return result;
+
+  if((data->state.authhost.multi || data->state.authproxy.multi) &&
+     (httpreq != HTTPREQ_GET) &&
+     (httpreq != HTTPREQ_HEAD)) {
+    /* Auth is required and we are not authenticated yet. Make a PUT or POST
+       with content-length zero as a "probe". */
+    conn->bits.authneg = TRUE;
+  }
+  else
+    conn->bits.authneg = FALSE;
+
+  Curl_safefree(conn->allocptr.ref);
+  if(data->change.referer && !Curl_checkheaders(data, "Referer:"))
+    conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer);
+  else
+    conn->allocptr.ref = NULL;
+
+  if(data->set.str[STRING_COOKIE] && !Curl_checkheaders(data, "Cookie:"))
+    addcookies = data->set.str[STRING_COOKIE];
+
+  if(!Curl_checkheaders(data, "Accept-Encoding:") &&
+     data->set.str[STRING_ENCODING]) {
+    Curl_safefree(conn->allocptr.accept_encoding);
+    conn->allocptr.accept_encoding =
+      aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
+    if(!conn->allocptr.accept_encoding)
+      return CURLE_OUT_OF_MEMORY;
+  }
+
+#ifdef HAVE_LIBZ
+  /* we only consider transfer-encoding magic if libz support is built-in */
+
+  if(!Curl_checkheaders(data, "TE:") && data->set.http_transfer_encoding) {
+    /* When we are to insert a TE: header in the request, we must also insert
+       TE in a Connection: header, so we need to merge the custom provided
+       Connection: header and prevent the original to get sent. Note that if
+       the user has inserted his/hers own TE: header we don't do this magic
+       but then assume that the user will handle it all! */
+    char *cptr = Curl_checkheaders(data, "Connection:");
+#define TE_HEADER "TE: gzip\r\n"
+
+    Curl_safefree(conn->allocptr.te);
+
+    /* Create the (updated) Connection: header */
+    conn->allocptr.te = cptr? aprintf("%s, TE\r\n" TE_HEADER, cptr):
+      strdup("Connection: TE\r\n" TE_HEADER);
+
+    if(!conn->allocptr.te)
+      return CURLE_OUT_OF_MEMORY;
+  }
+#endif
+
+  ptr = Curl_checkheaders(data, "Transfer-Encoding:");
+  if(ptr) {
+    /* Some kind of TE is requested, check if 'chunked' is chosen */
+    data->req.upload_chunky =
+      Curl_compareheader(ptr, "Transfer-Encoding:", "chunked");
+  }
+  else {
+    if((conn->handler->protocol&CURLPROTO_HTTP) &&
+       data->set.upload &&
+       (data->set.infilesize == -1)) {
+      if(conn->bits.authneg)
+        /* don't enable chunked during auth neg */
+        ;
+      else if(use_http_1_1(data, conn)) {
+        /* HTTP, upload, unknown file size and not HTTP 1.0 */
+        data->req.upload_chunky = TRUE;
+      }
+      else {
+        failf(data, "Chunky upload is not supported by HTTP 1.0");
+        return CURLE_UPLOAD_FAILED;
+      }
+    }
+    else {
+      /* else, no chunky upload */
+      data->req.upload_chunky = FALSE;
+    }
+
+    if(data->req.upload_chunky)
+      te = "Transfer-Encoding: chunked\r\n";
+  }
+
+  Curl_safefree(conn->allocptr.host);
+
+  ptr = Curl_checkheaders(data, "Host:");
+  if(ptr && (!data->state.this_is_a_follow ||
+             Curl_raw_equal(data->state.first_host, conn->host.name))) {
+#if !defined(CURL_DISABLE_COOKIES)
+    /* If we have a given custom Host: header, we extract the host name in
+       order to possibly use it for cookie reasons later on. We only allow the
+       custom Host: header if this is NOT a redirect, as setting Host: in the
+       redirected request is being out on thin ice. Except if the host name
+       is the same as the first one! */
+    char *cookiehost = copy_header_value(ptr);
+    if(!cookiehost)
+      return CURLE_OUT_OF_MEMORY;
+    if(!*cookiehost)
+      /* ignore empty data */
+      free(cookiehost);
+    else {
+      /* If the host begins with '[', we start searching for the port after
+         the bracket has been closed */
+      int startsearch = 0;
+      if(*cookiehost == '[') {
+        char *closingbracket;
+        /* since the 'cookiehost' is an allocated memory area that will be
+           freed later we cannot simply increment the pointer */
+        memmove(cookiehost, cookiehost + 1, strlen(cookiehost) - 1);
+        closingbracket = strchr(cookiehost, ']');
+        if(closingbracket)
+          *closingbracket = 0;
+      }
+      else {
+        char *colon = strchr(cookiehost + startsearch, ':');
+        if(colon)
+          *colon = 0; /* The host must not include an embedded port number */
+      }
+      Curl_safefree(conn->allocptr.cookiehost);
+      conn->allocptr.cookiehost = cookiehost;
+    }
+#endif
+
+    conn->allocptr.host = NULL;
+  }
+  else {
+    /* When building Host: headers, we must put the host name within
+       [brackets] if the host name is a plain IPv6-address. RFC2732-style. */
+
+    if(((conn->given->protocol&CURLPROTO_HTTPS) &&
+        (conn->remote_port == PORT_HTTPS)) ||
+       ((conn->given->protocol&CURLPROTO_HTTP) &&
+        (conn->remote_port == PORT_HTTP)) )
+      /* if(HTTPS on port 443) OR (HTTP on port 80) then don't include
+         the port number in the host string */
+      conn->allocptr.host = aprintf("Host: %s%s%s\r\n",
+                                    conn->bits.ipv6_ip?"[":"",
+                                    host,
+                                    conn->bits.ipv6_ip?"]":"");
+    else
+      conn->allocptr.host = aprintf("Host: %s%s%s:%hu\r\n",
+                                    conn->bits.ipv6_ip?"[":"",
+                                    host,
+                                    conn->bits.ipv6_ip?"]":"",
+                                    conn->remote_port);
+
+    if(!conn->allocptr.host)
+      /* without Host: we can't make a nice request */
+      return CURLE_OUT_OF_MEMORY;
+  }
+
+#ifndef CURL_DISABLE_PROXY
+  if(conn->bits.httpproxy && !conn->bits.tunnel_proxy)  {
+    /* Using a proxy but does not tunnel through it */
+
+    /* The path sent to the proxy is in fact the entire URL. But if the remote
+       host is a IDN-name, we must make sure that the request we produce only
+       uses the encoded host name! */
+    if(conn->host.dispname != conn->host.name) {
+      char *url = data->change.url;
+      ptr = strstr(url, conn->host.dispname);
+      if(ptr) {
+        /* This is where the display name starts in the URL, now replace this
+           part with the encoded name. TODO: This method of replacing the host
+           name is rather crude as I believe there's a slight risk that the
+           user has entered a user name or password that contain the host name
+           string. */
+        size_t currlen = strlen(conn->host.dispname);
+        size_t newlen = strlen(conn->host.name);
+        size_t urllen = strlen(url);
+
+        char *newurl;
+
+        newurl = malloc(urllen + newlen - currlen + 1);
+        if(newurl) {
+          /* copy the part before the host name */
+          memcpy(newurl, url, ptr - url);
+          /* append the new host name instead of the old */
+          memcpy(newurl + (ptr - url), conn->host.name, newlen);
+          /* append the piece after the host name */
+          memcpy(newurl + newlen + (ptr - url),
+                 ptr + currlen, /* copy the trailing zero byte too */
+                 urllen - (ptr-url) - currlen + 1);
+          if(data->change.url_alloc) {
+            Curl_safefree(data->change.url);
+            data->change.url_alloc = FALSE;
+          }
+          data->change.url = newurl;
+          data->change.url_alloc = TRUE;
+        }
+        else
+          return CURLE_OUT_OF_MEMORY;
+      }
+    }
+    ppath = data->change.url;
+    if(checkprefix("ftp://", ppath)) {
+      if(data->set.proxy_transfer_mode) {
+        /* when doing ftp, append ;type=<a|i> if not present */
+        char *type = strstr(ppath, ";type=");
+        if(type && type[6] && type[7] == 0) {
+          switch (Curl_raw_toupper(type[6])) {
+          case 'A':
+          case 'D':
+          case 'I':
+            break;
+          default:
+            type = NULL;
+          }
+        }
+        if(!type) {
+          char *p = ftp_typecode;
+          /* avoid sending invalid URLs like ftp://example.com;type=i if the
+           * user specified ftp://example.com without the slash */
+          if(!*data->state.path && ppath[strlen(ppath) - 1] != '/') {
+            *p++ = '/';
+          }
+          snprintf(p, sizeof(ftp_typecode) - 1, ";type=%c",
+                   data->set.prefer_ascii ? 'a' : 'i');
+        }
+      }
+      if(conn->bits.user_passwd && !conn->bits.userpwd_in_url)
+        paste_ftp_userpwd = TRUE;
+    }
+  }
+#endif /* CURL_DISABLE_PROXY */
+
+  if(HTTPREQ_POST_FORM == httpreq) {
+    /* we must build the whole post sequence first, so that we have a size of
+       the whole transfer before we start to send it */
+    result = Curl_getformdata(data, &http->sendit, data->set.httppost,
+                              Curl_checkheaders(data, "Content-Type:"),
+                              &http->postsize);
+    if(result)
+      return result;
+  }
+
+  http->p_accept = Curl_checkheaders(data, "Accept:")?NULL:"Accept: */*\r\n";
+
+  if(( (HTTPREQ_POST == httpreq) ||
+       (HTTPREQ_POST_FORM == httpreq) ||
+       (HTTPREQ_PUT == httpreq) ) &&
+     data->state.resume_from) {
+    /**********************************************************************
+     * Resuming upload in HTTP means that we PUT or POST and that we have
+     * got a resume_from value set. The resume value has already created
+     * a Range: header that will be passed along. We need to "fast forward"
+     * the file the given number of bytes and decrease the assume upload
+     * file size before we continue this venture in the dark lands of HTTP.
+     *********************************************************************/
+
+    if(data->state.resume_from < 0 ) {
+      /*
+       * This is meant to get the size of the present remote-file by itself.
+       * We don't support this now. Bail out!
+       */
+      data->state.resume_from = 0;
+    }
+
+    if(data->state.resume_from && !data->state.this_is_a_follow) {
+      /* do we still game? */
+
+      /* Now, let's read off the proper amount of bytes from the
+         input. */
+      if(conn->seek_func) {
+        seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
+                                  SEEK_SET);
+      }
+
+      if(seekerr != CURL_SEEKFUNC_OK) {
+        if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
+          failf(data, "Could not seek stream");
+          return CURLE_READ_ERROR;
+        }
+        /* when seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
+        else {
+          curl_off_t passed=0;
+          do {
+            size_t readthisamountnow =
+              (data->state.resume_from - passed > CURL_OFF_T_C(BUFSIZE)) ?
+              BUFSIZE : curlx_sotouz(data->state.resume_from - passed);
+
+            size_t actuallyread =
+              data->set.fread_func(data->state.buffer, 1, readthisamountnow,
+                                   data->set.in);
+
+            passed += actuallyread;
+            if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
+              /* this checks for greater-than only to make sure that the
+                 CURL_READFUNC_ABORT return code still aborts */
+              failf(data, "Could only read %" FORMAT_OFF_T
+                    " bytes from the input",
+                    passed);
+              return CURLE_READ_ERROR;
+            }
+          } while(passed < data->state.resume_from);
+        }
+      }
+
+      /* now, decrease the size of the read */
+      if(data->set.infilesize>0) {
+        data->set.infilesize -= data->state.resume_from;
+
+        if(data->set.infilesize <= 0) {
+          failf(data, "File already completely uploaded");
+          return CURLE_PARTIAL_FILE;
+        }
+      }
+      /* we've passed, proceed as normal */
+    }
+  }
+  if(data->state.use_range) {
+    /*
+     * A range is selected. We use different headers whether we're downloading
+     * or uploading and we always let customized headers override our internal
+     * ones if any such are specified.
+     */
+    if(((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) &&
+       !Curl_checkheaders(data, "Range:")) {
+      /* if a line like this was already allocated, free the previous one */
+      if(conn->allocptr.rangeline)
+        free(conn->allocptr.rangeline);
+      conn->allocptr.rangeline = aprintf("Range: bytes=%s\r\n",
+                                         data->state.range);
+    }
+    else if((httpreq != HTTPREQ_GET) &&
+            !Curl_checkheaders(data, "Content-Range:")) {
+
+      /* if a line like this was already allocated, free the previous one */
+      if(conn->allocptr.rangeline)
+        free(conn->allocptr.rangeline);
+
+      if(data->set.set_resume_from < 0) {
+        /* Upload resume was asked for, but we don't know the size of the
+           remote part so we tell the server (and act accordingly) that we
+           upload the whole file (again) */
+        conn->allocptr.rangeline =
+          aprintf("Content-Range: bytes 0-%" FORMAT_OFF_T
+                  "/%" FORMAT_OFF_T "\r\n",
+                  data->set.infilesize - 1, data->set.infilesize);
+
+      }
+      else if(data->state.resume_from) {
+        /* This is because "resume" was selected */
+        curl_off_t total_expected_size=
+          data->state.resume_from + data->set.infilesize;
+        conn->allocptr.rangeline =
+          aprintf("Content-Range: bytes %s%" FORMAT_OFF_T
+                  "/%" FORMAT_OFF_T "\r\n",
+                  data->state.range, total_expected_size-1,
+                  total_expected_size);
+      }
+      else {
+        /* Range was selected and then we just pass the incoming range and
+           append total size */
+        conn->allocptr.rangeline =
+          aprintf("Content-Range: bytes %s/%" FORMAT_OFF_T "\r\n",
+                  data->state.range, data->set.infilesize);
+      }
+      if(!conn->allocptr.rangeline)
+        return CURLE_OUT_OF_MEMORY;
+    }
+  }
+
+  /* Use 1.1 unless the user specifically asked for 1.0 or the server only
+     supports 1.0 */
+  httpstring= use_http_1_1(data, conn)?"1.1":"1.0";
+
+  /* initialize a dynamic send-buffer */
+  req_buffer = Curl_add_buffer_init();
+
+  if(!req_buffer)
+    return CURLE_OUT_OF_MEMORY;
+
+  /* add the main request stuff */
+  /* GET/HEAD/POST/PUT */
+  result = Curl_add_bufferf(req_buffer, "%s ", request);
+  if(result)
+    return result;
+
+  /* url */
+  if(paste_ftp_userpwd)
+    result = Curl_add_bufferf(req_buffer, "ftp://%s:%s@%s",
+                              conn->user, conn->passwd,
+                              ppath + sizeof("ftp://") - 1);
+  else
+    result = Curl_add_buffer(req_buffer, ppath, strlen(ppath));
+  if(result)
+    return result;
+
+  result =
+    Curl_add_bufferf(req_buffer,
+                     "%s" /* ftp typecode (;type=x) */
+                     " HTTP/%s\r\n" /* HTTP version */
+                     "%s" /* proxyuserpwd */
+                     "%s" /* userpwd */
+                     "%s" /* range */
+                     "%s" /* user agent */
+                     "%s" /* host */
+                     "%s" /* accept */
+                     "%s" /* TE: */
+                     "%s" /* accept-encoding */
+                     "%s" /* referer */
+                     "%s" /* Proxy-Connection */
+                     "%s",/* transfer-encoding */
+
+                     ftp_typecode,
+                     httpstring,
+                     conn->allocptr.proxyuserpwd?
+                     conn->allocptr.proxyuserpwd:"",
+                     conn->allocptr.userpwd?conn->allocptr.userpwd:"",
+                     (data->state.use_range && conn->allocptr.rangeline)?
+                     conn->allocptr.rangeline:"",
+                     (data->set.str[STRING_USERAGENT] &&
+                      *data->set.str[STRING_USERAGENT] &&
+                      conn->allocptr.uagent)?
+                     conn->allocptr.uagent:"",
+                     (conn->allocptr.host?conn->allocptr.host:""),
+                     http->p_accept?http->p_accept:"",
+                     conn->allocptr.te?conn->allocptr.te:"",
+                     (data->set.str[STRING_ENCODING] &&
+                      *data->set.str[STRING_ENCODING] &&
+                      conn->allocptr.accept_encoding)?
+                     conn->allocptr.accept_encoding:"",
+                     (data->change.referer && conn->allocptr.ref)?
+                     conn->allocptr.ref:"" /* Referer: <data> */,
+                     (conn->bits.httpproxy &&
+                      !conn->bits.tunnel_proxy &&
+                      !Curl_checkheaders(data, "Proxy-Connection:"))?
+                     "Proxy-Connection: Keep-Alive\r\n":"",
+                     te
+      );
+
+  /*
+   * Free userpwd now --- cannot reuse this for Negotiate and possibly NTLM
+   * with basic and digest, it will be freed anyway by the next request
+   */
+
+  Curl_safefree (conn->allocptr.userpwd);
+  conn->allocptr.userpwd = NULL;
+
+  if(result)
+    return result;
+
+#if !defined(CURL_DISABLE_COOKIES)
+  if(data->cookies || addcookies) {
+    struct Cookie *co=NULL; /* no cookies from start */
+    int count=0;
+
+    if(data->cookies) {
+      Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
+      co = Curl_cookie_getlist(data->cookies,
+                               conn->allocptr.cookiehost?
+                               conn->allocptr.cookiehost:host,
+                               data->state.path,
+                               (conn->handler->protocol&CURLPROTO_HTTPS)?
+                               TRUE:FALSE);
+      Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
+    }
+    if(co) {
+      struct Cookie *store=co;
+      /* now loop through all cookies that matched */
+      while(co) {
+        if(co->value) {
+          if(0 == count) {
+            result = Curl_add_bufferf(req_buffer, "Cookie: ");
+            if(result)
+              break;
+          }
+          result = Curl_add_bufferf(req_buffer,
+                                    "%s%s=%s", count?"; ":"",
+                                    co->name, co->value);
+          if(result)
+            break;
+          count++;
+        }
+        co = co->next; /* next cookie please */
+      }
+      Curl_cookie_freelist(store, FALSE); /* free the cookie list */
+    }
+    if(addcookies && (CURLE_OK == result)) {
+      if(!count)
+        result = Curl_add_bufferf(req_buffer, "Cookie: ");
+      if(CURLE_OK == result) {
+        result = Curl_add_bufferf(req_buffer, "%s%s",
+                                  count?"; ":"",
+                                  addcookies);
+        count++;
+      }
+    }
+    if(count && (CURLE_OK == result))
+      result = Curl_add_buffer(req_buffer, "\r\n", 2);
+
+    if(result)
+      return result;
+  }
+#endif
+
+  if(data->set.timecondition) {
+    result = Curl_add_timecondition(data, req_buffer);
+    if(result)
+      return result;
+  }
+
+  result = Curl_add_custom_headers(conn, req_buffer);
+  if(result)
+    return result;
+
+  http->postdata = NULL;  /* nothing to post at this point */
+  Curl_pgrsSetUploadSize(data, 0); /* upload size is 0 atm */
+
+  /* If 'authdone' is FALSE, we must not set the write socket index to the
+     Curl_transfer() call below, as we're not ready to actually upload any
+     data yet. */
+
+  switch(httpreq) {
+
+  case HTTPREQ_POST_FORM:
+    if(!http->sendit || conn->bits.authneg) {
+      /* nothing to post! */
+      result = Curl_add_bufferf(req_buffer, "Content-Length: 0\r\n\r\n");
+      if(result)
+        return result;
+
+      result = Curl_add_buffer_send(req_buffer, conn,
+                                    &data->info.request_size, 0, FIRSTSOCKET);
+      if(result)
+        failf(data, "Failed sending POST request");
+      else
+        /* setup variables for the upcoming transfer */
+        Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount,
+                            -1, NULL);
+      break;
+    }
+
+    if(Curl_FormInit(&http->form, http->sendit)) {
+      failf(data, "Internal HTTP POST error!");
+      return CURLE_HTTP_POST_ERROR;
+    }
+
+    /* Get the currently set callback function pointer and store that in the
+       form struct since we might want the actual user-provided callback later
+       on. The conn->fread_func pointer itself will be changed for the
+       multipart case to the function that returns a multipart formatted
+       stream. */
+    http->form.fread_func = conn->fread_func;
+
+    /* Set the read function to read from the generated form data */
+    conn->fread_func = (curl_read_callback)Curl_FormReader;
+    conn->fread_in = &http->form;
+
+    http->sending = HTTPSEND_BODY;
+
+    if(!data->req.upload_chunky &&
+       !Curl_checkheaders(data, "Content-Length:")) {
+      /* only add Content-Length if not uploading chunked */
+      result = Curl_add_bufferf(req_buffer,
+                                "Content-Length: %" FORMAT_OFF_T "\r\n",
+                                http->postsize);
+      if(result)
+        return result;
+    }
+
+    result = expect100(data, conn, req_buffer);
+    if(result)
+      return result;
+
+    {
+
+      /* Get Content-Type: line from Curl_formpostheader.
+       */
+      char *contentType;
+      size_t linelength=0;
+      contentType = Curl_formpostheader((void *)&http->form,
+                                        &linelength);
+      if(!contentType) {
+        failf(data, "Could not get Content-Type header line!");
+        return CURLE_HTTP_POST_ERROR;
+      }
+
+      result = Curl_add_buffer(req_buffer, contentType, linelength);
+      if(result)
+        return result;
+    }
+
+    /* make the request end in a true CRLF */
+    result = Curl_add_buffer(req_buffer, "\r\n", 2);
+    if(result)
+      return result;
+
+    /* set upload size to the progress meter */
+    Curl_pgrsSetUploadSize(data, http->postsize);
+
+    /* fire away the whole request to the server */
+    result = Curl_add_buffer_send(req_buffer, conn,
+                                  &data->info.request_size, 0, FIRSTSOCKET);
+    if(result)
+      failf(data, "Failed sending POST request");
+    else
+      /* setup variables for the upcoming transfer */
+      Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
+                          &http->readbytecount, FIRSTSOCKET,
+                          &http->writebytecount);
+
+    if(result) {
+      Curl_formclean(&http->sendit); /* free that whole lot */
+      return result;
+    }
+
+    /* convert the form data */
+    result = Curl_convert_form(data, http->sendit);
+    if(result) {
+      Curl_formclean(&http->sendit); /* free that whole lot */
+      return result;
+    }
+
+    break;
+
+  case HTTPREQ_PUT: /* Let's PUT the data to the server! */
+
+    if(conn->bits.authneg)
+      postsize = 0;
+    else
+      postsize = data->set.infilesize;
+
+    if((postsize != -1) && !data->req.upload_chunky &&
+       !Curl_checkheaders(data, "Content-Length:")) {
+      /* only add Content-Length if not uploading chunked */
+      result = Curl_add_bufferf(req_buffer,
+                                "Content-Length: %" FORMAT_OFF_T "\r\n",
+                                postsize );
+      if(result)
+        return result;
+    }
+
+    result = expect100(data, conn, req_buffer);
+    if(result)
+      return result;
+
+    result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers */
+    if(result)
+      return result;
+
+    /* set the upload size to the progress meter */
+    Curl_pgrsSetUploadSize(data, postsize);
+
+    /* this sends the buffer and frees all the buffer resources */
+    result = Curl_add_buffer_send(req_buffer, conn,
+                                  &data->info.request_size, 0, FIRSTSOCKET);
+    if(result)
+      failf(data, "Failed sending PUT request");
+    else
+      /* prepare for transfer */
+      Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
+                          &http->readbytecount, postsize?FIRSTSOCKET:-1,
+                          postsize?&http->writebytecount:NULL);
+    if(result)
+      return result;
+    break;
+
+  case HTTPREQ_POST:
+    /* this is the simple POST, using x-www-form-urlencoded style */
+
+    if(conn->bits.authneg)
+      postsize = 0;
+    else {
+      /* figure out the size of the postfields */
+      postsize = (data->set.postfieldsize != -1)?
+        data->set.postfieldsize:
+        (data->set.postfields? (curl_off_t)strlen(data->set.postfields):-1);
+    }
+    if(!data->req.upload_chunky) {
+      /* We only set Content-Length and allow a custom Content-Length if
+         we don't upload data chunked, as RFC2616 forbids us to set both
+         kinds of headers (Transfer-Encoding: chunked and Content-Length) */
+
+      if(conn->bits.authneg || !Curl_checkheaders(data, "Content-Length:")) {
+        /* we allow replacing this header if not during auth negotiation,
+           although it isn't very wise to actually set your own */
+        result = Curl_add_bufferf(req_buffer,
+                                  "Content-Length: %" FORMAT_OFF_T"\r\n",
+                                  postsize);
+        if(result)
+          return result;
+      }
+    }
+
+    if(!Curl_checkheaders(data, "Content-Type:")) {
+      result = Curl_add_bufferf(req_buffer,
+                                "Content-Type: application/"
+                                "x-www-form-urlencoded\r\n");
+      if(result)
+        return result;
+    }
+
+    /* For really small posts we don't use Expect: headers at all, and for
+       the somewhat bigger ones we allow the app to disable it. Just make
+       sure that the expect100header is always set to the preferred value
+       here. */
+    ptr = Curl_checkheaders(data, "Expect:");
+    if(ptr) {
+      data->state.expect100header =
+        Curl_compareheader(ptr, "Expect:", "100-continue");
+    }
+    else if(postsize > TINY_INITIAL_POST_SIZE || postsize < 0) {
+      result = expect100(data, conn, req_buffer);
+      if(result)
+        return result;
+    }
+    else
+      data->state.expect100header = FALSE;
+
+    if(data->set.postfields) {
+
+      if(!data->state.expect100header &&
+         (postsize < MAX_INITIAL_POST_SIZE))  {
+        /* if we don't use expect: 100  AND
+           postsize is less than MAX_INITIAL_POST_SIZE
+
+           then append the post data to the HTTP request header. This limit
+           is no magic limit but only set to prevent really huge POSTs to
+           get the data duplicated with malloc() and family. */
+
+        result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
+        if(result)
+          return result;
+
+        if(!data->req.upload_chunky) {
+          /* We're not sending it 'chunked', append it to the request
+             already now to reduce the number if send() calls */
+          result = Curl_add_buffer(req_buffer, data->set.postfields,
+                                   (size_t)postsize);
+          included_body = postsize;
+        }
+        else {
+          if(postsize) {
+            /* Append the POST data chunky-style */
+            result = Curl_add_bufferf(req_buffer, "%x\r\n", (int)postsize);
+            if(CURLE_OK == result) {
+              result = Curl_add_buffer(req_buffer, data->set.postfields,
+                                       (size_t)postsize);
+              if(CURLE_OK == result)
+                 result = Curl_add_buffer(req_buffer, "\r\n", 2);
+              included_body = postsize + 2;
+            }
+          }
+          if(CURLE_OK == result)
+            result = Curl_add_buffer(req_buffer,
+                                     "\x30\x0d\x0a\x0d\x0a", 5);
+          /* 0  CR  LF  CR  LF */
+          included_body += 5;
+        }
+        if(result)
+          return result;
+        /* Make sure the progress information is accurate */
+        Curl_pgrsSetUploadSize(data, postsize);
+      }
+      else {
+        /* A huge POST coming up, do data separate from the request */
+        http->postsize = postsize;
+        http->postdata = data->set.postfields;
+
+        http->sending = HTTPSEND_BODY;
+
+        conn->fread_func = (curl_read_callback)readmoredata;
+        conn->fread_in = (void *)conn;
+
+        /* set the upload size to the progress meter */
+        Curl_pgrsSetUploadSize(data, http->postsize);
+
+        result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
+        if(result)
+          return result;
+      }
+    }
+    else {
+      result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
+      if(result)
+        return result;
+
+      if(data->req.upload_chunky && conn->bits.authneg) {
+        /* Chunky upload is selected and we're negotiating auth still, send
+           end-of-data only */
+        result = Curl_add_buffer(req_buffer,
+                                 "\x30\x0d\x0a\x0d\x0a", 5);
+        /* 0  CR  LF  CR  LF */
+        if(result)
+          return result;
+      }
+
+      else if(data->set.postfieldsize) {
+        /* set the upload size to the progress meter */
+        Curl_pgrsSetUploadSize(data, postsize?postsize:-1);
+
+        /* set the pointer to mark that we will send the post body using the
+           read callback, but only if we're not in authenticate
+           negotiation  */
+        if(!conn->bits.authneg) {
+          http->postdata = (char *)&http->postdata;
+          http->postsize = postsize;
+        }
+      }
+    }
+    /* issue the request */
+    result = Curl_add_buffer_send(req_buffer, conn, &data->info.request_size,
+                                  (size_t)included_body, FIRSTSOCKET);
+
+    if(result)
+      failf(data, "Failed sending HTTP POST request");
+    else
+      Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
+                          &http->readbytecount, http->postdata?FIRSTSOCKET:-1,
+                          http->postdata?&http->writebytecount:NULL);
+    break;
+
+  default:
+    result = Curl_add_buffer(req_buffer, "\r\n", 2);
+    if(result)
+      return result;
+
+    /* issue the request */
+    result = Curl_add_buffer_send(req_buffer, conn,
+                                  &data->info.request_size, 0, FIRSTSOCKET);
+
+    if(result)
+      failf(data, "Failed sending HTTP request");
+    else
+      /* HTTP GET/HEAD download: */
+      Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount,
+                          http->postdata?FIRSTSOCKET:-1,
+                          http->postdata?&http->writebytecount:NULL);
+  }
+  if(result)
+    return result;
+
+  if(http->writebytecount) {
+    /* if a request-body has been sent off, we make sure this progress is noted
+       properly */
+    Curl_pgrsSetUploadCounter(data, http->writebytecount);
+    if(Curl_pgrsUpdate(conn))
+      result = CURLE_ABORTED_BY_CALLBACK;
+
+    if(http->writebytecount >= postsize) {
+      /* already sent the entire request body, mark the "upload" as
+         complete */
+      infof(data, "upload completely sent off: %" FORMAT_OFF_T " out of "
+            "%" FORMAT_OFF_T " bytes\n",
+            http->writebytecount, postsize);
+      data->req.upload_done = TRUE;
+      data->req.keepon &= ~KEEP_SEND; /* we're done writing */
+      data->req.exp100 = EXP100_SEND_DATA; /* already sent */
+    }
+  }
+
+  return result;
+}
+
+/*
+ * checkhttpprefix()
+ *
+ * Returns TRUE if member of the list matches prefix of string
+ */
+static bool
+checkhttpprefix(struct SessionHandle *data,
+                const char *s)
+{
+  struct curl_slist *head = data->set.http200aliases;
+  bool rc = FALSE;
+#ifdef CURL_DOES_CONVERSIONS
+  /* convert from the network encoding using a scratch area */
+  char *scratch = strdup(s);
+  if(NULL == scratch) {
+    failf (data, "Failed to allocate memory for conversion!");
+    return FALSE; /* can't return CURLE_OUT_OF_MEMORY so return FALSE */
+  }
+  if(CURLE_OK != Curl_convert_from_network(data, scratch, strlen(s)+1)) {
+    /* Curl_convert_from_network calls failf if unsuccessful */
+    free(scratch);
+    return FALSE; /* can't return CURLE_foobar so return FALSE */
+  }
+  s = scratch;
+#endif /* CURL_DOES_CONVERSIONS */
+
+  while(head) {
+    if(checkprefix(head->data, s)) {
+      rc = TRUE;
+      break;
+    }
+    head = head->next;
+  }
+
+  if(!rc && (checkprefix("HTTP/", s)))
+    rc = TRUE;
+
+#ifdef CURL_DOES_CONVERSIONS
+  free(scratch);
+#endif /* CURL_DOES_CONVERSIONS */
+  return rc;
+}
+
+#ifndef CURL_DISABLE_RTSP
+static bool
+checkrtspprefix(struct SessionHandle *data,
+                const char *s)
+{
+
+#ifdef CURL_DOES_CONVERSIONS
+  /* convert from the network encoding using a scratch area */
+  char *scratch = strdup(s);
+  if(NULL == scratch) {
+    failf (data, "Failed to allocate memory for conversion!");
+    return FALSE; /* can't return CURLE_OUT_OF_MEMORY so return FALSE */
+  }
+  if(CURLE_OK != Curl_convert_from_network(data, scratch, strlen(s)+1)) {
+    /* Curl_convert_from_network calls failf if unsuccessful */
+    free(scratch);
+    return FALSE; /* can't return CURLE_foobar so return FALSE */
+  }
+  s = scratch;
+#else
+  (void)data; /* unused */
+#endif /* CURL_DOES_CONVERSIONS */
+  if(checkprefix("RTSP/", s))
+    return TRUE;
+  else
+    return FALSE;
+}
+#endif /* CURL_DISABLE_RTSP */
+
+static bool
+checkprotoprefix(struct SessionHandle *data, struct connectdata *conn,
+                 const char *s)
+{
+#ifndef CURL_DISABLE_RTSP
+  if(conn->handler->protocol & CURLPROTO_RTSP)
+    return checkrtspprefix(data, s);
+#else
+  (void)conn;
+#endif /* CURL_DISABLE_RTSP */
+
+  return checkhttpprefix(data, s);
+}
+
+/*
+ * header_append() copies a chunk of data to the end of the already received
+ * header. We make sure that the full string fit in the allocated header
+ * buffer, or else we enlarge it.
+ */
+static CURLcode header_append(struct SessionHandle *data,
+                              struct SingleRequest *k,
+                              size_t length)
+{
+  if(k->hbuflen + length >= data->state.headersize) {
+    /* We enlarge the header buffer as it is too small */
+    char *newbuff;
+    size_t hbufp_index;
+    size_t newsize;
+
+    if(k->hbuflen + length > CURL_MAX_HTTP_HEADER) {
+      /* The reason to have a max limit for this is to avoid the risk of a bad
+         server feeding libcurl with a never-ending header that will cause
+         reallocs infinitely */
+      failf (data, "Avoided giant realloc for header (max is %d)!",
+             CURL_MAX_HTTP_HEADER);
+      return CURLE_OUT_OF_MEMORY;
+    }
+
+    newsize=CURLMAX((k->hbuflen+ length)*3/2, data->state.headersize*2);
+    hbufp_index = k->hbufp - data->state.headerbuff;
+    newbuff = realloc(data->state.headerbuff, newsize);
+    if(!newbuff) {
+      failf (data, "Failed to alloc memory for big header!");
+      return CURLE_OUT_OF_MEMORY;
+    }
+    data->state.headersize=newsize;
+    data->state.headerbuff = newbuff;
+    k->hbufp = data->state.headerbuff + hbufp_index;
+  }
+  memcpy(k->hbufp, k->str_start, length);
+  k->hbufp += length;
+  k->hbuflen += length;
+  *k->hbufp = 0;
+
+  return CURLE_OK;
+}
+
+static void print_http_error(struct SessionHandle *data)
+{
+  struct SingleRequest *k = &data->req;
+  char *beg = k->p;
+
+  /* make sure that data->req.p points to the HTTP status line */
+  if(!strncmp(beg, "HTTP", 4)) {
+
+    /* skip to HTTP status code */
+    beg = strchr(beg, ' ');
+    if(beg && *++beg) {
+
+      /* find trailing CR */
+      char end_char = '\r';
+      char *end = strchr(beg, end_char);
+      if(!end) {
+        /* try to find LF (workaround for non-compliant HTTP servers) */
+        end_char = '\n';
+        end = strchr(beg, end_char);
+      }
+
+      if(end) {
+        /* temporarily replace CR or LF by NUL and print the error message */
+        *end = '\0';
+        failf(data, "The requested URL returned error: %s", beg);
+
+        /* restore the previously replaced CR or LF */
+        *end = end_char;
+        return;
+      }
+    }
+  }
+
+  /* fall-back to printing the HTTP status code only */
+  failf(data, "The requested URL returned error: %d", k->httpcode);
+}
+
+/*
+ * Read any HTTP header lines from the server and pass them to the client app.
+ */
+CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
+                                       struct connectdata *conn,
+                                       ssize_t *nread,
+                                       bool *stop_reading)
+{
+  CURLcode result;
+  struct SingleRequest *k = &data->req;
+
+  /* header line within buffer loop */
+  do {
+    size_t rest_length;
+    size_t full_length;
+    int writetype;
+
+    /* str_start is start of line within buf */
+    k->str_start = k->str;
+
+    /* data is in network encoding so use 0x0a instead of '\n' */
+    k->end_ptr = memchr(k->str_start, 0x0a, *nread);
+
+    if(!k->end_ptr) {
+      /* Not a complete header line within buffer, append the data to
+         the end of the headerbuff. */
+      result = header_append(data, k, *nread);
+      if(result)
+        return result;
+
+      if(!k->headerline && (k->hbuflen>5)) {
+        /* make a first check that this looks like a protocol header */
+        if(!checkprotoprefix(data, conn, data->state.headerbuff)) {
+          /* this is not the beginning of a protocol first header line */
+          k->header = FALSE;
+          k->badheader = HEADER_ALLBAD;
+          break;
+        }
+      }
+
+      break; /* read more and try again */
+    }
+
+    /* decrease the size of the remaining (supposed) header line */
+    rest_length = (k->end_ptr - k->str)+1;
+    *nread -= (ssize_t)rest_length;
+
+    k->str = k->end_ptr + 1; /* move past new line */
+
+    full_length = k->str - k->str_start;
+
+    result = header_append(data, k, full_length);
+    if(result)
+      return result;
+
+    k->end_ptr = k->hbufp;
+    k->p = data->state.headerbuff;
+
+    /****
+     * We now have a FULL header line that p points to
+     *****/
+
+    if(!k->headerline) {
+      /* the first read header */
+      if((k->hbuflen>5) &&
+         !checkprotoprefix(data, conn, data->state.headerbuff)) {
+        /* this is not the beginning of a protocol first header line */
+        k->header = FALSE;
+        if(*nread)
+          /* since there's more, this is a partial bad header */
+          k->badheader = HEADER_PARTHEADER;
+        else {
+          /* this was all we read so it's all a bad header */
+          k->badheader = HEADER_ALLBAD;
+          *nread = (ssize_t)rest_length;
+        }
+        break;
+      }
+    }
+
+    /* headers are in network encoding so
+       use 0x0a and 0x0d instead of '\n' and '\r' */
+    if((0x0a == *k->p) || (0x0d == *k->p)) {
+      size_t headerlen;
+      /* Zero-length header line means end of headers! */
+
+#ifdef CURL_DOES_CONVERSIONS
+      if(0x0d == *k->p) {
+        *k->p = '\r'; /* replace with CR in host encoding */
+        k->p++;       /* pass the CR byte */
+      }
+      if(0x0a == *k->p) {
+        *k->p = '\n'; /* replace with LF in host encoding */
+        k->p++;       /* pass the LF byte */
+      }
+#else
+      if('\r' == *k->p)
+        k->p++; /* pass the \r byte */
+      if('\n' == *k->p)
+        k->p++; /* pass the \n byte */
+#endif /* CURL_DOES_CONVERSIONS */
+
+      if(100 <= k->httpcode && 199 >= k->httpcode) {
+        /*
+         * We have made a HTTP PUT or POST and this is 1.1-lingo
+         * that tells us that the server is OK with this and ready
+         * to receive the data.
+         * However, we'll get more headers now so we must get
+         * back into the header-parsing state!
+         */
+        k->header = TRUE;
+        k->headerline = 0; /* restart the header line counter */
+
+        /* if we did wait for this do enable write now! */
+        if(k->exp100) {
+          k->exp100 = EXP100_SEND_DATA;
+          k->keepon |= KEEP_SEND;
+        }
+      }
+      else {
+        k->header = FALSE; /* no more header to parse! */
+
+        if((k->size == -1) && !k->chunk && !conn->bits.close &&
+           (conn->httpversion >= 11) &&
+           !(conn->handler->protocol & CURLPROTO_RTSP) &&
+           data->set.httpreq != HTTPREQ_HEAD) {
+          /* On HTTP 1.1, when connection is not to get closed, but no
+             Content-Length nor Content-Encoding chunked have been
+             received, according to RFC2616 section 4.4 point 5, we
+             assume that the server will close the connection to
+             signal the end of the document. */
+          infof(data, "no chunk, no close, no size. Assume close to "
+                "signal end\n");
+          conn->bits.close = TRUE;
+        }
+      }
+
+      /*
+       * When all the headers have been parsed, see if we should give
+       * up and return an error.
+       */
+      if(http_should_fail(conn)) {
+        failf (data, "The requested URL returned error: %d",
+               k->httpcode);
+        return CURLE_HTTP_RETURNED_ERROR;
+      }
+
+      /* now, only output this if the header AND body are requested:
+       */
+      writetype = CLIENTWRITE_HEADER;
+      if(data->set.include_header)
+        writetype |= CLIENTWRITE_BODY;
+
+      headerlen = k->p - data->state.headerbuff;
+
+      result = Curl_client_write(conn, writetype,
+                                 data->state.headerbuff,
+                                 headerlen);
+      if(result)
+        return result;
+
+      data->info.header_size += (long)headerlen;
+      data->req.headerbytecount += (long)headerlen;
+
+      data->req.deductheadercount =
+        (100 <= k->httpcode && 199 >= k->httpcode)?data->req.headerbytecount:0;
+
+      if(!*stop_reading) {
+        /* Curl_http_auth_act() checks what authentication methods
+         * that are available and decides which one (if any) to
+         * use. It will set 'newurl' if an auth method was picked. */
+        result = Curl_http_auth_act(conn);
+
+        if(result)
+          return result;
+
+        if(k->httpcode >= 300) {
+          if((!conn->bits.authneg) && !conn->bits.close &&
+             !conn->bits.rewindaftersend) {
+            /*
+             * General treatment of errors when about to send data. Including :
+             * "417 Expectation Failed", while waiting for 100-continue.
+             *
+             * The check for close above is done simply because of something
+             * else has already deemed the connection to get closed then
+             * something else should've considered the big picture and we
+             * avoid this check.
+             *
+             * rewindaftersend indicates that something has told libcurl to
+             * continue sending even if it gets discarded
+             */
+
+            switch(data->set.httpreq) {
+            case HTTPREQ_PUT:
+            case HTTPREQ_POST:
+            case HTTPREQ_POST_FORM:
+              /* We got an error response. If this happened before the whole
+               * request body has been sent we stop sending and mark the
+               * connection for closure after we've read the entire response.
+               */
+              if(!k->upload_done) {
+                infof(data, "HTTP error before end of send, stop sending\n");
+                conn->bits.close = TRUE; /* close after this */
+                k->upload_done = TRUE;
+                k->keepon &= ~KEEP_SEND; /* don't send */
+                if(data->state.expect100header)
+                  k->exp100 = EXP100_FAILED;
+              }
+              break;
+
+            default: /* default label present to avoid compiler warnings */
+              break;
+            }
+          }
+        }
+
+        if(conn->bits.rewindaftersend) {
+          /* We rewind after a complete send, so thus we continue
+             sending now */
+          infof(data, "Keep sending data to get tossed away!\n");
+          k->keepon |= KEEP_SEND;
+        }
+      }
+
+      if(!k->header) {
+        /*
+         * really end-of-headers.
+         *
+         * If we requested a "no body", this is a good time to get
+         * out and return home.
+         */
+        if(data->set.opt_no_body)
+          *stop_reading = TRUE;
+        else {
+          /* If we know the expected size of this document, we set the
+             maximum download size to the size of the expected
+             document or else, we won't know when to stop reading!
+
+             Note that we set the download maximum even if we read a
+             "Connection: close" header, to make sure that
+             "Content-Length: 0" still prevents us from attempting to
+             read the (missing) response-body.
+          */
+          /* According to RFC2616 section 4.4, we MUST ignore
+             Content-Length: headers if we are now receiving data
+             using chunked Transfer-Encoding.
+          */
+          if(k->chunk)
+            k->maxdownload = k->size = -1;
+        }
+        if(-1 != k->size) {
+          /* We do this operation even if no_body is true, since this
+             data might be retrieved later with curl_easy_getinfo()
+             and its CURLINFO_CONTENT_LENGTH_DOWNLOAD option. */
+
+          Curl_pgrsSetDownloadSize(data, k->size);
+          k->maxdownload = k->size;
+        }
+
+        /* If max download size is *zero* (nothing) we already
+           have nothing and can safely return ok now! */
+        if(0 == k->maxdownload)
+          *stop_reading = TRUE;
+
+        if(*stop_reading) {
+          /* we make sure that this socket isn't read more now */
+          k->keepon &= ~KEEP_RECV;
+        }
+
+        if(data->set.verbose)
+          Curl_debug(data, CURLINFO_HEADER_IN,
+                     k->str_start, headerlen, conn);
+        break;          /* exit header line loop */
+      }
+
+      /* We continue reading headers, so reset the line-based
+         header parsing variables hbufp && hbuflen */
+      k->hbufp = data->state.headerbuff;
+      k->hbuflen = 0;
+      continue;
+    }
+
+    /*
+     * Checks for special headers coming up.
+     */
+
+    if(!k->headerline++) {
+      /* This is the first header, it MUST be the error code line
+         or else we consider this to be the body right away! */
+      int httpversion_major;
+      int rtspversion_major;
+      int nc = 0;
+#ifdef CURL_DOES_CONVERSIONS
+#define HEADER1 scratch
+#define SCRATCHSIZE 21
+      CURLcode res;
+      char scratch[SCRATCHSIZE+1]; /* "HTTP/major.minor 123" */
+      /* We can't really convert this yet because we
+         don't know if it's the 1st header line or the body.
+         So we do a partial conversion into a scratch area,
+         leaving the data at k->p as-is.
+      */
+      strncpy(&scratch[0], k->p, SCRATCHSIZE);
+      scratch[SCRATCHSIZE] = 0; /* null terminate */
+      res = Curl_convert_from_network(data,
+                                      &scratch[0],
+                                      SCRATCHSIZE);
+      if(res)
+        /* Curl_convert_from_network calls failf if unsuccessful */
+        return res;
+#else
+#define HEADER1 k->p /* no conversion needed, just use k->p */
+#endif /* CURL_DOES_CONVERSIONS */
+
+      if(conn->handler->protocol & CURLPROTO_HTTP) {
+        nc = sscanf(HEADER1,
+                    " HTTP/%d.%d %3d",
+                    &httpversion_major,
+                    &conn->httpversion,
+                    &k->httpcode);
+        if(nc==3) {
+          conn->httpversion += 10 * httpversion_major;
+        }
+        else {
+          /* this is the real world, not a Nirvana
+             NCSA 1.5.x returns this crap when asked for HTTP/1.1
+          */
+          nc=sscanf(HEADER1, " HTTP %3d", &k->httpcode);
+          conn->httpversion = 10;
+
+          /* If user has set option HTTP200ALIASES,
+             compare header line against list of aliases
+          */
+          if(!nc) {
+            if(checkhttpprefix(data, k->p)) {
+              nc = 1;
+              k->httpcode = 200;
+              conn->httpversion = 10;
+            }
+          }
+        }
+      }
+      else if(conn->handler->protocol & CURLPROTO_RTSP) {
+        nc = sscanf(HEADER1,
+                    " RTSP/%d.%d %3d",
+                    &rtspversion_major,
+                    &conn->rtspversion,
+                    &k->httpcode);
+        if(nc==3) {
+          conn->rtspversion += 10 * rtspversion_major;
+          conn->httpversion = 11; /* For us, RTSP acts like HTTP 1.1 */
+        }
+        else {
+          /* TODO: do we care about the other cases here? */
+          nc = 0;
+        }
+      }
+
+      if(nc) {
+        data->info.httpcode = k->httpcode;
+
+        data->info.httpversion = conn->httpversion;
+        if(!data->state.httpversion ||
+           data->state.httpversion > conn->httpversion)
+          /* store the lowest server version we encounter */
+          data->state.httpversion = conn->httpversion;
+
+        /*
+         * This code executes as part of processing the header.  As a
+         * result, it's not totally clear how to interpret the
+         * response code yet as that depends on what other headers may
+         * be present.  401 and 407 may be errors, but may be OK
+         * depending on how authentication is working.  Other codes
+         * are definitely errors, so give up here.
+         */
+        if(data->set.http_fail_on_error && (k->httpcode >= 400) &&
+           ((k->httpcode != 401) || !conn->bits.user_passwd) &&
+           ((k->httpcode != 407) || !conn->bits.proxy_user_passwd) ) {
+
+          if(data->state.resume_from &&
+             (data->set.httpreq==HTTPREQ_GET) &&
+             (k->httpcode == 416)) {
+            /* "Requested Range Not Satisfiable", just proceed and
+               pretend this is no error */
+          }
+          else {
+            /* serious error, go home! */
+            print_http_error(data);
+            return CURLE_HTTP_RETURNED_ERROR;
+          }
+        }
+
+        if(conn->httpversion == 10) {
+          /* Default action for HTTP/1.0 must be to close, unless
+             we get one of those fancy headers that tell us the
+             server keeps it open for us! */
+          infof(data, "HTTP 1.0, assume close after body\n");
+          conn->bits.close = TRUE;
+        }
+        else if(conn->httpversion >= 11 &&
+                !conn->bits.close) {
+
+          /* If HTTP version is >= 1.1 and connection is persistent
+             server supports pipelining. */
+          DEBUGF(infof(data,
+                       "HTTP 1.1 or later with persistent connection, "
+                       "pipelining supported\n"));
+          conn->server_supports_pipelining = TRUE;
+        }
+
+        switch(k->httpcode) {
+        case 204:
+          /* (quote from RFC2616, section 10.2.5): The server has
+           * fulfilled the request but does not need to return an
+           * entity-body ... The 204 response MUST NOT include a
+           * message-body, and thus is always terminated by the first
+           * empty line after the header fields. */
+          /* FALLTHROUGH */
+        case 304:
+          /* (quote from RFC2616, section 10.3.5): The 304 response
+           * MUST NOT contain a message-body, and thus is always
+           * terminated by the first empty line after the header
+           * fields.  */
+          if(data->set.timecondition)
+            data->info.timecond = TRUE;
+          k->size=0;
+          k->maxdownload=0;
+          k->ignorecl = TRUE; /* ignore Content-Length headers */
+          break;
+        default:
+          /* nothing */
+          break;
+        }
+      }
+      else {
+        k->header = FALSE;   /* this is not a header line */
+        break;
+      }
+    }
+
+    result = Curl_convert_from_network(data, k->p, strlen(k->p));
+    /* Curl_convert_from_network calls failf if unsuccessful */
+    if(result)
+      return result;
+
+    /* Check for Content-Length: header lines to get size */
+    if(!k->ignorecl && !data->set.ignorecl &&
+       checkprefix("Content-Length:", k->p)) {
+      curl_off_t contentlength = curlx_strtoofft(k->p+15, NULL, 10);
+      if(data->set.max_filesize &&
+         contentlength > data->set.max_filesize) {
+        failf(data, "Maximum file size exceeded");
+        return CURLE_FILESIZE_EXCEEDED;
+      }
+      if(contentlength >= 0) {
+        k->size = contentlength;
+        k->maxdownload = k->size;
+        /* we set the progress download size already at this point
+           just to make it easier for apps/callbacks to extract this
+           info as soon as possible */
+        Curl_pgrsSetDownloadSize(data, k->size);
+      }
+      else {
+        /* Negative Content-Length is really odd, and we know it
+           happens for example when older Apache servers send large
+           files */
+        conn->bits.close = TRUE;
+        infof(data, "Negative content-length: %" FORMAT_OFF_T
+              ", closing after transfer\n", contentlength);
+      }
+    }
+    /* check for Content-Type: header lines to get the MIME-type */
+    else if(checkprefix("Content-Type:", k->p)) {
+      char *contenttype = copy_header_value(k->p);
+      if(!contenttype)
+        return CURLE_OUT_OF_MEMORY;
+      if(!*contenttype)
+        /* ignore empty data */
+        free(contenttype);
+      else {
+        Curl_safefree(data->info.contenttype);
+        data->info.contenttype = contenttype;
+      }
+    }
+    else if((conn->httpversion == 10) &&
+            conn->bits.httpproxy &&
+            Curl_compareheader(k->p,
+                               "Proxy-Connection:", "keep-alive")) {
+      /*
+       * When a HTTP/1.0 reply comes when using a proxy, the
+       * 'Proxy-Connection: keep-alive' line tells us the
+       * connection will be kept alive for our pleasure.
+       * Default action for 1.0 is to close.
+       */
+      conn->bits.close = FALSE; /* don't close when done */
+      infof(data, "HTTP/1.0 proxy connection set to keep alive!\n");
+    }
+    else if((conn->httpversion == 11) &&
+            conn->bits.httpproxy &&
+            Curl_compareheader(k->p,
+                               "Proxy-Connection:", "close")) {
+      /*
+       * We get a HTTP/1.1 response from a proxy and it says it'll
+       * close down after this transfer.
+       */
+      conn->bits.close = TRUE; /* close when done */
+      infof(data, "HTTP/1.1 proxy connection set close!\n");
+    }
+    else if((conn->httpversion == 10) &&
+            Curl_compareheader(k->p, "Connection:", "keep-alive")) {
+      /*
+       * A HTTP/1.0 reply with the 'Connection: keep-alive' line
+       * tells us the connection will be kept alive for our
+       * pleasure.  Default action for 1.0 is to close.
+       *
+       * [RFC2068, section 19.7.1] */
+      conn->bits.close = FALSE; /* don't close when done */
+      infof(data, "HTTP/1.0 connection set to keep alive!\n");
+    }
+    else if(Curl_compareheader(k->p, "Connection:", "close")) {
+      /*
+       * [RFC 2616, section 8.1.2.1]
+       * "Connection: close" is HTTP/1.1 language and means that
+       * the connection will close when this request has been
+       * served.
+       */
+      conn->bits.close = TRUE; /* close when done */
+    }
+    else if(checkprefix("Transfer-Encoding:", k->p)) {
+      /* One or more encodings. We check for chunked and/or a compression
+         algorithm. */
+      /*
+       * [RFC 2616, section 3.6.1] A 'chunked' transfer encoding
+       * means that the server will send a series of "chunks". Each
+       * chunk starts with line with info (including size of the
+       * coming block) (terminated with CRLF), then a block of data
+       * with the previously mentioned size. There can be any amount
+       * of chunks, and a chunk-data set to zero signals the
+       * end-of-chunks. */
+
+      char *start;
+
+      /* Find the first non-space letter */
+      start = k->p + 18;
+
+      for(;;) {
+        /* skip whitespaces and commas */
+        while(*start && (ISSPACE(*start) || (*start == ',')))
+          start++;
+
+        if(checkprefix("chunked", start)) {
+          k->chunk = TRUE; /* chunks coming our way */
+
+          /* init our chunky engine */
+          Curl_httpchunk_init(conn);
+
+          start += 7;
+        }
+
+        if(k->auto_decoding)
+          /* TODO: we only support the first mentioned compression for now */
+          break;
+
+        if(checkprefix("identity", start)) {
+          k->auto_decoding = IDENTITY;
+          start += 8;
+        }
+        else if(checkprefix("deflate", start)) {
+          k->auto_decoding = DEFLATE;
+          start += 7;
+        }
+        else if(checkprefix("gzip", start)) {
+          k->auto_decoding = GZIP;
+          start += 4;
+        }
+        else if(checkprefix("x-gzip", start)) {
+          k->auto_decoding = GZIP;
+          start += 6;
+        }
+        else if(checkprefix("compress", start)) {
+          k->auto_decoding = COMPRESS;
+          start += 8;
+        }
+        else if(checkprefix("x-compress", start)) {
+          k->auto_decoding = COMPRESS;
+          start += 10;
+        }
+        else
+          /* unknown! */
+          break;
+
+      }
+
+    }
+    else if(checkprefix("Content-Encoding:", k->p) &&
+            data->set.str[STRING_ENCODING]) {
+      /*
+       * Process Content-Encoding. Look for the values: identity,
+       * gzip, deflate, compress, x-gzip and x-compress. x-gzip and
+       * x-compress are the same as gzip and compress. (Sec 3.5 RFC
+       * 2616). zlib cannot handle compress.  However, errors are
+       * handled further down when the response body is processed
+       */
+      char *start;
+
+      /* Find the first non-space letter */
+      start = k->p + 17;
+      while(*start && ISSPACE(*start))
+        start++;
+
+      /* Record the content-encoding for later use */
+      if(checkprefix("identity", start))
+        k->auto_decoding = IDENTITY;
+      else if(checkprefix("deflate", start))
+        k->auto_decoding = DEFLATE;
+      else if(checkprefix("gzip", start)
+              || checkprefix("x-gzip", start))
+        k->auto_decoding = GZIP;
+      else if(checkprefix("compress", start)
+              || checkprefix("x-compress", start))
+        k->auto_decoding = COMPRESS;
+    }
+    else if(checkprefix("Content-Range:", k->p)) {
+      /* Content-Range: bytes [num]-
+         Content-Range: bytes: [num]-
+         Content-Range: [num]-
+
+         The second format was added since Sun's webserver
+         JavaWebServer/1.1.1 obviously sends the header this way!
+         The third added since some servers use that!
+      */
+
+      char *ptr = k->p + 14;
+
+      /* Move forward until first digit */
+      while(*ptr && !ISDIGIT(*ptr))
+        ptr++;
+
+      k->offset = curlx_strtoofft(ptr, NULL, 10);
+
+      if(data->state.resume_from == k->offset)
+        /* we asked for a resume and we got it */
+        k->content_range = TRUE;
+    }
+#if !defined(CURL_DISABLE_COOKIES)
+    else if(data->cookies &&
+            checkprefix("Set-Cookie:", k->p)) {
+      Curl_share_lock(data, CURL_LOCK_DATA_COOKIE,
+                      CURL_LOCK_ACCESS_SINGLE);
+      Curl_cookie_add(data,
+                      data->cookies, TRUE, k->p+11,
+                      /* If there is a custom-set Host: name, use it
+                         here, or else use real peer host name. */
+                      conn->allocptr.cookiehost?
+                      conn->allocptr.cookiehost:conn->host.name,
+                      data->state.path);
+      Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
+    }
+#endif
+    else if(checkprefix("Last-Modified:", k->p) &&
+            (data->set.timecondition || data->set.get_filetime) ) {
+      time_t secs=time(NULL);
+      k->timeofdoc = curl_getdate(k->p+strlen("Last-Modified:"),
+                                  &secs);
+      if(data->set.get_filetime)
+        data->info.filetime = (long)k->timeofdoc;
+    }
+    else if((checkprefix("WWW-Authenticate:", k->p) &&
+             (401 == k->httpcode)) ||
+            (checkprefix("Proxy-authenticate:", k->p) &&
+             (407 == k->httpcode))) {
+      result = Curl_http_input_auth(conn, k->httpcode, k->p);
+      if(result)
+        return result;
+    }
+    else if((k->httpcode >= 300 && k->httpcode < 400) &&
+            checkprefix("Location:", k->p) &&
+            !data->req.location) {
+      /* this is the URL that the server advises us to use instead */
+      char *location = copy_header_value(k->p);
+      if(!location)
+        return CURLE_OUT_OF_MEMORY;
+      if(!*location)
+        /* ignore empty data */
+        free(location);
+      else {
+        data->req.location = location;
+
+        if(data->set.http_follow_location) {
+          DEBUGASSERT(!data->req.newurl);
+          data->req.newurl = strdup(data->req.location); /* clone */
+          if(!data->req.newurl)
+            return CURLE_OUT_OF_MEMORY;
+
+          /* some cases of POST and PUT etc needs to rewind the data
+             stream at this point */
+          result = http_perhapsrewind(conn);
+          if(result)
+            return result;
+        }
+      }
+    }
+    else if(conn->handler->protocol & CURLPROTO_RTSP) {
+      result = Curl_rtsp_parseheader(conn, k->p);
+      if(result)
+        return result;
+    }
+
+    /*
+     * End of header-checks. Write them to the client.
+     */
+
+    writetype = CLIENTWRITE_HEADER;
+    if(data->set.include_header)
+      writetype |= CLIENTWRITE_BODY;
+
+    if(data->set.verbose)
+      Curl_debug(data, CURLINFO_HEADER_IN,
+                 k->p, (size_t)k->hbuflen, conn);
+
+    result = Curl_client_write(conn, writetype, k->p, k->hbuflen);
+    if(result)
+      return result;
+
+    data->info.header_size += (long)k->hbuflen;
+    data->req.headerbytecount += (long)k->hbuflen;
+
+    /* reset hbufp pointer && hbuflen */
+    k->hbufp = data->state.headerbuff;
+    k->hbuflen = 0;
+  }
+  while(!*stop_reading && *k->str); /* header line within buffer */
+
+  /* We might have reached the end of the header part here, but
+     there might be a non-header part left in the end of the read
+     buffer. */
+
+  return CURLE_OK;
+}
+
+#endif /* CURL_DISABLE_HTTP */
diff --git a/lib/curl_http_chunks.c b/lib/curl_http_chunks.c
new file mode 100644 (file)
index 0000000..2112f72
--- /dev/null
@@ -0,0 +1,397 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_HTTP
+
+#include "curl_urldata.h" /* it includes curl_http_chunks.h */
+#include "curl_sendf.h"   /* for the client write stuff */
+
+#include "curl_content_encoding.h"
+#include "curl_http.h"
+#include "curl_memory.h"
+#include "curl_non_ascii.h" /* for Curl_convert_to_network prototype */
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/*
+ * Chunk format (simplified):
+ *
+ * <HEX SIZE>[ chunk extension ] CRLF
+ * <DATA> CRLF
+ *
+ * Highlights from RFC2616 section 3.6 say:
+
+   The chunked encoding modifies the body of a message in order to
+   transfer it as a series of chunks, each with its own size indicator,
+   followed by an OPTIONAL trailer containing entity-header fields. This
+   allows dynamically produced content to be transferred along with the
+   information necessary for the recipient to verify that it has
+   received the full message.
+
+       Chunked-Body   = *chunk
+                        last-chunk
+                        trailer
+                        CRLF
+
+       chunk          = chunk-size [ chunk-extension ] CRLF
+                        chunk-data CRLF
+       chunk-size     = 1*HEX
+       last-chunk     = 1*("0") [ chunk-extension ] CRLF
+
+       chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
+       chunk-ext-name = token
+       chunk-ext-val  = token | quoted-string
+       chunk-data     = chunk-size(OCTET)
+       trailer        = *(entity-header CRLF)
+
+   The chunk-size field is a string of hex digits indicating the size of
+   the chunk. The chunked encoding is ended by any chunk whose size is
+   zero, followed by the trailer, which is terminated by an empty line.
+
+ */
+
+/* Check for an ASCII hex digit.
+ We avoid the use of isxdigit to accommodate non-ASCII hosts. */
+static bool Curl_isxdigit(char digit)
+{
+  return ( (digit >= 0x30 && digit <= 0x39) /* 0-9 */
+        || (digit >= 0x41 && digit <= 0x46) /* A-F */
+        || (digit >= 0x61 && digit <= 0x66) /* a-f */ ) ? TRUE : FALSE;
+}
+
+void Curl_httpchunk_init(struct connectdata *conn)
+{
+  struct Curl_chunker *chunk = &conn->chunk;
+  chunk->hexindex=0; /* start at 0 */
+  chunk->dataleft=0; /* no data left yet! */
+  chunk->state = CHUNK_HEX; /* we get hex first! */
+}
+
+/*
+ * chunk_read() returns a OK for normal operations, or a positive return code
+ * for errors. STOP means this sequence of chunks is complete.  The 'wrote'
+ * argument is set to tell the caller how many bytes we actually passed to the
+ * client (for byte-counting and whatever).
+ *
+ * The states and the state-machine is further explained in the header file.
+ *
+ * This function always uses ASCII hex values to accommodate non-ASCII hosts.
+ * For example, 0x0d and 0x0a are used instead of '\r' and '\n'.
+ */
+CHUNKcode Curl_httpchunk_read(struct connectdata *conn,
+                              char *datap,
+                              ssize_t datalen,
+                              ssize_t *wrotep)
+{
+  CURLcode result=CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  struct Curl_chunker *ch = &conn->chunk;
+  struct SingleRequest *k = &data->req;
+  size_t piece;
+  size_t length = (size_t)datalen;
+  size_t *wrote = (size_t *)wrotep;
+
+  *wrote = 0; /* nothing's written yet */
+
+  /* the original data is written to the client, but we go on with the
+     chunk read process, to properly calculate the content length*/
+  if(data->set.http_te_skip && !k->ignorebody) {
+    result = Curl_client_write(conn, CLIENTWRITE_BODY, datap, datalen);
+    if(result)
+      return CHUNKE_WRITE_ERROR;
+  }
+
+  while(length) {
+    switch(ch->state) {
+    case CHUNK_HEX:
+      if(Curl_isxdigit(*datap)) {
+        if(ch->hexindex < MAXNUM_SIZE) {
+          ch->hexbuffer[ch->hexindex] = *datap;
+          datap++;
+          length--;
+          ch->hexindex++;
+        }
+        else {
+          return CHUNKE_TOO_LONG_HEX; /* longer hex than we support */
+        }
+      }
+      else {
+        if(0 == ch->hexindex) {
+          /* This is illegal data, we received junk where we expected
+             a hexadecimal digit. */
+          return CHUNKE_ILLEGAL_HEX;
+        }
+        /* length and datap are unmodified */
+        ch->hexbuffer[ch->hexindex]=0;
+
+        /* convert to host encoding before calling strtoul */
+        result = Curl_convert_from_network(conn->data, ch->hexbuffer,
+                                           ch->hexindex);
+        if(result) {
+          /* Curl_convert_from_network calls failf if unsuccessful */
+          /* Treat it as a bad hex character */
+          return(CHUNKE_ILLEGAL_HEX);
+        }
+
+        ch->datasize=strtoul(ch->hexbuffer, NULL, 16);
+        ch->state = CHUNK_POSTHEX;
+      }
+      break;
+
+    case CHUNK_POSTHEX:
+      /* In this state, we're waiting for CRLF to arrive. We support
+         this to allow so called chunk-extensions to show up here
+         before the CRLF comes. */
+      if(*datap == 0x0d)
+        ch->state = CHUNK_CR;
+      length--;
+      datap++;
+      break;
+
+    case CHUNK_CR:
+      /* waiting for the LF */
+      if(*datap == 0x0a) {
+        /* we're now expecting data to come, unless size was zero! */
+        if(0 == ch->datasize) {
+          ch->state = CHUNK_TRAILER; /* now check for trailers */
+          conn->trlPos=0;
+        }
+        else {
+          ch->state = CHUNK_DATA;
+        }
+      }
+      else
+        /* previously we got a fake CR, go back to CR waiting! */
+        ch->state = CHUNK_CR;
+      datap++;
+      length--;
+      break;
+
+    case CHUNK_DATA:
+      /* we get pure and fine data
+
+         We expect another 'datasize' of data. We have 'length' right now,
+         it can be more or less than 'datasize'. Get the smallest piece.
+      */
+      piece = (ch->datasize >= length)?length:ch->datasize;
+
+      /* Write the data portion available */
+#ifdef HAVE_LIBZ
+      switch (conn->data->set.http_ce_skip?
+              IDENTITY : data->req.auto_decoding) {
+      case IDENTITY:
+#endif
+        if(!k->ignorebody) {
+          if(!data->set.http_te_skip)
+            result = Curl_client_write(conn, CLIENTWRITE_BODY, datap,
+                                       piece);
+          else
+            result = CURLE_OK;
+        }
+#ifdef HAVE_LIBZ
+        break;
+
+      case DEFLATE:
+        /* update data->req.keep.str to point to the chunk data. */
+        data->req.str = datap;
+        result = Curl_unencode_deflate_write(conn, &data->req,
+                                             (ssize_t)piece);
+        break;
+
+      case GZIP:
+        /* update data->req.keep.str to point to the chunk data. */
+        data->req.str = datap;
+        result = Curl_unencode_gzip_write(conn, &data->req,
+                                          (ssize_t)piece);
+        break;
+
+      case COMPRESS:
+      default:
+        failf (conn->data,
+               "Unrecognized content encoding type. "
+               "libcurl understands `identity', `deflate' and `gzip' "
+               "content encodings.");
+        return CHUNKE_BAD_ENCODING;
+      }
+#endif
+
+      if(result)
+        return CHUNKE_WRITE_ERROR;
+
+      *wrote += piece;
+
+      ch->datasize -= piece; /* decrease amount left to expect */
+      datap += piece;    /* move read pointer forward */
+      length -= piece;   /* decrease space left in this round */
+
+      if(0 == ch->datasize)
+        /* end of data this round, we now expect a trailing CRLF */
+        ch->state = CHUNK_POSTCR;
+      break;
+
+    case CHUNK_POSTCR:
+      if(*datap == 0x0d) {
+        ch->state = CHUNK_POSTLF;
+        datap++;
+        length--;
+      }
+      else
+        return CHUNKE_BAD_CHUNK;
+
+      break;
+
+    case CHUNK_POSTLF:
+      if(*datap == 0x0a) {
+        /*
+         * The last one before we go back to hex state and start all
+         * over.
+         */
+        Curl_httpchunk_init(conn);
+        datap++;
+        length--;
+      }
+      else
+        return CHUNKE_BAD_CHUNK;
+
+      break;
+
+    case CHUNK_TRAILER:
+      if(*datap == 0x0d) {
+        /* this is the end of a trailer, but if the trailer was zero bytes
+           there was no trailer and we move on */
+
+        if(conn->trlPos) {
+          /* we allocate trailer with 3 bytes extra room to fit this */
+          conn->trailer[conn->trlPos++]=0x0d;
+          conn->trailer[conn->trlPos++]=0x0a;
+          conn->trailer[conn->trlPos]=0;
+
+          /* Convert to host encoding before calling Curl_client_write */
+          result = Curl_convert_from_network(conn->data, conn->trailer,
+                                             conn->trlPos);
+          if(result)
+            /* Curl_convert_from_network calls failf if unsuccessful */
+            /* Treat it as a bad chunk */
+            return CHUNKE_BAD_CHUNK;
+
+          if(!data->set.http_te_skip) {
+            result = Curl_client_write(conn, CLIENTWRITE_HEADER,
+                                       conn->trailer, conn->trlPos);
+            if(result)
+              return CHUNKE_WRITE_ERROR;
+          }
+          conn->trlPos=0;
+          ch->state = CHUNK_TRAILER_CR;
+        }
+        else {
+          /* no trailer, we're on the final CRLF pair */
+          ch->state = CHUNK_TRAILER_POSTCR;
+          break; /* don't advance the pointer */
+        }
+      }
+      else {
+        /* conn->trailer is assumed to be freed in curl_url.c on a
+           connection basis */
+        if(conn->trlPos >= conn->trlMax) {
+          /* we always allocate three extra bytes, just because when the full
+             header has been received we append CRLF\0 */
+          char *ptr;
+          if(conn->trlMax) {
+            conn->trlMax *= 2;
+            ptr = realloc(conn->trailer, conn->trlMax + 3);
+          }
+          else {
+            conn->trlMax=128;
+            ptr = malloc(conn->trlMax + 3);
+          }
+          if(!ptr)
+            return CHUNKE_OUT_OF_MEMORY;
+          conn->trailer = ptr;
+        }
+        conn->trailer[conn->trlPos++]=*datap;
+      }
+      datap++;
+      length--;
+      break;
+
+    case CHUNK_TRAILER_CR:
+      if(*datap == 0x0a) {
+        ch->state = CHUNK_TRAILER_POSTCR;
+        datap++;
+        length--;
+      }
+      else
+        return CHUNKE_BAD_CHUNK;
+      break;
+
+    case CHUNK_TRAILER_POSTCR:
+      /* We enter this state when a CR should arrive so we expect to
+         have to first pass a CR before we wait for LF */
+      if(*datap != 0x0d) {
+        /* not a CR then it must be another header in the trailer */
+        ch->state = CHUNK_TRAILER;
+        break;
+      }
+      datap++;
+      length--;
+      /* now wait for the final LF */
+      ch->state = CHUNK_STOP;
+      break;
+
+    case CHUNK_STOPCR:
+      /* Read the final CRLF that ends all chunk bodies */
+
+      if(*datap == 0x0d) {
+        ch->state = CHUNK_STOP;
+        datap++;
+        length--;
+      }
+      else
+        return CHUNKE_BAD_CHUNK;
+      break;
+
+    case CHUNK_STOP:
+      if(*datap == 0x0a) {
+        length--;
+
+        /* Record the length of any data left in the end of the buffer
+           even if there's no more chunks to read */
+
+        ch->dataleft = length;
+        return CHUNKE_STOP; /* return stop */
+      }
+      else
+        return CHUNKE_BAD_CHUNK;
+
+    default:
+      return CHUNKE_STATE_ERROR;
+    }
+  }
+  return CHUNKE_OK;
+}
+#endif /* CURL_DISABLE_HTTP */
diff --git a/lib/curl_http_digest.c b/lib/curl_http_digest.c
new file mode 100644 (file)
index 0000000..dae6799
--- /dev/null
@@ -0,0 +1,583 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
+
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_rawstr.h"
+#include "curl_base64.h"
+#include "curl_md5.h"
+#include "curl_http_digest.h"
+#include "curl_strtok.h"
+#include "curl_url.h"
+#include "curl_memory.h"
+#include "curl_non_ascii.h" /* included for Curl_convert_... prototypes */
+#include "curl_warnless.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+#define MAX_VALUE_LENGTH 256
+#define MAX_CONTENT_LENGTH 1024
+
+static void digest_cleanup_one(struct digestdata *dig);
+
+/*
+ * Return 0 on success and then the buffers are filled in fine.
+ *
+ * Non-zero means failure to parse.
+ */
+static int get_pair(const char *str, char *value, char *content,
+                    const char **endptr)
+{
+  int c;
+  bool starts_with_quote = FALSE;
+  bool escape = FALSE;
+
+  for(c=MAX_VALUE_LENGTH-1; (*str && (*str != '=') && c--); )
+    *value++ = *str++;
+  *value=0;
+
+  if('=' != *str++)
+    /* eek, no match */
+    return 1;
+
+  if('\"' == *str) {
+    /* this starts with a quote so it must end with one as well! */
+    str++;
+    starts_with_quote = TRUE;
+  }
+
+  for(c=MAX_CONTENT_LENGTH-1; *str && c--; str++) {
+    switch(*str) {
+    case '\\':
+      if(!escape) {
+        /* possibly the start of an escaped quote */
+        escape = TRUE;
+        *content++ = '\\'; /* even though this is an escape character, we still
+                              store it as-is in the target buffer */
+        continue;
+      }
+      break;
+    case ',':
+      if(!starts_with_quote) {
+        /* this signals the end of the content if we didn't get a starting
+           quote and then we do "sloppy" parsing */
+        c=0; /* the end */
+        continue;
+      }
+      break;
+    case '\r':
+    case '\n':
+      /* end of string */
+      c=0;
+      continue;
+    case '\"':
+      if(!escape && starts_with_quote) {
+        /* end of string */
+        c=0;
+        continue;
+      }
+      break;
+    }
+    escape = FALSE;
+    *content++ = *str;
+  }
+  *content=0;
+
+  *endptr = str;
+
+  return 0; /* all is fine! */
+}
+
+/* Test example headers:
+
+WWW-Authenticate: Digest realm="testrealm", nonce="1053604598"
+Proxy-Authenticate: Digest realm="testrealm", nonce="1053604598"
+
+*/
+
+CURLdigest Curl_input_digest(struct connectdata *conn,
+                             bool proxy,
+                             const char *header) /* rest of the *-authenticate:
+                                                    header */
+{
+  char *token = NULL;
+  char *tmp = NULL;
+  bool foundAuth = FALSE;
+  bool foundAuthInt = FALSE;
+  struct SessionHandle *data=conn->data;
+  bool before = FALSE; /* got a nonce before */
+  struct digestdata *d;
+
+  if(proxy) {
+    d = &data->state.proxydigest;
+  }
+  else {
+    d = &data->state.digest;
+  }
+
+  /* skip initial whitespaces */
+  while(*header && ISSPACE(*header))
+    header++;
+
+  if(checkprefix("Digest", header)) {
+    header += strlen("Digest");
+
+    /* If we already have received a nonce, keep that in mind */
+    if(d->nonce)
+      before = TRUE;
+
+    /* clear off any former leftovers and init to defaults */
+    digest_cleanup_one(d);
+
+    for(;;) {
+      char value[MAX_VALUE_LENGTH];
+      char content[MAX_CONTENT_LENGTH];
+
+      while(*header && ISSPACE(*header))
+        header++;
+
+      /* extract a value=content pair */
+      if(!get_pair(header, value, content, &header)) {
+        if(Curl_raw_equal(value, "nonce")) {
+          d->nonce = strdup(content);
+          if(!d->nonce)
+            return CURLDIGEST_NOMEM;
+        }
+        else if(Curl_raw_equal(value, "stale")) {
+          if(Curl_raw_equal(content, "true")) {
+            d->stale = TRUE;
+            d->nc = 1; /* we make a new nonce now */
+          }
+        }
+        else if(Curl_raw_equal(value, "realm")) {
+          d->realm = strdup(content);
+          if(!d->realm)
+            return CURLDIGEST_NOMEM;
+        }
+        else if(Curl_raw_equal(value, "opaque")) {
+          d->opaque = strdup(content);
+          if(!d->opaque)
+            return CURLDIGEST_NOMEM;
+        }
+        else if(Curl_raw_equal(value, "qop")) {
+          char *tok_buf;
+          /* tokenize the list and choose auth if possible, use a temporary
+             clone of the buffer since strtok_r() ruins it */
+          tmp = strdup(content);
+          if(!tmp)
+            return CURLDIGEST_NOMEM;
+          token = strtok_r(tmp, ",", &tok_buf);
+          while(token != NULL) {
+            if(Curl_raw_equal(token, "auth")) {
+              foundAuth = TRUE;
+            }
+            else if(Curl_raw_equal(token, "auth-int")) {
+              foundAuthInt = TRUE;
+            }
+            token = strtok_r(NULL, ",", &tok_buf);
+          }
+          free(tmp);
+          /*select only auth o auth-int. Otherwise, ignore*/
+          if(foundAuth) {
+            d->qop = strdup("auth");
+            if(!d->qop)
+              return CURLDIGEST_NOMEM;
+          }
+          else if(foundAuthInt) {
+            d->qop = strdup("auth-int");
+            if(!d->qop)
+              return CURLDIGEST_NOMEM;
+          }
+        }
+        else if(Curl_raw_equal(value, "algorithm")) {
+          d->algorithm = strdup(content);
+          if(!d->algorithm)
+            return CURLDIGEST_NOMEM;
+          if(Curl_raw_equal(content, "MD5-sess"))
+            d->algo = CURLDIGESTALGO_MD5SESS;
+          else if(Curl_raw_equal(content, "MD5"))
+            d->algo = CURLDIGESTALGO_MD5;
+          else
+            return CURLDIGEST_BADALGO;
+        }
+        else {
+          /* unknown specifier, ignore it! */
+        }
+      }
+      else
+        break; /* we're done here */
+
+      /* pass all additional spaces here */
+      while(*header && ISSPACE(*header))
+        header++;
+      if(',' == *header)
+        /* allow the list to be comma-separated */
+        header++;
+    }
+    /* We had a nonce since before, and we got another one now without
+       'stale=true'. This means we provided bad credentials in the previous
+       request */
+    if(before && !d->stale)
+      return CURLDIGEST_BAD;
+
+    /* We got this header without a nonce, that's a bad Digest line! */
+    if(!d->nonce)
+      return CURLDIGEST_BAD;
+  }
+  else
+    /* else not a digest, get out */
+    return CURLDIGEST_NONE;
+
+  return CURLDIGEST_FINE;
+}
+
+/* convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/
+static void md5_to_ascii(unsigned char *source, /* 16 bytes */
+                         unsigned char *dest) /* 33 bytes */
+{
+  int i;
+  for(i=0; i<16; i++)
+    snprintf((char *)&dest[i*2], 3, "%02x", source[i]);
+}
+
+CURLcode Curl_output_digest(struct connectdata *conn,
+                            bool proxy,
+                            const unsigned char *request,
+                            const unsigned char *uripath)
+{
+  /* We have a Digest setup for this, use it!  Now, to get all the details for
+     this sorted out, I must urge you dear friend to read up on the RFC2617
+     section 3.2.2, */
+  unsigned char md5buf[16]; /* 16 bytes/128 bits */
+  unsigned char request_digest[33];
+  unsigned char *md5this;
+  unsigned char *ha1;
+  unsigned char ha2[33];/* 32 digits and 1 zero byte */
+  char cnoncebuf[33];
+  char *cnonce = NULL;
+  size_t cnonce_sz = 0;
+  char *tmp = NULL;
+  struct timeval now;
+
+  char **allocuserpwd;
+  const char *userp;
+  const char *passwdp;
+  struct auth *authp;
+
+  struct SessionHandle *data = conn->data;
+  struct digestdata *d;
+  CURLcode rc;
+/* The CURL_OUTPUT_DIGEST_CONV macro below is for non-ASCII machines.
+   It converts digest text to ASCII so the MD5 will be correct for
+   what ultimately goes over the network.
+*/
+#define CURL_OUTPUT_DIGEST_CONV(a, b) \
+  rc = Curl_convert_to_network(a, (char *)b, strlen((const char*)b)); \
+  if(rc != CURLE_OK) { \
+    free(b); \
+    return rc; \
+  }
+
+  if(proxy) {
+    d = &data->state.proxydigest;
+    allocuserpwd = &conn->allocptr.proxyuserpwd;
+    userp = conn->proxyuser;
+    passwdp = conn->proxypasswd;
+    authp = &data->state.authproxy;
+  }
+  else {
+    d = &data->state.digest;
+    allocuserpwd = &conn->allocptr.userpwd;
+    userp = conn->user;
+    passwdp = conn->passwd;
+    authp = &data->state.authhost;
+  }
+
+  if(*allocuserpwd) {
+    Curl_safefree(*allocuserpwd);
+    *allocuserpwd = NULL;
+  }
+
+  /* not set means empty */
+  if(!userp)
+    userp="";
+
+  if(!passwdp)
+    passwdp="";
+
+  if(!d->nonce) {
+    authp->done = FALSE;
+    return CURLE_OK;
+  }
+  authp->done = TRUE;
+
+  if(!d->nc)
+    d->nc = 1;
+
+  if(!d->cnonce) {
+    /* Generate a cnonce */
+    now = Curl_tvnow();
+    snprintf(cnoncebuf, sizeof(cnoncebuf), "%32ld",
+             (long)now.tv_sec + now.tv_usec);
+
+    rc = Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf),
+                            &cnonce, &cnonce_sz);
+    if(rc)
+      return rc;
+    d->cnonce = cnonce;
+  }
+
+  /*
+    if the algorithm is "MD5" or unspecified (which then defaults to MD5):
+
+    A1 = unq(username-value) ":" unq(realm-value) ":" passwd
+
+    if the algorithm is "MD5-sess" then:
+
+    A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd )
+         ":" unq(nonce-value) ":" unq(cnonce-value)
+  */
+
+  md5this = (unsigned char *)
+    aprintf("%s:%s:%s", userp, d->realm, passwdp);
+  if(!md5this)
+    return CURLE_OUT_OF_MEMORY;
+
+  CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
+  Curl_md5it(md5buf, md5this);
+  free(md5this); /* free this again */
+
+  ha1 = malloc(33); /* 32 digits and 1 zero byte */
+  if(!ha1)
+    return CURLE_OUT_OF_MEMORY;
+
+  md5_to_ascii(md5buf, ha1);
+
+  if(d->algo == CURLDIGESTALGO_MD5SESS) {
+    /* nonce and cnonce are OUTSIDE the hash */
+    tmp = aprintf("%s:%s:%s", ha1, d->nonce, d->cnonce);
+    if(!tmp)
+      return CURLE_OUT_OF_MEMORY;
+    CURL_OUTPUT_DIGEST_CONV(data, tmp); /* convert on non-ASCII machines */
+    Curl_md5it(md5buf, (unsigned char *)tmp);
+    free(tmp); /* free this again */
+    md5_to_ascii(md5buf, ha1);
+  }
+
+  /*
+    If the "qop" directive's value is "auth" or is unspecified, then A2 is:
+
+      A2       = Method ":" digest-uri-value
+
+          If the "qop" value is "auth-int", then A2 is:
+
+      A2       = Method ":" digest-uri-value ":" H(entity-body)
+
+    (The "Method" value is the HTTP request method as specified in section
+    5.1.1 of RFC 2616)
+  */
+
+  /* So IE browsers < v7 cut off the URI part at the query part when they
+     evaluate the MD5 and some (IIS?) servers work with them so we may need to
+     do the Digest IE-style. Note that the different ways cause different MD5
+     sums to get sent.
+
+     Apache servers can be set to do the Digest IE-style automatically using
+     the BrowserMatch feature:
+     http://httpd.apache.org/docs/2.2/mod/mod_auth_digest.html#msie
+
+     Further details on Digest implementation differences:
+     http://www.fngtps.com/2006/09/http-authentication
+  */
+  if(authp->iestyle && ((tmp = strchr((char *)uripath, '?')) != NULL)) {
+    md5this = (unsigned char *)aprintf("%s:%.*s", request,
+                                       curlx_sztosi(tmp - (char *)uripath),
+                                       uripath);
+  }
+  else
+    md5this = (unsigned char *)aprintf("%s:%s", request, uripath);
+
+  if(!md5this) {
+    free(ha1);
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  if(d->qop && Curl_raw_equal(d->qop, "auth-int")) {
+    /* We don't support auth-int at the moment. I can't see a easy way to get
+       entity-body here */
+    /* TODO: Append H(entity-body)*/
+  }
+  CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
+  Curl_md5it(md5buf, md5this);
+  free(md5this); /* free this again */
+  md5_to_ascii(md5buf, ha2);
+
+  if(d->qop) {
+    md5this = (unsigned char *)aprintf("%s:%s:%08x:%s:%s:%s",
+                                       ha1,
+                                       d->nonce,
+                                       d->nc,
+                                       d->cnonce,
+                                       d->qop,
+                                       ha2);
+  }
+  else {
+    md5this = (unsigned char *)aprintf("%s:%s:%s",
+                                       ha1,
+                                       d->nonce,
+                                       ha2);
+  }
+  free(ha1);
+  if(!md5this)
+    return CURLE_OUT_OF_MEMORY;
+
+  CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
+  Curl_md5it(md5buf, md5this);
+  free(md5this); /* free this again */
+  md5_to_ascii(md5buf, request_digest);
+
+  /* for test case 64 (snooped from a Mozilla 1.3a request)
+
+    Authorization: Digest username="testuser", realm="testrealm", \
+    nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
+  */
+
+  if(d->qop) {
+    *allocuserpwd =
+      aprintf( "%sAuthorization: Digest "
+               "username=\"%s\", "
+               "realm=\"%s\", "
+               "nonce=\"%s\", "
+               "uri=\"%s\", "
+               "cnonce=\"%s\", "
+               "nc=%08x, "
+               "qop=%s, "
+               "response=\"%s\"",
+               proxy?"Proxy-":"",
+               userp,
+               d->realm,
+               d->nonce,
+               uripath, /* this is the PATH part of the URL */
+               d->cnonce,
+               d->nc,
+               d->qop,
+               request_digest);
+
+    if(Curl_raw_equal(d->qop, "auth"))
+      d->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0 padded
+                  which tells to the server how many times you are using the
+                  same nonce in the qop=auth mode. */
+  }
+  else {
+    *allocuserpwd =
+      aprintf( "%sAuthorization: Digest "
+               "username=\"%s\", "
+               "realm=\"%s\", "
+               "nonce=\"%s\", "
+               "uri=\"%s\", "
+               "response=\"%s\"",
+               proxy?"Proxy-":"",
+               userp,
+               d->realm,
+               d->nonce,
+               uripath, /* this is the PATH part of the URL */
+               request_digest);
+  }
+  if(!*allocuserpwd)
+    return CURLE_OUT_OF_MEMORY;
+
+  /* Add optional fields */
+  if(d->opaque) {
+    /* append opaque */
+    tmp = aprintf("%s, opaque=\"%s\"", *allocuserpwd, d->opaque);
+    if(!tmp)
+      return CURLE_OUT_OF_MEMORY;
+    free(*allocuserpwd);
+    *allocuserpwd = tmp;
+  }
+
+  if(d->algorithm) {
+    /* append algorithm */
+    tmp = aprintf("%s, algorithm=\"%s\"", *allocuserpwd, d->algorithm);
+    if(!tmp)
+      return CURLE_OUT_OF_MEMORY;
+    free(*allocuserpwd);
+    *allocuserpwd = tmp;
+  }
+
+  /* append CRLF + zero (3 bytes) to the userpwd header */
+  tmp = realloc(*allocuserpwd, strlen(*allocuserpwd) + 3);
+  if(!tmp)
+    return CURLE_OUT_OF_MEMORY;
+  strcat(tmp, "\r\n");
+  *allocuserpwd = tmp;
+
+  return CURLE_OK;
+}
+
+static void digest_cleanup_one(struct digestdata *d)
+{
+  if(d->nonce)
+    free(d->nonce);
+  d->nonce = NULL;
+
+  if(d->cnonce)
+    free(d->cnonce);
+  d->cnonce = NULL;
+
+  if(d->realm)
+    free(d->realm);
+  d->realm = NULL;
+
+  if(d->opaque)
+    free(d->opaque);
+  d->opaque = NULL;
+
+  if(d->qop)
+    free(d->qop);
+  d->qop = NULL;
+
+  if(d->algorithm)
+    free(d->algorithm);
+  d->algorithm = NULL;
+
+  d->nc = 0;
+  d->algo = CURLDIGESTALGO_MD5; /* default algorithm */
+  d->stale = FALSE; /* default means normal, not stale */
+}
+
+
+void Curl_digest_cleanup(struct SessionHandle *data)
+{
+  digest_cleanup_one(&data->state.digest);
+  digest_cleanup_one(&data->state.proxydigest);
+}
+
+#endif
diff --git a/lib/curl_http_negotiate.c b/lib/curl_http_negotiate.c
new file mode 100644 (file)
index 0000000..446c49b
--- /dev/null
@@ -0,0 +1,373 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_GSSAPI
+#ifdef HAVE_OLD_GSSMIT
+#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
+#define NCOMPAT 1
+#endif
+
+#ifndef CURL_DISABLE_HTTP
+
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_gssapi.h"
+#include "curl_rawstr.h"
+#include "curl_base64.h"
+#include "curl_http_negotiate.h"
+#include "curl_memory.h"
+#include "curl_url.h"
+
+#ifdef HAVE_SPNEGO
+#  include <spnegohelp.h>
+#  ifdef USE_SSLEAY
+#    ifdef USE_OPENSSL
+#      include <openssl/objects.h>
+#    else
+#      include <objects.h>
+#    endif
+#  else
+#    error "Can't compile SPNEGO support without OpenSSL."
+#  endif
+#endif
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+static int
+get_gss_name(struct connectdata *conn, bool proxy, gss_name_t *server)
+{
+  struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg:
+    &conn->data->state.negotiate;
+  OM_uint32 major_status, minor_status;
+  gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
+  char name[2048];
+  const char* service;
+
+  /* GSSAPI implementation by Globus (known as GSI) requires the name to be
+     of form "<service>/<fqdn>" instead of <service>@<fqdn> (ie. slash instead
+     of at-sign). Also GSI servers are often identified as 'host' not 'khttp'.
+     Change following lines if you want to use GSI */
+
+  /* IIS uses the <service>@<fqdn> form but uses 'http' as the service name */
+
+  if(neg_ctx->gss)
+    service = "KHTTP";
+  else
+    service = "HTTP";
+
+  token.length = strlen(service) + 1 + strlen(proxy ? conn->proxy.name :
+                                              conn->host.name) + 1;
+  if(token.length + 1 > sizeof(name))
+    return EMSGSIZE;
+
+  snprintf(name, sizeof(name), "%s@%s", service, proxy ? conn->proxy.name :
+           conn->host.name);
+
+  token.value = (void *) name;
+  major_status = gss_import_name(&minor_status,
+                                 &token,
+                                 GSS_C_NT_HOSTBASED_SERVICE,
+                                 server);
+
+  return GSS_ERROR(major_status) ? -1 : 0;
+}
+
+static void
+log_gss_error(struct connectdata *conn, OM_uint32 error_status,
+              const char *prefix)
+{
+  OM_uint32 maj_stat, min_stat;
+  OM_uint32 msg_ctx = 0;
+  gss_buffer_desc status_string;
+  char buf[1024];
+  size_t len;
+
+  snprintf(buf, sizeof(buf), "%s", prefix);
+  len = strlen(buf);
+  do {
+    maj_stat = gss_display_status(&min_stat,
+                                  error_status,
+                                  GSS_C_MECH_CODE,
+                                  GSS_C_NO_OID,
+                                  &msg_ctx,
+                                  &status_string);
+      if(sizeof(buf) > len + status_string.length + 1) {
+        snprintf(buf + len, sizeof(buf) - len,
+                 ": %s", (char*) status_string.value);
+      len += status_string.length;
+    }
+    gss_release_buffer(&min_stat, &status_string);
+  } while(!GSS_ERROR(maj_stat) && msg_ctx != 0);
+
+  infof(conn->data, "%s\n", buf);
+}
+
+/* returning zero (0) means success, everything else is treated as "failure"
+   with no care exactly what the failure was */
+int Curl_input_negotiate(struct connectdata *conn, bool proxy,
+                         const char *header)
+{
+  struct SessionHandle *data = conn->data;
+  struct negotiatedata *neg_ctx = proxy?&data->state.proxyneg:
+    &data->state.negotiate;
+  OM_uint32 major_status, minor_status, minor_status2;
+  gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+  gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+  int ret;
+  size_t len;
+  size_t rawlen = 0;
+  bool gss;
+  const char* protocol;
+  CURLcode error;
+
+  while(*header && ISSPACE(*header))
+    header++;
+  if(checkprefix("GSS-Negotiate", header)) {
+    protocol = "GSS-Negotiate";
+    gss = TRUE;
+  }
+  else if(checkprefix("Negotiate", header)) {
+    protocol = "Negotiate";
+    gss = FALSE;
+  }
+  else
+    return -1;
+
+  if(neg_ctx->context) {
+    if(neg_ctx->gss != gss) {
+      return -1;
+    }
+  }
+  else {
+    neg_ctx->protocol = protocol;
+    neg_ctx->gss = gss;
+  }
+
+  if(neg_ctx->context && neg_ctx->status == GSS_S_COMPLETE) {
+    /* We finished successfully our part of authentication, but server
+     * rejected it (since we're again here). Exit with an error since we
+     * can't invent anything better */
+    Curl_cleanup_negotiate(data);
+    return -1;
+  }
+
+  if(neg_ctx->server_name == NULL &&
+      (ret = get_gss_name(conn, proxy, &neg_ctx->server_name)))
+    return ret;
+
+  header += strlen(neg_ctx->protocol);
+  while(*header && ISSPACE(*header))
+    header++;
+
+  len = strlen(header);
+  if(len > 0) {
+    error = Curl_base64_decode(header,
+                               (unsigned char **)&input_token.value, &rawlen);
+    if(error || rawlen == 0)
+      return -1;
+    input_token.length = rawlen;
+
+#ifdef HAVE_SPNEGO /* Handle SPNEGO */
+    if(checkprefix("Negotiate", header)) {
+      ASN1_OBJECT *   object            = NULL;
+      unsigned char * spnegoToken       = NULL;
+      size_t          spnegoTokenLength = 0;
+      unsigned char * mechToken         = NULL;
+      size_t          mechTokenLength   = 0;
+
+      if(input_token.value == NULL)
+        return CURLE_OUT_OF_MEMORY;
+
+      spnegoToken = malloc(input_token.length);
+      if(spnegoToken == NULL)
+        return CURLE_OUT_OF_MEMORY;
+
+      spnegoTokenLength = input_token.length;
+
+      object = OBJ_txt2obj ("1.2.840.113554.1.2.2", 1);
+      if(!parseSpnegoTargetToken(spnegoToken,
+                                 spnegoTokenLength,
+                                 NULL,
+                                 NULL,
+                                 &mechToken,
+                                 &mechTokenLength,
+                                 NULL,
+                                 NULL)) {
+        free(spnegoToken);
+        spnegoToken = NULL;
+        infof(data, "Parse SPNEGO Target Token failed\n");
+      }
+      else {
+        free(input_token.value);
+        input_token.value = malloc(mechTokenLength);
+        if(input_token.value == NULL)
+          return CURLE_OUT_OF_MEMORY;
+
+        memcpy(input_token.value, mechToken,mechTokenLength);
+        input_token.length = mechTokenLength;
+        free(mechToken);
+        mechToken = NULL;
+        infof(data, "Parse SPNEGO Target Token succeeded\n");
+      }
+    }
+#endif
+  }
+
+  major_status = Curl_gss_init_sec_context(data,
+                                           &minor_status,
+                                           &neg_ctx->context,
+                                           neg_ctx->server_name,
+                                           GSS_C_NO_CHANNEL_BINDINGS,
+                                           &input_token,
+                                           &output_token,
+                                           NULL);
+  if(input_token.length > 0)
+    gss_release_buffer(&minor_status2, &input_token);
+  neg_ctx->status = major_status;
+  if(GSS_ERROR(major_status)) {
+    /* Curl_cleanup_negotiate(data) ??? */
+    log_gss_error(conn, minor_status,
+                  "gss_init_sec_context() failed: ");
+    return -1;
+  }
+
+  if(output_token.length == 0) {
+    return -1;
+  }
+
+  neg_ctx->output_token = output_token;
+  /* conn->bits.close = FALSE; */
+
+  return 0;
+}
+
+
+CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy)
+{
+  struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg:
+    &conn->data->state.negotiate;
+  char *encoded = NULL;
+  size_t len = 0;
+  char *userp;
+  CURLcode error;
+
+#ifdef HAVE_SPNEGO /* Handle SPNEGO */
+  if(checkprefix("Negotiate", neg_ctx->protocol)) {
+    ASN1_OBJECT *   object            = NULL;
+    unsigned char * spnegoToken       = NULL;
+    size_t          spnegoTokenLength = 0;
+    unsigned char * responseToken       = NULL;
+    size_t          responseTokenLength = 0;
+
+    responseToken = malloc(neg_ctx->output_token.length);
+    if(responseToken == NULL)
+      return CURLE_OUT_OF_MEMORY;
+    memcpy(responseToken, neg_ctx->output_token.value,
+           neg_ctx->output_token.length);
+    responseTokenLength = neg_ctx->output_token.length;
+
+    object=OBJ_txt2obj ("1.2.840.113554.1.2.2", 1);
+    if(!makeSpnegoInitialToken (object,
+                                 responseToken,
+                                 responseTokenLength,
+                                 &spnegoToken,
+                                 &spnegoTokenLength)) {
+      free(responseToken);
+      responseToken = NULL;
+      infof(conn->data, "Make SPNEGO Initial Token failed\n");
+    }
+    else {
+      free(responseToken);
+      responseToken = NULL;
+      free(neg_ctx->output_token.value);
+      neg_ctx->output_token.value = malloc(spnegoTokenLength);
+      if(neg_ctx->output_token.value == NULL) {
+        free(spnegoToken);
+        spnegoToken = NULL;
+        return CURLE_OUT_OF_MEMORY;
+      }
+      memcpy(neg_ctx->output_token.value, spnegoToken,spnegoTokenLength);
+      neg_ctx->output_token.length = spnegoTokenLength;
+      free(spnegoToken);
+      spnegoToken = NULL;
+      infof(conn->data, "Make SPNEGO Initial Token succeeded\n");
+    }
+  }
+#endif
+  error = Curl_base64_encode(conn->data,
+                             neg_ctx->output_token.value,
+                             neg_ctx->output_token.length,
+                             &encoded, &len);
+  if(error) {
+    Curl_safefree(neg_ctx->output_token.value);
+    neg_ctx->output_token.value = NULL;
+    return error;
+  }
+
+  if(len == 0) {
+    Curl_safefree(neg_ctx->output_token.value);
+    neg_ctx->output_token.value = NULL;
+    return CURLE_REMOTE_ACCESS_DENIED;
+  }
+
+  userp = aprintf("%sAuthorization: %s %s\r\n", proxy ? "Proxy-" : "",
+                  neg_ctx->protocol, encoded);
+
+  if(proxy)
+    conn->allocptr.proxyuserpwd = userp;
+  else
+    conn->allocptr.userpwd = userp;
+  free(encoded);
+  Curl_cleanup_negotiate (conn->data);
+  return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK;
+}
+
+static void cleanup(struct negotiatedata *neg_ctx)
+{
+  OM_uint32 minor_status;
+  if(neg_ctx->context != GSS_C_NO_CONTEXT)
+    gss_delete_sec_context(&minor_status, &neg_ctx->context, GSS_C_NO_BUFFER);
+
+  if(neg_ctx->output_token.length != 0)
+    gss_release_buffer(&minor_status, &neg_ctx->output_token);
+
+  if(neg_ctx->server_name != GSS_C_NO_NAME)
+    gss_release_name(&minor_status, &neg_ctx->server_name);
+
+  memset(neg_ctx, 0, sizeof(*neg_ctx));
+}
+
+void Curl_cleanup_negotiate(struct SessionHandle *data)
+{
+  cleanup(&data->state.negotiate);
+  cleanup(&data->state.proxyneg);
+}
+
+
+#endif
+#endif
diff --git a/lib/curl_http_negotiate_sspi.c b/lib/curl_http_negotiate_sspi.c
new file mode 100644 (file)
index 0000000..d82b271
--- /dev/null
@@ -0,0 +1,308 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_WINDOWS_SSPI
+
+#ifndef CURL_DISABLE_HTTP
+
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_rawstr.h"
+#include "curl_warnless.h"
+#include "curl_base64.h"
+#include "curl_http_negotiate.h"
+#include "curl_memory.h"
+#include "curl_multibyte.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+static int
+get_gss_name(struct connectdata *conn, bool proxy,
+             struct negotiatedata *neg_ctx)
+{
+  const char* service;
+  size_t length;
+
+  if(proxy && !conn->proxy.name)
+    /* proxy auth requested but no given proxy name, error out! */
+    return -1;
+
+  /* GSSAPI implementation by Globus (known as GSI) requires the name to be
+     of form "<service>/<fqdn>" instead of <service>@<fqdn> (ie. slash instead
+     of at-sign). Also GSI servers are often identified as 'host' not 'khttp'.
+     Change following lines if you want to use GSI */
+
+  /* IIS uses the <service>@<fqdn> form but uses 'http' as the service name,
+     and SSPI then generates an NTLM token. When using <service>/<fqdn> a
+     Kerberos token is generated. */
+
+  if(neg_ctx->gss)
+    service = "KHTTP";
+  else
+    service = "HTTP";
+
+  length = strlen(service) + 1 + strlen(proxy ? conn->proxy.name :
+                                        conn->host.name) + 1;
+  if(length + 1 > sizeof(neg_ctx->server_name))
+    return EMSGSIZE;
+
+  snprintf(neg_ctx->server_name, sizeof(neg_ctx->server_name), "%s/%s",
+           service, proxy ? conn->proxy.name : conn->host.name);
+
+  return 0;
+}
+
+/* returning zero (0) means success, everything else is treated as "failure"
+   with no care exactly what the failure was */
+int Curl_input_negotiate(struct connectdata *conn, bool proxy,
+                         const char *header)
+{
+  struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg:
+    &conn->data->state.negotiate;
+  BYTE              *input_token = 0;
+  SecBufferDesc     out_buff_desc;
+  SecBuffer         out_sec_buff;
+  SecBufferDesc     in_buff_desc;
+  SecBuffer         in_sec_buff;
+  unsigned long     context_attributes;
+  TimeStamp         lifetime;
+  TCHAR             *sname;
+  int ret;
+  size_t len = 0, input_token_len = 0;
+  bool gss = FALSE;
+  const char* protocol;
+  CURLcode error;
+
+  while(*header && ISSPACE(*header))
+    header++;
+
+  if(checkprefix("GSS-Negotiate", header)) {
+    protocol = "GSS-Negotiate";
+    gss = TRUE;
+  }
+  else if(checkprefix("Negotiate", header)) {
+    protocol = "Negotiate";
+    gss = FALSE;
+  }
+  else
+    return -1;
+
+  if(neg_ctx->context) {
+    if(neg_ctx->gss != gss) {
+      return -1;
+    }
+  }
+  else {
+    neg_ctx->protocol = protocol;
+    neg_ctx->gss = gss;
+  }
+
+  if(neg_ctx->context && neg_ctx->status == SEC_E_OK) {
+    /* We finished successfully our part of authentication, but server
+     * rejected it (since we're again here). Exit with an error since we
+     * can't invent anything better */
+    Curl_cleanup_negotiate(conn->data);
+    return -1;
+  }
+
+  if(0 == strlen(neg_ctx->server_name)) {
+    ret = get_gss_name(conn, proxy, neg_ctx);
+    if(ret)
+      return ret;
+  }
+
+  if(!neg_ctx->output_token) {
+    PSecPkgInfo SecurityPackage;
+    ret = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT("Negotiate"),
+                                             &SecurityPackage);
+    if(ret != SEC_E_OK)
+      return -1;
+
+    /* Allocate input and output buffers according to the max token size
+       as indicated by the security package */
+    neg_ctx->max_token_length = SecurityPackage->cbMaxToken;
+    neg_ctx->output_token = malloc(neg_ctx->max_token_length);
+    s_pSecFn->FreeContextBuffer(SecurityPackage);
+  }
+
+  /* Obtain the input token, if any */
+  header += strlen(neg_ctx->protocol);
+  while(*header && ISSPACE(*header))
+    header++;
+
+  len = strlen(header);
+  if(!len) {
+    /* first call in a new negotation, we have to acquire credentials,
+       and allocate memory for the context */
+
+    neg_ctx->credentials = malloc(sizeof(CredHandle));
+    neg_ctx->context = malloc(sizeof(CtxtHandle));
+
+    if(!neg_ctx->credentials || !neg_ctx->context)
+      return -1;
+
+    neg_ctx->status =
+      s_pSecFn->AcquireCredentialsHandle(NULL,
+                                         (TCHAR *) TEXT("Negotiate"),
+                                         SECPKG_CRED_OUTBOUND, NULL, NULL,
+                                         NULL, NULL, neg_ctx->credentials,
+                                         &lifetime);
+    if(neg_ctx->status != SEC_E_OK)
+      return -1;
+  }
+  else {
+    input_token = malloc(neg_ctx->max_token_length);
+    if(!input_token)
+      return -1;
+
+    error = Curl_base64_decode(header,
+                               (unsigned char **)&input_token,
+                               &input_token_len);
+    if(error || input_token_len == 0)
+      return -1;
+  }
+
+  /* prepare the output buffers, and input buffers if present */
+  out_buff_desc.ulVersion = 0;
+  out_buff_desc.cBuffers  = 1;
+  out_buff_desc.pBuffers  = &out_sec_buff;
+
+  out_sec_buff.cbBuffer   = curlx_uztoul(neg_ctx->max_token_length);
+  out_sec_buff.BufferType = SECBUFFER_TOKEN;
+  out_sec_buff.pvBuffer   = neg_ctx->output_token;
+
+
+  if(input_token) {
+    in_buff_desc.ulVersion = 0;
+    in_buff_desc.cBuffers  = 1;
+    in_buff_desc.pBuffers  = &in_sec_buff;
+
+    in_sec_buff.cbBuffer   = curlx_uztoul(input_token_len);
+    in_sec_buff.BufferType = SECBUFFER_TOKEN;
+    in_sec_buff.pvBuffer   = input_token;
+  }
+
+  sname = Curl_convert_UTF8_to_tchar(neg_ctx->server_name);
+  if(!sname)
+    return CURLE_OUT_OF_MEMORY;
+
+  neg_ctx->status = s_pSecFn->InitializeSecurityContext(
+    neg_ctx->credentials,
+    input_token ? neg_ctx->context : 0,
+    sname,
+    ISC_REQ_CONFIDENTIALITY,
+    0,
+    SECURITY_NATIVE_DREP,
+    input_token ? &in_buff_desc : 0,
+    0,
+    neg_ctx->context,
+    &out_buff_desc,
+    &context_attributes,
+    &lifetime);
+
+  Curl_unicodefree(sname);
+
+  if(GSS_ERROR(neg_ctx->status))
+    return -1;
+
+  if(neg_ctx->status == SEC_I_COMPLETE_NEEDED ||
+     neg_ctx->status == SEC_I_COMPLETE_AND_CONTINUE) {
+    neg_ctx->status = s_pSecFn->CompleteAuthToken(neg_ctx->context,
+                                                  &out_buff_desc);
+    if(GSS_ERROR(neg_ctx->status))
+      return -1;
+  }
+
+  neg_ctx->output_token_length = out_sec_buff.cbBuffer;
+
+  return 0;
+}
+
+
+CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy)
+{
+  struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg:
+    &conn->data->state.negotiate;
+  char *encoded = NULL;
+  size_t len = 0;
+  char *userp;
+  CURLcode error;
+
+  error = Curl_base64_encode(conn->data,
+                             (const char*)neg_ctx->output_token,
+                             neg_ctx->output_token_length,
+                             &encoded, &len);
+  if(error)
+    return error;
+
+  if(len == 0)
+    return CURLE_REMOTE_ACCESS_DENIED;
+
+  userp = aprintf("%sAuthorization: %s %s\r\n", proxy ? "Proxy-" : "",
+                  neg_ctx->protocol, encoded);
+
+  if(proxy)
+    conn->allocptr.proxyuserpwd = userp;
+  else
+    conn->allocptr.userpwd = userp;
+  free(encoded);
+  Curl_cleanup_negotiate (conn->data);
+  return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK;
+}
+
+static void cleanup(struct negotiatedata *neg_ctx)
+{
+  if(neg_ctx->context) {
+    s_pSecFn->DeleteSecurityContext(neg_ctx->context);
+    free(neg_ctx->context);
+    neg_ctx->context = 0;
+  }
+
+  if(neg_ctx->credentials) {
+    s_pSecFn->FreeCredentialsHandle(neg_ctx->credentials);
+    free(neg_ctx->credentials);
+    neg_ctx->credentials = 0;
+  }
+
+  if(neg_ctx->output_token) {
+    free(neg_ctx->output_token);
+    neg_ctx->output_token = 0;
+  }
+
+  neg_ctx->max_token_length = 0;
+}
+
+void Curl_cleanup_negotiate(struct SessionHandle *data)
+{
+  cleanup(&data->state.negotiate);
+  cleanup(&data->state.proxyneg);
+}
+
+
+#endif
+#endif
diff --git a/lib/curl_http_proxy.c b/lib/curl_http_proxy.c
new file mode 100644 (file)
index 0000000..14ef9dc
--- /dev/null
@@ -0,0 +1,595 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
+
+#include "curl_urldata.h"
+#include <curl/curl.h>
+#include "curl_http_proxy.h"
+#include "curl_sendf.h"
+#include "curl_http.h"
+#include "curl_url.h"
+#include "curl_select.h"
+#include "curl_rawstr.h"
+#include "curl_progress.h"
+#include "curl_non_ascii.h"
+#include "curl_connect.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curlx.h"
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+CURLcode Curl_proxy_connect(struct connectdata *conn)
+{
+  if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
+#ifndef CURL_DISABLE_PROXY
+    /* for [protocol] tunneled through HTTP proxy */
+    struct HTTP http_proxy;
+    void *prot_save;
+    CURLcode result;
+
+    /* BLOCKING */
+    /* We want "seamless" operations through HTTP proxy tunnel */
+
+    /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the
+     * member conn->proto.http; we want [protocol] through HTTP and we have
+     * to change the member temporarily for connecting to the HTTP
+     * proxy. After Curl_proxyCONNECT we have to set back the member to the
+     * original pointer
+     *
+     * This function might be called several times in the multi interface case
+     * if the proxy's CONNTECT response is not instant.
+     */
+    prot_save = conn->data->state.proto.generic;
+    memset(&http_proxy, 0, sizeof(http_proxy));
+    conn->data->state.proto.http = &http_proxy;
+    conn->bits.close = FALSE;
+    result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
+                               conn->host.name, conn->remote_port);
+    conn->data->state.proto.generic = prot_save;
+    if(CURLE_OK != result)
+      return result;
+#else
+    return CURLE_NOT_BUILT_IN;
+#endif
+  }
+  /* no HTTP tunnel proxy, just return */
+  return CURLE_OK;
+}
+
+/*
+ * Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This
+ * function will issue the necessary commands to get a seamless tunnel through
+ * this proxy. After that, the socket can be used just as a normal socket.
+ *
+ * This badly needs to be rewritten. CONNECT should be sent and dealt with
+ * like any ordinary HTTP request, and not specially crafted like this. This
+ * function only remains here like this for now since the rewrite is a bit too
+ * much work to do at the moment.
+ *
+ * This function is BLOCKING which is nasty for all multi interface using apps.
+ */
+
+CURLcode Curl_proxyCONNECT(struct connectdata *conn,
+                           int sockindex,
+                           const char *hostname,
+                           unsigned short remote_port)
+{
+  int subversion=0;
+  struct SessionHandle *data=conn->data;
+  struct SingleRequest *k = &data->req;
+  CURLcode result;
+  long timeout =
+    data->set.timeout?data->set.timeout:PROXY_TIMEOUT; /* in milliseconds */
+  curl_socket_t tunnelsocket = conn->sock[sockindex];
+  curl_off_t cl=0;
+  bool closeConnection = FALSE;
+  bool chunked_encoding = FALSE;
+  long check;
+
+#define SELECT_OK      0
+#define SELECT_ERROR   1
+#define SELECT_TIMEOUT 2
+  int error = SELECT_OK;
+
+  if(conn->tunnel_state[sockindex] == TUNNEL_COMPLETE)
+    return CURLE_OK; /* CONNECT is already completed */
+
+  conn->bits.proxy_connect_closed = FALSE;
+
+  do {
+    if(TUNNEL_INIT == conn->tunnel_state[sockindex]) {
+      /* BEGIN CONNECT PHASE */
+      char *host_port;
+      Curl_send_buffer *req_buffer;
+
+      infof(data, "Establish HTTP proxy tunnel to %s:%hu\n",
+            hostname, remote_port);
+
+      if(data->req.newurl) {
+        /* This only happens if we've looped here due to authentication
+           reasons, and we don't really use the newly cloned URL here
+           then. Just free() it. */
+        free(data->req.newurl);
+        data->req.newurl = NULL;
+      }
+
+      /* initialize a dynamic send-buffer */
+      req_buffer = Curl_add_buffer_init();
+
+      if(!req_buffer)
+        return CURLE_OUT_OF_MEMORY;
+
+      host_port = aprintf("%s:%hu", hostname, remote_port);
+      if(!host_port) {
+        free(req_buffer);
+        return CURLE_OUT_OF_MEMORY;
+      }
+
+      /* Setup the proxy-authorization header, if any */
+      result = Curl_http_output_auth(conn, "CONNECT", host_port, TRUE);
+
+      free(host_port);
+
+      if(CURLE_OK == result) {
+        char *host=(char *)"";
+        const char *proxyconn="";
+        const char *useragent="";
+        const char *http = (conn->proxytype == CURLPROXY_HTTP_1_0) ?
+          "1.0" : "1.1";
+        char *hostheader= /* host:port with IPv6 support */
+          aprintf("%s%s%s:%hu", conn->bits.ipv6_ip?"[":"",
+                  hostname, conn->bits.ipv6_ip?"]":"",
+                  remote_port);
+        if(!hostheader) {
+          free(req_buffer);
+          return CURLE_OUT_OF_MEMORY;
+        }
+
+        if(!Curl_checkheaders(data, "Host:")) {
+          host = aprintf("Host: %s\r\n", hostheader);
+          if(!host) {
+            free(hostheader);
+            free(req_buffer);
+            return CURLE_OUT_OF_MEMORY;
+          }
+        }
+        if(!Curl_checkheaders(data, "Proxy-Connection:"))
+          proxyconn = "Proxy-Connection: Keep-Alive\r\n";
+
+        if(!Curl_checkheaders(data, "User-Agent:") &&
+           data->set.str[STRING_USERAGENT])
+          useragent = conn->allocptr.uagent;
+
+        result =
+          Curl_add_bufferf(req_buffer,
+                           "CONNECT %s HTTP/%s\r\n"
+                           "%s"  /* Host: */
+                           "%s"  /* Proxy-Authorization */
+                           "%s"  /* User-Agent */
+                           "%s", /* Proxy-Connection */
+                           hostheader,
+                           http,
+                           host,
+                           conn->allocptr.proxyuserpwd?
+                           conn->allocptr.proxyuserpwd:"",
+                           useragent,
+                           proxyconn);
+
+        if(host && *host)
+          free(host);
+        free(hostheader);
+
+        if(CURLE_OK == result)
+          result = Curl_add_custom_headers(conn, req_buffer);
+
+        if(CURLE_OK == result)
+          /* CRLF terminate the request */
+          result = Curl_add_bufferf(req_buffer, "\r\n");
+
+        if(CURLE_OK == result) {
+          /* Send the connect request to the proxy */
+          /* BLOCKING */
+          result =
+            Curl_add_buffer_send(req_buffer, conn,
+                                 &data->info.request_size, 0, sockindex);
+        }
+        req_buffer = NULL;
+        if(result)
+          failf(data, "Failed sending CONNECT to proxy");
+      }
+
+      Curl_safefree(req_buffer);
+      if(result)
+        return result;
+
+      conn->tunnel_state[sockindex] = TUNNEL_CONNECT;
+    } /* END CONNECT PHASE */
+
+    /* now we've issued the CONNECT and we're waiting to hear back -
+       we try not to block here in multi-mode because that might be a LONG
+       wait if the proxy cannot connect-through to the remote host. */
+
+    /* if timeout is requested, find out how much remaining time we have */
+    check = timeout - /* timeout time */
+      Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
+    if(check <= 0) {
+      failf(data, "Proxy CONNECT aborted due to timeout");
+      return CURLE_RECV_ERROR;
+    }
+
+    /* if we're in multi-mode and we would block, return instead for a retry */
+    if(Curl_if_multi == data->state.used_interface) {
+      if(0 == Curl_socket_ready(tunnelsocket, CURL_SOCKET_BAD, 0))
+        /* return so we'll be called again polling-style */
+        return CURLE_OK;
+      else {
+        DEBUGF(infof(data,
+                     "Multi mode finished polling for response from "
+                     "proxy CONNECT\n"));
+      }
+    }
+    else {
+      DEBUGF(infof(data, "Easy mode waiting response from proxy CONNECT\n"));
+    }
+
+    /* at this point, either:
+       1) we're in easy-mode and so it's okay to block waiting for a CONNECT
+       response
+       2) we're in multi-mode and we didn't block - it's either an error or we
+       now have some data waiting.
+       In any case, the tunnel_connecting phase is over. */
+
+    { /* BEGIN NEGOTIATION PHASE */
+      size_t nread;   /* total size read */
+      int perline; /* count bytes per line */
+      int keepon=TRUE;
+      ssize_t gotbytes;
+      char *ptr;
+      char *line_start;
+
+      ptr=data->state.buffer;
+      line_start = ptr;
+
+      nread=0;
+      perline=0;
+      keepon=TRUE;
+
+      while((nread<BUFSIZE) && (keepon && !error)) {
+
+        /* if timeout is requested, find out how much remaining time we have */
+        check = timeout - /* timeout time */
+          Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
+        if(check <= 0) {
+          failf(data, "Proxy CONNECT aborted due to timeout");
+          error = SELECT_TIMEOUT; /* already too little time */
+          break;
+        }
+
+        /* loop every second at least, less if the timeout is near */
+        switch (Curl_socket_ready(tunnelsocket, CURL_SOCKET_BAD,
+                                  check<1000L?check:1000)) {
+        case -1: /* select() error, stop reading */
+          error = SELECT_ERROR;
+          failf(data, "Proxy CONNECT aborted due to select/poll error");
+          break;
+        case 0: /* timeout */
+          break;
+        default:
+          DEBUGASSERT(ptr+BUFSIZE-nread <= data->state.buffer+BUFSIZE+1);
+          result = Curl_read(conn, tunnelsocket, ptr, BUFSIZE-nread,
+                             &gotbytes);
+          if(result==CURLE_AGAIN)
+            continue; /* go loop yourself */
+          else if(result)
+            keepon = FALSE;
+          else if(gotbytes <= 0) {
+            keepon = FALSE;
+            if(data->set.proxyauth && data->state.authproxy.avail) {
+              /* proxy auth was requested and there was proxy auth available,
+                 then deem this as "mere" proxy disconnect */
+              conn->bits.proxy_connect_closed = TRUE;
+            }
+            else {
+              error = SELECT_ERROR;
+              failf(data, "Proxy CONNECT aborted");
+            }
+          }
+          else {
+            /*
+             * We got a whole chunk of data, which can be anything from one
+             * byte to a set of lines and possibly just a piece of the last
+             * line.
+             */
+            int i;
+
+            nread += gotbytes;
+
+            if(keepon > TRUE) {
+              /* This means we are currently ignoring a response-body */
+
+              nread = 0; /* make next read start over in the read buffer */
+              ptr=data->state.buffer;
+              if(cl) {
+                /* A Content-Length based body: simply count down the counter
+                   and make sure to break out of the loop when we're done! */
+                cl -= gotbytes;
+                if(cl<=0) {
+                  keepon = FALSE;
+                  break;
+                }
+              }
+              else {
+                /* chunked-encoded body, so we need to do the chunked dance
+                   properly to know when the end of the body is reached */
+                CHUNKcode r;
+                ssize_t tookcareof=0;
+
+                /* now parse the chunked piece of data so that we can
+                   properly tell when the stream ends */
+                r = Curl_httpchunk_read(conn, ptr, gotbytes, &tookcareof);
+                if(r == CHUNKE_STOP) {
+                  /* we're done reading chunks! */
+                  infof(data, "chunk reading DONE\n");
+                  keepon = FALSE;
+                  /* we did the full CONNECT treatment, go COMPLETE */
+                  conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
+                }
+                else
+                  infof(data, "Read %zd bytes of chunk, continue\n",
+                        tookcareof);
+              }
+            }
+            else
+              for(i = 0; i < gotbytes; ptr++, i++) {
+                perline++; /* amount of bytes in this line so far */
+                if(*ptr == 0x0a) {
+                  char letter;
+                  int writetype;
+
+                  /* convert from the network encoding */
+                  result = Curl_convert_from_network(data, line_start,
+                                                     perline);
+                  /* Curl_convert_from_network calls failf if unsuccessful */
+                  if(result)
+                    return result;
+
+                  /* output debug if that is requested */
+                  if(data->set.verbose)
+                    Curl_debug(data, CURLINFO_HEADER_IN,
+                               line_start, (size_t)perline, conn);
+
+                  /* send the header to the callback */
+                  writetype = CLIENTWRITE_HEADER;
+                  if(data->set.include_header)
+                    writetype |= CLIENTWRITE_BODY;
+
+                  result = Curl_client_write(conn, writetype, line_start,
+                                             perline);
+                  if(result)
+                    return result;
+
+                  /* Newlines are CRLF, so the CR is ignored as the line isn't
+                     really terminated until the LF comes. Treat a following CR
+                     as end-of-headers as well.*/
+
+                  if(('\r' == line_start[0]) ||
+                     ('\n' == line_start[0])) {
+                    /* end of response-headers from the proxy */
+                    nread = 0; /* make next read start over in the read
+                                  buffer */
+                    ptr=data->state.buffer;
+                    if((407 == k->httpcode) && !data->state.authproblem) {
+                      /* If we get a 407 response code with content length
+                         when we have no auth problem, we must ignore the
+                         whole response-body */
+                      keepon = 2;
+
+                      if(cl) {
+
+                        infof(data, "Ignore %" FORMAT_OFF_T
+                              " bytes of response-body\n", cl);
+                        /* remove the remaining chunk of what we already
+                           read */
+                        cl -= (gotbytes - i);
+
+                        if(cl<=0)
+                          /* if the whole thing was already read, we are done!
+                           */
+                          keepon=FALSE;
+                      }
+                      else if(chunked_encoding) {
+                        CHUNKcode r;
+                        /* We set ignorebody true here since the chunked
+                           decoder function will acknowledge that. Pay
+                           attention so that this is cleared again when this
+                           function returns! */
+                        k->ignorebody = TRUE;
+                        infof(data, "%zd bytes of chunk left\n", gotbytes-i);
+
+                        if(line_start[1] == '\n') {
+                          /* this can only be a LF if the letter at index 0
+                             was a CR */
+                          line_start++;
+                          i++;
+                        }
+
+                        /* now parse the chunked piece of data so that we can
+                           properly tell when the stream ends */
+                        r = Curl_httpchunk_read(conn, line_start+1,
+                                                  gotbytes -i, &gotbytes);
+                        if(r == CHUNKE_STOP) {
+                          /* we're done reading chunks! */
+                          infof(data, "chunk reading DONE\n");
+                          keepon = FALSE;
+                          /* we did the full CONNECT treatment, go to
+                             COMPLETE */
+                          conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
+                        }
+                        else
+                          infof(data, "Read %zd bytes of chunk, continue\n",
+                                gotbytes);
+                      }
+                      else {
+                        /* without content-length or chunked encoding, we
+                           can't keep the connection alive since the close is
+                           the end signal so we bail out at once instead */
+                        keepon=FALSE;
+                      }
+                    }
+                    else {
+                      keepon = FALSE;
+                      if(200 == data->info.httpproxycode) {
+                        if(gotbytes - (i+1))
+                          failf(data, "Proxy CONNECT followed by %zd bytes "
+                                "of opaque data. Data ignored (known bug #39)",
+                                gotbytes - (i+1));
+                      }
+                    }
+                    /* we did the full CONNECT treatment, go to COMPLETE */
+                    conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
+                    break; /* breaks out of for-loop, not switch() */
+                  }
+
+                  /* keep a backup of the position we are about to blank */
+                  letter = line_start[perline];
+                  line_start[perline]=0; /* zero terminate the buffer */
+                  if((checkprefix("WWW-Authenticate:", line_start) &&
+                      (401 == k->httpcode)) ||
+                     (checkprefix("Proxy-authenticate:", line_start) &&
+                      (407 == k->httpcode))) {
+                    result = Curl_http_input_auth(conn, k->httpcode,
+                                                  line_start);
+                    if(result)
+                      return result;
+                  }
+                  else if(checkprefix("Content-Length:", line_start)) {
+                    cl = curlx_strtoofft(line_start +
+                                         strlen("Content-Length:"), NULL, 10);
+                  }
+                  else if(Curl_compareheader(line_start,
+                                             "Connection:", "close"))
+                    closeConnection = TRUE;
+                  else if(Curl_compareheader(line_start,
+                                             "Transfer-Encoding:",
+                                             "chunked")) {
+                    infof(data, "CONNECT responded chunked\n");
+                    chunked_encoding = TRUE;
+                    /* init our chunky engine */
+                    Curl_httpchunk_init(conn);
+                  }
+                  else if(Curl_compareheader(line_start,
+                                             "Proxy-Connection:", "close"))
+                    closeConnection = TRUE;
+                  else if(2 == sscanf(line_start, "HTTP/1.%d %d",
+                                      &subversion,
+                                      &k->httpcode)) {
+                    /* store the HTTP code from the proxy */
+                    data->info.httpproxycode = k->httpcode;
+                  }
+                  /* put back the letter we blanked out before */
+                  line_start[perline]= letter;
+
+                  perline=0; /* line starts over here */
+                  line_start = ptr+1; /* this skips the zero byte we wrote */
+                }
+              }
+          }
+          break;
+        } /* switch */
+        if(Curl_pgrsUpdate(conn))
+          return CURLE_ABORTED_BY_CALLBACK;
+      } /* while there's buffer left and loop is requested */
+
+      if(error)
+        return CURLE_RECV_ERROR;
+
+      if(data->info.httpproxycode != 200) {
+        /* Deal with the possibly already received authenticate
+           headers. 'newurl' is set to a new URL if we must loop. */
+        result = Curl_http_auth_act(conn);
+        if(result)
+          return result;
+
+        if(conn->bits.close)
+          /* the connection has been marked for closure, most likely in the
+             Curl_http_auth_act() function and thus we can kill it at once
+             below
+          */
+          closeConnection = TRUE;
+      }
+
+      if(closeConnection && data->req.newurl) {
+        /* Connection closed by server. Don't use it anymore */
+        Curl_closesocket(conn, conn->sock[sockindex]);
+        conn->sock[sockindex] = CURL_SOCKET_BAD;
+        break;
+      }
+    } /* END NEGOTIATION PHASE */
+
+    /* If we are supposed to continue and request a new URL, which basically
+     * means the HTTP authentication is still going on so if the tunnel
+     * is complete we start over in INIT state */
+    if(data->req.newurl &&
+       (TUNNEL_COMPLETE == conn->tunnel_state[sockindex])) {
+      conn->tunnel_state[sockindex] = TUNNEL_INIT;
+      infof(data, "TUNNEL_STATE switched to: %d\n",
+            conn->tunnel_state[sockindex]);
+    }
+
+  } while(data->req.newurl);
+
+  if(200 != data->req.httpcode) {
+    failf(data, "Received HTTP code %d from proxy after CONNECT",
+          data->req.httpcode);
+
+    if(closeConnection && data->req.newurl)
+      conn->bits.proxy_connect_closed = TRUE;
+
+    /* to back to init state */
+    conn->tunnel_state[sockindex] = TUNNEL_INIT;
+
+    return CURLE_RECV_ERROR;
+  }
+
+  conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
+
+  /* If a proxy-authorization header was used for the proxy, then we should
+     make sure that it isn't accidentally used for the document request
+     after we've connected. So let's free and clear it here. */
+  Curl_safefree(conn->allocptr.proxyuserpwd);
+  conn->allocptr.proxyuserpwd = NULL;
+
+  data->state.authproxy.done = TRUE;
+
+  infof (data, "Proxy replied OK to CONNECT request\n");
+  data->req.ignorebody = FALSE; /* put it (back) to non-ignore state */
+  return CURLE_OK;
+}
+#endif /* CURL_DISABLE_PROXY */
diff --git a/lib/curl_idn_win32.c b/lib/curl_idn_win32.c
new file mode 100644 (file)
index 0000000..eca2254
--- /dev/null
@@ -0,0 +1,85 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+ /*
+  * IDN conversions using Windows kernel32 and normaliz libraries.
+  */
+
+#include "curl_setup.h"
+
+#ifdef USE_WIN32_IDN
+
+#include "curl_multibyte.h"
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+#ifdef WANT_IDN_PROTOTYPES
+WINBASEAPI int WINAPI IdnToAscii(DWORD, const WCHAR *, int, WCHAR *, int);
+WINBASEAPI int WINAPI IdnToUnicode(DWORD, const WCHAR *, int, WCHAR *, int);
+#endif
+
+#define IDN_MAX_LENGTH 255
+
+int curl_win32_idn_to_ascii(const char *in, char **out);
+int curl_win32_ascii_to_idn(const char *in, size_t in_len, char **out_utf8);
+
+int curl_win32_idn_to_ascii(const char *in, char **out)
+{
+  wchar_t *in_w = Curl_convert_UTF8_to_wchar(in);
+  if(in_w) {
+    wchar_t punycode[IDN_MAX_LENGTH];
+    if(IdnToAscii(0, in_w, -1, punycode, IDN_MAX_LENGTH) == 0) {
+      wprintf(L"ERROR %d converting to Punycode\n", GetLastError());
+      free(in_w);
+      return 0;
+    }
+    free(in_w);
+
+    *out = Curl_convert_wchar_to_UTF8(punycode);
+    if(!*out)
+      return 0;
+  }
+  return 1;
+}
+
+int curl_win32_ascii_to_idn(const char *in, size_t in_len, char **out_utf8)
+{
+  (void)in_len; /* unused */
+  if(in) {
+    WCHAR unicode[IDN_MAX_LENGTH];
+
+    if(IdnToUnicode(0, (wchar_t *)in, -1, unicode, IDN_MAX_LENGTH) == 0) {
+      wprintf(L"ERROR %d converting to Punycode\n", GetLastError());
+      return 0;
+    }
+    else {
+      *out_utf8 = Curl_convert_wchar_to_UTF8(unicode);
+      if(!*out_utf8)
+        return 0;
+    }
+  }
+  return 1;
+}
+
+#endif /* USE_WIN32_IDN */
diff --git a/lib/curl_if2ip.c b/lib/curl_if2ip.c
new file mode 100644 (file)
index 0000000..db9c784
--- /dev/null
@@ -0,0 +1,189 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_NETINET_IN_H
+#  include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#  include <arpa/inet.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#  include <net/if.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#  include <sys/ioctl.h>
+#endif
+#ifdef HAVE_NETDB_H
+#  include <netdb.h>
+#endif
+#ifdef HAVE_SYS_SOCKIO_H
+#  include <sys/sockio.h>
+#endif
+#ifdef HAVE_IFADDRS_H
+#  include <ifaddrs.h>
+#endif
+#ifdef HAVE_STROPTS_H
+#  include <stropts.h>
+#endif
+#ifdef __VMS
+#  include <inet.h>
+#endif
+
+#include "curl_inet_ntop.h"
+#include "curl_strequal.h"
+#include "curl_if2ip.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/* ------------------------------------------------------------------ */
+
+#if defined(HAVE_GETIFADDRS)
+
+bool Curl_if_is_interface_name(const char *interf)
+{
+  bool result = FALSE;
+
+  struct ifaddrs *iface, *head;
+
+  if(getifaddrs(&head) >= 0) {
+    for(iface=head; iface != NULL; iface=iface->ifa_next) {
+      if(curl_strequal(iface->ifa_name, interf)) {
+        result = TRUE;
+        break;
+      }
+    }
+    freeifaddrs(head);
+  }
+  return result;
+}
+
+char *Curl_if2ip(int af, const char *interf, char *buf, int buf_size)
+{
+  struct ifaddrs *iface, *head;
+  char *ip = NULL;
+
+  if(getifaddrs(&head) >= 0) {
+    for(iface=head; iface != NULL; iface=iface->ifa_next) {
+      if((iface->ifa_addr != NULL) &&
+         (iface->ifa_addr->sa_family == af) &&
+         curl_strequal(iface->ifa_name, interf)) {
+        void *addr;
+        char scope[12]="";
+#ifdef ENABLE_IPV6
+        if(af == AF_INET6) {
+          unsigned int scopeid = 0;
+          addr = &((struct sockaddr_in6 *)iface->ifa_addr)->sin6_addr;
+#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
+          /* Include the scope of this interface as part of the address */
+          scopeid = ((struct sockaddr_in6 *)iface->ifa_addr)->sin6_scope_id;
+#endif
+          if(scopeid)
+            snprintf(scope, sizeof(scope), "%%%u", scopeid);
+        }
+        else
+#endif
+          addr = &((struct sockaddr_in *)iface->ifa_addr)->sin_addr;
+        ip = (char *) Curl_inet_ntop(af, addr, buf, buf_size);
+        strlcat(buf, scope, buf_size);
+        break;
+      }
+    }
+    freeifaddrs(head);
+  }
+  return ip;
+}
+
+#elif defined(HAVE_IOCTL_SIOCGIFADDR)
+
+bool Curl_if_is_interface_name(const char *interf)
+{
+  /* This is here just to support the old interfaces */
+  char buf[256];
+
+  char *ip = Curl_if2ip(AF_INET, interf, buf, sizeof(buf));
+
+  return (ip != NULL) ? TRUE : FALSE;
+}
+
+char *Curl_if2ip(int af, const char *interf, char *buf, int buf_size)
+{
+  struct ifreq req;
+  struct in_addr in;
+  struct sockaddr_in *s;
+  curl_socket_t dummy;
+  size_t len;
+  char *ip;
+
+  if(!interf || (af != AF_INET))
+    return NULL;
+
+  len = strlen(interf);
+  if(len >= sizeof(req.ifr_name))
+    return NULL;
+
+  dummy = socket(AF_INET, SOCK_STREAM, 0);
+  if(CURL_SOCKET_BAD == dummy)
+    return NULL;
+
+  memset(&req, 0, sizeof(req));
+  memcpy(req.ifr_name, interf, len+1);
+  req.ifr_addr.sa_family = AF_INET;
+
+  if(ioctl(dummy, SIOCGIFADDR, &req) < 0) {
+    sclose(dummy);
+    return NULL;
+  }
+
+  s = (struct sockaddr_in *)&req.ifr_addr;
+  memcpy(&in, &s->sin_addr, sizeof(in));
+  ip = (char *) Curl_inet_ntop(s->sin_family, &in, buf, buf_size);
+
+  sclose(dummy);
+  return ip;
+}
+
+#else
+
+bool Curl_if_is_interface_name(const char *interf)
+{
+  (void) interf;
+
+  return FALSE;
+}
+
+char *Curl_if2ip(int af, const char *interf, char *buf, int buf_size)
+{
+    (void) af;
+    (void) interf;
+    (void) buf;
+    (void) buf_size;
+    return NULL;
+}
+
+#endif
diff --git a/lib/curl_imap.c b/lib/curl_imap.c
new file mode 100644 (file)
index 0000000..8175daa
--- /dev/null
@@ -0,0 +1,1139 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * RFC3501 IMAPv4 protocol
+ * RFC5092 IMAP URL Scheme
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_IMAP
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_UTSNAME_H
+#include <sys/utsname.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
+#undef in_addr_t
+#define in_addr_t unsigned long
+#endif
+
+#include <curl/curl.h>
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_if2ip.h"
+#include "curl_hostip.h"
+#include "curl_progress.h"
+#include "curl_transfer.h"
+#include "curl_escape.h"
+#include "curl_http.h" /* for HTTP proxy tunnel stuff */
+#include "curl_socks.h"
+#include "curl_imap.h"
+
+#include "curl_strtoofft.h"
+#include "curl_strequal.h"
+#include "curl_sslgen.h"
+#include "curl_connect.h"
+#include "curl_strerror.h"
+#include "curl_select.h"
+#include "curl_multiif.h"
+#include "curl_url.h"
+#include "curl_rawstr.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/* Local API functions */
+static CURLcode imap_parse_url_path(struct connectdata *conn);
+static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done);
+static CURLcode imap_do(struct connectdata *conn, bool *done);
+static CURLcode imap_done(struct connectdata *conn, CURLcode status,
+                          bool premature);
+static CURLcode imap_connect(struct connectdata *conn, bool *done);
+static CURLcode imap_disconnect(struct connectdata *conn, bool dead);
+static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done);
+static int imap_getsock(struct connectdata *conn,
+                        curl_socket_t *socks,
+                        int numsocks);
+static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done);
+static CURLcode imap_setup_connection(struct connectdata *conn);
+static CURLcode imap_state_upgrade_tls(struct connectdata *conn);
+
+/*
+ * IMAP protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_imap = {
+  "IMAP",                           /* scheme */
+  imap_setup_connection,            /* setup_connection */
+  imap_do,                          /* do_it */
+  imap_done,                        /* done */
+  ZERO_NULL,                        /* do_more */
+  imap_connect,                     /* connect_it */
+  imap_multi_statemach,             /* connecting */
+  imap_doing,                       /* doing */
+  imap_getsock,                     /* proto_getsock */
+  imap_getsock,                     /* doing_getsock */
+  ZERO_NULL,                        /* domore_getsock */
+  ZERO_NULL,                        /* perform_getsock */
+  imap_disconnect,                  /* disconnect */
+  ZERO_NULL,                        /* readwrite */
+  PORT_IMAP,                        /* defport */
+  CURLPROTO_IMAP,                   /* protocol */
+  PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD
+  | PROTOPT_NOURLQUERY              /* flags */
+};
+
+
+#ifdef USE_SSL
+/*
+ * IMAPS protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_imaps = {
+  "IMAPS",                          /* scheme */
+  imap_setup_connection,            /* setup_connection */
+  imap_do,                          /* do_it */
+  imap_done,                        /* done */
+  ZERO_NULL,                        /* do_more */
+  imap_connect,                     /* connect_it */
+  imap_multi_statemach,             /* connecting */
+  imap_doing,                       /* doing */
+  imap_getsock,                     /* proto_getsock */
+  imap_getsock,                     /* doing_getsock */
+  ZERO_NULL,                        /* domore_getsock */
+  ZERO_NULL,                        /* perform_getsock */
+  imap_disconnect,                  /* disconnect */
+  ZERO_NULL,                        /* readwrite */
+  PORT_IMAPS,                       /* defport */
+  CURLPROTO_IMAP | CURLPROTO_IMAPS, /* protocol */
+  PROTOPT_CLOSEACTION | PROTOPT_SSL | PROTOPT_NEEDSPWD
+  | PROTOPT_NOURLQUERY              /* flags */
+};
+#endif
+
+#ifndef CURL_DISABLE_HTTP
+/*
+ * HTTP-proxyed IMAP protocol handler.
+ */
+
+static const struct Curl_handler Curl_handler_imap_proxy = {
+  "IMAP",                               /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  Curl_http,                            /* do_it */
+  Curl_http_done,                       /* done */
+  ZERO_NULL,                            /* do_more */
+  ZERO_NULL,                            /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  PORT_IMAP,                            /* defport */
+  CURLPROTO_HTTP,                       /* protocol */
+  PROTOPT_NONE                          /* flags */
+};
+
+
+#ifdef USE_SSL
+/*
+ * HTTP-proxyed IMAPS protocol handler.
+ */
+
+static const struct Curl_handler Curl_handler_imaps_proxy = {
+  "IMAPS",                              /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  Curl_http,                            /* do_it */
+  Curl_http_done,                       /* done */
+  ZERO_NULL,                            /* do_more */
+  ZERO_NULL,                            /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  PORT_IMAPS,                           /* defport */
+  CURLPROTO_HTTP,                       /* protocol */
+  PROTOPT_NONE                          /* flags */
+};
+#endif
+#endif
+
+/***********************************************************************
+ *
+ * imap_sendf()
+ *
+ * Sends the formated string as an IMAP command to the server.
+ *
+ * Designed to never block.
+ */
+static CURLcode imap_sendf(struct connectdata *conn,
+                           const char *idstr, /* command id to wait for */
+                           const char *fmt, ...)
+{
+  CURLcode res;
+  struct imap_conn *imapc = &conn->proto.imapc;
+  va_list ap;
+  va_start(ap, fmt);
+
+  imapc->idstr = idstr;
+
+  res = Curl_pp_vsendf(&imapc->pp, fmt, ap);
+
+  va_end(ap);
+
+  return res;
+}
+
+static const char *getcmdid(struct connectdata *conn)
+{
+  static const char * const ids[]= {
+    "A",
+    "B",
+    "C",
+    "D"
+  };
+
+  struct imap_conn *imapc = &conn->proto.imapc;
+
+  /* Get the next id, but wrap at end of table */
+  imapc->cmdid = (int)((imapc->cmdid + 1) % (sizeof(ids) / sizeof(ids[0])));
+
+  return ids[imapc->cmdid];
+}
+
+/***********************************************************************
+ *
+ * imap_atom()
+ *
+ * Checks the input string for characters that need escaping and returns an
+ * atom ready for sending to the server.
+ *
+ * The returned string needs to be freed.
+ *
+ */
+static char* imap_atom(const char* str)
+{
+  const char *p1;
+  char *p2;
+  size_t backsp_count = 0;
+  size_t quote_count = 0;
+  bool space_exists = FALSE;
+  size_t newlen = 0;
+  char *newstr = NULL;
+
+  if(!str)
+    return NULL;
+
+  /* Count any unescapped characters */
+  p1 = str;
+  while(*p1) {
+    if(*p1 == '\\')
+      backsp_count++;
+    else if(*p1 == '"')
+      quote_count++;
+    else if(*p1 == ' ')
+      space_exists = TRUE;
+
+    p1++;
+  }
+
+  /* Does the input contain any unescapped characters? */
+  if(!backsp_count && !quote_count && !space_exists)
+    return strdup(str);
+
+  /* Calculate the new string length */
+  newlen = strlen(str) + backsp_count + quote_count + (space_exists ? 2 : 0);
+
+  /* Allocate the new string */
+  newstr = (char *) malloc((newlen + 1) * sizeof(char));
+  if(!newstr)
+    return NULL;
+
+  /* Surround the string in quotes if necessary */
+  p2 = newstr;
+  if(space_exists) {
+    newstr[0] = '"';
+    newstr[newlen - 1] = '"';
+    p2++;
+  }
+
+  /* Copy the string, escaping backslash and quote characters along the way */
+  p1 = str;
+  while(*p1) {
+    if(*p1 == '\\' || *p1 == '"') {
+      *p2 = '\\';
+      p2++;
+    }
+
+   *p2 = *p1;
+
+    p1++;
+    p2++;
+  }
+
+  /* Terminate the string */
+  newstr[newlen] = '\0';
+
+  return newstr;
+}
+
+/* Function that checks for an ending imap status code at the start of the
+   given string. */
+static int imap_endofresp(struct pingpong *pp, int *resp)
+{
+  char *line = pp->linestart_resp;
+  size_t len = pp->nread_resp;
+  struct imap_conn *imapc = &pp->conn->proto.imapc;
+  const char *id = imapc->idstr;
+  size_t id_len = strlen(id);
+
+  /* Do we have a generic command response? */
+  if(len >= id_len + 3) {
+    if(!memcmp(id, line, id_len) && line[id_len] == ' ') {
+      *resp = line[id_len + 1]; /* O, N or B */
+      return TRUE;
+    }
+  }
+
+  /* Are we processing FETCH command responses? */
+  if(imapc->state == IMAP_FETCH) {
+    /* Do we have a valid response? */
+    if(len >= 2 && !memcmp("* ", line, 2)) {
+      *resp = '*';
+      return TRUE;
+    }
+  }
+
+  return FALSE; /* nothing for us */
+}
+
+/* This is the ONLY way to change IMAP state! */
+static void state(struct connectdata *conn,
+                  imapstate newstate)
+{
+  struct imap_conn *imapc = &conn->proto.imapc;
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+  /* for debug purposes */
+  static const char * const names[]={
+    "STOP",
+    "SERVERGREET",
+    "STARTTLS",
+    "UPGRADETLS",
+    "LOGIN",
+    "SELECT",
+    "FETCH",
+    "LOGOUT",
+    /* LAST */
+  };
+
+  if(imapc->state != newstate)
+    infof(conn->data, "IMAP %p state change from %s to %s\n",
+          imapc, names[imapc->state], names[newstate]);
+#endif
+
+  imapc->state = newstate;
+}
+
+static CURLcode imap_state_login(struct connectdata *conn)
+{
+  CURLcode result;
+  struct FTP *imap = conn->data->state.proto.imap;
+  const char *str = getcmdid(conn);
+  char *user = imap_atom(imap->user);
+  char *passwd = imap_atom(imap->passwd);
+
+  /* send USER and password */
+  result = imap_sendf(conn, str, "%s LOGIN %s %s", str,
+                      user ? user : "", passwd ? passwd : "");
+
+  Curl_safefree(user);
+  Curl_safefree(passwd);
+
+  if(result)
+    return result;
+
+  state(conn, IMAP_LOGIN);
+
+  return CURLE_OK;
+}
+
+/* For the IMAP "protocol connect" and "doing" phases only */
+static int imap_getsock(struct connectdata *conn,
+                        curl_socket_t *socks,
+                        int numsocks)
+{
+  return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
+}
+
+#ifdef USE_SSL
+static void imap_to_imaps(struct connectdata *conn)
+{
+  conn->handler = &Curl_handler_imaps;
+}
+#else
+#define imap_to_imaps(x) Curl_nop_stmt
+#endif
+
+/* For the initial server greeting */
+static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
+                                            int imapcode,
+                                            imapstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+
+  (void)instate; /* no use for this yet */
+
+  if(imapcode != 'O') {
+    failf(data, "Got unexpected imap-server response");
+    return CURLE_FTP_WEIRD_SERVER_REPLY;
+  }
+
+  if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
+    /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
+       to TLS connection now */
+    const char *str = getcmdid(conn);
+    result = imap_sendf(conn, str, "%s STARTTLS", str);
+    state(conn, IMAP_STARTTLS);
+  }
+  else
+    result = imap_state_login(conn);
+
+  return result;
+}
+
+/* For STARTTLS responses */
+static CURLcode imap_state_starttls_resp(struct connectdata *conn,
+                                         int imapcode,
+                                         imapstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+
+  (void)instate; /* no use for this yet */
+
+  if(imapcode != 'O') {
+    if(data->set.use_ssl != CURLUSESSL_TRY) {
+      failf(data, "STARTTLS denied. %c", imapcode);
+      result = CURLE_USE_SSL_FAILED;
+    }
+    else
+      result = imap_state_login(conn);
+  }
+  else {
+    if(data->state.used_interface == Curl_if_multi) {
+      state(conn, IMAP_UPGRADETLS);
+      result = imap_state_upgrade_tls(conn);
+    }
+    else {
+      result = Curl_ssl_connect(conn, FIRSTSOCKET);
+      if(CURLE_OK == result) {
+        imap_to_imaps(conn);
+        result = imap_state_login(conn);
+      }
+    }
+  }
+
+  return result;
+}
+
+static CURLcode imap_state_upgrade_tls(struct connectdata *conn)
+{
+  struct imap_conn *imapc = &conn->proto.imapc;
+  CURLcode result;
+
+  result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
+
+  if(imapc->ssldone) {
+    imap_to_imaps(conn);
+    result = imap_state_login(conn);
+  }
+
+  return result;
+}
+
+/* For LOGIN responses */
+static CURLcode imap_state_login_resp(struct connectdata *conn,
+                                      int imapcode,
+                                      imapstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+
+  (void)instate; /* no use for this yet */
+
+  if(imapcode != 'O') {
+    failf(data, "Access denied. %c", imapcode);
+    result = CURLE_LOGIN_DENIED;
+  }
+  else
+    /* End of connect phase */
+    state(conn, IMAP_STOP);
+
+  return result;
+}
+
+/* Start the DO phase */
+static CURLcode imap_select(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+  struct imap_conn *imapc = &conn->proto.imapc;
+  const char *str = getcmdid(conn);
+
+  result = imap_sendf(conn, str, "%s SELECT %s", str,
+                      imapc->mailbox?imapc->mailbox:"");
+  if(result)
+    return result;
+
+  state(conn, IMAP_SELECT);
+
+  return result;
+}
+
+static CURLcode imap_fetch(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+  const char *str = getcmdid(conn);
+
+  /* TODO: make this select the correct mail
+   * Use "1 body[text]" to get the full mail body of mail 1
+   */
+  result = imap_sendf(conn, str, "%s FETCH 1 BODY[TEXT]", str);
+  if(result)
+    return result;
+
+  /*
+   * When issued, the server will respond with a single line similar to
+   * '* 1 FETCH (BODY[TEXT] {2021}'
+   *
+   * Identifying the fetch and how many bytes of contents we can expect. We
+   * must extract that number before continuing to "download as usual".
+   */
+
+  state(conn, IMAP_FETCH);
+
+  return result;
+}
+
+/* For SELECT responses */
+static CURLcode imap_state_select_resp(struct connectdata *conn,
+                                       int imapcode,
+                                       imapstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+
+  (void)instate; /* no use for this yet */
+
+  if(imapcode != 'O') {
+    failf(data, "Select failed");
+    result = CURLE_LOGIN_DENIED;
+  }
+  else
+    result = imap_fetch(conn);
+
+  return result;
+}
+
+/* For the (first line of) FETCH BODY[TEXT] response */
+static CURLcode imap_state_fetch_resp(struct connectdata *conn,
+                                      int imapcode,
+                                      imapstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  struct imap_conn *imapc = &conn->proto.imapc;
+  struct FTP *imap = data->state.proto.imap;
+  struct pingpong *pp = &imapc->pp;
+  const char *ptr = data->state.buffer;
+
+  (void)instate; /* no use for this yet */
+
+  if('*' != imapcode) {
+    Curl_pgrsSetDownloadSize(data, 0);
+    state(conn, IMAP_STOP);
+    return CURLE_OK;
+  }
+
+  /* Something like this comes "* 1 FETCH (BODY[TEXT] {2021}\r" */
+  while(*ptr && (*ptr != '{'))
+    ptr++;
+
+  if(*ptr == '{') {
+    curl_off_t filesize = curlx_strtoofft(ptr + 1, NULL, 10);
+    if(filesize)
+      Curl_pgrsSetDownloadSize(data, filesize);
+
+    infof(data, "Found %" FORMAT_OFF_TU " bytes to download\n", filesize);
+
+    if(pp->cache) {
+      /* At this point there is a bunch of data in the header "cache" that is
+         actually body content, send it as body and then skip it. Do note
+         that there may even be additional "headers" after the body. */
+      size_t chunk = pp->cache_size;
+
+      if(chunk > (size_t)filesize)
+        /* the conversion from curl_off_t to size_t is always fine here */
+        chunk = (size_t)filesize;
+
+      result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
+      if(result)
+        return result;
+
+      filesize -= chunk;
+
+      /* we've now used parts of or the entire cache */
+      if(pp->cache_size > chunk) {
+        /* part of, move the trailing data to the start and reduce the size */
+        memmove(pp->cache, pp->cache+chunk,
+                pp->cache_size - chunk);
+        pp->cache_size -= chunk;
+      }
+      else {
+        /* cache is drained */
+        Curl_safefree(pp->cache);
+        pp->cache = NULL;
+        pp->cache_size = 0;
+      }
+    }
+
+    infof(data, "Filesize left: %" FORMAT_OFF_T "\n", filesize);
+
+    if(!filesize)
+      /* the entire data is already transferred! */
+      Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+    else
+      /* IMAP download */
+      Curl_setup_transfer(conn, FIRSTSOCKET, filesize, FALSE,
+                          imap->bytecountp, -1, NULL); /* no upload here */
+
+    data->req.maxdownload = filesize;
+  }
+  else
+    /* We don't know how to parse this line */
+    result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
+
+  /* End of do phase */
+  state(conn, IMAP_STOP);
+
+  return result;
+}
+
+static CURLcode imap_statemach_act(struct connectdata *conn)
+{
+  CURLcode result;
+  curl_socket_t sock = conn->sock[FIRSTSOCKET];
+  int imapcode;
+  struct imap_conn *imapc = &conn->proto.imapc;
+  struct pingpong *pp = &imapc->pp;
+  size_t nread = 0;
+
+  /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
+  if(imapc->state == IMAP_UPGRADETLS)
+    return imap_state_upgrade_tls(conn);
+
+  /* Flush any data that needs to be sent */
+  if(pp->sendleft)
+    return Curl_pp_flushsend(pp);
+
+  /* Read the response from the server */
+  result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
+  if(result)
+    return result;
+
+  if(imapcode) {
+    /* We have now received a full IMAP server response */
+    switch(imapc->state) {
+    case IMAP_SERVERGREET:
+      result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
+      break;
+
+    case IMAP_STARTTLS:
+      result = imap_state_starttls_resp(conn, imapcode, imapc->state);
+      break;
+
+    case IMAP_LOGIN:
+      result = imap_state_login_resp(conn, imapcode, imapc->state);
+      break;
+
+    case IMAP_FETCH:
+      result = imap_state_fetch_resp(conn, imapcode, imapc->state);
+      break;
+
+    case IMAP_SELECT:
+      result = imap_state_select_resp(conn, imapcode, imapc->state);
+      break;
+
+    case IMAP_LOGOUT:
+      /* fallthrough, just stop! */
+    default:
+      /* internal error */
+      state(conn, IMAP_STOP);
+      break;
+    }
+  }
+
+  return result;
+}
+
+/* Called repeatedly until done from curl_multi.c */
+static CURLcode imap_multi_statemach(struct connectdata *conn,
+                                         bool *done)
+{
+  struct imap_conn *imapc = &conn->proto.imapc;
+  CURLcode result;
+
+  if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone)
+    result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
+  else
+    result = Curl_pp_multi_statemach(&imapc->pp);
+
+  *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
+
+  return result;
+}
+
+static CURLcode imap_easy_statemach(struct connectdata *conn)
+{
+  struct imap_conn *imapc = &conn->proto.imapc;
+  struct pingpong *pp = &imapc->pp;
+  CURLcode result = CURLE_OK;
+
+  while(imapc->state != IMAP_STOP) {
+    result = Curl_pp_easy_statemach(pp);
+    if(result)
+      break;
+  }
+
+  return result;
+}
+
+/* Allocate and initialize the struct IMAP for the current SessionHandle if
+   required */
+static CURLcode imap_init(struct connectdata *conn)
+{
+  struct SessionHandle *data = conn->data;
+  struct FTP *imap = data->state.proto.imap;
+
+  if(!imap) {
+    imap = data->state.proto.imap = calloc(sizeof(struct FTP), 1);
+    if(!imap)
+      return CURLE_OUT_OF_MEMORY;
+  }
+
+  /* Get some initial data into the imap struct */
+  imap->bytecountp = &data->req.bytecount;
+
+  /* No need to duplicate user+password, the connectdata struct won't change
+     during a session, but we re-init them here since on subsequent inits
+     since the conn struct may have changed or been replaced.
+  */
+  imap->user = conn->user;
+  imap->passwd = conn->passwd;
+
+  return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * imap_connect() should do everything that is to be considered a part of
+ * the connection phase.
+ *
+ * The variable 'done' points to will be TRUE if the protocol-layer connect
+ * phase is done when this function returns, or FALSE is not. When called as
+ * a part of the easy interface, it will always be TRUE.
+ */
+static CURLcode imap_connect(struct connectdata *conn,
+                                 bool *done) /* see description above */
+{
+  CURLcode result;
+  struct imap_conn *imapc = &conn->proto.imapc;
+  struct SessionHandle *data=conn->data;
+  struct pingpong *pp = &imapc->pp;
+
+  *done = FALSE; /* default to not done yet */
+
+  /* If there already is a protocol-specific struct allocated for this
+     sessionhandle, deal with it */
+  Curl_reset_reqproto(conn);
+
+  result = imap_init(conn);
+  if(CURLE_OK != result)
+    return result;
+
+  /* We always support persistent connections on imap */
+  conn->bits.close = FALSE;
+
+  pp->response_time = RESP_TIMEOUT; /* set default response time-out */
+  pp->statemach_act = imap_statemach_act;
+  pp->endofresp = imap_endofresp;
+  pp->conn = conn;
+
+  if((conn->handler->flags & PROTOPT_SSL) &&
+     data->state.used_interface != Curl_if_multi) {
+    /* IMAPS is simply imap with SSL for the control channel */
+    /* so perform the SSL initialization for this socket */
+    result = Curl_ssl_connect(conn, FIRSTSOCKET);
+    if(result)
+      return result;
+  }
+
+  /* Initialise the response reader stuff */
+  Curl_pp_init(pp);
+
+  /* Start off waiting for the server greeting response */
+  state(conn, IMAP_SERVERGREET);
+
+  /* Start off with an id of '*' */
+  imapc->idstr = "*";
+
+  if(data->state.used_interface == Curl_if_multi)
+    result = imap_multi_statemach(conn, done);
+  else {
+    result = imap_easy_statemach(conn);
+    if(!result)
+      *done = TRUE;
+  }
+
+  return result;
+}
+
+/***********************************************************************
+ *
+ * imap_done()
+ *
+ * The DONE function. This does what needs to be done after a single DO has
+ * performed.
+ *
+ * Input argument is already checked for validity.
+ */
+static CURLcode imap_done(struct connectdata *conn, CURLcode status,
+                          bool premature)
+{
+  struct SessionHandle *data = conn->data;
+  struct FTP *imap = data->state.proto.imap;
+  CURLcode result=CURLE_OK;
+
+  (void)premature;
+
+  if(!imap)
+    /* When the easy handle is removed from the multi while libcurl is still
+     * trying to resolve the host name, it seems that the imap struct is not
+     * yet initialized, but the removal action calls Curl_done() which calls
+     * this function. So we simply return success if no imap pointer is set.
+     */
+    return CURLE_OK;
+
+  if(status) {
+    conn->bits.close = TRUE; /* marked for closure */
+    result = status;         /* use the already set error code */
+  }
+
+  /* Clear the transfer mode for the next connection */
+  imap->transfer = FTPTRANSFER_BODY;
+
+  return result;
+}
+
+/***********************************************************************
+ *
+ * imap_perform()
+ *
+ * This is the actual DO function for IMAP. Get a file/directory according to
+ * the options previously setup.
+ */
+static CURLcode imap_perform(struct connectdata *conn, bool *connected,
+                             bool *dophase_done)
+{
+  /* This is IMAP and no proxy */
+  CURLcode result = CURLE_OK;
+
+  DEBUGF(infof(conn->data, "DO phase starts\n"));
+
+  if(conn->data->set.opt_no_body) {
+    /* Requested no body means no transfer */
+    struct FTP *imap = conn->data->state.proto.imap;
+    imap->transfer = FTPTRANSFER_INFO;
+  }
+
+  *dophase_done = FALSE; /* not done yet */
+
+  /* Start the first command in the DO phase */
+  result = imap_select(conn);
+  if(result)
+    return result;
+
+  /* Run the state-machine */
+  if(conn->data->state.used_interface == Curl_if_multi)
+    result = imap_multi_statemach(conn, dophase_done);
+  else {
+    result = imap_easy_statemach(conn);
+    *dophase_done = TRUE; /* with the easy interface we are done here */
+  }
+  *connected = conn->bits.tcpconnect[FIRSTSOCKET];
+
+  if(*dophase_done)
+    DEBUGF(infof(conn->data, "DO phase is complete\n"));
+
+  return result;
+}
+
+/***********************************************************************
+ *
+ * imap_do()
+ *
+ * This function is registered as 'curl_do' function. It decodes the path
+ * parts etc as a wrapper to the actual DO function (imap_perform).
+ *
+ * The input argument is already checked for validity.
+ */
+static CURLcode imap_do(struct connectdata *conn, bool *done)
+{
+  CURLcode retcode = CURLE_OK;
+
+  *done = FALSE; /* default to false */
+
+  /*
+    Since connections can be re-used between SessionHandles, this might be a
+    connection already existing but on a fresh SessionHandle struct so we must
+    make sure we have a good 'struct IMAP' to play with. For new connections,
+    the struct IMAP is allocated and setup in the imap_connect() function.
+  */
+  Curl_reset_reqproto(conn);
+  retcode = imap_init(conn);
+  if(retcode)
+    return retcode;
+
+  /* Parse the URL path */
+  retcode = imap_parse_url_path(conn);
+  if(retcode)
+    return retcode;
+
+  retcode = imap_regular_transfer(conn, done);
+
+  return retcode;
+}
+
+/***********************************************************************
+ *
+ * imap_logout()
+ *
+ * This should be called before calling sclose().  We should then wait for the
+ * response from the server before returning. The calling code should then try
+ * to close the connection.
+ *
+ */
+static CURLcode imap_logout(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+  const char *str = getcmdid(conn);
+
+  result = imap_sendf(conn, str, "%s LOGOUT", str, NULL);
+  if(result)
+    return result;
+
+  state(conn, IMAP_LOGOUT);
+
+  result = imap_easy_statemach(conn);
+
+  return result;
+}
+
+/***********************************************************************
+ *
+ * imap_disconnect()
+ *
+ * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
+ * resources. BLOCKING.
+ */
+static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
+{
+  struct imap_conn *imapc= &conn->proto.imapc;
+
+  /* We cannot send quit unconditionally. If this connection is stale or
+     bad in any way, sending quit and waiting around here will make the
+     disconnect wait in vain and cause more problems than we need to */
+
+  /* The IMAP session may or may not have been allocated/setup at this
+     point! */
+  if(!dead_connection && imapc->pp.conn)
+    (void)imap_logout(conn); /* ignore errors on the LOGOUT */
+
+  /* Disconnect from the server */
+  Curl_pp_disconnect(&imapc->pp);
+
+  /* Cleanup our connection based variables */
+  Curl_safefree(imapc->mailbox);
+
+  return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * imap_parse_url_path()
+ *
+ * Parse the URL path into separate path components.
+ *
+ */
+static CURLcode imap_parse_url_path(struct connectdata *conn)
+{
+  /* The imap struct is already inited in imap_connect() */
+  struct imap_conn *imapc = &conn->proto.imapc;
+  struct SessionHandle *data = conn->data;
+  const char *path = data->state.path;
+
+  if(!*path)
+    path = "INBOX";
+
+  /* URL decode the path and use this mailbox */
+  return Curl_urldecode(data, path, 0, &imapc->mailbox, NULL, TRUE);
+}
+
+/* Call this when the DO phase has completed */
+static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
+{
+  struct FTP *imap = conn->data->state.proto.imap;
+
+  (void)connected;
+
+  if(imap->transfer != FTPTRANSFER_BODY)
+    /* no data to transfer */
+    Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+
+  return CURLE_OK;
+}
+
+/* Called from curl_multi.c while DOing */
+static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
+{
+  CURLcode result = imap_multi_statemach(conn, dophase_done);
+
+  if(result)
+    DEBUGF(infof(conn->data, "DO phase failed\n"));
+  else {
+    if(*dophase_done) {
+      result = imap_dophase_done(conn, FALSE /* not connected */);
+
+      DEBUGF(infof(conn->data, "DO phase is complete\n"));
+    }
+  }
+
+  return result;
+}
+
+/***********************************************************************
+ *
+ * imap_regular_transfer()
+ *
+ * The input argument is already checked for validity.
+ *
+ * Performs all commands done before a regular transfer between a local and a
+ * remote host.
+ */
+static CURLcode imap_regular_transfer(struct connectdata *conn,
+                                      bool *dophase_done)
+{
+  CURLcode result = CURLE_OK;
+  bool connected = FALSE;
+  struct SessionHandle *data = conn->data;
+
+  /* Make sure size is unknown at this point */
+  data->req.size = -1;
+
+  Curl_pgrsSetUploadCounter(data, 0);
+  Curl_pgrsSetDownloadCounter(data, 0);
+  Curl_pgrsSetUploadSize(data, 0);
+  Curl_pgrsSetDownloadSize(data, 0);
+
+  result = imap_perform(conn, &connected, dophase_done);
+
+  if(CURLE_OK == result) {
+    if(!*dophase_done)
+      /* The DO phase has not completed yet */
+      return CURLE_OK;
+
+    result = imap_dophase_done(conn, connected);
+    if(result)
+      return result;
+  }
+
+  return result;
+}
+
+static CURLcode imap_setup_connection(struct connectdata * conn)
+{
+  struct SessionHandle *data = conn->data;
+
+  if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
+    /* Unless we have asked to tunnel imap operations through the proxy, we
+       switch and use HTTP operations only */
+#ifndef CURL_DISABLE_HTTP
+    if(conn->handler == &Curl_handler_imap)
+      conn->handler = &Curl_handler_imap_proxy;
+    else {
+#ifdef USE_SSL
+      conn->handler = &Curl_handler_imaps_proxy;
+#else
+      failf(data, "IMAPS not supported!");
+      return CURLE_UNSUPPORTED_PROTOCOL;
+#endif
+    }
+
+    /* We explicitly mark this connection as persistent here as we're doing
+       IMAP over HTTP and thus we accidentally avoid setting this value
+       otherwise */
+    conn->bits.close = FALSE;
+#else
+    failf(data, "IMAP over http proxy requires HTTP support built-in!");
+    return CURLE_UNSUPPORTED_PROTOCOL;
+#endif
+  }
+
+  data->state.path++;   /* don't include the initial slash */
+
+  return CURLE_OK;
+}
+
+#endif /* CURL_DISABLE_IMAP */
diff --git a/lib/curl_inet_ntop.c b/lib/curl_inet_ntop.c
new file mode 100644 (file)
index 0000000..b184029
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 1996-2001  Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * Original code by Paul Vixie. "curlified" by Gisle Vanem.
+ */
+
+#include "curl_setup.h"
+
+#ifndef HAVE_INET_NTOP
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_inet_ntop.h"
+
+#define IN6ADDRSZ       16
+#define INADDRSZ         4
+#define INT16SZ          2
+
+/*
+ * Format an IPv4 address, more or less like inet_ntoa().
+ *
+ * Returns `dst' (as a const)
+ * Note:
+ *  - uses no statics
+ *  - takes a unsigned char* not an in_addr as input
+ */
+static char *inet_ntop4 (const unsigned char *src, char *dst, size_t size)
+{
+  char tmp[sizeof "255.255.255.255"];
+  size_t len;
+
+  DEBUGASSERT(size >= 16);
+
+  tmp[0] = '\0';
+  (void)snprintf(tmp, sizeof(tmp), "%d.%d.%d.%d",
+          ((int)((unsigned char)src[0])) & 0xff,
+          ((int)((unsigned char)src[1])) & 0xff,
+          ((int)((unsigned char)src[2])) & 0xff,
+          ((int)((unsigned char)src[3])) & 0xff);
+
+  len = strlen(tmp);
+  if(len == 0 || len >= size) {
+    SET_ERRNO(ENOSPC);
+    return (NULL);
+  }
+  strcpy(dst, tmp);
+  return dst;
+}
+
+#ifdef ENABLE_IPV6
+/*
+ * Convert IPv6 binary address into presentation (printable) format.
+ */
+static char *inet_ntop6 (const unsigned char *src, char *dst, size_t size)
+{
+  /*
+   * Note that int32_t and int16_t need only be "at least" large enough
+   * to contain a value of the specified size.  On some systems, like
+   * Crays, there is no such thing as an integer variable with 16 bits.
+   * Keep this in mind if you think this function should have been coded
+   * to use pointer overlays.  All the world's not a VAX.
+   */
+  char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+  char *tp;
+  struct {
+    long base;
+    long len;
+  } best, cur;
+  unsigned long words[IN6ADDRSZ / INT16SZ];
+  int i;
+
+  /* Preprocess:
+   *  Copy the input (bytewise) array into a wordwise array.
+   *  Find the longest run of 0x00's in src[] for :: shorthanding.
+   */
+  memset(words, '\0', sizeof(words));
+  for(i = 0; i < IN6ADDRSZ; i++)
+    words[i/2] |= (src[i] << ((1 - (i % 2)) << 3));
+
+  best.base = -1;
+  cur.base  = -1;
+  best.len = 0;
+  cur.len = 0;
+
+  for(i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
+    if(words[i] == 0) {
+      if(cur.base == -1)
+        cur.base = i, cur.len = 1;
+      else
+        cur.len++;
+    }
+    else if(cur.base != -1) {
+      if(best.base == -1 || cur.len > best.len)
+        best = cur;
+      cur.base = -1;
+    }
+  }
+  if((cur.base != -1) && (best.base == -1 || cur.len > best.len))
+    best = cur;
+  if(best.base != -1 && best.len < 2)
+    best.base = -1;
+  /* Format the result. */
+  tp = tmp;
+  for(i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
+    /* Are we inside the best run of 0x00's? */
+    if(best.base != -1 && i >= best.base && i < (best.base + best.len)) {
+      if(i == best.base)
+        *tp++ = ':';
+      continue;
+    }
+
+    /* Are we following an initial run of 0x00s or any real hex?
+     */
+    if(i != 0)
+      *tp++ = ':';
+
+    /* Is this address an encapsulated IPv4?
+     */
+    if(i == 6 && best.base == 0 &&
+        (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
+      if(!inet_ntop4(src+12, tp, sizeof(tmp) - (tp - tmp))) {
+        SET_ERRNO(ENOSPC);
+        return (NULL);
+      }
+      tp += strlen(tp);
+      break;
+    }
+    tp += snprintf(tp, 5, "%lx", words[i]);
+  }
+
+  /* Was it a trailing run of 0x00's?
+   */
+  if(best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ))
+     *tp++ = ':';
+  *tp++ = '\0';
+
+  /* Check for overflow, copy, and we're done.
+   */
+  if((size_t)(tp - tmp) > size) {
+    SET_ERRNO(ENOSPC);
+    return (NULL);
+  }
+  strcpy(dst, tmp);
+  return dst;
+}
+#endif  /* ENABLE_IPV6 */
+
+/*
+ * Convert a network format address to presentation format.
+ *
+ * Returns pointer to presentation format address (`buf').
+ * Returns NULL on error and errno set with the specific
+ * error, EAFNOSUPPORT or ENOSPC.
+ *
+ * On Windows we store the error in the thread errno, not
+ * in the winsock error code. This is to avoid losing the
+ * actual last winsock error. So use macro ERRNO to fetch the
+ * errno this function sets when returning NULL, not SOCKERRNO.
+ */
+char *Curl_inet_ntop(int af, const void *src, char *buf, size_t size)
+{
+  switch (af) {
+  case AF_INET:
+    return inet_ntop4((const unsigned char*)src, buf, size);
+#ifdef ENABLE_IPV6
+  case AF_INET6:
+    return inet_ntop6((const unsigned char*)src, buf, size);
+#endif
+  default:
+    SET_ERRNO(EAFNOSUPPORT);
+    return NULL;
+  }
+}
+#endif  /* HAVE_INET_NTOP */
diff --git a/lib/curl_inet_pton.c b/lib/curl_inet_pton.c
new file mode 100644 (file)
index 0000000..175f938
--- /dev/null
@@ -0,0 +1,234 @@
+/* This is from the BIND 4.9.4 release, modified to compile by itself */
+
+/* Copyright (c) 1996 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+#include "curl_setup.h"
+
+#ifndef HAVE_INET_PTON
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include "curl_inet_pton.h"
+
+#define IN6ADDRSZ       16
+#define INADDRSZ         4
+#define INT16SZ          2
+
+/*
+ * WARNING: Don't even consider trying to compile this on a system where
+ * sizeof(int) < 4.  sizeof(int) > 4 is fine; all the world's not a VAX.
+ */
+
+static int      inet_pton4(const char *src, unsigned char *dst);
+#ifdef ENABLE_IPV6
+static int      inet_pton6(const char *src, unsigned char *dst);
+#endif
+
+/* int
+ * inet_pton(af, src, dst)
+ *      convert from presentation format (which usually means ASCII printable)
+ *      to network format (which is usually some kind of binary format).
+ * return:
+ *      1 if the address was valid for the specified address family
+ *      0 if the address wasn't valid (`dst' is untouched in this case)
+ *      -1 if some other error occurred (`dst' is untouched in this case, too)
+ * notice:
+ *      On Windows we store the error in the thread errno, not
+ *      in the winsock error code. This is to avoid losing the
+ *      actual last winsock error. So use macro ERRNO to fetch the
+ *      errno this function sets when returning (-1), not SOCKERRNO.
+ * author:
+ *      Paul Vixie, 1996.
+ */
+int
+Curl_inet_pton(int af, const char *src, void *dst)
+{
+  switch (af) {
+  case AF_INET:
+    return (inet_pton4(src, (unsigned char *)dst));
+#ifdef ENABLE_IPV6
+  case AF_INET6:
+    return (inet_pton6(src, (unsigned char *)dst));
+#endif
+  default:
+    SET_ERRNO(EAFNOSUPPORT);
+    return (-1);
+  }
+  /* NOTREACHED */
+}
+
+/* int
+ * inet_pton4(src, dst)
+ *      like inet_aton() but without all the hexadecimal and shorthand.
+ * return:
+ *      1 if `src' is a valid dotted quad, else 0.
+ * notice:
+ *      does not touch `dst' unless it's returning 1.
+ * author:
+ *      Paul Vixie, 1996.
+ */
+static int
+inet_pton4(const char *src, unsigned char *dst)
+{
+  static const char digits[] = "0123456789";
+  int saw_digit, octets, ch;
+  unsigned char tmp[INADDRSZ], *tp;
+
+  saw_digit = 0;
+  octets = 0;
+  tp = tmp;
+  *tp = 0;
+  while((ch = *src++) != '\0') {
+    const char *pch;
+
+    if((pch = strchr(digits, ch)) != NULL) {
+      unsigned int val = *tp * 10 + (unsigned int)(pch - digits);
+
+      if(saw_digit && *tp == 0)
+        return (0);
+      if(val > 255)
+        return (0);
+      *tp = (unsigned char)val;
+      if(! saw_digit) {
+        if(++octets > 4)
+          return (0);
+        saw_digit = 1;
+      }
+    }
+    else if(ch == '.' && saw_digit) {
+      if(octets == 4)
+        return (0);
+      *++tp = 0;
+      saw_digit = 0;
+    }
+    else
+      return (0);
+  }
+  if(octets < 4)
+    return (0);
+  memcpy(dst, tmp, INADDRSZ);
+  return (1);
+}
+
+#ifdef ENABLE_IPV6
+/* int
+ * inet_pton6(src, dst)
+ *      convert presentation level address to network order binary form.
+ * return:
+ *      1 if `src' is a valid [RFC1884 2.2] address, else 0.
+ * notice:
+ *      (1) does not touch `dst' unless it's returning 1.
+ *      (2) :: in a full address is silently ignored.
+ * credit:
+ *      inspired by Mark Andrews.
+ * author:
+ *      Paul Vixie, 1996.
+ */
+static int
+inet_pton6(const char *src, unsigned char *dst)
+{
+  static const char xdigits_l[] = "0123456789abcdef",
+    xdigits_u[] = "0123456789ABCDEF";
+  unsigned char tmp[IN6ADDRSZ], *tp, *endp, *colonp;
+  const char *xdigits, *curtok;
+  int ch, saw_xdigit;
+  size_t val;
+
+  memset((tp = tmp), 0, IN6ADDRSZ);
+  endp = tp + IN6ADDRSZ;
+  colonp = NULL;
+  /* Leading :: requires some special handling. */
+  if(*src == ':')
+    if(*++src != ':')
+      return (0);
+  curtok = src;
+  saw_xdigit = 0;
+  val = 0;
+  while((ch = *src++) != '\0') {
+    const char *pch;
+
+    if((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
+      pch = strchr((xdigits = xdigits_u), ch);
+    if(pch != NULL) {
+      val <<= 4;
+      val |= (pch - xdigits);
+      if(++saw_xdigit > 4)
+        return (0);
+      continue;
+    }
+    if(ch == ':') {
+      curtok = src;
+      if(!saw_xdigit) {
+        if(colonp)
+          return (0);
+        colonp = tp;
+        continue;
+      }
+      if(tp + INT16SZ > endp)
+        return (0);
+      *tp++ = (unsigned char) (val >> 8) & 0xff;
+      *tp++ = (unsigned char) val & 0xff;
+      saw_xdigit = 0;
+      val = 0;
+      continue;
+    }
+    if(ch == '.' && ((tp + INADDRSZ) <= endp) &&
+        inet_pton4(curtok, tp) > 0) {
+      tp += INADDRSZ;
+      saw_xdigit = 0;
+      break;    /* '\0' was seen by inet_pton4(). */
+    }
+    return (0);
+  }
+  if(saw_xdigit) {
+    if(tp + INT16SZ > endp)
+      return (0);
+    *tp++ = (unsigned char) (val >> 8) & 0xff;
+    *tp++ = (unsigned char) val & 0xff;
+  }
+  if(colonp != NULL) {
+    /*
+     * Since some memmove()'s erroneously fail to handle
+     * overlapping regions, we'll do the shift by hand.
+     */
+    const ssize_t n = tp - colonp;
+    ssize_t i;
+
+    if(tp == endp)
+      return (0);
+    for(i = 1; i <= n; i++) {
+      *(endp - i) = *(colonp + n - i);
+      *(colonp + n - i) = 0;
+    }
+    tp = endp;
+  }
+  if(tp != endp)
+    return (0);
+  memcpy(dst, tmp, IN6ADDRSZ);
+  return (1);
+}
+#endif /* ENABLE_IPV6 */
+
+#endif /* HAVE_INET_PTON */
diff --git a/lib/curl_krb4.c b/lib/curl_krb4.c
new file mode 100644 (file)
index 0000000..8ba326e
--- /dev/null
@@ -0,0 +1,440 @@
+/* This source code was modified by Martin Hedenfalk <mhe@stacken.kth.se> for
+ * use in Curl. Martin's latest changes were done 2000-09-18.
+ *
+ * It has since been patched away like a madman by Daniel Stenberg to make it
+ * better applied to curl conditions, and to make it not use globals, pollute
+ * name space and more.
+ *
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * Copyright (c) 2004 - 2011 Daniel Stenberg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_FTP
+#ifdef HAVE_KRB4
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#include <krb.h>
+#include <des.h>
+
+#include "curl_urldata.h"
+#include "curl_base64.h"
+#include "curl_ftp.h"
+#include "curl_sendf.h"
+#include "curl_krb4.h"
+#include "curl_inet_ntop.h"
+#include "curl_memory.h"
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+#define LOCAL_ADDR (&conn->local_addr)
+#define REMOTE_ADDR conn->ip_addr->ai_addr
+#define myctladdr LOCAL_ADDR
+#define hisctladdr REMOTE_ADDR
+
+struct krb4_data {
+  des_cblock key;
+  des_key_schedule schedule;
+  char name[ANAME_SZ];
+  char instance[INST_SZ];
+  char realm[REALM_SZ];
+};
+
+#ifndef HAVE_STRLCPY
+/* if it ever goes non-static, make it Curl_ prefixed! */
+static size_t
+strlcpy (char *dst, const char *src, size_t dst_sz)
+{
+  size_t n;
+  char *p;
+
+  for(p = dst, n = 0;
+      n + 1 < dst_sz && *src != '\0';
+      ++p, ++src, ++n)
+    *p = *src;
+  *p = '\0';
+  if(*src == '\0')
+    return n;
+  else
+    return n + strlen (src);
+}
+#else
+size_t strlcpy (char *dst, const char *src, size_t dst_sz);
+#endif
+
+static int
+krb4_check_prot(void *app_data, int level)
+{
+  app_data = NULL; /* prevent compiler warning */
+  if(level == PROT_CONFIDENTIAL)
+    return -1;
+  return 0;
+}
+
+static int
+krb4_decode(void *app_data, void *buf, int len, int level,
+            struct connectdata *conn)
+{
+  MSG_DAT m;
+  int e;
+  struct krb4_data *d = app_data;
+
+  if(level == PROT_SAFE)
+    e = krb_rd_safe(buf, len, &d->key,
+                    (struct sockaddr_in *)REMOTE_ADDR,
+                    (struct sockaddr_in *)LOCAL_ADDR, &m);
+  else
+    e = krb_rd_priv(buf, len, d->schedule, &d->key,
+                    (struct sockaddr_in *)REMOTE_ADDR,
+                    (struct sockaddr_in *)LOCAL_ADDR, &m);
+  if(e) {
+    struct SessionHandle *data = conn->data;
+    infof(data, "krb4_decode: %s\n", krb_get_err_text(e));
+    return -1;
+  }
+  memmove(buf, m.app_data, m.app_length);
+  return m.app_length;
+}
+
+static int
+krb4_overhead(void *app_data, int level, int len)
+{
+  /* no arguments are used, just init them to prevent compiler warnings */
+  app_data = NULL;
+  level = 0;
+  len = 0;
+  return 31;
+}
+
+static int
+krb4_encode(void *app_data, const void *from, int length, int level, void **to,
+            struct connectdata *conn)
+{
+  struct krb4_data *d = app_data;
+  *to = malloc(length + 31);
+  if(!*to)
+    return -1;
+  if(level == PROT_SAFE)
+    /* NOTE that the void* cast is safe, krb_mk_safe/priv don't modify the
+     * input buffer
+     */
+    return krb_mk_safe((void*)from, *to, length, &d->key,
+                       (struct sockaddr_in *)LOCAL_ADDR,
+                       (struct sockaddr_in *)REMOTE_ADDR);
+  else if(level == PROT_PRIVATE)
+    return krb_mk_priv((void*)from, *to, length, d->schedule, &d->key,
+                       (struct sockaddr_in *)LOCAL_ADDR,
+                       (struct sockaddr_in *)REMOTE_ADDR);
+  else
+    return -1;
+}
+
+static int
+mk_auth(struct krb4_data *d, KTEXT adat,
+        const char *service, char *host, int checksum)
+{
+  int ret;
+  CREDENTIALS cred;
+  char sname[SNAME_SZ], inst[INST_SZ], realm[REALM_SZ];
+
+  strlcpy(sname, service, sizeof(sname));
+  strlcpy(inst, krb_get_phost(host), sizeof(inst));
+  strlcpy(realm, krb_realmofhost(host), sizeof(realm));
+  ret = krb_mk_req(adat, sname, inst, realm, checksum);
+  if(ret)
+    return ret;
+  strlcpy(sname, service, sizeof(sname));
+  strlcpy(inst, krb_get_phost(host), sizeof(inst));
+  strlcpy(realm, krb_realmofhost(host), sizeof(realm));
+  ret = krb_get_cred(sname, inst, realm, &cred);
+  memmove(&d->key, &cred.session, sizeof(des_cblock));
+  des_key_sched(&d->key, d->schedule);
+  memset(&cred, 0, sizeof(cred));
+  return ret;
+}
+
+#ifdef HAVE_KRB_GET_OUR_IP_FOR_REALM
+int krb_get_our_ip_for_realm(char *, struct in_addr *);
+#endif
+
+static int
+krb4_auth(void *app_data, struct connectdata *conn)
+{
+  int ret;
+  char *p;
+  unsigned char *ptr;
+  size_t len = 0;
+  KTEXT_ST adat;
+  MSG_DAT msg_data;
+  int checksum;
+  u_int32_t cs;
+  struct krb4_data *d = app_data;
+  char *host = conn->host.name;
+  ssize_t nread;
+  int l = sizeof(conn->local_addr);
+  struct SessionHandle *data = conn->data;
+  CURLcode result;
+  size_t base64_sz = 0;
+
+  if(getsockname(conn->sock[FIRSTSOCKET],
+                 (struct sockaddr *)LOCAL_ADDR, &l) < 0)
+    perror("getsockname()");
+
+  checksum = getpid();
+  ret = mk_auth(d, &adat, "ftp", host, checksum);
+  if(ret == KDC_PR_UNKNOWN)
+    ret = mk_auth(d, &adat, "rcmd", host, checksum);
+  if(ret) {
+    infof(data, "%s\n", krb_get_err_text(ret));
+    return AUTH_CONTINUE;
+  }
+
+#ifdef HAVE_KRB_GET_OUR_IP_FOR_REALM
+  if(krb_get_config_bool("nat_in_use")) {
+    struct sockaddr_in *localaddr  = (struct sockaddr_in *)LOCAL_ADDR;
+    struct in_addr natAddr;
+
+    if(krb_get_our_ip_for_realm(krb_realmofhost(host),
+                                 &natAddr) != KSUCCESS
+        && krb_get_our_ip_for_realm(NULL, &natAddr) != KSUCCESS)
+      infof(data, "Can't get address for realm %s\n",
+                 krb_realmofhost(host));
+    else {
+      if(natAddr.s_addr != localaddr->sin_addr.s_addr) {
+        char addr_buf[128];
+        if(Curl_inet_ntop(AF_INET, natAddr, addr_buf, sizeof(addr_buf)))
+          infof(data, "Using NAT IP address (%s) for kerberos 4\n", addr_buf);
+        localaddr->sin_addr = natAddr;
+      }
+    }
+  }
+#endif
+
+  result = Curl_base64_encode(conn->data, (char *)adat.dat, adat.length,
+                              &p, &base64_sz);
+  if(result) {
+    Curl_failf(data, "base64-encoding: %s", curl_easy_strerror(result));
+    return AUTH_CONTINUE;
+  }
+
+  result = Curl_ftpsendf(conn, "ADAT %s", p);
+
+  free(p);
+
+  if(result)
+    return -2;
+
+  if(Curl_GetFTPResponse(&nread, conn, NULL))
+    return -1;
+
+  if(data->state.buffer[0] != '2') {
+    Curl_failf(data, "Server didn't accept auth data");
+    return AUTH_ERROR;
+  }
+
+  p = strstr(data->state.buffer, "ADAT=");
+  if(!p) {
+    Curl_failf(data, "Remote host didn't send adat reply");
+    return AUTH_ERROR;
+  }
+  p += 5;
+  result = Curl_base64_decode(p, &ptr, &len);
+  if(result) {
+    Curl_failf(data, "base64-decoding: %s", curl_easy_strerror(result));
+    return AUTH_ERROR;
+  }
+  if(len > sizeof(adat.dat)-1) {
+    free(ptr);
+    ptr = NULL;
+    len = 0;
+  }
+  if(!len || !ptr) {
+    Curl_failf(data, "Failed to decode base64 from server");
+    return AUTH_ERROR;
+  }
+  memcpy((char *)adat.dat, ptr, len);
+  free(ptr);
+  adat.length = len;
+  ret = krb_rd_safe(adat.dat, adat.length, &d->key,
+                    (struct sockaddr_in *)hisctladdr,
+                    (struct sockaddr_in *)myctladdr, &msg_data);
+  if(ret) {
+    Curl_failf(data, "Error reading reply from server: %s",
+               krb_get_err_text(ret));
+    return AUTH_ERROR;
+  }
+  krb_get_int(msg_data.app_data, &cs, 4, 0);
+  if(cs - checksum != 1) {
+    Curl_failf(data, "Bad checksum returned from server");
+    return AUTH_ERROR;
+  }
+  return AUTH_OK;
+}
+
+struct Curl_sec_client_mech Curl_krb4_client_mech = {
+    "KERBEROS_V4",
+    sizeof(struct krb4_data),
+    NULL, /* init */
+    krb4_auth,
+    NULL, /* end */
+    krb4_check_prot,
+    krb4_overhead,
+    krb4_encode,
+    krb4_decode
+};
+
+static enum protection_level
+krb4_set_command_prot(struct connectdata *conn, enum protection_level level)
+{
+  enum protection_level old = conn->command_prot;
+  DEBUGASSERT(level > PROT_NONE && level < PROT_LAST);
+  conn->command_prot = level;
+  return old;
+}
+
+CURLcode Curl_krb_kauth(struct connectdata *conn)
+{
+  des_cblock key;
+  des_key_schedule schedule;
+  KTEXT_ST tkt, tktcopy;
+  char *name;
+  char *p;
+  char passwd[100];
+  size_t tmp = 0;
+  ssize_t nread;
+  enum protection_level save;
+  CURLcode result;
+  unsigned char *ptr;
+  size_t base64_sz = 0;
+
+  save = krb4_set_command_prot(conn, PROT_PRIVATE);
+
+  result = Curl_ftpsendf(conn, "SITE KAUTH %s", conn->user);
+
+  if(result)
+    return result;
+
+  result = Curl_GetFTPResponse(&nread, conn, NULL);
+  if(result)
+    return result;
+
+  if(conn->data->state.buffer[0] != '3') {
+    krb4_set_command_prot(conn, save);
+    return CURLE_FTP_WEIRD_SERVER_REPLY;
+  }
+
+  p = strstr(conn->data->state.buffer, "T=");
+  if(!p) {
+    Curl_failf(conn->data, "Bad reply from server");
+    krb4_set_command_prot(conn, save);
+    return CURLE_FTP_WEIRD_SERVER_REPLY;
+  }
+
+  p += 2;
+  result = Curl_base64_decode(p, &ptr, &tmp);
+  if(result) {
+    Curl_failf(conn->data, "base64-decoding: %s", curl_easy_strerror(result));
+    return result;
+  }
+  if(tmp >= sizeof(tkt.dat)) {
+    free(ptr);
+    ptr = NULL;
+    tmp = 0;
+  }
+  if(!tmp || !ptr) {
+    Curl_failf(conn->data, "Failed to decode base64 in reply");
+    krb4_set_command_prot(conn, save);
+    return CURLE_FTP_WEIRD_SERVER_REPLY;
+  }
+  memcpy((char *)tkt.dat, ptr, tmp);
+  free(ptr);
+  tkt.length = tmp;
+  tktcopy.length = tkt.length;
+
+  p = strstr(conn->data->state.buffer, "P=");
+  if(!p) {
+    Curl_failf(conn->data, "Bad reply from server");
+    krb4_set_command_prot(conn, save);
+    return CURLE_FTP_WEIRD_SERVER_REPLY;
+  }
+  name = p + 2;
+  for(; *p && *p != ' ' && *p != '\r' && *p != '\n'; p++);
+  *p = 0;
+
+  des_string_to_key (conn->passwd, &key);
+  des_key_sched(&key, schedule);
+
+  des_pcbc_encrypt((void *)tkt.dat, (void *)tktcopy.dat,
+                   tkt.length,
+                   schedule, &key, DES_DECRYPT);
+  if(strcmp ((char*)tktcopy.dat + 8,
+              KRB_TICKET_GRANTING_TICKET) != 0) {
+    afs_string_to_key(passwd,
+                      krb_realmofhost(conn->host.name),
+                      &key);
+    des_key_sched(&key, schedule);
+    des_pcbc_encrypt((void *)tkt.dat, (void *)tktcopy.dat,
+                     tkt.length,
+                     schedule, &key, DES_DECRYPT);
+  }
+  memset(key, 0, sizeof(key));
+  memset(schedule, 0, sizeof(schedule));
+  memset(passwd, 0, sizeof(passwd));
+  result = Curl_base64_encode(conn->data, (char *)tktcopy.dat, tktcopy.length,
+                              &p, &base64_sz);
+  if(result) {
+    Curl_failf(conn->data, "base64-encoding: %s", curl_easy_strerror(result));
+    krb4_set_command_prot(conn, save);
+    return result;
+  }
+  memset (tktcopy.dat, 0, tktcopy.length);
+
+  result = Curl_ftpsendf(conn, "SITE KAUTH %s %s", name, p);
+  free(p);
+  if(result)
+    return result;
+
+  result = Curl_GetFTPResponse(&nread, conn, NULL);
+  if(result)
+    return result;
+  krb4_set_command_prot(conn, save);
+
+  return CURLE_OK;
+}
+
+#endif /* HAVE_KRB4 */
+#endif /* CURL_DISABLE_FTP */
diff --git a/lib/curl_krb5.c b/lib/curl_krb5.c
new file mode 100644 (file)
index 0000000..d793fef
--- /dev/null
@@ -0,0 +1,341 @@
+/* GSSAPI/krb5 support for FTP - loosely based on old curl_krb4.c
+ *
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * Copyright (c) 2004 - 2013 Daniel Stenberg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.  */
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_FTP
+#ifdef HAVE_GSSAPI
+
+#ifdef HAVE_OLD_GSSMIT
+#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
+#define NCOMPAT 1
+#endif
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#include "curl_urldata.h"
+#include "curl_base64.h"
+#include "curl_ftp.h"
+#include "curl_gssapi.h"
+#include "curl_sendf.h"
+#include "curl_krb4.h"
+#include "curl_memory.h"
+#include "curl_warnless.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+#define LOCAL_ADDR (&conn->local_addr)
+#define REMOTE_ADDR conn->ip_addr->ai_addr
+
+static int
+krb5_init(void *app_data)
+{
+  gss_ctx_id_t *context = app_data;
+  /* Make sure our context is initialized for krb5_end. */
+  *context = GSS_C_NO_CONTEXT;
+  return 0;
+}
+
+static int
+krb5_check_prot(void *app_data, int level)
+{
+  (void)app_data; /* unused */
+  if(level == PROT_CONFIDENTIAL)
+    return -1;
+  return 0;
+}
+
+static int
+krb5_decode(void *app_data, void *buf, int len,
+            int level UNUSED_PARAM,
+            struct connectdata *conn UNUSED_PARAM)
+{
+  gss_ctx_id_t *context = app_data;
+  OM_uint32 maj, min;
+  gss_buffer_desc enc, dec;
+
+  (void)level;
+  (void)conn;
+
+  enc.value = buf;
+  enc.length = len;
+  maj = gss_unseal(&min, *context, &enc, &dec, NULL, NULL);
+  if(maj != GSS_S_COMPLETE) {
+    if(len >= 4)
+      strcpy(buf, "599 ");
+    return -1;
+  }
+
+  memcpy(buf, dec.value, dec.length);
+  len = curlx_uztosi(dec.length);
+  gss_release_buffer(&min, &dec);
+
+  return len;
+}
+
+static int
+krb5_overhead(void *app_data, int level, int len)
+{
+  /* no arguments are used */
+  (void)app_data;
+  (void)level;
+  (void)len;
+  return 0;
+}
+
+static int
+krb5_encode(void *app_data, const void *from, int length, int level, void **to,
+            struct connectdata *conn UNUSED_PARAM)
+{
+  gss_ctx_id_t *context = app_data;
+  gss_buffer_desc dec, enc;
+  OM_uint32 maj, min;
+  int state;
+  int len;
+
+  /* shut gcc up */
+  conn = NULL;
+
+  /* NOTE that the cast is safe, neither of the krb5, gnu gss and heimdal
+   * libraries modify the input buffer in gss_seal()
+   */
+  dec.value = (void*)from;
+  dec.length = length;
+  maj = gss_seal(&min, *context,
+                 level == PROT_PRIVATE,
+                 GSS_C_QOP_DEFAULT,
+                 &dec, &state, &enc);
+
+  if(maj != GSS_S_COMPLETE)
+    return -1;
+
+  /* malloc a new buffer, in case gss_release_buffer doesn't work as
+     expected */
+  *to = malloc(enc.length);
+  if(!*to)
+    return -1;
+  memcpy(*to, enc.value, enc.length);
+  len = curlx_uztosi(enc.length);
+  gss_release_buffer(&min, &enc);
+  return len;
+}
+
+static int
+krb5_auth(void *app_data, struct connectdata *conn)
+{
+  int ret = AUTH_OK;
+  char *p;
+  const char *host = conn->host.name;
+  ssize_t nread;
+  curl_socklen_t l = sizeof(conn->local_addr);
+  struct SessionHandle *data = conn->data;
+  CURLcode result;
+  const char *service = "ftp", *srv_host = "host";
+  gss_buffer_desc input_buffer, output_buffer, _gssresp, *gssresp;
+  OM_uint32 maj, min;
+  gss_name_t gssname;
+  gss_ctx_id_t *context = app_data;
+  struct gss_channel_bindings_struct chan;
+  size_t base64_sz = 0;
+
+  if(getsockname(conn->sock[FIRSTSOCKET],
+                 (struct sockaddr *)LOCAL_ADDR, &l) < 0)
+    perror("getsockname()");
+
+  chan.initiator_addrtype = GSS_C_AF_INET;
+  chan.initiator_address.length = l - 4;
+  chan.initiator_address.value =
+    &((struct sockaddr_in *)LOCAL_ADDR)->sin_addr.s_addr;
+  chan.acceptor_addrtype = GSS_C_AF_INET;
+  chan.acceptor_address.length = l - 4;
+  chan.acceptor_address.value =
+    &((struct sockaddr_in *)REMOTE_ADDR)->sin_addr.s_addr;
+  chan.application_data.length = 0;
+  chan.application_data.value = NULL;
+
+  /* this loop will execute twice (once for service, once for host) */
+  for(;;) {
+    /* this really shouldn't be repeated here, but can't help it */
+    if(service == srv_host) {
+      result = Curl_ftpsendf(conn, "AUTH GSSAPI");
+
+      if(result)
+        return -2;
+      if(Curl_GetFTPResponse(&nread, conn, NULL))
+        return -1;
+
+      if(data->state.buffer[0] != '3')
+        return -1;
+    }
+
+    input_buffer.value = data->state.buffer;
+    input_buffer.length = snprintf(input_buffer.value, BUFSIZE, "%s@%s",
+                                   service, host);
+    maj = gss_import_name(&min, &input_buffer, GSS_C_NT_HOSTBASED_SERVICE,
+                          &gssname);
+    if(maj != GSS_S_COMPLETE) {
+      gss_release_name(&min, &gssname);
+      if(service == srv_host) {
+        Curl_failf(data, "Error importing service name %s",
+                   input_buffer.value);
+        return AUTH_ERROR;
+      }
+      service = srv_host;
+      continue;
+    }
+    /* We pass NULL as |output_name_type| to avoid a leak. */
+    gss_display_name(&min, gssname, &output_buffer, NULL);
+    Curl_infof(data, "Trying against %s\n", output_buffer.value);
+    gssresp = GSS_C_NO_BUFFER;
+    *context = GSS_C_NO_CONTEXT;
+
+    do {
+      /* Release the buffer at each iteration to avoid leaking: the first time
+         we are releasing the memory from gss_display_name. The last item is
+         taken care by a final gss_release_buffer. */
+      gss_release_buffer(&min, &output_buffer);
+      ret = AUTH_OK;
+      maj = Curl_gss_init_sec_context(data,
+                                      &min,
+                                      context,
+                                      gssname,
+                                      &chan,
+                                      gssresp,
+                                      &output_buffer,
+                                      NULL);
+
+      if(gssresp) {
+        free(_gssresp.value);
+        gssresp = NULL;
+      }
+
+      if(GSS_ERROR(maj)) {
+        Curl_infof(data, "Error creating security context\n");
+        ret = AUTH_ERROR;
+        break;
+      }
+
+      if(output_buffer.length != 0) {
+        result = Curl_base64_encode(data, (char *)output_buffer.value,
+                                    output_buffer.length, &p, &base64_sz);
+        if(result) {
+          Curl_infof(data,"base64-encoding: %s\n", curl_easy_strerror(result));
+          ret = AUTH_CONTINUE;
+          break;
+        }
+
+        result = Curl_ftpsendf(conn, "ADAT %s", p);
+
+        free(p);
+
+        if(result) {
+          ret = -2;
+          break;
+        }
+
+        if(Curl_GetFTPResponse(&nread, conn, NULL)) {
+          ret = -1;
+          break;
+        }
+
+        if(data->state.buffer[0] != '2' && data->state.buffer[0] != '3') {
+          Curl_infof(data, "Server didn't accept auth data\n");
+          ret = AUTH_ERROR;
+          break;
+        }
+
+        p = data->state.buffer + 4;
+        p = strstr(p, "ADAT=");
+        if(p) {
+          result = Curl_base64_decode(p + 5,
+                                      (unsigned char **)&_gssresp.value,
+                                      &_gssresp.length);
+          if(result) {
+            Curl_failf(data,"base64-decoding: %s", curl_easy_strerror(result));
+            ret = AUTH_CONTINUE;
+            break;
+          }
+        }
+
+        gssresp = &_gssresp;
+      }
+    } while(maj == GSS_S_CONTINUE_NEEDED);
+
+    gss_release_name(&min, &gssname);
+    gss_release_buffer(&min, &output_buffer);
+
+    if(gssresp)
+      free(_gssresp.value);
+
+    if(ret == AUTH_OK || service == srv_host)
+      return ret;
+
+    service = srv_host;
+  }
+  return ret;
+}
+
+static void krb5_end(void *app_data)
+{
+    OM_uint32 min;
+    gss_ctx_id_t *context = app_data;
+    if(*context != GSS_C_NO_CONTEXT) {
+#ifdef DEBUGBUILD
+      OM_uint32 maj =
+#endif
+      gss_delete_sec_context(&min, context, GSS_C_NO_BUFFER);
+      DEBUGASSERT(maj == GSS_S_COMPLETE);
+    }
+}
+
+struct Curl_sec_client_mech Curl_krb5_client_mech = {
+    "GSSAPI",
+    sizeof(gss_ctx_id_t),
+    krb5_init,
+    krb5_auth,
+    krb5_end,
+    krb5_check_prot,
+    krb5_overhead,
+    krb5_encode,
+    krb5_decode
+};
+
+#endif /* HAVE_GSSAPI */
+#endif /* CURL_DISABLE_FTP */
diff --git a/lib/curl_ldap.c b/lib/curl_ldap.c
new file mode 100644 (file)
index 0000000..59f3b83
--- /dev/null
@@ -0,0 +1,725 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_LDAP) && !defined(USE_OPENLDAP)
+
+/*
+ * Notice that USE_OPENLDAP is only a source code selection switch. When
+ * libcurl is built with USE_OPENLDAP defined the libcurl source code that
+ * gets compiled is the code from curl_openldap.c, otherwise the code that
+ * gets compiled is the code from curl_ldap.c.
+ *
+ * When USE_OPENLDAP is defined a recent version of the OpenLDAP library
+ * might be required for compilation and runtime. In order to use ancient
+ * OpenLDAP library versions, USE_OPENLDAP shall not be defined.
+ */
+
+#ifdef CURL_LDAP_WIN            /* Use Windows LDAP implementation. */
+# include <winldap.h>
+# ifndef LDAP_VENDOR_NAME
+#  error Your Platform SDK is NOT sufficient for LDAP support! \
+         Update your Platform SDK, or disable LDAP support!
+# else
+#  include <winber.h>
+# endif
+#else
+# define LDAP_DEPRECATED 1      /* Be sure ldap_init() is defined. */
+# ifdef HAVE_LBER_H
+#  include <lber.h>
+# endif
+# include <ldap.h>
+# if (defined(HAVE_LDAP_SSL) && defined(HAVE_LDAP_SSL_H))
+#  include <ldap_ssl.h>
+# endif /* HAVE_LDAP_SSL && HAVE_LDAP_SSL_H */
+#endif
+
+#include "curl_urldata.h"
+#include <curl/curl.h>
+#include "curl_sendf.h"
+#include "curl_escape.h"
+#include "curl_progress.h"
+#include "curl_transfer.h"
+#include "curl_strequal.h"
+#include "curl_strtok.h"
+#include "curl_ldap.h"
+#include "curl_memory.h"
+#include "curl_base64.h"
+#include "curl_rawstr.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memdebug.h"
+
+#ifndef HAVE_LDAP_URL_PARSE
+
+/* Use our own implementation. */
+
+typedef struct {
+    char   *lud_host;
+    int     lud_port;
+    char   *lud_dn;
+    char  **lud_attrs;
+    int     lud_scope;
+    char   *lud_filter;
+    char  **lud_exts;
+} CURL_LDAPURLDesc;
+
+#undef LDAPURLDesc
+#define LDAPURLDesc             CURL_LDAPURLDesc
+
+static int  _ldap_url_parse (const struct connectdata *conn,
+                             LDAPURLDesc **ludp);
+static void _ldap_free_urldesc (LDAPURLDesc *ludp);
+
+#undef ldap_free_urldesc
+#define ldap_free_urldesc       _ldap_free_urldesc
+#endif
+
+#ifdef DEBUG_LDAP
+  #define LDAP_TRACE(x)   do { \
+                            _ldap_trace ("%u: ", __LINE__); \
+                            _ldap_trace x; \
+                          } WHILE_FALSE
+
+  static void _ldap_trace (const char *fmt, ...);
+#else
+  #define LDAP_TRACE(x)   Curl_nop_stmt
+#endif
+
+
+static CURLcode Curl_ldap(struct connectdata *conn, bool *done);
+
+/*
+ * LDAP protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_ldap = {
+  "LDAP",                               /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  Curl_ldap,                            /* do_it */
+  ZERO_NULL,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  ZERO_NULL,                            /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  PORT_LDAP,                            /* defport */
+  CURLPROTO_LDAP,                       /* protocol */
+  PROTOPT_NONE                          /* flags */
+};
+
+#ifdef HAVE_LDAP_SSL
+/*
+ * LDAPS protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_ldaps = {
+  "LDAPS",                              /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  Curl_ldap,                            /* do_it */
+  ZERO_NULL,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  ZERO_NULL,                            /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  PORT_LDAPS,                           /* defport */
+  CURLPROTO_LDAP | CURLPROTO_LDAPS,     /* protocol */
+  PROTOPT_SSL                           /* flags */
+};
+#endif
+
+
+static CURLcode Curl_ldap(struct connectdata *conn, bool *done)
+{
+  CURLcode status = CURLE_OK;
+  int rc = 0;
+  LDAP *server = NULL;
+  LDAPURLDesc *ludp = NULL;
+  LDAPMessage *result = NULL;
+  LDAPMessage *entryIterator;
+  int num = 0;
+  struct SessionHandle *data=conn->data;
+  int ldap_proto = LDAP_VERSION3;
+  int ldap_ssl = 0;
+  char *val_b64 = NULL;
+  size_t val_b64_sz = 0;
+  curl_off_t dlsize = 0;
+#ifdef LDAP_OPT_NETWORK_TIMEOUT
+  struct timeval ldap_timeout = {10,0}; /* 10 sec connection/search timeout */
+#endif
+
+  *done = TRUE; /* unconditionally */
+  infof(data, "LDAP local: LDAP Vendor = %s ; LDAP Version = %d\n",
+          LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION);
+  infof(data, "LDAP local: %s\n", data->change.url);
+
+#ifdef HAVE_LDAP_URL_PARSE
+  rc = ldap_url_parse(data->change.url, &ludp);
+#else
+  rc = _ldap_url_parse(conn, &ludp);
+#endif
+  if(rc != 0) {
+    failf(data, "LDAP local: %s", ldap_err2string(rc));
+    status = CURLE_LDAP_INVALID_URL;
+    goto quit;
+  }
+
+  /* Get the URL scheme ( either ldap or ldaps ) */
+  if(conn->given->flags & PROTOPT_SSL)
+    ldap_ssl = 1;
+  infof(data, "LDAP local: trying to establish %s connection\n",
+          ldap_ssl ? "encrypted" : "cleartext");
+
+#ifdef LDAP_OPT_NETWORK_TIMEOUT
+  ldap_set_option(NULL, LDAP_OPT_NETWORK_TIMEOUT, &ldap_timeout);
+#endif
+  ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
+
+  if(ldap_ssl) {
+#ifdef HAVE_LDAP_SSL
+#ifdef CURL_LDAP_WIN
+    /* Win32 LDAP SDK doesn't support insecure mode without CA! */
+    server = ldap_sslinit(conn->host.name, (int)conn->port, 1);
+    ldap_set_option(server, LDAP_OPT_SSL, LDAP_OPT_ON);
+#else
+    int ldap_option;
+    char* ldap_ca = data->set.str[STRING_SSL_CAFILE];
+#if defined(CURL_HAS_NOVELL_LDAPSDK)
+    rc = ldapssl_client_init(NULL, NULL);
+    if(rc != LDAP_SUCCESS) {
+      failf(data, "LDAP local: ldapssl_client_init %s", ldap_err2string(rc));
+      status = CURLE_SSL_CERTPROBLEM;
+      goto quit;
+    }
+    if(data->set.ssl.verifypeer) {
+      /* Novell SDK supports DER or BASE64 files. */
+      int cert_type = LDAPSSL_CERT_FILETYPE_B64;
+      if((data->set.str[STRING_CERT_TYPE]) &&
+         (Curl_raw_equal(data->set.str[STRING_CERT_TYPE], "DER")))
+        cert_type = LDAPSSL_CERT_FILETYPE_DER;
+      if(!ldap_ca) {
+        failf(data, "LDAP local: ERROR %s CA cert not set!",
+              (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"));
+        status = CURLE_SSL_CERTPROBLEM;
+        goto quit;
+      }
+      infof(data, "LDAP local: using %s CA cert '%s'\n",
+              (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"),
+              ldap_ca);
+      rc = ldapssl_add_trusted_cert(ldap_ca, cert_type);
+      if(rc != LDAP_SUCCESS) {
+        failf(data, "LDAP local: ERROR setting %s CA cert: %s",
+                (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"),
+                ldap_err2string(rc));
+        status = CURLE_SSL_CERTPROBLEM;
+        goto quit;
+      }
+      ldap_option = LDAPSSL_VERIFY_SERVER;
+    }
+    else
+      ldap_option = LDAPSSL_VERIFY_NONE;
+    rc = ldapssl_set_verify_mode(ldap_option);
+    if(rc != LDAP_SUCCESS) {
+      failf(data, "LDAP local: ERROR setting cert verify mode: %s",
+              ldap_err2string(rc));
+      status = CURLE_SSL_CERTPROBLEM;
+      goto quit;
+    }
+    server = ldapssl_init(conn->host.name, (int)conn->port, 1);
+    if(server == NULL) {
+      failf(data, "LDAP local: Cannot connect to %s:%hu",
+              conn->host.name, conn->port);
+      status = CURLE_COULDNT_CONNECT;
+      goto quit;
+    }
+#elif defined(LDAP_OPT_X_TLS)
+    if(data->set.ssl.verifypeer) {
+      /* OpenLDAP SDK supports BASE64 files. */
+      if((data->set.str[STRING_CERT_TYPE]) &&
+         (!Curl_raw_equal(data->set.str[STRING_CERT_TYPE], "PEM"))) {
+        failf(data, "LDAP local: ERROR OpenLDAP only supports PEM cert-type!");
+        status = CURLE_SSL_CERTPROBLEM;
+        goto quit;
+      }
+      if(!ldap_ca) {
+        failf(data, "LDAP local: ERROR PEM CA cert not set!");
+        status = CURLE_SSL_CERTPROBLEM;
+        goto quit;
+      }
+      infof(data, "LDAP local: using PEM CA cert: %s\n", ldap_ca);
+      rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, ldap_ca);
+      if(rc != LDAP_SUCCESS) {
+        failf(data, "LDAP local: ERROR setting PEM CA cert: %s",
+                ldap_err2string(rc));
+        status = CURLE_SSL_CERTPROBLEM;
+        goto quit;
+      }
+      ldap_option = LDAP_OPT_X_TLS_DEMAND;
+    }
+    else
+      ldap_option = LDAP_OPT_X_TLS_NEVER;
+
+    rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &ldap_option);
+    if(rc != LDAP_SUCCESS) {
+      failf(data, "LDAP local: ERROR setting cert verify mode: %s",
+              ldap_err2string(rc));
+      status = CURLE_SSL_CERTPROBLEM;
+      goto quit;
+    }
+    server = ldap_init(conn->host.name, (int)conn->port);
+    if(server == NULL) {
+      failf(data, "LDAP local: Cannot connect to %s:%hu",
+              conn->host.name, conn->port);
+      status = CURLE_COULDNT_CONNECT;
+      goto quit;
+    }
+    ldap_option = LDAP_OPT_X_TLS_HARD;
+    rc = ldap_set_option(server, LDAP_OPT_X_TLS, &ldap_option);
+    if(rc != LDAP_SUCCESS) {
+      failf(data, "LDAP local: ERROR setting SSL/TLS mode: %s",
+              ldap_err2string(rc));
+      status = CURLE_SSL_CERTPROBLEM;
+      goto quit;
+    }
+/*
+    rc = ldap_start_tls_s(server, NULL, NULL);
+    if(rc != LDAP_SUCCESS) {
+      failf(data, "LDAP local: ERROR starting SSL/TLS mode: %s",
+              ldap_err2string(rc));
+      status = CURLE_SSL_CERTPROBLEM;
+      goto quit;
+    }
+*/
+#else
+    /* we should probably never come up to here since configure
+       should check in first place if we can support LDAP SSL/TLS */
+    failf(data, "LDAP local: SSL/TLS not supported with this version "
+            "of the OpenLDAP toolkit\n");
+    status = CURLE_SSL_CERTPROBLEM;
+    goto quit;
+#endif
+#endif
+#endif /* CURL_LDAP_USE_SSL */
+  }
+  else {
+    server = ldap_init(conn->host.name, (int)conn->port);
+    if(server == NULL) {
+      failf(data, "LDAP local: Cannot connect to %s:%hu",
+              conn->host.name, conn->port);
+      status = CURLE_COULDNT_CONNECT;
+      goto quit;
+    }
+  }
+#ifdef CURL_LDAP_WIN
+  ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
+#endif
+
+  rc = ldap_simple_bind_s(server,
+                          conn->bits.user_passwd ? conn->user : NULL,
+                          conn->bits.user_passwd ? conn->passwd : NULL);
+  if(!ldap_ssl && rc != 0) {
+    ldap_proto = LDAP_VERSION2;
+    ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
+    rc = ldap_simple_bind_s(server,
+                            conn->bits.user_passwd ? conn->user : NULL,
+                            conn->bits.user_passwd ? conn->passwd : NULL);
+  }
+  if(rc != 0) {
+    failf(data, "LDAP local: ldap_simple_bind_s %s", ldap_err2string(rc));
+    status = CURLE_LDAP_CANNOT_BIND;
+    goto quit;
+  }
+
+  rc = ldap_search_s(server, ludp->lud_dn, ludp->lud_scope,
+                     ludp->lud_filter, ludp->lud_attrs, 0, &result);
+
+  if(rc != 0 && rc != LDAP_SIZELIMIT_EXCEEDED) {
+    failf(data, "LDAP remote: %s", ldap_err2string(rc));
+    status = CURLE_LDAP_SEARCH_FAILED;
+    goto quit;
+  }
+
+  for(num = 0, entryIterator = ldap_first_entry(server, result);
+      entryIterator;
+      entryIterator = ldap_next_entry(server, entryIterator), num++) {
+    BerElement *ber = NULL;
+    char  *attribute;       /*! suspicious that this isn't 'const' */
+    char  *dn = ldap_get_dn(server, entryIterator);
+    int i;
+
+    Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"DN: ", 4);
+    Curl_client_write(conn, CLIENTWRITE_BODY, (char *)dn, 0);
+    Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
+
+    dlsize += strlen(dn)+5;
+
+    for(attribute = ldap_first_attribute(server, entryIterator, &ber);
+        attribute;
+        attribute = ldap_next_attribute(server, entryIterator, ber)) {
+      BerValue **vals = ldap_get_values_len(server, entryIterator, attribute);
+
+      if(vals != NULL) {
+        for(i = 0; (vals[i] != NULL); i++) {
+          Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1);
+          Curl_client_write(conn, CLIENTWRITE_BODY, (char *) attribute, 0);
+          Curl_client_write(conn, CLIENTWRITE_BODY, (char *)": ", 2);
+          dlsize += strlen(attribute)+3;
+
+          if((strlen(attribute) > 7) &&
+              (strcmp(";binary",
+                      (char *)attribute +
+                      (strlen((char *)attribute) - 7)) == 0)) {
+            /* Binary attribute, encode to base64. */
+            CURLcode error = Curl_base64_encode(data,
+                                                vals[i]->bv_val,
+                                                vals[i]->bv_len,
+                                                &val_b64,
+                                                &val_b64_sz);
+            if(error) {
+              ldap_value_free_len(vals);
+              ldap_memfree(attribute);
+              ldap_memfree(dn);
+              if(ber)
+                ber_free(ber, 0);
+              status = error;
+              goto quit;
+            }
+            if(val_b64_sz > 0) {
+              Curl_client_write(conn, CLIENTWRITE_BODY, val_b64, val_b64_sz);
+              free(val_b64);
+              dlsize += val_b64_sz;
+            }
+          }
+          else {
+            Curl_client_write(conn, CLIENTWRITE_BODY, vals[i]->bv_val,
+                              vals[i]->bv_len);
+            dlsize += vals[i]->bv_len;
+          }
+          Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
+          dlsize++;
+        }
+
+        /* Free memory used to store values */
+        ldap_value_free_len(vals);
+      }
+      Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
+      dlsize++;
+      Curl_pgrsSetDownloadCounter(data, dlsize);
+      ldap_memfree(attribute);
+    }
+    ldap_memfree(dn);
+    if(ber)
+       ber_free(ber, 0);
+  }
+
+quit:
+  if(result) {
+    ldap_msgfree(result);
+    LDAP_TRACE (("Received %d entries\n", num));
+  }
+  if(rc == LDAP_SIZELIMIT_EXCEEDED)
+    infof(data, "There are more than %d entries\n", num);
+  if(ludp)
+    ldap_free_urldesc(ludp);
+  if(server)
+    ldap_unbind_s(server);
+#if defined(HAVE_LDAP_SSL) && defined(CURL_HAS_NOVELL_LDAPSDK)
+  if(ldap_ssl)
+    ldapssl_client_deinit();
+#endif /* HAVE_LDAP_SSL && CURL_HAS_NOVELL_LDAPSDK */
+
+  /* no data to transfer */
+  Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+  conn->bits.close = TRUE;
+
+  return status;
+}
+
+#ifdef DEBUG_LDAP
+static void _ldap_trace (const char *fmt, ...)
+{
+  static int do_trace = -1;
+  va_list args;
+
+  if(do_trace == -1) {
+    const char *env = getenv("CURL_TRACE");
+    do_trace = (env && strtol(env, NULL, 10) > 0);
+  }
+  if(!do_trace)
+    return;
+
+  va_start (args, fmt);
+  vfprintf (stderr, fmt, args);
+  va_end (args);
+}
+#endif
+
+#ifndef HAVE_LDAP_URL_PARSE
+
+/*
+ * Return scope-value for a scope-string.
+ */
+static int str2scope (const char *p)
+{
+  if(strequal(p, "one"))
+     return LDAP_SCOPE_ONELEVEL;
+  if(strequal(p, "onetree"))
+     return LDAP_SCOPE_ONELEVEL;
+  if(strequal(p, "base"))
+     return LDAP_SCOPE_BASE;
+  if(strequal(p, "sub"))
+     return LDAP_SCOPE_SUBTREE;
+  if(strequal( p, "subtree"))
+     return LDAP_SCOPE_SUBTREE;
+  return (-1);
+}
+
+/*
+ * Split 'str' into strings separated by commas.
+ * Note: res[] points into 'str'.
+ */
+static char **split_str (char *str)
+{
+  char **res, *lasts, *s;
+  int  i;
+
+  for(i = 2, s = strchr(str,','); s; i++)
+    s = strchr(++s,',');
+
+  res = calloc(i, sizeof(char*));
+  if(!res)
+    return NULL;
+
+  for(i = 0, s = strtok_r(str, ",", &lasts); s;
+      s = strtok_r(NULL, ",", &lasts), i++)
+    res[i] = s;
+  return res;
+}
+
+/*
+ * Unescape the LDAP-URL components
+ */
+static bool unescape_elements (void *data, LDAPURLDesc *ludp)
+{
+  int i;
+
+  if(ludp->lud_filter) {
+    ludp->lud_filter = curl_easy_unescape(data, ludp->lud_filter, 0, NULL);
+    if(!ludp->lud_filter)
+       return (FALSE);
+  }
+
+  for(i = 0; ludp->lud_attrs && ludp->lud_attrs[i]; i++) {
+    ludp->lud_attrs[i] = curl_easy_unescape(data, ludp->lud_attrs[i], 0, NULL);
+    if(!ludp->lud_attrs[i])
+      return (FALSE);
+  }
+
+  for(i = 0; ludp->lud_exts && ludp->lud_exts[i]; i++) {
+    ludp->lud_exts[i] = curl_easy_unescape(data, ludp->lud_exts[i], 0, NULL);
+    if(!ludp->lud_exts[i])
+      return (FALSE);
+  }
+
+  if(ludp->lud_dn) {
+    char *dn = ludp->lud_dn;
+    char *new_dn = curl_easy_unescape(data, dn, 0, NULL);
+
+    free(dn);
+    ludp->lud_dn = new_dn;
+    if(!new_dn)
+       return (FALSE);
+  }
+  return (TRUE);
+}
+
+/*
+ * Break apart the pieces of an LDAP URL.
+ * Syntax:
+ *   ldap://<hostname>:<port>/<base_dn>?<attributes>?<scope>?<filter>?<ext>
+ *
+ * <hostname> already known from 'conn->host.name'.
+ * <port>     already known from 'conn->remote_port'.
+ * extract the rest from 'conn->data->state.path+1'. All fields are optional.
+ * e.g.
+ *   ldap://<hostname>:<port>/?<attributes>?<scope>?<filter>
+ * yields ludp->lud_dn = "".
+ *
+ * Defined in RFC4516 section 2.
+ */
+static int _ldap_url_parse2 (const struct connectdata *conn, LDAPURLDesc *ludp)
+{
+  char *p, *q;
+  int i;
+
+  if(!conn->data ||
+      !conn->data->state.path ||
+      conn->data->state.path[0] != '/' ||
+      !checkprefix("LDAP", conn->data->change.url))
+    return LDAP_INVALID_SYNTAX;
+
+  ludp->lud_scope = LDAP_SCOPE_BASE;
+  ludp->lud_port  = conn->remote_port;
+  ludp->lud_host  = conn->host.name;
+
+  /* parse DN (Distinguished Name).
+   */
+  ludp->lud_dn = strdup(conn->data->state.path+1);
+  if(!ludp->lud_dn)
+    return LDAP_NO_MEMORY;
+
+  p = strchr(ludp->lud_dn, '?');
+  LDAP_TRACE (("DN '%.*s'\n", p ? (size_t)(p-ludp->lud_dn) :
+               strlen(ludp->lud_dn), ludp->lud_dn));
+
+  if(!p)
+    goto success;
+
+  *p++ = '\0';
+
+  /* parse attributes. skip "??".
+   */
+  q = strchr(p, '?');
+  if(q)
+    *q++ = '\0';
+
+  if(*p && *p != '?') {
+    ludp->lud_attrs = split_str(p);
+    if(!ludp->lud_attrs)
+      return LDAP_NO_MEMORY;
+
+    for(i = 0; ludp->lud_attrs[i]; i++)
+      LDAP_TRACE (("attr[%d] '%s'\n", i, ludp->lud_attrs[i]));
+  }
+
+  p = q;
+  if(!p)
+    goto success;
+
+  /* parse scope. skip "??"
+   */
+  q = strchr(p, '?');
+  if(q)
+    *q++ = '\0';
+
+  if(*p && *p != '?') {
+    ludp->lud_scope = str2scope(p);
+    if(ludp->lud_scope == -1)
+      return LDAP_INVALID_SYNTAX;
+    LDAP_TRACE (("scope %d\n", ludp->lud_scope));
+  }
+
+  p = q;
+  if(!p)
+    goto success;
+
+  /* parse filter
+   */
+  q = strchr(p, '?');
+  if(q)
+    *q++ = '\0';
+  if(!*p)
+    return LDAP_INVALID_SYNTAX;
+
+  ludp->lud_filter = p;
+  LDAP_TRACE (("filter '%s'\n", ludp->lud_filter));
+
+  p = q;
+  if(!p)
+    goto success;
+
+  /* parse extensions
+   */
+  ludp->lud_exts = split_str(p);
+  if(!ludp->lud_exts)
+    return LDAP_NO_MEMORY;
+
+  for(i = 0; ludp->lud_exts[i]; i++)
+    LDAP_TRACE (("exts[%d] '%s'\n", i, ludp->lud_exts[i]));
+
+  success:
+  if(!unescape_elements(conn->data, ludp))
+    return LDAP_NO_MEMORY;
+  return LDAP_SUCCESS;
+}
+
+static int _ldap_url_parse (const struct connectdata *conn,
+                            LDAPURLDesc **ludpp)
+{
+  LDAPURLDesc *ludp = calloc(1, sizeof(*ludp));
+  int rc;
+
+  *ludpp = NULL;
+  if(!ludp)
+     return LDAP_NO_MEMORY;
+
+  rc = _ldap_url_parse2 (conn, ludp);
+  if(rc != LDAP_SUCCESS) {
+    _ldap_free_urldesc(ludp);
+    ludp = NULL;
+  }
+  *ludpp = ludp;
+  return (rc);
+}
+
+static void _ldap_free_urldesc (LDAPURLDesc *ludp)
+{
+  int i;
+
+  if(!ludp)
+    return;
+
+  if(ludp->lud_dn)
+    free(ludp->lud_dn);
+
+  if(ludp->lud_filter)
+    free(ludp->lud_filter);
+
+  if(ludp->lud_attrs) {
+    for(i = 0; ludp->lud_attrs[i]; i++)
+      free(ludp->lud_attrs[i]);
+    free(ludp->lud_attrs);
+  }
+
+  if(ludp->lud_exts) {
+    for(i = 0; ludp->lud_exts[i]; i++)
+      free(ludp->lud_exts[i]);
+    free(ludp->lud_exts);
+  }
+  free (ludp);
+}
+#endif  /* !HAVE_LDAP_URL_PARSE */
+#endif  /* !CURL_DISABLE_LDAP && !USE_OPENLDAP */
diff --git a/lib/curl_llist.c b/lib/curl_llist.c
new file mode 100644 (file)
index 0000000..46a8d99
--- /dev/null
@@ -0,0 +1,212 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include "curl_llist.h"
+#include "curl_memory.h"
+
+/* this must be the last include file */
+#include "curl_memdebug.h"
+
+/*
+ * @unittest: 1300
+ */
+static void
+llist_init(struct curl_llist *l, curl_llist_dtor dtor)
+{
+  l->size = 0;
+  l->dtor = dtor;
+  l->head = NULL;
+  l->tail = NULL;
+}
+
+struct curl_llist *
+Curl_llist_alloc(curl_llist_dtor dtor)
+{
+  struct curl_llist *list;
+
+  list = malloc(sizeof(struct curl_llist));
+  if(!list)
+    return NULL;
+
+  llist_init(list, dtor);
+
+  return list;
+}
+
+/*
+ * Curl_llist_insert_next()
+ *
+ * Inserts a new list element after the given one 'e'. If the given existing
+ * entry is NULL and the list already has elements, the new one will be
+ * inserted first in the list.
+ *
+ * Returns: 1 on success and 0 on failure.
+ *
+ * @unittest: 1300
+ */
+int
+Curl_llist_insert_next(struct curl_llist *list, struct curl_llist_element *e,
+                       const void *p)
+{
+  struct curl_llist_element *ne = malloc(sizeof(struct curl_llist_element));
+  if(!ne)
+    return 0;
+
+  ne->ptr = (void *) p;
+  if(list->size == 0) {
+    list->head = ne;
+    list->head->prev = NULL;
+    list->head->next = NULL;
+    list->tail = ne;
+  }
+  else {
+    /* if 'e' is NULL here, we insert the new element first in the list */
+    ne->next = e?e->next:list->head;
+    ne->prev = e;
+    if(!e) {
+      list->head->prev = ne;
+      list->head = ne;
+    }
+    else if(e->next) {
+      e->next->prev = ne;
+    }
+    else {
+      list->tail = ne;
+    }
+    if(e)
+      e->next = ne;
+  }
+
+  ++list->size;
+
+  return 1;
+}
+
+/*
+ * @unittest: 1300
+ */
+int
+Curl_llist_remove(struct curl_llist *list, struct curl_llist_element *e,
+                  void *user)
+{
+  if(e == NULL || list->size == 0)
+    return 1;
+
+  if(e == list->head) {
+    list->head = e->next;
+
+    if(list->head == NULL)
+      list->tail = NULL;
+    else
+      e->next->prev = NULL;
+  }
+  else {
+    e->prev->next = e->next;
+    if(!e->next)
+      list->tail = e->prev;
+    else
+      e->next->prev = e->prev;
+  }
+
+  list->dtor(user, e->ptr);
+
+  e->ptr  = NULL;
+  e->prev = NULL;
+  e->next = NULL;
+
+  free(e);
+  --list->size;
+
+  return 1;
+}
+
+void
+Curl_llist_destroy(struct curl_llist *list, void *user)
+{
+  if(list) {
+    while(list->size > 0)
+      Curl_llist_remove(list, list->tail, user);
+
+    free(list);
+  }
+}
+
+size_t
+Curl_llist_count(struct curl_llist *list)
+{
+  return list->size;
+}
+
+/*
+ * @unittest: 1300
+ */
+int Curl_llist_move(struct curl_llist *list, struct curl_llist_element *e,
+                    struct curl_llist *to_list,
+                    struct curl_llist_element *to_e)
+{
+  /* Remove element from list */
+  if(e == NULL || list->size == 0)
+    return 0;
+
+  if(e == list->head) {
+    list->head = e->next;
+
+    if(list->head == NULL)
+      list->tail = NULL;
+    else
+      e->next->prev = NULL;
+  }
+  else {
+    e->prev->next = e->next;
+    if(!e->next)
+      list->tail = e->prev;
+    else
+      e->next->prev = e->prev;
+  }
+
+  --list->size;
+
+  /* Add element to to_list after to_e */
+  if(to_list->size == 0) {
+    to_list->head = e;
+    to_list->head->prev = NULL;
+    to_list->head->next = NULL;
+    to_list->tail = e;
+  }
+  else {
+    e->next = to_e->next;
+    e->prev = to_e;
+    if(to_e->next) {
+      to_e->next->prev = e;
+    }
+    else {
+      to_list->tail = e;
+    }
+    to_e->next = e;
+  }
+
+  ++to_list->size;
+
+  return 1;
+}
diff --git a/lib/curl_md4.c b/lib/curl_md4.c
new file mode 100644 (file)
index 0000000..d64b472
--- /dev/null
@@ -0,0 +1,282 @@
+/*-
+   Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved.
+
+   License to copy and use this software is granted provided that it
+   is identified as the "RSA Data Security, Inc. MD4 Message-Digest
+   Algorithm" in all material mentioning or referencing this software
+   or this function.
+
+   License is also granted to make and use derivative works provided
+   that such works are identified as "derived from the RSA Data
+   Security, Inc. MD4 Message-Digest Algorithm" in all material
+   mentioning or referencing the derived work.
+
+   RSA Data Security, Inc. makes no representations concerning either
+   the merchantability of this software or the suitability of this
+   software for any particular purpose. It is provided "as is"
+   without express or implied warranty of any kind.
+
+   These notices must be retained in any copies of any part of this
+   documentation and/or software.
+ */
+
+#include "curl_setup.h"
+
+/* NSS crypto library does not provide the MD4 hash algorithm, so that we have
+ * a local implementation of it */
+#ifdef USE_NSS
+
+#include "curl_md4.h"
+#include "curl_warnless.h"
+
+typedef unsigned int UINT4;
+
+typedef struct MD4Context {
+  UINT4 state[4];               /* state (ABCD) */
+  UINT4 count[2];               /* number of bits, modulo 2^64 (lsb first) */
+  unsigned char buffer[64];     /* input buffer */
+} MD4_CTX;
+
+/* Constants for MD4Transform routine.
+ */
+#define S11 3
+#define S12 7
+#define S13 11
+#define S14 19
+#define S21 3
+#define S22 5
+#define S23 9
+#define S24 13
+#define S31 3
+#define S32 9
+#define S33 11
+#define S34 15
+
+static void MD4Transform(UINT4 [4], const unsigned char [64]);
+static void Encode(unsigned char *, UINT4 *, unsigned int);
+static void Decode(UINT4 *, const unsigned char *, unsigned int);
+
+static unsigned char PADDING[64] = {
+  0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G and H are basic MD4 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG and HH are transformations for rounds 1, 2 and 3 */
+/* Rotation is separate from addition to prevent recomputation */
+#define FF(a, b, c, d, x, s) { \
+    (a) += F ((b), (c), (d)) + (x); \
+    (a) = ROTATE_LEFT ((a), (s)); \
+  }
+#define GG(a, b, c, d, x, s) { \
+    (a) += G ((b), (c), (d)) + (x) + (UINT4)0x5a827999; \
+    (a) = ROTATE_LEFT ((a), (s)); \
+  }
+#define HH(a, b, c, d, x, s) { \
+    (a) += H ((b), (c), (d)) + (x) + (UINT4)0x6ed9eba1; \
+    (a) = ROTATE_LEFT ((a), (s)); \
+  }
+
+/* MD4 initialization. Begins an MD4 operation, writing a new context.
+ */
+static void MD4Init(MD4_CTX *context)
+{
+  context->count[0] = context->count[1] = 0;
+
+  /* Load magic initialization constants.
+   */
+  context->state[0] = 0x67452301;
+  context->state[1] = 0xefcdab89;
+  context->state[2] = 0x98badcfe;
+  context->state[3] = 0x10325476;
+}
+
+/* MD4 block update operation. Continues an MD4 message-digest
+     operation, processing another message block, and updating the
+     context.
+ */
+static void MD4Update(MD4_CTX *context, const unsigned char *input,
+                      unsigned int inputLen)
+{
+  unsigned int i, bufindex, partLen;
+
+  /* Compute number of bytes mod 64 */
+  bufindex = (unsigned int)((context->count[0] >> 3) & 0x3F);
+  /* Update number of bits */
+  if((context->count[0] += ((UINT4)inputLen << 3))
+     < ((UINT4)inputLen << 3))
+    context->count[1]++;
+  context->count[1] += ((UINT4)inputLen >> 29);
+
+  partLen = 64 - bufindex;
+  /* Transform as many times as possible.
+   */
+  if(inputLen >= partLen) {
+    memcpy(&context->buffer[bufindex], input, partLen);
+    MD4Transform (context->state, context->buffer);
+
+    for(i = partLen; i + 63 < inputLen; i += 64)
+      MD4Transform (context->state, &input[i]);
+
+    bufindex = 0;
+  }
+  else
+    i = 0;
+
+  /* Buffer remaining input */
+  memcpy(&context->buffer[bufindex], &input[i], inputLen-i);
+}
+
+/* MD4 padding. */
+static void MD4Pad(MD4_CTX *context)
+{
+  unsigned char bits[8];
+  unsigned int bufindex, padLen;
+
+  /* Save number of bits */
+  Encode (bits, context->count, 8);
+
+  /* Pad out to 56 mod 64.
+   */
+  bufindex = (unsigned int)((context->count[0] >> 3) & 0x3f);
+  padLen = (bufindex < 56) ? (56 - bufindex) : (120 - bufindex);
+  MD4Update (context, PADDING, padLen);
+
+  /* Append length (before padding) */
+  MD4Update (context, bits, 8);
+}
+
+/* MD4 finalization. Ends an MD4 message-digest operation, writing the
+     the message digest and zeroizing the context.
+ */
+static void MD4Final (unsigned char digest[16], MD4_CTX *context)
+{
+  /* Do padding */
+  MD4Pad (context);
+
+  /* Store state in digest */
+  Encode (digest, context->state, 16);
+
+  /* Zeroize sensitive information.
+   */
+  memset(context, 0, sizeof(*context));
+}
+
+/* MD4 basic transformation. Transforms state based on block.
+ */
+static void MD4Transform (UINT4 state[4], const unsigned char block[64])
+{
+  UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+  Decode (x, block, 64);
+
+  /* Round 1 */
+  FF (a, b, c, d, x[ 0], S11); /* 1 */
+  FF (d, a, b, c, x[ 1], S12); /* 2 */
+  FF (c, d, a, b, x[ 2], S13); /* 3 */
+  FF (b, c, d, a, x[ 3], S14); /* 4 */
+  FF (a, b, c, d, x[ 4], S11); /* 5 */
+  FF (d, a, b, c, x[ 5], S12); /* 6 */
+  FF (c, d, a, b, x[ 6], S13); /* 7 */
+  FF (b, c, d, a, x[ 7], S14); /* 8 */
+  FF (a, b, c, d, x[ 8], S11); /* 9 */
+  FF (d, a, b, c, x[ 9], S12); /* 10 */
+  FF (c, d, a, b, x[10], S13); /* 11 */
+  FF (b, c, d, a, x[11], S14); /* 12 */
+  FF (a, b, c, d, x[12], S11); /* 13 */
+  FF (d, a, b, c, x[13], S12); /* 14 */
+  FF (c, d, a, b, x[14], S13); /* 15 */
+  FF (b, c, d, a, x[15], S14); /* 16 */
+
+  /* Round 2 */
+  GG (a, b, c, d, x[ 0], S21); /* 17 */
+  GG (d, a, b, c, x[ 4], S22); /* 18 */
+  GG (c, d, a, b, x[ 8], S23); /* 19 */
+  GG (b, c, d, a, x[12], S24); /* 20 */
+  GG (a, b, c, d, x[ 1], S21); /* 21 */
+  GG (d, a, b, c, x[ 5], S22); /* 22 */
+  GG (c, d, a, b, x[ 9], S23); /* 23 */
+  GG (b, c, d, a, x[13], S24); /* 24 */
+  GG (a, b, c, d, x[ 2], S21); /* 25 */
+  GG (d, a, b, c, x[ 6], S22); /* 26 */
+  GG (c, d, a, b, x[10], S23); /* 27 */
+  GG (b, c, d, a, x[14], S24); /* 28 */
+  GG (a, b, c, d, x[ 3], S21); /* 29 */
+  GG (d, a, b, c, x[ 7], S22); /* 30 */
+  GG (c, d, a, b, x[11], S23); /* 31 */
+  GG (b, c, d, a, x[15], S24); /* 32 */
+
+  /* Round 3 */
+  HH (a, b, c, d, x[ 0], S31); /* 33 */
+  HH (d, a, b, c, x[ 8], S32); /* 34 */
+  HH (c, d, a, b, x[ 4], S33); /* 35 */
+  HH (b, c, d, a, x[12], S34); /* 36 */
+  HH (a, b, c, d, x[ 2], S31); /* 37 */
+  HH (d, a, b, c, x[10], S32); /* 38 */
+  HH (c, d, a, b, x[ 6], S33); /* 39 */
+  HH (b, c, d, a, x[14], S34); /* 40 */
+  HH (a, b, c, d, x[ 1], S31); /* 41 */
+  HH (d, a, b, c, x[ 9], S32); /* 42 */
+  HH (c, d, a, b, x[ 5], S33); /* 43 */
+  HH (b, c, d, a, x[13], S34); /* 44 */
+  HH (a, b, c, d, x[ 3], S31); /* 45 */
+  HH (d, a, b, c, x[11], S32); /* 46 */
+  HH (c, d, a, b, x[ 7], S33); /* 47 */
+  HH (b, c, d, a, x[15], S34); /* 48 */
+
+  state[0] += a;
+  state[1] += b;
+  state[2] += c;
+  state[3] += d;
+
+  /* Zeroize sensitive information.
+   */
+  memset(x, 0, sizeof(x));
+}
+
+/* Encodes input (UINT4) into output (unsigned char). Assumes len is
+     a multiple of 4.
+ */
+static void Encode(unsigned char *output, UINT4 *input, unsigned int len)
+{
+  unsigned int i, j;
+
+  for(i = 0, j = 0; j < len; i++, j += 4) {
+    output[j] = (unsigned char)(input[i] & 0xff);
+    output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
+    output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
+    output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
+  }
+}
+
+/* Decodes input (unsigned char) into output (UINT4). Assumes len is
+     a multiple of 4.
+ */
+static void Decode (UINT4 *output, const unsigned char *input,
+                    unsigned int len)
+{
+  unsigned int i, j;
+
+  for(i = 0, j = 0; j < len; i++, j += 4)
+    output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
+      (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
+}
+
+void Curl_md4it(unsigned char *output, const unsigned char *input, size_t len)
+{
+  MD4_CTX ctx;
+  MD4Init(&ctx);
+  MD4Update(&ctx, input, curlx_uztoui(len));
+  MD4Final(output, &ctx);
+}
+#endif /* USE_NSS */
diff --git a/lib/curl_md5.c b/lib/curl_md5.c
new file mode 100644 (file)
index 0000000..74f53f6
--- /dev/null
@@ -0,0 +1,521 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+
+#include "curl_md5.h"
+#include "curl_hmac.h"
+#include "curl_warnless.h"
+
+#include "curl_memory.h"
+
+#if defined(USE_GNUTLS_NETTLE)
+
+#include <nettle/md5.h>
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+typedef struct md5_ctx MD5_CTX;
+
+static void MD5_Init(MD5_CTX * ctx)
+{
+  md5_init(ctx);
+}
+
+static void MD5_Update(MD5_CTX * ctx,
+                       const unsigned char * input,
+                       unsigned int inputLen)
+{
+  md5_update(ctx, inputLen, input);
+}
+
+static void MD5_Final(unsigned char digest[16], MD5_CTX * ctx)
+{
+  md5_digest(ctx, 16, digest);
+}
+
+#elif defined(USE_GNUTLS)
+
+#include <gcrypt.h>
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+typedef gcry_md_hd_t MD5_CTX;
+
+static void MD5_Init(MD5_CTX * ctx)
+{
+  gcry_md_open(ctx, GCRY_MD_MD5, 0);
+}
+
+static void MD5_Update(MD5_CTX * ctx,
+                       const unsigned char * input,
+                       unsigned int inputLen)
+{
+  gcry_md_write(*ctx, input, inputLen);
+}
+
+static void MD5_Final(unsigned char digest[16], MD5_CTX * ctx)
+{
+  memcpy(digest, gcry_md_read(*ctx, 0), 16);
+  gcry_md_close(*ctx);
+}
+
+#elif defined(USE_SSLEAY)
+/* When OpenSSL is available we use the MD5-function from OpenSSL */
+
+#  ifdef USE_OPENSSL
+#    include <openssl/md5.h>
+#  else
+#    include <md5.h>
+#  endif
+
+#elif defined(__MAC_10_4) || defined(__IPHONE_5_0)
+
+/* For Apple operating systems: CommonCrypto has the functions we need.
+   The library's headers are even backward-compatible with OpenSSL's
+   headers as long as we define COMMON_DIGEST_FOR_OPENSSL first.
+
+   These functions are available on Tiger and later, as well as iOS 5.0
+   and later. If you're building for an older cat, well, sorry. */
+#  define COMMON_DIGEST_FOR_OPENSSL
+#  include <CommonCrypto/CommonDigest.h>
+
+#elif defined(_WIN32)
+
+#include <wincrypt.h>
+
+typedef struct {
+  HCRYPTPROV hCryptProv;
+  HCRYPTHASH hHash;
+} MD5_CTX;
+
+static void MD5_Init(MD5_CTX *ctx)
+{
+  if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL,
+                         PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
+    CryptCreateHash(ctx->hCryptProv, CALG_MD5, 0, 0, &ctx->hHash);
+  }
+}
+
+static void MD5_Update(MD5_CTX *ctx,
+                       const unsigned char *input,
+                       unsigned int inputLen)
+{
+  CryptHashData(ctx->hHash, (unsigned char *)input, inputLen, 0);
+}
+
+static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx)
+{
+  unsigned long length;
+  CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0);
+  if(length == 16)
+    CryptGetHashParam(ctx->hHash, HP_HASHVAL, digest, &length, 0);
+  if(ctx->hHash)
+    CryptDestroyHash(ctx->hHash);
+  if(ctx->hCryptProv)
+    CryptReleaseContext(ctx->hCryptProv, 0);
+}
+
+#else
+/* When no other crypto library is available we use this code segment */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+ */
+
+/* UINT4 defines a four byte word */
+typedef unsigned int UINT4;
+
+/* MD5 context. */
+struct md5_ctx {
+  UINT4 state[4];                                   /* state (ABCD) */
+  UINT4 count[2];        /* number of bits, modulo 2^64 (lsb first) */
+  unsigned char buffer[64];                         /* input buffer */
+};
+
+typedef struct md5_ctx MD5_CTX;
+
+static void MD5_Init(struct md5_ctx *);
+static void MD5_Update(struct md5_ctx *, const unsigned char *, unsigned int);
+static void MD5_Final(unsigned char [16], struct md5_ctx *);
+
+/* Constants for MD5Transform routine.
+ */
+
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+static void MD5Transform(UINT4 [4], const unsigned char [64]);
+static void Encode(unsigned char *, UINT4 *, unsigned int);
+static void Decode(UINT4 *, const unsigned char *, unsigned int);
+
+static const unsigned char PADDING[64] = {
+  0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G, H and I are basic MD5 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+ (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define GG(a, b, c, d, x, s, ac) { \
+ (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define HH(a, b, c, d, x, s, ac) { \
+ (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define II(a, b, c, d, x, s, ac) { \
+ (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+
+/* MD5 initialization. Begins an MD5 operation, writing a new context.
+ */
+static void MD5_Init(struct md5_ctx *context)
+{
+  context->count[0] = context->count[1] = 0;
+  /* Load magic initialization constants. */
+  context->state[0] = 0x67452301;
+  context->state[1] = 0xefcdab89;
+  context->state[2] = 0x98badcfe;
+  context->state[3] = 0x10325476;
+}
+
+/* MD5 block update operation. Continues an MD5 message-digest
+  operation, processing another message block, and updating the
+  context.
+ */
+static void MD5_Update (struct md5_ctx *context,    /* context */
+                        const unsigned char *input, /* input block */
+                        unsigned int inputLen)      /* length of input block */
+{
+  unsigned int i, bufindex, partLen;
+
+  /* Compute number of bytes mod 64 */
+  bufindex = (unsigned int)((context->count[0] >> 3) & 0x3F);
+
+  /* Update number of bits */
+  if((context->count[0] += ((UINT4)inputLen << 3))
+      < ((UINT4)inputLen << 3))
+    context->count[1]++;
+  context->count[1] += ((UINT4)inputLen >> 29);
+
+  partLen = 64 - bufindex;
+
+  /* Transform as many times as possible. */
+  if(inputLen >= partLen) {
+    memcpy(&context->buffer[bufindex], input, partLen);
+    MD5Transform(context->state, context->buffer);
+
+    for(i = partLen; i + 63 < inputLen; i += 64)
+      MD5Transform(context->state, &input[i]);
+
+    bufindex = 0;
+  }
+  else
+    i = 0;
+
+  /* Buffer remaining input */
+  memcpy(&context->buffer[bufindex], &input[i], inputLen-i);
+}
+
+/* MD5 finalization. Ends an MD5 message-digest operation, writing the
+   the message digest and zeroizing the context.
+*/
+static void MD5_Final(unsigned char digest[16], /* message digest */
+                      struct md5_ctx *context) /* context */
+{
+  unsigned char bits[8];
+  unsigned int count, padLen;
+
+  /* Save number of bits */
+  Encode (bits, context->count, 8);
+
+  /* Pad out to 56 mod 64. */
+  count = (unsigned int)((context->count[0] >> 3) & 0x3f);
+  padLen = (count < 56) ? (56 - count) : (120 - count);
+  MD5_Update (context, PADDING, padLen);
+
+  /* Append length (before padding) */
+  MD5_Update (context, bits, 8);
+
+  /* Store state in digest */
+  Encode (digest, context->state, 16);
+
+  /* Zeroize sensitive information. */
+  memset ((void *)context, 0, sizeof (*context));
+}
+
+/* MD5 basic transformation. Transforms state based on block. */
+static void MD5Transform(UINT4 state[4],
+                         const unsigned char block[64])
+{
+  UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+  Decode (x, block, 64);
+
+  /* Round 1 */
+  FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
+  FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
+  FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
+  FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
+  FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
+  FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
+  FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
+  FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
+  FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
+  FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
+  FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+  FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+  FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+  FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+  FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+  FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+  GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
+  GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
+  GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+  GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
+  GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
+  GG (d, a, b, c, x[10], S22,  0x2441453); /* 22 */
+  GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+  GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
+  GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
+  GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+  GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
+  GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
+  GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+  GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
+  GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
+  GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+  /* Round 3 */
+  HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
+  HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
+  HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+  HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+  HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
+  HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
+  HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
+  HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+  HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+  HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
+  HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
+  HH (b, c, d, a, x[ 6], S34,  0x4881d05); /* 44 */
+  HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
+  HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+  HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+  HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
+
+  /* Round 4 */
+  II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
+  II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
+  II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+  II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
+  II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+  II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
+  II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+  II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
+  II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
+  II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+  II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
+  II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+  II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
+  II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+  II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
+  II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
+
+  state[0] += a;
+  state[1] += b;
+  state[2] += c;
+  state[3] += d;
+
+  /* Zeroize sensitive information. */
+  memset((void *)x, 0, sizeof (x));
+}
+
+/* Encodes input (UINT4) into output (unsigned char). Assumes len is
+  a multiple of 4.
+ */
+static void Encode (unsigned char *output,
+                    UINT4 *input,
+                    unsigned int len)
+{
+  unsigned int i, j;
+
+  for(i = 0, j = 0; j < len; i++, j += 4) {
+    output[j] = (unsigned char)(input[i] & 0xff);
+    output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
+    output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
+    output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
+  }
+}
+
+/* Decodes input (unsigned char) into output (UINT4). Assumes len is
+   a multiple of 4.
+*/
+static void Decode (UINT4 *output,
+                    const unsigned char *input,
+                    unsigned int len)
+{
+  unsigned int i, j;
+
+  for(i = 0, j = 0; j < len; i++, j += 4)
+    output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
+      (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
+}
+
+#endif /* CRYPTO LIBS */
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+const HMAC_params Curl_HMAC_MD5[] = {
+  {
+    (HMAC_hinit_func) MD5_Init,           /* Hash initialization function. */
+    (HMAC_hupdate_func) MD5_Update,       /* Hash update function. */
+    (HMAC_hfinal_func) MD5_Final,         /* Hash computation end function. */
+    sizeof(MD5_CTX),                      /* Size of hash context structure. */
+    64,                                   /* Maximum key length. */
+    16                                    /* Result size. */
+  }
+};
+
+const MD5_params Curl_DIGEST_MD5[] = {
+  {
+    (Curl_MD5_init_func) MD5_Init,      /* Digest initialization function */
+    (Curl_MD5_update_func) MD5_Update,  /* Digest update function */
+    (Curl_MD5_final_func) MD5_Final,    /* Digest computation end function */
+    sizeof(MD5_CTX),                    /* Size of digest context struct */
+    16                                  /* Result size */
+  }
+};
+
+void Curl_md5it(unsigned char *outbuffer, /* 16 bytes */
+                const unsigned char *input)
+{
+  MD5_CTX ctx;
+  MD5_Init(&ctx);
+  MD5_Update(&ctx, input, curlx_uztoui(strlen((char *)input)));
+  MD5_Final(outbuffer, &ctx);
+}
+
+MD5_context *Curl_MD5_init(const MD5_params *md5params)
+{
+  MD5_context *ctxt;
+
+  /* Create MD5 context */
+  ctxt = malloc(sizeof *ctxt);
+
+  if(!ctxt)
+    return ctxt;
+
+  ctxt->md5_hashctx = malloc(md5params->md5_ctxtsize);
+
+  if(!ctxt->md5_hashctx) {
+    free(ctxt);
+    return NULL;
+  }
+
+  ctxt->md5_hash = md5params;
+
+  (*md5params->md5_init_func)(ctxt->md5_hashctx);
+
+  return ctxt;
+}
+
+int Curl_MD5_update(MD5_context *context,
+                    const unsigned char *data,
+                    unsigned int len)
+{
+  (*context->md5_hash->md5_update_func)(context->md5_hashctx, data, len);
+
+  return 0;
+}
+
+int Curl_MD5_final(MD5_context *context, unsigned char *result)
+{
+  (*context->md5_hash->md5_final_func)(result, context->md5_hashctx);
+
+  free(context->md5_hashctx);
+  free(context);
+
+  return 0;
+}
+
+#endif /* CURL_DISABLE_CRYPTO_AUTH */
diff --git a/lib/curl_memdebug.c b/lib/curl_memdebug.c
new file mode 100644 (file)
index 0000000..e756126
--- /dev/null
@@ -0,0 +1,445 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef CURLDEBUG
+
+#include <curl/curl.h>
+
+#define _MPRINTF_REPLACE
+#include <curl/mprintf.h>
+#include "curl_urldata.h"
+
+#define MEMDEBUG_NODEFINES /* don't redefine the standard functions */
+#include "curl_memory.h"
+#include "curl_memdebug.h"
+
+#ifndef HAVE_ASSERT_H
+#  define assert(x) Curl_nop_stmt
+#endif
+
+/*
+ * Until 2011-08-17 libcurl's Memory Tracking feature also performed
+ * automatic malloc and free filling operations using 0xA5 and 0x13
+ * values. Our own preinitialization of dynamically allocated memory
+ * might be useful when not using third party memory debuggers, but
+ * on the other hand this would fool memory debuggers into thinking
+ * that all dynamically allocated memory is properly initialized.
+ *
+ * As a default setting, libcurl's Memory Tracking feature no longer
+ * performs preinitialization of dynamically allocated memory on its
+ * own. If you know what you are doing, and really want to retain old
+ * behavior, you can achieve this compiling with preprocessor symbols
+ * CURL_MT_MALLOC_FILL and CURL_MT_FREE_FILL defined with appropriate
+ * values.
+ */
+
+#ifdef CURL_MT_MALLOC_FILL
+# if (CURL_MT_MALLOC_FILL < 0) || (CURL_MT_MALLOC_FILL > 0xff)
+#   error "invalid CURL_MT_MALLOC_FILL or out of range"
+# endif
+#endif
+
+#ifdef CURL_MT_FREE_FILL
+# if (CURL_MT_FREE_FILL < 0) || (CURL_MT_FREE_FILL > 0xff)
+#   error "invalid CURL_MT_FREE_FILL or out of range"
+# endif
+#endif
+
+#if defined(CURL_MT_MALLOC_FILL) && defined(CURL_MT_FREE_FILL)
+# if (CURL_MT_MALLOC_FILL == CURL_MT_FREE_FILL)
+#   error "CURL_MT_MALLOC_FILL same as CURL_MT_FREE_FILL"
+# endif
+#endif
+
+#ifdef CURL_MT_MALLOC_FILL
+#  define mt_malloc_fill(buf,len) memset((buf), CURL_MT_MALLOC_FILL, (len))
+#else
+#  define mt_malloc_fill(buf,len) Curl_nop_stmt
+#endif
+
+#ifdef CURL_MT_FREE_FILL
+#  define mt_free_fill(buf,len) memset((buf), CURL_MT_FREE_FILL, (len))
+#else
+#  define mt_free_fill(buf,len) Curl_nop_stmt
+#endif
+
+struct memdebug {
+  size_t size;
+  union {
+    curl_off_t o;
+    double d;
+    void * p;
+  } mem[1];
+  /* I'm hoping this is the thing with the strictest alignment
+   * requirements.  That also means we waste some space :-( */
+};
+
+/*
+ * Note that these debug functions are very simple and they are meant to
+ * remain so. For advanced analysis, record a log file and write perl scripts
+ * to analyze them!
+ *
+ * Don't use these with multithreaded test programs!
+ */
+
+#define logfile curl_debuglogfile
+FILE *curl_debuglogfile = NULL;
+static bool memlimit = FALSE; /* enable memory limit */
+static long memsize = 0;  /* set number of mallocs allowed */
+
+/* this sets the log file name */
+void curl_memdebug(const char *logname)
+{
+  if(!logfile) {
+    if(logname && *logname)
+      logfile = fopen(logname, "w");
+    else
+      logfile = stderr;
+#ifdef MEMDEBUG_LOG_SYNC
+    /* Flush the log file after every line so the log isn't lost in a crash */
+    setvbuf(logfile, (char *)NULL, _IOLBF, 0);
+#endif
+  }
+}
+
+/* This function sets the number of malloc() calls that should return
+   successfully! */
+void curl_memlimit(long limit)
+{
+  if(!memlimit) {
+    memlimit = TRUE;
+    memsize = limit;
+  }
+}
+
+/* returns TRUE if this isn't allowed! */
+static bool countcheck(const char *func, int line, const char *source)
+{
+  /* if source is NULL, then the call is made internally and this check
+     should not be made */
+  if(memlimit && source) {
+    if(!memsize) {
+      if(source) {
+        /* log to file */
+        curl_memlog("LIMIT %s:%d %s reached memlimit\n",
+                    source, line, func);
+        /* log to stderr also */
+        fprintf(stderr, "LIMIT %s:%d %s reached memlimit\n",
+                source, line, func);
+      }
+      SET_ERRNO(ENOMEM);
+      return TRUE; /* RETURN ERROR! */
+    }
+    else
+      memsize--; /* countdown */
+
+    /* log the countdown */
+    if(source)
+      curl_memlog("LIMIT %s:%d %ld ALLOCS left\n",
+                  source, line, memsize);
+
+  }
+
+  return FALSE; /* allow this */
+}
+
+void *curl_domalloc(size_t wantedsize, int line, const char *source)
+{
+  struct memdebug *mem;
+  size_t size;
+
+  assert(wantedsize != 0);
+
+  if(countcheck("malloc", line, source))
+    return NULL;
+
+  /* alloc at least 64 bytes */
+  size = sizeof(struct memdebug)+wantedsize;
+
+  mem = (Curl_cmalloc)(size);
+  if(mem) {
+    /* fill memory with junk */
+    mt_malloc_fill(mem->mem, wantedsize);
+    mem->size = wantedsize;
+  }
+
+  if(source)
+    curl_memlog("MEM %s:%d malloc(%zd) = %p\n",
+                source, line, wantedsize, mem ? mem->mem : 0);
+  return (mem ? mem->mem : NULL);
+}
+
+void *curl_docalloc(size_t wanted_elements, size_t wanted_size,
+                    int line, const char *source)
+{
+  struct memdebug *mem;
+  size_t size, user_size;
+
+  assert(wanted_elements != 0);
+  assert(wanted_size != 0);
+
+  if(countcheck("calloc", line, source))
+    return NULL;
+
+  /* alloc at least 64 bytes */
+  user_size = wanted_size * wanted_elements;
+  size = sizeof(struct memdebug) + user_size;
+
+  mem = (Curl_ccalloc)(1, size);
+  if(mem)
+    mem->size = user_size;
+
+  if(source)
+    curl_memlog("MEM %s:%d calloc(%zu,%zu) = %p\n",
+                source, line, wanted_elements, wanted_size, mem?mem->mem:0);
+  return (mem ? mem->mem : NULL);
+}
+
+char *curl_dostrdup(const char *str, int line, const char *source)
+{
+  char *mem;
+  size_t len;
+
+  assert(str != NULL);
+
+  if(countcheck("strdup", line, source))
+    return NULL;
+
+  len=strlen(str)+1;
+
+  mem=curl_domalloc(len, 0, NULL); /* NULL prevents logging */
+  if(mem)
+    memcpy(mem, str, len);
+
+  if(source)
+    curl_memlog("MEM %s:%d strdup(%p) (%zu) = %p\n",
+                source, line, str, len, mem);
+
+  return mem;
+}
+
+/* We provide a realloc() that accepts a NULL as pointer, which then
+   performs a malloc(). In order to work with ares. */
+void *curl_dorealloc(void *ptr, size_t wantedsize,
+                     int line, const char *source)
+{
+  struct memdebug *mem=NULL;
+
+  size_t size = sizeof(struct memdebug)+wantedsize;
+
+  assert(wantedsize != 0);
+
+  if(countcheck("realloc", line, source))
+    return NULL;
+
+#ifdef __INTEL_COMPILER
+#  pragma warning(push)
+#  pragma warning(disable:1684)
+   /* 1684: conversion from pointer to same-sized integral type */
+#endif
+
+  if(ptr)
+    mem = (void *)((char *)ptr - offsetof(struct memdebug, mem));
+
+#ifdef __INTEL_COMPILER
+#  pragma warning(pop)
+#endif
+
+  mem = (Curl_crealloc)(mem, size);
+  if(source)
+    curl_memlog("MEM %s:%d realloc(%p, %zu) = %p\n",
+                source, line, ptr, wantedsize, mem?mem->mem:NULL);
+
+  if(mem) {
+    mem->size = wantedsize;
+    return mem->mem;
+  }
+
+  return NULL;
+}
+
+void curl_dofree(void *ptr, int line, const char *source)
+{
+  struct memdebug *mem;
+
+  assert(ptr != NULL);
+
+#ifdef __INTEL_COMPILER
+#  pragma warning(push)
+#  pragma warning(disable:1684)
+   /* 1684: conversion from pointer to same-sized integral type */
+#endif
+
+  mem = (void *)((char *)ptr - offsetof(struct memdebug, mem));
+
+#ifdef __INTEL_COMPILER
+#  pragma warning(pop)
+#endif
+
+  /* destroy */
+  mt_free_fill(mem->mem, mem->size);
+
+  /* free for real */
+  (Curl_cfree)(mem);
+
+  if(source)
+    curl_memlog("MEM %s:%d free(%p)\n", source, line, ptr);
+}
+
+curl_socket_t curl_socket(int domain, int type, int protocol,
+                          int line, const char *source)
+{
+  const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
+                    "FD %s:%d socket() = %d\n" :
+                    (sizeof(curl_socket_t) == sizeof(long)) ?
+                    "FD %s:%d socket() = %ld\n" :
+                    "FD %s:%d socket() = %zd\n" ;
+
+  curl_socket_t sockfd = socket(domain, type, protocol);
+  if(source && (sockfd != CURL_SOCKET_BAD))
+    curl_memlog(fmt, source, line, sockfd);
+  return sockfd;
+}
+
+#ifdef HAVE_SOCKETPAIR
+int curl_socketpair(int domain, int type, int protocol,
+                    curl_socket_t socket_vector[2],
+                    int line, const char *source)
+{
+  const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
+                    "FD %s:%d socketpair() = %d %d\n" :
+                    (sizeof(curl_socket_t) == sizeof(long)) ?
+                    "FD %s:%d socketpair() = %ld %ld\n" :
+                    "FD %s:%d socketpair() = %zd %zd\n" ;
+
+  int res = socketpair(domain, type, protocol, socket_vector);
+  if(source && (0 == res))
+    curl_memlog(fmt, source, line, socket_vector[0], socket_vector[1]);
+  return res;
+}
+#endif
+
+curl_socket_t curl_accept(curl_socket_t s, void *saddr, void *saddrlen,
+                          int line, const char *source)
+{
+  const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
+                    "FD %s:%d accept() = %d\n" :
+                    (sizeof(curl_socket_t) == sizeof(long)) ?
+                    "FD %s:%d accept() = %ld\n" :
+                    "FD %s:%d accept() = %zd\n" ;
+
+  struct sockaddr *addr = (struct sockaddr *)saddr;
+  curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen;
+  curl_socket_t sockfd = accept(s, addr, addrlen);
+  if(source && (sockfd != CURL_SOCKET_BAD))
+    curl_memlog(fmt, source, line, sockfd);
+  return sockfd;
+}
+
+/* separate function to allow libcurl to mark a "faked" close */
+void curl_mark_sclose(curl_socket_t sockfd, int line, const char *source)
+{
+  const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
+                    "FD %s:%d sclose(%d)\n" :
+                    (sizeof(curl_socket_t) == sizeof(long)) ?
+                    "FD %s:%d sclose(%ld)\n" :
+                    "FD %s:%d sclose(%zd)\n" ;
+
+  if(source)
+    curl_memlog(fmt, source, line, sockfd);
+}
+
+/* this is our own defined way to close sockets on *ALL* platforms */
+int curl_sclose(curl_socket_t sockfd, int line, const char *source)
+{
+  int res=sclose(sockfd);
+  curl_mark_sclose(sockfd, line, source);
+  return res;
+}
+
+FILE *curl_fopen(const char *file, const char *mode,
+                 int line, const char *source)
+{
+  FILE *res=fopen(file, mode);
+  if(source)
+    curl_memlog("FILE %s:%d fopen(\"%s\",\"%s\") = %p\n",
+                source, line, file, mode, res);
+  return res;
+}
+
+#ifdef HAVE_FDOPEN
+FILE *curl_fdopen(int filedes, const char *mode,
+                  int line, const char *source)
+{
+  FILE *res=fdopen(filedes, mode);
+  if(source)
+    curl_memlog("FILE %s:%d fdopen(\"%d\",\"%s\") = %p\n",
+                source, line, filedes, mode, res);
+  return res;
+}
+#endif
+
+int curl_fclose(FILE *file, int line, const char *source)
+{
+  int res;
+
+  assert(file != NULL);
+
+  res=fclose(file);
+  if(source)
+    curl_memlog("FILE %s:%d fclose(%p)\n",
+                source, line, file);
+  return res;
+}
+
+#define LOGLINE_BUFSIZE  1024
+
+/* this does the writting to the memory tracking log file */
+void curl_memlog(const char *format, ...)
+{
+  char *buf;
+  int nchars;
+  va_list ap;
+
+  if(!logfile)
+    return;
+
+  buf = (Curl_cmalloc)(LOGLINE_BUFSIZE);
+  if(!buf)
+    return;
+
+  va_start(ap, format);
+  nchars = vsnprintf(buf, LOGLINE_BUFSIZE, format, ap);
+  va_end(ap);
+
+  if(nchars > LOGLINE_BUFSIZE - 1)
+    nchars = LOGLINE_BUFSIZE - 1;
+
+  if(nchars > 0)
+    fwrite(buf, 1, nchars, logfile);
+
+  (Curl_cfree)(buf);
+}
+
+#endif /* CURLDEBUG */
diff --git a/lib/curl_mprintf.c b/lib/curl_mprintf.c
new file mode 100644 (file)
index 0000000..35b9f64
--- /dev/null
@@ -0,0 +1,1197 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1999 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ *
+ * Purpose:
+ *  A merge of Bjorn Reese's format() function and Daniel's dsprintf()
+ *  1.0. A full blooded printf() clone with full support for <num>$
+ *  everywhere (parameters, widths and precisions) including variabled
+ *  sized parameters (like doubles, long longs, long doubles and even
+ *  void * in 64-bit architectures).
+ *
+ * Current restrictions:
+ * - Max 128 parameters
+ * - No 'long double' support.
+ *
+ * If you ever want truly portable and good *printf() clones, the project that
+ * took on from here is named 'Trio' and you find more details on the trio web
+ * page at http://daniel.haxx.se/trio/
+ */
+
+#include "curl_setup.h"
+
+#if defined(DJGPP) && (DJGPP_MINOR < 4)
+#undef _MPRINTF_REPLACE /* don't use x_was_used() here */
+#endif
+
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+#ifndef SIZEOF_LONG_DOUBLE
+#define SIZEOF_LONG_DOUBLE 0
+#endif
+
+/*
+ * If SIZEOF_SIZE_T has not been defined, default to the size of long.
+ */
+
+#ifndef SIZEOF_SIZE_T
+#  define SIZEOF_SIZE_T CURL_SIZEOF_LONG
+#endif
+
+#ifdef HAVE_LONGLONG
+#  define LONG_LONG_TYPE long long
+#  define HAVE_LONG_LONG_TYPE
+#else
+#  if defined(_MSC_VER) && (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)
+#    define LONG_LONG_TYPE __int64
+#    define HAVE_LONG_LONG_TYPE
+#  else
+#    undef LONG_LONG_TYPE
+#    undef HAVE_LONG_LONG_TYPE
+#  endif
+#endif
+
+/*
+ * Max integer data types that curl_mprintf.c is capable
+ */
+
+#ifdef HAVE_LONG_LONG_TYPE
+#  define mp_intmax_t LONG_LONG_TYPE
+#  define mp_uintmax_t unsigned LONG_LONG_TYPE
+#else
+#  define mp_intmax_t long
+#  define mp_uintmax_t unsigned long
+#endif
+
+#define BUFFSIZE 256 /* buffer for long-to-str and float-to-str calcs */
+#define MAX_PARAMETERS 128 /* lame static limit */
+
+#ifdef __AMIGA__
+# undef FORMAT_INT
+#endif
+
+/* Lower-case digits.  */
+static const char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+
+/* Upper-case digits.  */
+static const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+#define OUTCHAR(x) \
+  do{ \
+    if(stream((unsigned char)(x), (FILE *)data) != -1) \
+      done++; \
+    else \
+     return done; /* return immediately on failure */ \
+  } WHILE_FALSE
+
+/* Data type to read from the arglist */
+typedef enum  {
+  FORMAT_UNKNOWN = 0,
+  FORMAT_STRING,
+  FORMAT_PTR,
+  FORMAT_INT,
+  FORMAT_INTPTR,
+  FORMAT_LONG,
+  FORMAT_LONGLONG,
+  FORMAT_DOUBLE,
+  FORMAT_LONGDOUBLE,
+  FORMAT_WIDTH /* For internal use */
+} FormatType;
+
+/* conversion and display flags */
+enum {
+  FLAGS_NEW        = 0,
+  FLAGS_SPACE      = 1<<0,
+  FLAGS_SHOWSIGN   = 1<<1,
+  FLAGS_LEFT       = 1<<2,
+  FLAGS_ALT        = 1<<3,
+  FLAGS_SHORT      = 1<<4,
+  FLAGS_LONG       = 1<<5,
+  FLAGS_LONGLONG   = 1<<6,
+  FLAGS_LONGDOUBLE = 1<<7,
+  FLAGS_PAD_NIL    = 1<<8,
+  FLAGS_UNSIGNED   = 1<<9,
+  FLAGS_OCTAL      = 1<<10,
+  FLAGS_HEX        = 1<<11,
+  FLAGS_UPPER      = 1<<12,
+  FLAGS_WIDTH      = 1<<13, /* '*' or '*<num>$' used */
+  FLAGS_WIDTHPARAM = 1<<14, /* width PARAMETER was specified */
+  FLAGS_PREC       = 1<<15, /* precision was specified */
+  FLAGS_PRECPARAM  = 1<<16, /* precision PARAMETER was specified */
+  FLAGS_CHAR       = 1<<17, /* %c story */
+  FLAGS_FLOATE     = 1<<18, /* %e or %E */
+  FLAGS_FLOATG     = 1<<19  /* %g or %G */
+};
+
+typedef struct {
+  FormatType type;
+  int flags;
+  long width;     /* width OR width parameter number */
+  long precision; /* precision OR precision parameter number */
+  union {
+    char *str;
+    void *ptr;
+    union {
+      mp_intmax_t as_signed;
+      mp_uintmax_t as_unsigned;
+    } num;
+    double dnum;
+  } data;
+} va_stack_t;
+
+struct nsprintf {
+  char *buffer;
+  size_t length;
+  size_t max;
+};
+
+struct asprintf {
+  char *buffer; /* allocated buffer */
+  size_t len;   /* length of string */
+  size_t alloc; /* length of alloc */
+  int fail;     /* (!= 0) if an alloc has failed and thus
+                   the output is not the complete data */
+};
+
+static long dprintf_DollarString(char *input, char **end)
+{
+  int number=0;
+  while(ISDIGIT(*input)) {
+    number *= 10;
+    number += *input-'0';
+    input++;
+  }
+  if(number && ('$'==*input++)) {
+    *end = input;
+    return number;
+  }
+  return 0;
+}
+
+static int dprintf_IsQualifierNoDollar(char c)
+{
+  switch (c) {
+  case '-': case '+': case ' ': case '#': case '.':
+  case '0': case '1': case '2': case '3': case '4':
+  case '5': case '6': case '7': case '8': case '9':
+  case 'h': case 'l': case 'L': case 'z': case 'q':
+  case '*': case 'O':
+    return 1; /* true */
+  default:
+    return 0; /* false */
+  }
+}
+
+#ifdef DPRINTF_DEBUG2
+static void dprintf_Pass1Report(va_stack_t *vto, int max)
+{
+  int i;
+  char buffer[256];
+  int bit;
+  int flags;
+
+  for(i=0; i<max; i++) {
+    char *type;
+    switch(vto[i].type) {
+    case FORMAT_UNKNOWN:
+      type = "unknown";
+      break;
+    case FORMAT_STRING:
+      type ="string";
+      break;
+    case FORMAT_PTR:
+      type ="pointer";
+      break;
+    case FORMAT_INT:
+      type = "int";
+      break;
+    case FORMAT_INTPTR:
+      type = "intptr";
+      break;
+    case FORMAT_LONG:
+      type = "long";
+      break;
+    case FORMAT_LONGLONG:
+      type = "long long";
+      break;
+    case FORMAT_DOUBLE:
+      type = "double";
+      break;
+    case FORMAT_LONGDOUBLE:
+      type = "long double";
+      break;
+    }
+
+
+    buffer[0]=0;
+
+    for(bit=0; bit<31; bit++) {
+      flags = vto[i].flags & (1<<bit);
+
+      if(flags & FLAGS_SPACE)
+        strcat(buffer, "space ");
+      else if(flags & FLAGS_SHOWSIGN)
+        strcat(buffer, "plus ");
+      else if(flags & FLAGS_LEFT)
+        strcat(buffer, "left ");
+      else if(flags & FLAGS_ALT)
+        strcat(buffer, "alt ");
+      else if(flags & FLAGS_SHORT)
+        strcat(buffer, "short ");
+      else if(flags & FLAGS_LONG)
+        strcat(buffer, "long ");
+      else if(flags & FLAGS_LONGLONG)
+        strcat(buffer, "longlong ");
+      else if(flags & FLAGS_LONGDOUBLE)
+        strcat(buffer, "longdouble ");
+      else if(flags & FLAGS_PAD_NIL)
+        strcat(buffer, "padnil ");
+      else if(flags & FLAGS_UNSIGNED)
+        strcat(buffer, "unsigned ");
+      else if(flags & FLAGS_OCTAL)
+        strcat(buffer, "octal ");
+      else if(flags & FLAGS_HEX)
+        strcat(buffer, "hex ");
+      else if(flags & FLAGS_UPPER)
+        strcat(buffer, "upper ");
+      else if(flags & FLAGS_WIDTH)
+        strcat(buffer, "width ");
+      else if(flags & FLAGS_WIDTHPARAM)
+        strcat(buffer, "widthparam ");
+      else if(flags & FLAGS_PREC)
+        strcat(buffer, "precision ");
+      else if(flags & FLAGS_PRECPARAM)
+        strcat(buffer, "precparam ");
+      else if(flags & FLAGS_CHAR)
+        strcat(buffer, "char ");
+      else if(flags & FLAGS_FLOATE)
+        strcat(buffer, "floate ");
+      else if(flags & FLAGS_FLOATG)
+        strcat(buffer, "floatg ");
+    }
+    printf("REPORT: %d. %s [%s]\n", i, type, buffer);
+
+  }
+
+
+}
+#endif
+
+/******************************************************************
+ *
+ * Pass 1:
+ * Create an index with the type of each parameter entry and its
+ * value (may vary in size)
+ *
+ ******************************************************************/
+
+static long dprintf_Pass1(const char *format, va_stack_t *vto, char **endpos,
+                          va_list arglist)
+{
+  char *fmt = (char *)format;
+  int param_num = 0;
+  long this_param;
+  long width;
+  long precision;
+  int flags;
+  long max_param=0;
+  long i;
+
+  while(*fmt) {
+    if(*fmt++ == '%') {
+      if(*fmt == '%') {
+        fmt++;
+        continue; /* while */
+      }
+
+      flags = FLAGS_NEW;
+
+      /* Handle the positional case (N$) */
+
+      param_num++;
+
+      this_param = dprintf_DollarString(fmt, &fmt);
+      if(0 == this_param)
+        /* we got no positional, get the next counter */
+        this_param = param_num;
+
+      if(this_param > max_param)
+        max_param = this_param;
+
+      /*
+       * The parameter with number 'i' should be used. Next, we need
+       * to get SIZE and TYPE of the parameter. Add the information
+       * to our array.
+       */
+
+      width = 0;
+      precision = 0;
+
+      /* Handle the flags */
+
+      while(dprintf_IsQualifierNoDollar(*fmt)) {
+        switch (*fmt++) {
+        case ' ':
+          flags |= FLAGS_SPACE;
+          break;
+        case '+':
+          flags |= FLAGS_SHOWSIGN;
+          break;
+        case '-':
+          flags |= FLAGS_LEFT;
+          flags &= ~FLAGS_PAD_NIL;
+          break;
+        case '#':
+          flags |= FLAGS_ALT;
+          break;
+        case '.':
+          flags |= FLAGS_PREC;
+          if('*' == *fmt) {
+            /* The precision is picked from a specified parameter */
+
+            flags |= FLAGS_PRECPARAM;
+            fmt++;
+            param_num++;
+
+            i = dprintf_DollarString(fmt, &fmt);
+            if(i)
+              precision = i;
+            else
+              precision = param_num;
+
+            if(precision > max_param)
+              max_param = precision;
+          }
+          else {
+            flags |= FLAGS_PREC;
+            precision = strtol(fmt, &fmt, 10);
+          }
+          break;
+        case 'h':
+          flags |= FLAGS_SHORT;
+          break;
+        case 'l':
+          if(flags & FLAGS_LONG)
+            flags |= FLAGS_LONGLONG;
+          else
+            flags |= FLAGS_LONG;
+          break;
+        case 'L':
+          flags |= FLAGS_LONGDOUBLE;
+          break;
+        case 'q':
+          flags |= FLAGS_LONGLONG;
+          break;
+        case 'z':
+          /* the code below generates a warning if -Wunreachable-code is
+             used */
+#if (SIZEOF_SIZE_T > CURL_SIZEOF_LONG)
+          flags |= FLAGS_LONGLONG;
+#else
+          flags |= FLAGS_LONG;
+#endif
+          break;
+        case 'O':
+#if (CURL_SIZEOF_CURL_OFF_T > CURL_SIZEOF_LONG)
+          flags |= FLAGS_LONGLONG;
+#else
+          flags |= FLAGS_LONG;
+#endif
+          break;
+        case '0':
+          if(!(flags & FLAGS_LEFT))
+            flags |= FLAGS_PAD_NIL;
+          /* FALLTHROUGH */
+        case '1': case '2': case '3': case '4':
+        case '5': case '6': case '7': case '8': case '9':
+          flags |= FLAGS_WIDTH;
+          width = strtol(fmt-1, &fmt, 10);
+          break;
+        case '*':  /* Special case */
+          flags |= FLAGS_WIDTHPARAM;
+          param_num++;
+
+          i = dprintf_DollarString(fmt, &fmt);
+          if(i)
+            width = i;
+          else
+            width = param_num;
+          if(width > max_param)
+            max_param=width;
+          break;
+        default:
+          break;
+        }
+      } /* switch */
+
+      /* Handle the specifier */
+
+      i = this_param - 1;
+
+      switch (*fmt) {
+      case 'S':
+        flags |= FLAGS_ALT;
+        /* FALLTHROUGH */
+      case 's':
+        vto[i].type = FORMAT_STRING;
+        break;
+      case 'n':
+        vto[i].type = FORMAT_INTPTR;
+        break;
+      case 'p':
+        vto[i].type = FORMAT_PTR;
+        break;
+      case 'd': case 'i':
+        vto[i].type = FORMAT_INT;
+        break;
+      case 'u':
+        vto[i].type = FORMAT_INT;
+        flags |= FLAGS_UNSIGNED;
+        break;
+      case 'o':
+        vto[i].type = FORMAT_INT;
+        flags |= FLAGS_OCTAL;
+        break;
+      case 'x':
+        vto[i].type = FORMAT_INT;
+        flags |= FLAGS_HEX;
+        break;
+      case 'X':
+        vto[i].type = FORMAT_INT;
+        flags |= FLAGS_HEX|FLAGS_UPPER;
+        break;
+      case 'c':
+        vto[i].type = FORMAT_INT;
+        flags |= FLAGS_CHAR;
+        break;
+      case 'f':
+        vto[i].type = FORMAT_DOUBLE;
+        break;
+      case 'e':
+        vto[i].type = FORMAT_DOUBLE;
+        flags |= FLAGS_FLOATE;
+        break;
+      case 'E':
+        vto[i].type = FORMAT_DOUBLE;
+        flags |= FLAGS_FLOATE|FLAGS_UPPER;
+        break;
+      case 'g':
+        vto[i].type = FORMAT_DOUBLE;
+        flags |= FLAGS_FLOATG;
+        break;
+      case 'G':
+        vto[i].type = FORMAT_DOUBLE;
+        flags |= FLAGS_FLOATG|FLAGS_UPPER;
+        break;
+      default:
+        vto[i].type = FORMAT_UNKNOWN;
+        break;
+      } /* switch */
+
+      vto[i].flags = flags;
+      vto[i].width = width;
+      vto[i].precision = precision;
+
+      if(flags & FLAGS_WIDTHPARAM) {
+        /* we have the width specified from a parameter, so we make that
+           parameter's info setup properly */
+        vto[i].width = width - 1;
+        i = width - 1;
+        vto[i].type = FORMAT_WIDTH;
+        vto[i].flags = FLAGS_NEW;
+        vto[i].precision = vto[i].width = 0; /* can't use width or precision
+                                                of width! */
+      }
+      if(flags & FLAGS_PRECPARAM) {
+        /* we have the precision specified from a parameter, so we make that
+           parameter's info setup properly */
+        vto[i].precision = precision - 1;
+        i = precision - 1;
+        vto[i].type = FORMAT_WIDTH;
+        vto[i].flags = FLAGS_NEW;
+        vto[i].precision = vto[i].width = 0; /* can't use width or precision
+                                                of width! */
+      }
+      *endpos++ = fmt + 1; /* end of this sequence */
+    }
+  }
+
+#ifdef DPRINTF_DEBUG2
+  dprintf_Pass1Report(vto, max_param);
+#endif
+
+  /* Read the arg list parameters into our data list */
+  for(i=0; i<max_param; i++) {
+    if((i + 1 < max_param) && (vto[i + 1].type == FORMAT_WIDTH)) {
+      /* Width/precision arguments must be read before the main argument
+       * they are attached to
+       */
+      vto[i + 1].data.num.as_signed = (mp_intmax_t)va_arg(arglist, int);
+    }
+
+    switch (vto[i].type) {
+    case FORMAT_STRING:
+      vto[i].data.str = va_arg(arglist, char *);
+      break;
+
+    case FORMAT_INTPTR:
+    case FORMAT_UNKNOWN:
+    case FORMAT_PTR:
+      vto[i].data.ptr = va_arg(arglist, void *);
+      break;
+
+    case FORMAT_INT:
+#ifdef HAVE_LONG_LONG_TYPE
+      if((vto[i].flags & FLAGS_LONGLONG) && (vto[i].flags & FLAGS_UNSIGNED))
+        vto[i].data.num.as_unsigned =
+          (mp_uintmax_t)va_arg(arglist, mp_uintmax_t);
+      else if(vto[i].flags & FLAGS_LONGLONG)
+        vto[i].data.num.as_signed =
+          (mp_intmax_t)va_arg(arglist, mp_intmax_t);
+      else
+#endif
+      {
+        if((vto[i].flags & FLAGS_LONG) && (vto[i].flags & FLAGS_UNSIGNED))
+          vto[i].data.num.as_unsigned =
+            (mp_uintmax_t)va_arg(arglist, unsigned long);
+        else if(vto[i].flags & FLAGS_LONG)
+          vto[i].data.num.as_signed =
+            (mp_intmax_t)va_arg(arglist, long);
+        else if(vto[i].flags & FLAGS_UNSIGNED)
+          vto[i].data.num.as_unsigned =
+            (mp_uintmax_t)va_arg(arglist, unsigned int);
+        else
+          vto[i].data.num.as_signed =
+            (mp_intmax_t)va_arg(arglist, int);
+      }
+      break;
+
+    case FORMAT_DOUBLE:
+      vto[i].data.dnum = va_arg(arglist, double);
+      break;
+
+    case FORMAT_WIDTH:
+      /* Argument has been read. Silently convert it into an integer
+       * for later use
+       */
+      vto[i].type = FORMAT_INT;
+      break;
+
+    default:
+      break;
+    }
+  }
+
+  return max_param;
+
+}
+
+static int dprintf_formatf(
+  void *data, /* untouched by format(), just sent to the stream() function in
+                 the second argument */
+  /* function pointer called for each output character */
+  int (*stream)(int, FILE *),
+  const char *format,    /* %-formatted string */
+  va_list ap_save) /* list of parameters */
+{
+  /* Base-36 digits for numbers.  */
+  const char *digits = lower_digits;
+
+  /* Pointer into the format string.  */
+  char *f;
+
+  /* Number of characters written.  */
+  int done = 0;
+
+  long param; /* current parameter to read */
+  long param_num=0; /* parameter counter */
+
+  va_stack_t vto[MAX_PARAMETERS];
+  char *endpos[MAX_PARAMETERS];
+  char **end;
+
+  char work[BUFFSIZE];
+
+  va_stack_t *p;
+
+  /* Do the actual %-code parsing */
+  dprintf_Pass1(format, vto, endpos, ap_save);
+
+  end = &endpos[0]; /* the initial end-position from the list dprintf_Pass1()
+                       created for us */
+
+  f = (char *)format;
+  while(*f != '\0') {
+    /* Format spec modifiers.  */
+    int is_alt;
+
+    /* Width of a field.  */
+    long width;
+
+    /* Precision of a field.  */
+    long prec;
+
+    /* Decimal integer is negative.  */
+    int is_neg;
+
+    /* Base of a number to be written.  */
+    long base;
+
+    /* Integral values to be written.  */
+    mp_uintmax_t num;
+
+    /* Used to convert negative in positive.  */
+    mp_intmax_t signed_num;
+
+    if(*f != '%') {
+      /* This isn't a format spec, so write everything out until the next one
+         OR end of string is reached.  */
+      do {
+        OUTCHAR(*f);
+      } while(*++f && ('%' != *f));
+      continue;
+    }
+
+    ++f;
+
+    /* Check for "%%".  Note that although the ANSI standard lists
+       '%' as a conversion specifier, it says "The complete format
+       specification shall be `%%'," so we can avoid all the width
+       and precision processing.  */
+    if(*f == '%') {
+      ++f;
+      OUTCHAR('%');
+      continue;
+    }
+
+    /* If this is a positional parameter, the position must follow immediately
+       after the %, thus create a %<num>$ sequence */
+    param=dprintf_DollarString(f, &f);
+
+    if(!param)
+      param = param_num;
+    else
+      --param;
+
+    param_num++; /* increase this always to allow "%2$s %1$s %s" and then the
+                    third %s will pick the 3rd argument */
+
+    p = &vto[param];
+
+    /* pick up the specified width */
+    if(p->flags & FLAGS_WIDTHPARAM)
+      width = (long)vto[p->width].data.num.as_signed;
+    else
+      width = p->width;
+
+    /* pick up the specified precision */
+    if(p->flags & FLAGS_PRECPARAM) {
+      prec = (long)vto[p->precision].data.num.as_signed;
+      param_num++; /* since the precision is extraced from a parameter, we
+                      must skip that to get to the next one properly */
+    }
+    else if(p->flags & FLAGS_PREC)
+      prec = p->precision;
+    else
+      prec = -1;
+
+    is_alt = (p->flags & FLAGS_ALT) ? 1 : 0;
+
+    switch (p->type) {
+    case FORMAT_INT:
+      num = p->data.num.as_unsigned;
+      if(p->flags & FLAGS_CHAR) {
+        /* Character.  */
+        if(!(p->flags & FLAGS_LEFT))
+          while(--width > 0)
+            OUTCHAR(' ');
+        OUTCHAR((char) num);
+        if(p->flags & FLAGS_LEFT)
+          while(--width > 0)
+            OUTCHAR(' ');
+        break;
+      }
+      if(p->flags & FLAGS_UNSIGNED) {
+        /* Decimal unsigned integer.  */
+        base = 10;
+        goto unsigned_number;
+      }
+      if(p->flags & FLAGS_OCTAL) {
+        /* Octal unsigned integer.  */
+        base = 8;
+        goto unsigned_number;
+      }
+      if(p->flags & FLAGS_HEX) {
+        /* Hexadecimal unsigned integer.  */
+
+        digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
+        base = 16;
+        goto unsigned_number;
+      }
+
+      /* Decimal integer.  */
+      base = 10;
+
+      is_neg = (p->data.num.as_signed < (mp_intmax_t)0) ? 1 : 0;
+      if(is_neg) {
+        /* signed_num might fail to hold absolute negative minimum by 1 */
+        signed_num = p->data.num.as_signed + (mp_intmax_t)1;
+        signed_num = -signed_num;
+        num = (mp_uintmax_t)signed_num;
+        num += (mp_uintmax_t)1;
+      }
+
+      goto number;
+
+      unsigned_number:
+      /* Unsigned number of base BASE.  */
+      is_neg = 0;
+
+      number:
+      /* Number of base BASE.  */
+      {
+        char *workend = &work[sizeof(work) - 1];
+        char *w;
+
+        /* Supply a default precision if none was given.  */
+        if(prec == -1)
+          prec = 1;
+
+        /* Put the number in WORK.  */
+        w = workend;
+        while(num > 0) {
+          *w-- = digits[num % base];
+          num /= base;
+        }
+        width -= (long)(workend - w);
+        prec -= (long)(workend - w);
+
+        if(is_alt && base == 8 && prec <= 0) {
+          *w-- = '0';
+          --width;
+        }
+
+        if(prec > 0) {
+          width -= prec;
+          while(prec-- > 0)
+            *w-- = '0';
+        }
+
+        if(is_alt && base == 16)
+          width -= 2;
+
+        if(is_neg || (p->flags & FLAGS_SHOWSIGN) || (p->flags & FLAGS_SPACE))
+          --width;
+
+        if(!(p->flags & FLAGS_LEFT) && !(p->flags & FLAGS_PAD_NIL))
+          while(width-- > 0)
+            OUTCHAR(' ');
+
+        if(is_neg)
+          OUTCHAR('-');
+        else if(p->flags & FLAGS_SHOWSIGN)
+          OUTCHAR('+');
+        else if(p->flags & FLAGS_SPACE)
+          OUTCHAR(' ');
+
+        if(is_alt && base == 16) {
+          OUTCHAR('0');
+          if(p->flags & FLAGS_UPPER)
+            OUTCHAR('X');
+          else
+            OUTCHAR('x');
+        }
+
+        if(!(p->flags & FLAGS_LEFT) && (p->flags & FLAGS_PAD_NIL))
+          while(width-- > 0)
+            OUTCHAR('0');
+
+        /* Write the number.  */
+        while(++w <= workend) {
+          OUTCHAR(*w);
+        }
+
+        if(p->flags & FLAGS_LEFT)
+          while(width-- > 0)
+            OUTCHAR(' ');
+      }
+      break;
+
+    case FORMAT_STRING:
+            /* String.  */
+      {
+        static const char null[] = "(nil)";
+        const char *str;
+        size_t len;
+
+        str = (char *) p->data.str;
+        if(str == NULL) {
+          /* Write null[] if there's space.  */
+          if(prec == -1 || prec >= (long) sizeof(null) - 1) {
+            str = null;
+            len = sizeof(null) - 1;
+            /* Disable quotes around (nil) */
+            p->flags &= (~FLAGS_ALT);
+          }
+          else {
+            str = "";
+            len = 0;
+          }
+        }
+        else
+          len = strlen(str);
+
+        if(prec != -1 && (size_t) prec < len)
+          len = (size_t)prec;
+        width -= (long)len;
+
+        if(p->flags & FLAGS_ALT)
+          OUTCHAR('"');
+
+        if(!(p->flags&FLAGS_LEFT))
+          while(width-- > 0)
+            OUTCHAR(' ');
+
+        while(len-- > 0)
+          OUTCHAR(*str++);
+        if(p->flags&FLAGS_LEFT)
+          while(width-- > 0)
+            OUTCHAR(' ');
+
+        if(p->flags & FLAGS_ALT)
+          OUTCHAR('"');
+      }
+      break;
+
+    case FORMAT_PTR:
+      /* Generic pointer.  */
+      {
+        void *ptr;
+        ptr = (void *) p->data.ptr;
+        if(ptr != NULL) {
+          /* If the pointer is not NULL, write it as a %#x spec.  */
+          base = 16;
+          digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
+          is_alt = 1;
+          num = (size_t) ptr;
+          is_neg = 0;
+          goto number;
+        }
+        else {
+          /* Write "(nil)" for a nil pointer.  */
+          static const char strnil[] = "(nil)";
+          const char *point;
+
+          width -= (long)(sizeof(strnil) - 1);
+          if(p->flags & FLAGS_LEFT)
+            while(width-- > 0)
+              OUTCHAR(' ');
+          for(point = strnil; *point != '\0'; ++point)
+            OUTCHAR(*point);
+          if(! (p->flags & FLAGS_LEFT))
+            while(width-- > 0)
+              OUTCHAR(' ');
+        }
+      }
+      break;
+
+    case FORMAT_DOUBLE:
+      {
+        char formatbuf[32]="%";
+        char *fptr;
+        size_t left = sizeof(formatbuf)-strlen(formatbuf);
+        int len;
+
+        width = -1;
+        if(p->flags & FLAGS_WIDTH)
+          width = p->width;
+        else if(p->flags & FLAGS_WIDTHPARAM)
+          width = (long)vto[p->width].data.num.as_signed;
+
+        prec = -1;
+        if(p->flags & FLAGS_PREC)
+          prec = p->precision;
+        else if(p->flags & FLAGS_PRECPARAM)
+          prec = (long)vto[p->precision].data.num.as_signed;
+
+        if(p->flags & FLAGS_LEFT)
+          strcat(formatbuf, "-");
+        if(p->flags & FLAGS_SHOWSIGN)
+          strcat(formatbuf, "+");
+        if(p->flags & FLAGS_SPACE)
+          strcat(formatbuf, " ");
+        if(p->flags & FLAGS_ALT)
+          strcat(formatbuf, "#");
+
+        fptr=&formatbuf[strlen(formatbuf)];
+
+        if(width >= 0) {
+          /* RECURSIVE USAGE */
+          len = curl_msnprintf(fptr, left, "%ld", width);
+          fptr += len;
+          left -= len;
+        }
+        if(prec >= 0) {
+          /* RECURSIVE USAGE */
+          len = curl_msnprintf(fptr, left, ".%ld", prec);
+          fptr += len;
+        }
+        if(p->flags & FLAGS_LONG)
+          *fptr++ = 'l';
+
+        if(p->flags & FLAGS_FLOATE)
+          *fptr++ = (char)((p->flags & FLAGS_UPPER) ? 'E':'e');
+        else if(p->flags & FLAGS_FLOATG)
+          *fptr++ = (char)((p->flags & FLAGS_UPPER) ? 'G' : 'g');
+        else
+          *fptr++ = 'f';
+
+        *fptr = 0; /* and a final zero termination */
+
+        /* NOTE NOTE NOTE!! Not all sprintf() implementations returns number
+           of output characters */
+        (sprintf)(work, formatbuf, p->data.dnum);
+
+        for(fptr=work; *fptr; fptr++)
+          OUTCHAR(*fptr);
+      }
+      break;
+
+    case FORMAT_INTPTR:
+      /* Answer the count of characters written.  */
+#ifdef HAVE_LONG_LONG_TYPE
+      if(p->flags & FLAGS_LONGLONG)
+        *(LONG_LONG_TYPE *) p->data.ptr = (LONG_LONG_TYPE)done;
+      else
+#endif
+        if(p->flags & FLAGS_LONG)
+          *(long *) p->data.ptr = (long)done;
+      else if(!(p->flags & FLAGS_SHORT))
+        *(int *) p->data.ptr = (int)done;
+      else
+        *(short *) p->data.ptr = (short)done;
+      break;
+
+    default:
+      break;
+    }
+    f = *end++; /* goto end of %-code */
+
+  }
+  return done;
+}
+
+/* fputc() look-alike */
+static int addbyter(int output, FILE *data)
+{
+  struct nsprintf *infop=(struct nsprintf *)data;
+  unsigned char outc = (unsigned char)output;
+
+  if(infop->length < infop->max) {
+    /* only do this if we haven't reached max length yet */
+    infop->buffer[0] = outc; /* store */
+    infop->buffer++; /* increase pointer */
+    infop->length++; /* we are now one byte larger */
+    return outc;     /* fputc() returns like this on success */
+  }
+  return -1;
+}
+
+int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format,
+                    va_list ap_save)
+{
+  int retcode;
+  struct nsprintf info;
+
+  info.buffer = buffer;
+  info.length = 0;
+  info.max = maxlength;
+
+  retcode = dprintf_formatf(&info, addbyter, format, ap_save);
+  if(info.max) {
+    /* we terminate this with a zero byte */
+    if(info.max == info.length)
+      /* we're at maximum, scrap the last letter */
+      info.buffer[-1] = 0;
+    else
+      info.buffer[0] = 0;
+  }
+  return retcode;
+}
+
+int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...)
+{
+  int retcode;
+  va_list ap_save; /* argument pointer */
+  va_start(ap_save, format);
+  retcode = curl_mvsnprintf(buffer, maxlength, format, ap_save);
+  va_end(ap_save);
+  return retcode;
+}
+
+/* fputc() look-alike */
+static int alloc_addbyter(int output, FILE *data)
+{
+  struct asprintf *infop=(struct asprintf *)data;
+  unsigned char outc = (unsigned char)output;
+
+  if(!infop->buffer) {
+    infop->buffer = malloc(32);
+    if(!infop->buffer) {
+      infop->fail = 1;
+      return -1; /* fail */
+    }
+    infop->alloc = 32;
+    infop->len =0;
+  }
+  else if(infop->len+1 >= infop->alloc) {
+    char *newptr;
+
+    newptr = realloc(infop->buffer, infop->alloc*2);
+
+    if(!newptr) {
+      infop->fail = 1;
+      return -1; /* fail */
+    }
+    infop->buffer = newptr;
+    infop->alloc *= 2;
+  }
+
+  infop->buffer[ infop->len ] = outc;
+
+  infop->len++;
+
+  return outc; /* fputc() returns like this on success */
+}
+
+char *curl_maprintf(const char *format, ...)
+{
+  va_list ap_save; /* argument pointer */
+  int retcode;
+  struct asprintf info;
+
+  info.buffer = NULL;
+  info.len = 0;
+  info.alloc = 0;
+  info.fail = 0;
+
+  va_start(ap_save, format);
+  retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save);
+  va_end(ap_save);
+  if((-1 == retcode) || info.fail) {
+    if(info.alloc)
+      free(info.buffer);
+    return NULL;
+  }
+  if(info.alloc) {
+    info.buffer[info.len] = 0; /* we terminate this with a zero byte */
+    return info.buffer;
+  }
+  else
+    return strdup("");
+}
+
+char *curl_mvaprintf(const char *format, va_list ap_save)
+{
+  int retcode;
+  struct asprintf info;
+
+  info.buffer = NULL;
+  info.len = 0;
+  info.alloc = 0;
+  info.fail = 0;
+
+  retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save);
+  if((-1 == retcode) || info.fail) {
+    if(info.alloc)
+      free(info.buffer);
+    return NULL;
+  }
+
+  if(info.alloc) {
+    info.buffer[info.len] = 0; /* we terminate this with a zero byte */
+    return info.buffer;
+  }
+  else
+    return strdup("");
+}
+
+static int storebuffer(int output, FILE *data)
+{
+  char **buffer = (char **)data;
+  unsigned char outc = (unsigned char)output;
+  **buffer = outc;
+  (*buffer)++;
+  return outc; /* act like fputc() ! */
+}
+
+int curl_msprintf(char *buffer, const char *format, ...)
+{
+  va_list ap_save; /* argument pointer */
+  int retcode;
+  va_start(ap_save, format);
+  retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
+  va_end(ap_save);
+  *buffer=0; /* we terminate this with a zero byte */
+  return retcode;
+}
+
+int curl_mprintf(const char *format, ...)
+{
+  int retcode;
+  va_list ap_save; /* argument pointer */
+  va_start(ap_save, format);
+
+  retcode = dprintf_formatf(stdout, fputc, format, ap_save);
+  va_end(ap_save);
+  return retcode;
+}
+
+int curl_mfprintf(FILE *whereto, const char *format, ...)
+{
+  int retcode;
+  va_list ap_save; /* argument pointer */
+  va_start(ap_save, format);
+  retcode = dprintf_formatf(whereto, fputc, format, ap_save);
+  va_end(ap_save);
+  return retcode;
+}
+
+int curl_mvsprintf(char *buffer, const char *format, va_list ap_save)
+{
+  int retcode;
+  retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
+  *buffer=0; /* we terminate this with a zero byte */
+  return retcode;
+}
+
+int curl_mvprintf(const char *format, va_list ap_save)
+{
+  return dprintf_formatf(stdout, fputc, format, ap_save);
+}
+
+int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save)
+{
+  return dprintf_formatf(whereto, fputc, format, ap_save);
+}
diff --git a/lib/curl_multi.c b/lib/curl_multi.c
new file mode 100644 (file)
index 0000000..9553883
--- /dev/null
@@ -0,0 +1,2814 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+#include "curl_urldata.h"
+#include "curl_transfer.h"
+#include "curl_url.h"
+#include "curl_connect.h"
+#include "curl_progress.h"
+#include "curl_easyif.h"
+#include "curl_multiif.h"
+#include "curl_sendf.h"
+#include "curl_timeval.h"
+#include "curl_http.h"
+#include "curl_select.h"
+#include "curl_warnless.h"
+#include "curl_speedcheck.h"
+#include "curl_conncache.h"
+#include "curl_bundles.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/*
+  CURL_SOCKET_HASH_TABLE_SIZE should be a prime number. Increasing it from 97
+  to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes.  Still, every
+  CURL handle takes 45-50 K memory, therefore this 3K are not significant.
+*/
+#ifndef CURL_SOCKET_HASH_TABLE_SIZE
+#define CURL_SOCKET_HASH_TABLE_SIZE 911
+#endif
+
+struct Curl_message {
+  /* the 'CURLMsg' is the part that is visible to the external user */
+  struct CURLMsg extmsg;
+};
+
+/* NOTE: if you add a state here, add the name to the statename[] array as
+   well!
+*/
+typedef enum {
+  CURLM_STATE_INIT,        /* 0 - start in this state */
+  CURLM_STATE_CONNECT,     /* 1 - resolve/connect has been sent off */
+  CURLM_STATE_WAITRESOLVE, /* 2 - awaiting the resolve to finalize */
+  CURLM_STATE_WAITCONNECT, /* 3 - awaiting the connect to finalize */
+  CURLM_STATE_WAITPROXYCONNECT, /* 4 - awaiting proxy CONNECT to finalize */
+  CURLM_STATE_PROTOCONNECT, /* 5 - completing the protocol-specific connect
+                               phase */
+  CURLM_STATE_WAITDO,      /* 6 - wait for our turn to send the request */
+  CURLM_STATE_DO,          /* 7 - start send off the request (part 1) */
+  CURLM_STATE_DOING,       /* 8 - sending off the request (part 1) */
+  CURLM_STATE_DO_MORE,     /* 9 - send off the request (part 2) */
+  CURLM_STATE_DO_DONE,     /* 10 - done sending off request */
+  CURLM_STATE_WAITPERFORM, /* 11 - wait for our turn to read the response */
+  CURLM_STATE_PERFORM,     /* 12 - transfer data */
+  CURLM_STATE_TOOFAST,     /* 13 - wait because limit-rate exceeded */
+  CURLM_STATE_DONE,        /* 14 - post data transfer operation */
+  CURLM_STATE_COMPLETED,   /* 15 - operation complete */
+  CURLM_STATE_MSGSENT,     /* 16 - the operation complete message is sent */
+  CURLM_STATE_LAST         /* 17 - not a true state, never use this */
+} CURLMstate;
+
+/* we support N sockets per easy handle. Set the corresponding bit to what
+   action we should wait for */
+#define MAX_SOCKSPEREASYHANDLE 5
+#define GETSOCK_READABLE (0x00ff)
+#define GETSOCK_WRITABLE (0xff00)
+
+struct Curl_one_easy {
+  /* first, two fields for the linked list of these */
+  struct Curl_one_easy *next;
+  struct Curl_one_easy *prev;
+
+  struct SessionHandle *easy_handle; /* the easy handle for this unit */
+  struct connectdata *easy_conn;     /* the "unit's" connection */
+
+  CURLMstate state;  /* the handle's state */
+  CURLcode result;   /* previous result */
+
+  struct Curl_message msg; /* A single posted message. */
+
+  /* Array with the plain socket numbers this handle takes care of, in no
+     particular order. Note that all sockets are added to the sockhash, where
+     the state etc are also kept. This array is mostly used to detect when a
+     socket is to be removed from the hash. See singlesocket(). */
+  curl_socket_t sockets[MAX_SOCKSPEREASYHANDLE];
+  int numsocks;
+};
+
+#define CURL_MULTI_HANDLE 0x000bab1e
+
+#define GOOD_MULTI_HANDLE(x) \
+  ((x) && (((struct Curl_multi *)(x))->type == CURL_MULTI_HANDLE))
+#define GOOD_EASY_HANDLE(x) \
+  ((x) && (((struct SessionHandle *)(x))->magic == CURLEASY_MAGIC_NUMBER))
+
+/* This is the struct known as CURLM on the outside */
+struct Curl_multi {
+  /* First a simple identifier to easier detect if a user mix up
+     this multi handle with an easy handle. Set this to CURL_MULTI_HANDLE. */
+  long type;
+
+  /* We have a doubly-linked circular list with easy handles */
+  struct Curl_one_easy easy;
+
+  int num_easy; /* amount of entries in the linked list above. */
+  int num_alive; /* amount of easy handles that are added but have not yet
+                    reached COMPLETE state */
+
+  struct curl_llist *msglist; /* a list of messages from completed transfers */
+
+  /* callback function and user data pointer for the *socket() API */
+  curl_socket_callback socket_cb;
+  void *socket_userp;
+
+  /* Hostname cache */
+  struct curl_hash *hostcache;
+
+  /* timetree points to the splay-tree of time nodes to figure out expire
+     times of all currently set timers */
+  struct Curl_tree *timetree;
+
+  /* 'sockhash' is the lookup hash for socket descriptor => easy handles (note
+     the pluralis form, there can be more than one easy handle waiting on the
+     same actual socket) */
+  struct curl_hash *sockhash;
+
+  /* Whether pipelining is enabled for this multi handle */
+  bool pipelining_enabled;
+
+  /* Shared connection cache (bundles)*/
+  struct conncache *conn_cache;
+
+  /* This handle will be used for closing the cached connections in
+     curl_multi_cleanup() */
+  struct SessionHandle *closure_handle;
+
+  long maxconnects; /* if >0, a fixed limit of the maximum number of entries
+                       we're allowed to grow the connection cache to */
+
+  /* timer callback and user data pointer for the *socket() API */
+  curl_multi_timer_callback timer_cb;
+  void *timer_userp;
+  struct timeval timer_lastcall; /* the fixed time for the timeout for the
+                                    previous callback */
+};
+
+static void singlesocket(struct Curl_multi *multi,
+                         struct Curl_one_easy *easy);
+static int update_timer(struct Curl_multi *multi);
+
+static CURLcode addHandleToSendOrPendPipeline(struct SessionHandle *handle,
+                                              struct connectdata *conn);
+static int checkPendPipeline(struct connectdata *conn);
+static void moveHandleFromSendToRecvPipeline(struct SessionHandle *handle,
+                                             struct connectdata *conn);
+static void moveHandleFromRecvToDonePipeline(struct SessionHandle *handle,
+                                             struct connectdata *conn);
+static bool isHandleAtHead(struct SessionHandle *handle,
+                           struct curl_llist *pipeline);
+static CURLMcode add_next_timeout(struct timeval now,
+                                  struct Curl_multi *multi,
+                                  struct SessionHandle *d);
+
+#ifdef DEBUGBUILD
+static const char * const statename[]={
+  "INIT",
+  "CONNECT",
+  "WAITRESOLVE",
+  "WAITCONNECT",
+  "WAITPROXYCONNECT",
+  "PROTOCONNECT",
+  "WAITDO",
+  "DO",
+  "DOING",
+  "DO_MORE",
+  "DO_DONE",
+  "WAITPERFORM",
+  "PERFORM",
+  "TOOFAST",
+  "DONE",
+  "COMPLETED",
+  "MSGSENT",
+};
+#endif
+
+static void multi_freetimeout(void *a, void *b);
+
+/* always use this function to change state, to make debugging easier */
+static void multistate(struct Curl_one_easy *easy, CURLMstate state)
+{
+#ifdef DEBUGBUILD
+  long connection_id = -5000;
+#endif
+  CURLMstate oldstate = easy->state;
+
+  if(oldstate == state)
+    /* don't bother when the new state is the same as the old state */
+    return;
+
+  easy->state = state;
+
+#ifdef DEBUGBUILD
+  if(easy->easy_conn) {
+    if(easy->state > CURLM_STATE_CONNECT &&
+       easy->state < CURLM_STATE_COMPLETED)
+      connection_id = easy->easy_conn->connection_id;
+
+    infof(easy->easy_handle,
+          "STATE: %s => %s handle %p; (connection #%ld) \n",
+          statename[oldstate], statename[easy->state],
+          (char *)easy, connection_id);
+  }
+#endif
+  if(state == CURLM_STATE_COMPLETED)
+    /* changing to COMPLETED means there's one less easy handle 'alive' */
+    easy->easy_handle->multi->num_alive--;
+}
+
+/*
+ * We add one of these structs to the sockhash for a particular socket
+ */
+
+struct Curl_sh_entry {
+  struct SessionHandle *easy;
+  time_t timestamp;
+  int action;  /* what action READ/WRITE this socket waits for */
+  curl_socket_t socket; /* mainly to ease debugging */
+  void *socketp; /* settable by users with curl_multi_assign() */
+};
+/* bits for 'action' having no bits means this socket is not expecting any
+   action */
+#define SH_READ  1
+#define SH_WRITE 2
+
+/* make sure this socket is present in the hash for this handle */
+static struct Curl_sh_entry *sh_addentry(struct curl_hash *sh,
+                                         curl_socket_t s,
+                                         struct SessionHandle *data)
+{
+  struct Curl_sh_entry *there =
+    Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t));
+  struct Curl_sh_entry *check;
+
+  if(there)
+    /* it is present, return fine */
+    return there;
+
+  /* not present, add it */
+  check = calloc(1, sizeof(struct Curl_sh_entry));
+  if(!check)
+    return NULL; /* major failure */
+  check->easy = data;
+  check->socket = s;
+
+  /* make/add new hash entry */
+  if(NULL == Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) {
+    free(check);
+    return NULL; /* major failure */
+  }
+
+  return check; /* things are good in sockhash land */
+}
+
+
+/* delete the given socket + handle from the hash */
+static void sh_delentry(struct curl_hash *sh, curl_socket_t s)
+{
+  struct Curl_sh_entry *there =
+    Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t));
+
+  if(there) {
+    /* this socket is in the hash */
+    /* We remove the hash entry. (This'll end up in a call to
+       sh_freeentry().) */
+    Curl_hash_delete(sh, (char *)&s, sizeof(curl_socket_t));
+  }
+}
+
+/*
+ * free a sockhash entry
+ */
+static void sh_freeentry(void *freethis)
+{
+  struct Curl_sh_entry *p = (struct Curl_sh_entry *) freethis;
+
+  if(p)
+    free(p);
+}
+
+static size_t fd_key_compare(void*k1, size_t k1_len, void*k2, size_t k2_len)
+{
+  (void) k1_len; (void) k2_len;
+
+  return (*((int* ) k1)) == (*((int* ) k2));
+}
+
+static size_t hash_fd(void* key, size_t key_length, size_t slots_num)
+{
+  int fd = * ((int* ) key);
+  (void) key_length;
+
+  return (fd % (int)slots_num);
+}
+
+/*
+ * sh_init() creates a new socket hash and returns the handle for it.
+ *
+ * Quote from README.multi_socket:
+ *
+ * "Some tests at 7000 and 9000 connections showed that the socket hash lookup
+ * is somewhat of a bottle neck. Its current implementation may be a bit too
+ * limiting. It simply has a fixed-size array, and on each entry in the array
+ * it has a linked list with entries. So the hash only checks which list to
+ * scan through. The code I had used so for used a list with merely 7 slots
+ * (as that is what the DNS hash uses) but with 7000 connections that would
+ * make an average of 1000 nodes in each list to run through. I upped that to
+ * 97 slots (I believe a prime is suitable) and noticed a significant speed
+ * increase.  I need to reconsider the hash implementation or use a rather
+ * large default value like this. At 9000 connections I was still below 10us
+ * per call."
+ *
+ */
+static struct curl_hash *sh_init(void)
+{
+  return Curl_hash_alloc(CURL_SOCKET_HASH_TABLE_SIZE, hash_fd, fd_key_compare,
+                         sh_freeentry);
+}
+
+/*
+ * multi_addmsg()
+ *
+ * Called when a transfer is completed. Adds the given msg pointer to
+ * the list kept in the multi handle.
+ */
+static CURLMcode multi_addmsg(struct Curl_multi *multi,
+                              struct Curl_message *msg)
+{
+  if(!Curl_llist_insert_next(multi->msglist, multi->msglist->tail, msg))
+    return CURLM_OUT_OF_MEMORY;
+
+  return CURLM_OK;
+}
+
+/*
+ * multi_freeamsg()
+ *
+ * Callback used by the llist system when a single list entry is destroyed.
+ */
+static void multi_freeamsg(void *a, void *b)
+{
+  (void)a;
+  (void)b;
+}
+
+CURLM *curl_multi_init(void)
+{
+  struct Curl_multi *multi = calloc(1, sizeof(struct Curl_multi));
+
+  if(!multi)
+    return NULL;
+
+  multi->type = CURL_MULTI_HANDLE;
+
+  multi->hostcache = Curl_mk_dnscache();
+  if(!multi->hostcache)
+    goto error;
+
+  multi->sockhash = sh_init();
+  if(!multi->sockhash)
+    goto error;
+
+  multi->conn_cache = Curl_conncache_init(CONNCACHE_MULTI);
+  if(!multi->conn_cache)
+    goto error;
+
+  multi->msglist = Curl_llist_alloc(multi_freeamsg);
+  if(!multi->msglist)
+    goto error;
+
+  /* Let's make the doubly-linked list a circular list.  This makes
+     the linked list code simpler and allows inserting at the end
+     with less work (we didn't keep a tail pointer before). */
+  multi->easy.next = &multi->easy;
+  multi->easy.prev = &multi->easy;
+
+  return (CURLM *) multi;
+
+  error:
+
+  Curl_hash_destroy(multi->sockhash);
+  multi->sockhash = NULL;
+  Curl_hash_destroy(multi->hostcache);
+  multi->hostcache = NULL;
+  Curl_conncache_destroy(multi->conn_cache);
+  multi->conn_cache = NULL;
+
+  free(multi);
+  return NULL;
+}
+
+CURLMcode curl_multi_add_handle(CURLM *multi_handle,
+                                CURL *easy_handle)
+{
+  struct curl_llist *timeoutlist;
+  struct Curl_one_easy *easy;
+  struct Curl_multi *multi = (struct Curl_multi *)multi_handle;
+  struct SessionHandle *data = (struct SessionHandle *)easy_handle;
+  struct SessionHandle *new_closure = NULL;
+  struct curl_hash *hostcache = NULL;
+
+  /* First, make some basic checks that the CURLM handle is a good handle */
+  if(!GOOD_MULTI_HANDLE(multi))
+    return CURLM_BAD_HANDLE;
+
+  /* Verify that we got a somewhat good easy handle too */
+  if(!GOOD_EASY_HANDLE(easy_handle))
+    return CURLM_BAD_EASY_HANDLE;
+
+  /* Prevent users from adding same easy handle more than
+     once and prevent adding to more than one multi stack */
+  if(data->multi)
+    /* possibly we should create a new unique error code for this condition */
+    return CURLM_BAD_EASY_HANDLE;
+
+  /* Allocate and initialize timeout list for easy handle */
+  timeoutlist = Curl_llist_alloc(multi_freetimeout);
+  if(!timeoutlist)
+    return CURLM_OUT_OF_MEMORY;
+
+  /* Allocate new node for the doubly-linked circular list of
+     Curl_one_easy structs that holds pointers to easy handles */
+  easy = calloc(1, sizeof(struct Curl_one_easy));
+  if(!easy) {
+    Curl_llist_destroy(timeoutlist, NULL);
+    return CURLM_OUT_OF_MEMORY;
+  }
+
+  /* In case multi handle has no hostcache yet, allocate one */
+  if(!multi->hostcache) {
+    hostcache = Curl_mk_dnscache();
+    if(!hostcache) {
+      free(easy);
+      Curl_llist_destroy(timeoutlist, NULL);
+      return CURLM_OUT_OF_MEMORY;
+    }
+  }
+
+  /* In case multi handle has no closure_handle yet, allocate
+     a new easy handle to use when closing cached connections */
+  if(!multi->closure_handle) {
+    new_closure = (struct SessionHandle *)curl_easy_init();
+    if(!new_closure) {
+      Curl_hash_destroy(hostcache);
+      free(easy);
+      Curl_llist_destroy(timeoutlist, NULL);
+      return CURLM_OUT_OF_MEMORY;
+    }
+  }
+
+  /*
+  ** No failure allowed in this function beyond this point. And
+  ** no modification of easy nor multi handle allowed before this
+  ** except for potential multi's connection cache growing which
+  ** won't be undone in this function no matter what.
+  */
+
+  /* In case a new closure handle has been initialized above, it
+     is associated now with the multi handle which lacked one. */
+  if(new_closure) {
+    multi->closure_handle = new_closure;
+    Curl_easy_addmulti(multi->closure_handle, multi_handle);
+    multi->closure_handle->state.conn_cache = multi->conn_cache;
+  }
+
+  /* In case hostcache has been allocated above,
+     it is associated now with the multi handle. */
+  if(hostcache)
+    multi->hostcache = hostcache;
+
+  /* Make easy handle use timeout list initialized above */
+  data->state.timeoutlist = timeoutlist;
+  timeoutlist = NULL;
+
+  /* set the easy handle */
+  easy->easy_handle = data;
+  multistate(easy, CURLM_STATE_INIT);
+
+  /* set the back pointer to one_easy to assist in removal */
+  easy->easy_handle->multi_pos =  easy;
+
+  /* for multi interface connections, we share DNS cache automatically if the
+     easy handle's one is currently private. */
+  if(easy->easy_handle->dns.hostcache &&
+     (easy->easy_handle->dns.hostcachetype == HCACHE_PRIVATE)) {
+    Curl_hash_destroy(easy->easy_handle->dns.hostcache);
+    easy->easy_handle->dns.hostcache = NULL;
+    easy->easy_handle->dns.hostcachetype = HCACHE_NONE;
+  }
+
+  if(!easy->easy_handle->dns.hostcache ||
+     (easy->easy_handle->dns.hostcachetype == HCACHE_NONE)) {
+    easy->easy_handle->dns.hostcache = multi->hostcache;
+    easy->easy_handle->dns.hostcachetype = HCACHE_MULTI;
+  }
+
+  /* On a multi stack the connection cache, owned by the multi handle,
+     is shared between all easy handles within the multi handle.
+     Therefore we free the private connection cache if there is one */
+  if(easy->easy_handle->state.conn_cache &&
+     easy->easy_handle->state.conn_cache->type == CONNCACHE_PRIVATE) {
+    Curl_conncache_destroy(easy->easy_handle->state.conn_cache);
+  }
+
+  /* Point now to this multi's connection cache */
+  easy->easy_handle->state.conn_cache = multi->conn_cache;
+
+  /* This adds the new entry at the 'end' of the doubly-linked circular
+     list of Curl_one_easy structs to try and maintain a FIFO queue so
+     the pipelined requests are in order. */
+
+  /* We add this new entry last in the list. We make our 'next' point to the
+     'first' struct and our 'prev' point to the previous 'prev' */
+  easy->next = &multi->easy;
+  easy->prev = multi->easy.prev;
+
+  /* make 'easy' the last node in the chain */
+  multi->easy.prev = easy;
+
+  /* if there was a prev node, make sure its 'next' pointer links to
+     the new node */
+  easy->prev->next = easy;
+
+  /* make the SessionHandle refer back to this multi handle */
+  Curl_easy_addmulti(easy_handle, multi_handle);
+
+  /* make the SessionHandle struct refer back to this struct */
+  easy->easy_handle->set.one_easy = easy;
+
+  /* Set the timeout for this handle to expire really soon so that it will
+     be taken care of even when this handle is added in the midst of operation
+     when only the curl_multi_socket() API is used. During that flow, only
+     sockets that time-out or have actions will be dealt with. Since this
+     handle has no action yet, we make sure it times out to get things to
+     happen. */
+  Curl_expire(easy->easy_handle, 1);
+
+  /* increase the node-counter */
+  multi->num_easy++;
+
+  /* increase the alive-counter */
+  multi->num_alive++;
+
+  /* A somewhat crude work-around for a little glitch in update_timer() that
+     happens if the lastcall time is set to the same time when the handle is
+     removed as when the next handle is added, as then the check in
+     update_timer() that prevents calling the application multiple times with
+     the same timer infor will not trigger and then the new handle's timeout
+     will not be notified to the app.
+
+     The work-around is thus simply to clear the 'lastcall' variable to force
+     update_timer() to always trigger a callback to the app when a new easy
+     handle is added */
+  memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall));
+
+  update_timer(multi);
+  return CURLM_OK;
+}
+
+#if 0
+/* Debug-function, used like this:
+ *
+ * Curl_hash_print(multi->sockhash, debug_print_sock_hash);
+ *
+ * Enable the hash print function first by editing curl_hash.c
+ */
+static void debug_print_sock_hash(void *p)
+{
+  struct Curl_sh_entry *sh = (struct Curl_sh_entry *)p;
+
+  fprintf(stderr, " [easy %p/magic %x/socket %d]",
+          (void *)sh->easy, sh->easy->magic, (int)sh->socket);
+}
+#endif
+
+CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
+                                   CURL *curl_handle)
+{
+  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
+  struct Curl_one_easy *easy;
+  struct SessionHandle *data = curl_handle;
+
+  /* First, make some basic checks that the CURLM handle is a good handle */
+  if(!GOOD_MULTI_HANDLE(multi))
+    return CURLM_BAD_HANDLE;
+
+  /* Verify that we got a somewhat good easy handle too */
+  if(!GOOD_EASY_HANDLE(curl_handle))
+    return CURLM_BAD_EASY_HANDLE;
+
+  /* pick-up from the 'curl_handle' the kept position in the list */
+  easy = data->multi_pos;
+
+  if(easy) {
+    bool premature = (easy->state < CURLM_STATE_COMPLETED) ? TRUE : FALSE;
+    bool easy_owns_conn = (easy->easy_conn &&
+                           (easy->easy_conn->data == easy->easy_handle)) ?
+                           TRUE : FALSE;
+
+    /* If the 'state' is not INIT or COMPLETED, we might need to do something
+       nice to put the easy_handle in a good known state when this returns. */
+    if(premature)
+      /* this handle is "alive" so we need to count down the total number of
+         alive connections when this is removed */
+      multi->num_alive--;
+
+    if(easy->easy_conn &&
+       (easy->easy_conn->send_pipe->size +
+        easy->easy_conn->recv_pipe->size > 1) &&
+       easy->state > CURLM_STATE_WAITDO &&
+       easy->state < CURLM_STATE_COMPLETED) {
+      /* If the handle is in a pipeline and has started sending off its
+         request but not received its response yet, we need to close
+         connection. */
+      easy->easy_conn->bits.close = TRUE;
+      /* Set connection owner so that Curl_done() closes it.
+         We can sefely do this here since connection is killed. */
+      easy->easy_conn->data = easy->easy_handle;
+    }
+
+    /* The timer must be shut down before easy->multi is set to NULL,
+       else the timenode will remain in the splay tree after
+       curl_easy_cleanup is called. */
+    Curl_expire(easy->easy_handle, 0);
+
+    /* destroy the timeout list that is held in the easy handle */
+    if(data->state.timeoutlist) {
+      Curl_llist_destroy(data->state.timeoutlist, NULL);
+      data->state.timeoutlist = NULL;
+    }
+
+    if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) {
+      if(multi->num_easy == 1) {
+        if(easy_owns_conn) {
+          Curl_resolver_cancel(easy->easy_conn);
+          if(easy->easy_conn->dns_entry) {
+            Curl_resolv_unlock(easy->easy_handle, easy->easy_conn->dns_entry);
+            easy->easy_conn->dns_entry = NULL;
+          }
+        }
+        Curl_hostcache_destroy(easy->easy_handle);
+        multi->hostcache = NULL;
+      }
+      /* clear out the usage of the shared DNS cache */
+      easy->easy_handle->dns.hostcache = NULL;
+      easy->easy_handle->dns.hostcachetype = HCACHE_NONE;
+    }
+
+    if(easy->easy_conn) {
+
+      /* we must call Curl_done() here (if we still "own it") so that we don't
+         leave a half-baked one around */
+      if(easy_owns_conn) {
+
+        /* Curl_done() clears the conn->data field to lose the association
+           between the easy handle and the connection
+
+           Note that this ignores the return code simply because there's
+           nothing really useful to do with it anyway! */
+        (void)Curl_done(&easy->easy_conn, easy->result, premature);
+      }
+      else
+        /* Clear connection pipelines, if Curl_done above was not called */
+        Curl_getoff_all_pipelines(easy->easy_handle, easy->easy_conn);
+    }
+
+    if(easy->easy_handle->state.conn_cache->type == CONNCACHE_MULTI) {
+      /* if this was using the shared connection cache we clear the pointer
+         to that since we're not part of that handle anymore */
+      easy->easy_handle->state.conn_cache = NULL;
+      easy->easy_handle->state.lastconnect = NULL;
+    }
+
+    /* change state without using multistate(), only to make singlesocket() do
+       what we want */
+    easy->state = CURLM_STATE_COMPLETED;
+    singlesocket(multi, easy); /* to let the application know what sockets
+                                  that vanish with this handle */
+
+    /* Remove the association between the connection and the handle */
+    if(easy->easy_conn) {
+      easy->easy_conn->data = NULL;
+      easy->easy_conn = NULL;
+    }
+
+    Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association
+                                                    to this multi handle */
+
+    {
+      /* make sure there's no pending message in the queue sent from this easy
+         handle */
+      struct curl_llist_element *e;
+
+      for(e = multi->msglist->head; e; e = e->next) {
+        struct Curl_message *msg = e->ptr;
+
+        if(msg->extmsg.easy_handle == easy->easy_handle) {
+          Curl_llist_remove(multi->msglist, e, NULL);
+          /* there can only be one from this specific handle */
+          break;
+        }
+      }
+    }
+
+    /* make the previous node point to our next */
+    if(easy->prev)
+      easy->prev->next = easy->next;
+    /* make our next point to our previous node */
+    if(easy->next)
+      easy->next->prev = easy->prev;
+
+    easy->easy_handle->set.one_easy = NULL; /* detached */
+
+    /* Null the position in the controlling structure */
+    easy->easy_handle->multi_pos = NULL;
+
+    /* NOTE NOTE NOTE
+       We do not touch the easy handle here! */
+    free(easy);
+
+    multi->num_easy--; /* one less to care about now */
+
+    update_timer(multi);
+    return CURLM_OK;
+  }
+  else
+    return CURLM_BAD_EASY_HANDLE; /* twasn't found */
+}
+
+bool Curl_multi_canPipeline(const struct Curl_multi* multi)
+{
+  return multi->pipelining_enabled;
+}
+
+void Curl_multi_handlePipeBreak(struct SessionHandle *data)
+{
+  struct Curl_one_easy *one_easy = data->set.one_easy;
+
+  if(one_easy)
+    one_easy->easy_conn = NULL;
+}
+
+static int waitconnect_getsock(struct connectdata *conn,
+                               curl_socket_t *sock,
+                               int numsocks)
+{
+  if(!numsocks)
+    return GETSOCK_BLANK;
+
+  sock[0] = conn->sock[FIRSTSOCKET];
+
+  /* when we've sent a CONNECT to a proxy, we should rather wait for the
+     socket to become readable to be able to get the response headers */
+  if(conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
+    return GETSOCK_READSOCK(0);
+
+  return GETSOCK_WRITESOCK(0);
+}
+
+static int domore_getsock(struct connectdata *conn,
+                          curl_socket_t *socks,
+                          int numsocks)
+{
+  if(conn && conn->handler->domore_getsock)
+    return conn->handler->domore_getsock(conn, socks, numsocks);
+  return GETSOCK_BLANK;
+}
+
+/* returns bitmapped flags for this handle and its sockets */
+static int multi_getsock(struct Curl_one_easy *easy,
+                         curl_socket_t *socks, /* points to numsocks number
+                                                  of sockets */
+                         int numsocks)
+{
+  /* If the pipe broke, or if there's no connection left for this easy handle,
+     then we MUST bail out now with no bitmask set. The no connection case can
+     happen when this is called from curl_multi_remove_handle() =>
+     singlesocket() => multi_getsock().
+  */
+  if(easy->easy_handle->state.pipe_broke || !easy->easy_conn)
+    return 0;
+
+  if(easy->state > CURLM_STATE_CONNECT &&
+     easy->state < CURLM_STATE_COMPLETED) {
+    /* Set up ownership correctly */
+    easy->easy_conn->data = easy->easy_handle;
+  }
+
+  switch(easy->state) {
+  default:
+#if 0 /* switch back on these cases to get the compiler to check for all enums
+         to be present */
+  case CURLM_STATE_TOOFAST:  /* returns 0, so will not select. */
+  case CURLM_STATE_COMPLETED:
+  case CURLM_STATE_MSGSENT:
+  case CURLM_STATE_INIT:
+  case CURLM_STATE_CONNECT:
+  case CURLM_STATE_WAITDO:
+  case CURLM_STATE_DONE:
+  case CURLM_STATE_LAST:
+    /* this will get called with CURLM_STATE_COMPLETED when a handle is
+       removed */
+#endif
+    return 0;
+
+  case CURLM_STATE_WAITRESOLVE:
+    return Curl_resolver_getsock(easy->easy_conn, socks, numsocks);
+
+  case CURLM_STATE_PROTOCONNECT:
+    return Curl_protocol_getsock(easy->easy_conn, socks, numsocks);
+
+  case CURLM_STATE_DO:
+  case CURLM_STATE_DOING:
+    return Curl_doing_getsock(easy->easy_conn, socks, numsocks);
+
+  case CURLM_STATE_WAITPROXYCONNECT:
+  case CURLM_STATE_WAITCONNECT:
+    return waitconnect_getsock(easy->easy_conn, socks, numsocks);
+
+  case CURLM_STATE_DO_MORE:
+    return domore_getsock(easy->easy_conn, socks, numsocks);
+
+  case CURLM_STATE_DO_DONE: /* since is set after DO is completed, we switch
+                               to waiting for the same as the *PERFORM
+                               states */
+  case CURLM_STATE_PERFORM:
+  case CURLM_STATE_WAITPERFORM:
+    return Curl_single_getsock(easy->easy_conn, socks, numsocks);
+  }
+
+}
+
+CURLMcode curl_multi_fdset(CURLM *multi_handle,
+                           fd_set *read_fd_set, fd_set *write_fd_set,
+                           fd_set *exc_fd_set, int *max_fd)
+{
+  /* Scan through all the easy handles to get the file descriptors set.
+     Some easy handles may not have connected to the remote host yet,
+     and then we must make sure that is done. */
+  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
+  struct Curl_one_easy *easy;
+  int this_max_fd=-1;
+  curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE];
+  int bitmap;
+  int i;
+  (void)exc_fd_set; /* not used */
+
+  if(!GOOD_MULTI_HANDLE(multi))
+    return CURLM_BAD_HANDLE;
+
+  easy=multi->easy.next;
+  while(easy != &multi->easy) {
+    bitmap = multi_getsock(easy, sockbunch, MAX_SOCKSPEREASYHANDLE);
+
+    for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) {
+      curl_socket_t s = CURL_SOCKET_BAD;
+
+      if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK((sockbunch[i]))) {
+        FD_SET(sockbunch[i], read_fd_set);
+        s = sockbunch[i];
+      }
+      if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK((sockbunch[i]))) {
+        FD_SET(sockbunch[i], write_fd_set);
+        s = sockbunch[i];
+      }
+      if(s == CURL_SOCKET_BAD)
+        /* this socket is unused, break out of loop */
+        break;
+      else {
+        if((int)s > this_max_fd)
+          this_max_fd = (int)s;
+      }
+    }
+
+    easy = easy->next; /* check next handle */
+  }
+
+  *max_fd = this_max_fd;
+
+  return CURLM_OK;
+}
+
+CURLMcode curl_multi_wait(CURLM *multi_handle,
+                          struct curl_waitfd extra_fds[],
+                          unsigned int extra_nfds,
+                          int timeout_ms,
+                          int *ret)
+{
+  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
+  struct Curl_one_easy *easy;
+  curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE];
+  int bitmap;
+  unsigned int i;
+  unsigned int nfds = extra_nfds;
+  struct pollfd *ufds = NULL;
+
+  if(!GOOD_MULTI_HANDLE(multi))
+    return CURLM_BAD_HANDLE;
+
+  /* Count up how many fds we have from the multi handle */
+  easy=multi->easy.next;
+  while(easy != &multi->easy) {
+    bitmap = multi_getsock(easy, sockbunch, MAX_SOCKSPEREASYHANDLE);
+
+    for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) {
+      curl_socket_t s = CURL_SOCKET_BAD;
+
+      if(bitmap & GETSOCK_READSOCK(i)) {
+        ++nfds;
+        s = sockbunch[i];
+      }
+      if(bitmap & GETSOCK_WRITESOCK(i)) {
+        ++nfds;
+        s = sockbunch[i];
+      }
+      if(s == CURL_SOCKET_BAD) {
+        break;
+      }
+    }
+
+    easy = easy->next; /* check next handle */
+  }
+
+  if(nfds) {
+    ufds = malloc(nfds * sizeof(struct pollfd));
+    if(!ufds)
+      return CURLM_OUT_OF_MEMORY;
+  }
+  nfds = 0;
+
+  /* Add the curl handles to our pollfds first */
+  easy=multi->easy.next;
+  while(easy != &multi->easy) {
+    bitmap = multi_getsock(easy, sockbunch, MAX_SOCKSPEREASYHANDLE);
+
+    for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) {
+      curl_socket_t s = CURL_SOCKET_BAD;
+
+      if(bitmap & GETSOCK_READSOCK(i)) {
+        ufds[nfds].fd = sockbunch[i];
+        ufds[nfds].events = POLLIN;
+        ++nfds;
+        s = sockbunch[i];
+      }
+      if(bitmap & GETSOCK_WRITESOCK(i)) {
+        ufds[nfds].fd = sockbunch[i];
+        ufds[nfds].events = POLLOUT;
+        ++nfds;
+        s = sockbunch[i];
+      }
+      if(s == CURL_SOCKET_BAD) {
+        break;
+      }
+    }
+
+    easy = easy->next; /* check next handle */
+  }
+
+  /* Add external file descriptions from poll-like struct curl_waitfd */
+  for(i = 0; i < extra_nfds; i++) {
+    ufds[nfds].fd = extra_fds[i].fd;
+    ufds[nfds].events = 0;
+    if(extra_fds[i].events & CURL_WAIT_POLLIN)
+      ufds[nfds].events |= POLLIN;
+    if(extra_fds[i].events & CURL_WAIT_POLLPRI)
+      ufds[nfds].events |= POLLPRI;
+    if(extra_fds[i].events & CURL_WAIT_POLLOUT)
+      ufds[nfds].events |= POLLOUT;
+    ++nfds;
+  }
+
+  if(nfds)
+    /* wait... */
+    i = Curl_poll(ufds, nfds, timeout_ms);
+  else
+    i = 0;
+
+  Curl_safefree(ufds);
+  if(ret)
+    *ret = i;
+  return CURLM_OK;
+}
+
+static CURLMcode multi_runsingle(struct Curl_multi *multi,
+                                 struct timeval now,
+                                 struct Curl_one_easy *easy)
+{
+  struct Curl_message *msg = NULL;
+  bool connected;
+  bool async;
+  bool protocol_connect = FALSE;
+  bool dophase_done;
+  bool done = FALSE;
+  CURLMcode result = CURLM_OK;
+  struct SingleRequest *k;
+  struct SessionHandle *data;
+  long timeout_ms;
+
+  if(!GOOD_EASY_HANDLE(easy->easy_handle))
+    return CURLM_BAD_EASY_HANDLE;
+
+  data = easy->easy_handle;
+
+  do {
+    /* this is a single-iteration do-while loop just to allow a
+       break to skip to the end of it */
+    bool disconnect_conn = FALSE;
+
+    /* Handle the case when the pipe breaks, i.e., the connection
+       we're using gets cleaned up and we're left with nothing. */
+    if(data->state.pipe_broke) {
+      infof(data, "Pipe broke: handle 0x%p, url = %s\n",
+            easy, data->state.path);
+
+      if(easy->state < CURLM_STATE_COMPLETED) {
+        /* Head back to the CONNECT state */
+        multistate(easy, CURLM_STATE_CONNECT);
+        result = CURLM_CALL_MULTI_PERFORM;
+        easy->result = CURLE_OK;
+      }
+
+      data->state.pipe_broke = FALSE;
+      easy->easy_conn = NULL;
+      break;
+    }
+
+    if(!easy->easy_conn &&
+       easy->state > CURLM_STATE_CONNECT &&
+       easy->state < CURLM_STATE_DONE) {
+      /* In all these states, the code will blindly access 'easy->easy_conn'
+         so this is precaution that it isn't NULL. And it silences static
+         analyzers. */
+      failf(data, "In state %d with no easy_conn, bail out!\n", easy->state);
+      return CURLM_INTERNAL_ERROR;
+    }
+
+    if(easy->easy_conn && easy->state > CURLM_STATE_CONNECT &&
+       easy->state < CURLM_STATE_COMPLETED)
+      /* Make sure we set the connection's current owner */
+      easy->easy_conn->data = data;
+
+    if(easy->easy_conn &&
+       (easy->state >= CURLM_STATE_CONNECT) &&
+       (easy->state < CURLM_STATE_COMPLETED)) {
+      /* we need to wait for the connect state as only then is the start time
+         stored, but we must not check already completed handles */
+
+      timeout_ms = Curl_timeleft(data, &now,
+                                 (easy->state <= CURLM_STATE_WAITDO)?
+                                 TRUE:FALSE);
+
+      if(timeout_ms < 0) {
+        /* Handle timed out */
+        if(easy->state == CURLM_STATE_WAITRESOLVE)
+          failf(data, "Resolving timed out after %ld milliseconds",
+                Curl_tvdiff(now, data->progress.t_startsingle));
+        else if(easy->state == CURLM_STATE_WAITCONNECT)
+          failf(data, "Connection timed out after %ld milliseconds",
+                Curl_tvdiff(now, data->progress.t_startsingle));
+        else {
+          k = &data->req;
+          failf(data, "Operation timed out after %ld milliseconds with %"
+                FORMAT_OFF_T " out of %" FORMAT_OFF_T " bytes received",
+                Curl_tvdiff(now, data->progress.t_startsingle), k->bytecount,
+                k->size);
+        }
+
+        /* Force the connection closed because the server could continue to
+           send us stuff at any time. (The disconnect_conn logic used below
+           doesn't work at this point). */
+        easy->easy_conn->bits.close = TRUE;
+        easy->result = CURLE_OPERATION_TIMEDOUT;
+        multistate(easy, CURLM_STATE_COMPLETED);
+        break;
+      }
+    }
+
+    switch(easy->state) {
+    case CURLM_STATE_INIT:
+      /* init this transfer. */
+      easy->result=Curl_pretransfer(data);
+
+      if(CURLE_OK == easy->result) {
+        /* after init, go CONNECT */
+        multistate(easy, CURLM_STATE_CONNECT);
+        result = CURLM_CALL_MULTI_PERFORM;
+
+        data->state.used_interface = Curl_if_multi;
+      }
+      break;
+
+    case CURLM_STATE_CONNECT:
+      /* Connect. We get a connection identifier filled in. */
+      Curl_pgrsTime(data, TIMER_STARTSINGLE);
+      easy->result = Curl_connect(data, &easy->easy_conn,
+                                  &async, &protocol_connect);
+
+      if(CURLE_OK == easy->result) {
+        /* Add this handle to the send or pend pipeline */
+        easy->result = addHandleToSendOrPendPipeline(data,
+                                                     easy->easy_conn);
+        if(CURLE_OK != easy->result)
+          disconnect_conn = TRUE;
+        else {
+          if(async)
+            /* We're now waiting for an asynchronous name lookup */
+            multistate(easy, CURLM_STATE_WAITRESOLVE);
+          else {
+            /* after the connect has been sent off, go WAITCONNECT unless the
+               protocol connect is already done and we can go directly to
+               WAITDO or DO! */
+            result = CURLM_CALL_MULTI_PERFORM;
+
+            if(protocol_connect)
+              multistate(easy, multi->pipelining_enabled?
+                         CURLM_STATE_WAITDO:CURLM_STATE_DO);
+            else {
+#ifndef CURL_DISABLE_HTTP
+              if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
+                multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
+              else
+#endif
+                multistate(easy, CURLM_STATE_WAITCONNECT);
+            }
+          }
+        }
+      }
+      break;
+
+    case CURLM_STATE_WAITRESOLVE:
+      /* awaiting an asynch name resolve to complete */
+    {
+      struct Curl_dns_entry *dns = NULL;
+
+      /* check if we have the name resolved by now */
+      easy->result = Curl_resolver_is_resolved(easy->easy_conn, &dns);
+
+      /* Update sockets here, because the socket(s) may have been
+         closed and the application thus needs to be told, even if it
+         is likely that the same socket(s) will again be used further
+         down.  If the name has not yet been resolved, it is likely
+         that new sockets have been opened in an attempt to contact
+         another resolver. */
+      singlesocket(multi, easy);
+
+      if(dns) {
+        /* Perform the next step in the connection phase, and then move on
+           to the WAITCONNECT state */
+        easy->result = Curl_async_resolved(easy->easy_conn,
+                                           &protocol_connect);
+
+        if(CURLE_OK != easy->result)
+          /* if Curl_async_resolved() returns failure, the connection struct
+             is already freed and gone */
+          easy->easy_conn = NULL;           /* no more connection */
+        else {
+          /* call again please so that we get the next socket setup */
+          result = CURLM_CALL_MULTI_PERFORM;
+          if(protocol_connect)
+            multistate(easy, multi->pipelining_enabled?
+                       CURLM_STATE_WAITDO:CURLM_STATE_DO);
+          else {
+#ifndef CURL_DISABLE_HTTP
+            if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
+              multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
+            else
+#endif
+              multistate(easy, CURLM_STATE_WAITCONNECT);
+          }
+        }
+      }
+
+      if(CURLE_OK != easy->result) {
+        /* failure detected */
+        disconnect_conn = TRUE;
+        break;
+      }
+    }
+    break;
+
+#ifndef CURL_DISABLE_HTTP
+    case CURLM_STATE_WAITPROXYCONNECT:
+      /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */
+      easy->result = Curl_http_connect(easy->easy_conn, &protocol_connect);
+
+      if(easy->easy_conn->bits.proxy_connect_closed) {
+        /* reset the error buffer */
+        if(data->set.errorbuffer)
+          data->set.errorbuffer[0] = '\0';
+        data->state.errorbuf = FALSE;
+
+        easy->result = CURLE_OK;
+        result = CURLM_CALL_MULTI_PERFORM;
+        multistate(easy, CURLM_STATE_CONNECT);
+      }
+      else if(CURLE_OK == easy->result) {
+        if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_COMPLETE)
+          multistate(easy, CURLM_STATE_WAITCONNECT);
+      }
+      break;
+#endif
+
+    case CURLM_STATE_WAITCONNECT:
+      /* awaiting a completion of an asynch connect */
+      easy->result = Curl_is_connected(easy->easy_conn,
+                                       FIRSTSOCKET,
+                                       &connected);
+      if(connected) {
+
+        if(!easy->result)
+          /* if everything is still fine we do the protocol-specific connect
+             setup */
+          easy->result = Curl_protocol_connect(easy->easy_conn,
+                                               &protocol_connect);
+      }
+
+      if(CURLE_OK != easy->result) {
+        /* failure detected */
+        /* Just break, the cleaning up is handled all in one place */
+        disconnect_conn = TRUE;
+        break;
+      }
+
+      if(connected) {
+        if(!protocol_connect) {
+          /* We have a TCP connection, but 'protocol_connect' may be false
+             and then we continue to 'STATE_PROTOCONNECT'. If protocol
+             connect is TRUE, we move on to STATE_DO.
+             BUT if we are using a proxy we must change to WAITPROXYCONNECT
+          */
+#ifndef CURL_DISABLE_HTTP
+          if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
+            multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
+          else
+#endif
+            multistate(easy, CURLM_STATE_PROTOCONNECT);
+
+        }
+        else
+          /* after the connect has completed, go WAITDO or DO */
+          multistate(easy, multi->pipelining_enabled?
+                     CURLM_STATE_WAITDO:CURLM_STATE_DO);
+
+        result = CURLM_CALL_MULTI_PERFORM;
+      }
+      break;
+
+    case CURLM_STATE_PROTOCONNECT:
+      /* protocol-specific connect phase */
+      easy->result = Curl_protocol_connecting(easy->easy_conn,
+                                              &protocol_connect);
+      if((easy->result == CURLE_OK) && protocol_connect) {
+        /* after the connect has completed, go WAITDO or DO */
+        multistate(easy, multi->pipelining_enabled?
+                   CURLM_STATE_WAITDO:CURLM_STATE_DO);
+        result = CURLM_CALL_MULTI_PERFORM;
+      }
+      else if(easy->result) {
+        /* failure detected */
+        Curl_posttransfer(data);
+        Curl_done(&easy->easy_conn, easy->result, TRUE);
+        disconnect_conn = TRUE;
+      }
+      break;
+
+    case CURLM_STATE_WAITDO:
+      /* Wait for our turn to DO when we're pipelining requests */
+#ifdef DEBUGBUILD
+      infof(data, "WAITDO: Conn %ld send pipe %zu inuse %d athead %d\n",
+            easy->easy_conn->connection_id,
+            easy->easy_conn->send_pipe->size,
+            easy->easy_conn->writechannel_inuse?1:0,
+            isHandleAtHead(data,
+                           easy->easy_conn->send_pipe)?1:0);
+#endif
+      if(!easy->easy_conn->writechannel_inuse &&
+         isHandleAtHead(data,
+                        easy->easy_conn->send_pipe)) {
+        /* Grab the channel */
+        easy->easy_conn->writechannel_inuse = TRUE;
+        multistate(easy, CURLM_STATE_DO);
+        result = CURLM_CALL_MULTI_PERFORM;
+      }
+      break;
+
+    case CURLM_STATE_DO:
+      if(data->set.connect_only) {
+        /* keep connection open for application to use the socket */
+        easy->easy_conn->bits.close = FALSE;
+        multistate(easy, CURLM_STATE_DONE);
+        easy->result = CURLE_OK;
+        result = CURLM_CALL_MULTI_PERFORM;
+      }
+      else {
+        /* Perform the protocol's DO action */
+        easy->result = Curl_do(&easy->easy_conn,
+                               &dophase_done);
+
+        if(CURLE_OK == easy->result) {
+          if(!dophase_done) {
+            /* some steps needed for wildcard matching */
+            if(data->set.wildcardmatch) {
+              struct WildcardData *wc = &data->wildcard;
+              if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) {
+                /* skip some states if it is important */
+                Curl_done(&easy->easy_conn, CURLE_OK, FALSE);
+                multistate(easy, CURLM_STATE_DONE);
+                result = CURLM_CALL_MULTI_PERFORM;
+                break;
+              }
+            }
+            /* DO was not completed in one function call, we must continue
+               DOING... */
+            multistate(easy, CURLM_STATE_DOING);
+            result = CURLM_OK;
+          }
+
+          /* after DO, go DO_DONE... or DO_MORE */
+          else if(easy->easy_conn->bits.do_more) {
+            /* we're supposed to do more, but we need to sit down, relax
+               and wait a little while first */
+            multistate(easy, CURLM_STATE_DO_MORE);
+            result = CURLM_OK;
+          }
+          else {
+            /* we're done with the DO, now DO_DONE */
+            multistate(easy, CURLM_STATE_DO_DONE);
+            result = CURLM_CALL_MULTI_PERFORM;
+          }
+        }
+        else if((CURLE_SEND_ERROR == easy->result) &&
+                easy->easy_conn->bits.reuse) {
+          /*
+           * In this situation, a connection that we were trying to use
+           * may have unexpectedly died.  If possible, send the connection
+           * back to the CONNECT phase so we can try again.
+           */
+          char *newurl = NULL;
+          followtype follow=FOLLOW_NONE;
+          CURLcode drc;
+          bool retry = FALSE;
+
+          drc = Curl_retry_request(easy->easy_conn, &newurl);
+          if(drc) {
+            /* a failure here pretty much implies an out of memory */
+            easy->result = drc;
+            disconnect_conn = TRUE;
+          }
+          else
+            retry = (newurl)?TRUE:FALSE;
+
+          Curl_posttransfer(data);
+          drc = Curl_done(&easy->easy_conn, easy->result, FALSE);
+
+          /* When set to retry the connection, we must to go back to
+           * the CONNECT state */
+          if(retry) {
+            if((drc == CURLE_OK) || (drc == CURLE_SEND_ERROR)) {
+              follow = FOLLOW_RETRY;
+              drc = Curl_follow(data, newurl, follow);
+              if(drc == CURLE_OK) {
+                multistate(easy, CURLM_STATE_CONNECT);
+                result = CURLM_CALL_MULTI_PERFORM;
+                easy->result = CURLE_OK;
+              }
+              else {
+                /* Follow failed */
+                easy->result = drc;
+                free(newurl);
+              }
+            }
+            else {
+              /* done didn't return OK or SEND_ERROR */
+              easy->result = drc;
+              free(newurl);
+            }
+          }
+          else {
+            /* Have error handler disconnect conn if we can't retry */
+            disconnect_conn = TRUE;
+          }
+        }
+        else {
+          /* failure detected */
+          Curl_posttransfer(data);
+          Curl_done(&easy->easy_conn, easy->result, FALSE);
+          disconnect_conn = TRUE;
+        }
+      }
+      break;
+
+    case CURLM_STATE_DOING:
+      /* we continue DOING until the DO phase is complete */
+      easy->result = Curl_protocol_doing(easy->easy_conn,
+                                         &dophase_done);
+      if(CURLE_OK == easy->result) {
+        if(dophase_done) {
+          /* after DO, go DO_DONE or DO_MORE */
+          multistate(easy, easy->easy_conn->bits.do_more?
+                     CURLM_STATE_DO_MORE:
+                     CURLM_STATE_DO_DONE);
+          result = CURLM_CALL_MULTI_PERFORM;
+        } /* dophase_done */
+      }
+      else {
+        /* failure detected */
+        Curl_posttransfer(data);
+        Curl_done(&easy->easy_conn, easy->result, FALSE);
+        disconnect_conn = TRUE;
+      }
+      break;
+
+    case CURLM_STATE_DO_MORE:
+      /*
+       * When we are connected, DO MORE and then go DO_DONE
+       */
+      easy->result = Curl_do_more(easy->easy_conn, &dophase_done);
+
+      /* No need to remove this handle from the send pipeline here since that
+         is done in Curl_done() */
+      if(CURLE_OK == easy->result) {
+        if(dophase_done) {
+          multistate(easy, CURLM_STATE_DO_DONE);
+          result = CURLM_CALL_MULTI_PERFORM;
+        }
+        else
+          /* stay in DO_MORE */
+          result = CURLM_OK;
+      }
+      else {
+        /* failure detected */
+        Curl_posttransfer(data);
+        Curl_done(&easy->easy_conn, easy->result, FALSE);
+        disconnect_conn = TRUE;
+      }
+      break;
+
+    case CURLM_STATE_DO_DONE:
+      /* Move ourselves from the send to recv pipeline */
+      moveHandleFromSendToRecvPipeline(data, easy->easy_conn);
+      /* Check if we can move pending requests to send pipe */
+      checkPendPipeline(easy->easy_conn);
+      multistate(easy, CURLM_STATE_WAITPERFORM);
+      result = CURLM_CALL_MULTI_PERFORM;
+      break;
+
+    case CURLM_STATE_WAITPERFORM:
+      /* Wait for our turn to PERFORM */
+      if(!easy->easy_conn->readchannel_inuse &&
+         isHandleAtHead(data,
+                        easy->easy_conn->recv_pipe)) {
+        /* Grab the channel */
+        easy->easy_conn->readchannel_inuse = TRUE;
+        multistate(easy, CURLM_STATE_PERFORM);
+        result = CURLM_CALL_MULTI_PERFORM;
+      }
+#ifdef DEBUGBUILD
+      else {
+        infof(data, "WAITPERFORM: Conn %ld recv pipe %zu inuse %d athead %d\n",
+              easy->easy_conn->connection_id,
+              easy->easy_conn->recv_pipe->size,
+              easy->easy_conn->readchannel_inuse?1:0,
+              isHandleAtHead(data,
+                             easy->easy_conn->recv_pipe)?1:0);
+      }
+#endif
+      break;
+
+    case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */
+      /* if both rates are within spec, resume transfer */
+      if(Curl_pgrsUpdate(easy->easy_conn))
+        easy->result = CURLE_ABORTED_BY_CALLBACK;
+      else
+        easy->result = Curl_speedcheck(data, now);
+
+      if(( (data->set.max_send_speed == 0) ||
+           (data->progress.ulspeed < data->set.max_send_speed ))  &&
+         ( (data->set.max_recv_speed == 0) ||
+           (data->progress.dlspeed < data->set.max_recv_speed)))
+        multistate(easy, CURLM_STATE_PERFORM);
+      break;
+
+    case CURLM_STATE_PERFORM:
+      {
+      char *newurl = NULL;
+      bool retry = FALSE;
+
+      /* check if over send speed */
+      if((data->set.max_send_speed > 0) &&
+         (data->progress.ulspeed > data->set.max_send_speed)) {
+        int buffersize;
+
+        multistate(easy, CURLM_STATE_TOOFAST);
+
+        /* calculate upload rate-limitation timeout. */
+        buffersize = (int)(data->set.buffer_size ?
+                           data->set.buffer_size : BUFSIZE);
+        timeout_ms = Curl_sleep_time(data->set.max_send_speed,
+                                     data->progress.ulspeed, buffersize);
+        Curl_expire(data, timeout_ms);
+        break;
+      }
+
+      /* check if over recv speed */
+      if((data->set.max_recv_speed > 0) &&
+         (data->progress.dlspeed > data->set.max_recv_speed)) {
+        int buffersize;
+
+        multistate(easy, CURLM_STATE_TOOFAST);
+
+         /* Calculate download rate-limitation timeout. */
+        buffersize = (int)(data->set.buffer_size ?
+                           data->set.buffer_size : BUFSIZE);
+        timeout_ms = Curl_sleep_time(data->set.max_recv_speed,
+                                     data->progress.dlspeed, buffersize);
+        Curl_expire(data, timeout_ms);
+        break;
+      }
+
+      /* read/write data if it is ready to do so */
+      easy->result = Curl_readwrite(easy->easy_conn, &done);
+
+      k = &data->req;
+
+      if(!(k->keepon & KEEP_RECV)) {
+        /* We're done receiving */
+        easy->easy_conn->readchannel_inuse = FALSE;
+      }
+
+      if(!(k->keepon & KEEP_SEND)) {
+        /* We're done sending */
+        easy->easy_conn->writechannel_inuse = FALSE;
+      }
+
+      if(done || (easy->result == CURLE_RECV_ERROR)) {
+        /* If CURLE_RECV_ERROR happens early enough, we assume it was a race
+         * condition and the server closed the re-used connection exactly when
+         * we wanted to use it, so figure out if that is indeed the case.
+         */
+        CURLcode ret = Curl_retry_request(easy->easy_conn, &newurl);
+        if(!ret)
+          retry = (newurl)?TRUE:FALSE;
+
+        if(retry)
+          /* if we are to retry, set the result to OK */
+          easy->result = CURLE_OK;
+      }
+
+      if(easy->result) {
+        /*
+         * The transfer phase returned error, we mark the connection to get
+         * closed to prevent being re-used. This is because we can't possibly
+         * know if the connection is in a good shape or not now.  Unless it is
+         * a protocol which uses two "channels" like FTP, as then the error
+         * happened in the data connection.
+         */
+
+        if(!(easy->easy_conn->handler->flags & PROTOPT_DUAL))
+          easy->easy_conn->bits.close = TRUE;
+
+        Curl_posttransfer(data);
+        Curl_done(&easy->easy_conn, easy->result, FALSE);
+      }
+      else if(done) {
+        followtype follow=FOLLOW_NONE;
+
+        /* call this even if the readwrite function returned error */
+        Curl_posttransfer(data);
+
+        /* we're no longer receiving */
+        moveHandleFromRecvToDonePipeline(data,
+                                         easy->easy_conn);
+
+        /* expire the new receiving pipeline head */
+        if(easy->easy_conn->recv_pipe->head)
+          Curl_expire(easy->easy_conn->recv_pipe->head->ptr, 1);
+
+        /* Check if we can move pending requests to send pipe */
+        checkPendPipeline(easy->easy_conn);
+
+        /* When we follow redirects or is set to retry the connection, we must
+           to go back to the CONNECT state */
+        if(data->req.newurl || retry) {
+          if(!retry) {
+            /* if the URL is a follow-location and not just a retried request
+               then figure out the URL here */
+            newurl = data->req.newurl;
+            data->req.newurl = NULL;
+            follow = FOLLOW_REDIR;
+          }
+          else
+            follow = FOLLOW_RETRY;
+          easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE);
+          if(easy->result == CURLE_OK)
+            easy->result = Curl_follow(data, newurl, follow);
+          if(CURLE_OK == easy->result) {
+            multistate(easy, CURLM_STATE_CONNECT);
+            result = CURLM_CALL_MULTI_PERFORM;
+            newurl = NULL; /* handed over the memory ownership to
+                              Curl_follow(), make sure we don't free() it
+                              here */
+          }
+        }
+        else {
+          /* after the transfer is done, go DONE */
+
+          /* but first check to see if we got a location info even though we're
+             not following redirects */
+          if(data->req.location) {
+            if(newurl)
+              free(newurl);
+            newurl = data->req.location;
+            data->req.location = NULL;
+            easy->result = Curl_follow(data, newurl, FOLLOW_FAKE);
+            if(CURLE_OK == easy->result)
+              newurl = NULL; /* allocation was handed over Curl_follow() */
+            else
+              disconnect_conn = TRUE;
+          }
+
+          multistate(easy, CURLM_STATE_DONE);
+          result = CURLM_CALL_MULTI_PERFORM;
+        }
+      }
+
+      if(newurl)
+        free(newurl);
+      break;
+      }
+
+    case CURLM_STATE_DONE:
+
+      if(easy->easy_conn) {
+        /* Remove ourselves from the receive and done pipelines. Handle
+           should be on one of these lists, depending upon how we got here. */
+        Curl_removeHandleFromPipeline(data,
+                                      easy->easy_conn->recv_pipe);
+        Curl_removeHandleFromPipeline(data,
+                                      easy->easy_conn->done_pipe);
+        /* Check if we can move pending requests to send pipe */
+        checkPendPipeline(easy->easy_conn);
+
+        if(easy->easy_conn->bits.stream_was_rewound) {
+          /* This request read past its response boundary so we quickly let
+             the other requests consume those bytes since there is no
+             guarantee that the socket will become active again */
+          result = CURLM_CALL_MULTI_PERFORM;
+        }
+
+        /* post-transfer command */
+        easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE);
+        /*
+         * If there are other handles on the pipeline, Curl_done won't set
+         * easy_conn to NULL.  In such a case, curl_multi_remove_handle() can
+         * access free'd data, if the connection is free'd and the handle
+         * removed before we perform the processing in CURLM_STATE_COMPLETED
+         */
+        if(easy->easy_conn)
+          easy->easy_conn = NULL;
+      }
+
+      if(data->set.wildcardmatch) {
+        if(data->wildcard.state != CURLWC_DONE) {
+          /* if a wildcard is set and we are not ending -> lets start again
+             with CURLM_STATE_INIT */
+          result = CURLM_CALL_MULTI_PERFORM;
+          multistate(easy, CURLM_STATE_INIT);
+          break;
+        }
+      }
+
+      /* after we have DONE what we're supposed to do, go COMPLETED, and
+         it doesn't matter what the Curl_done() returned! */
+      multistate(easy, CURLM_STATE_COMPLETED);
+
+      break;
+
+    case CURLM_STATE_COMPLETED:
+      /* this is a completed transfer, it is likely to still be connected */
+
+      /* This node should be delinked from the list now and we should post
+         an information message that we are complete. */
+
+      /* Important: reset the conn pointer so that we don't point to memory
+         that could be freed anytime */
+      easy->easy_conn = NULL;
+
+      Curl_expire(data, 0); /* stop all timers */
+      break;
+
+    case CURLM_STATE_MSGSENT:
+      return CURLM_OK; /* do nothing */
+
+    default:
+      return CURLM_INTERNAL_ERROR;
+    }
+
+    if(easy->state < CURLM_STATE_COMPLETED) {
+      if(CURLE_OK != easy->result) {
+        /*
+         * If an error was returned, and we aren't in completed state now,
+         * then we go to completed and consider this transfer aborted.
+         */
+
+        /* NOTE: no attempt to disconnect connections must be made
+           in the case blocks above - cleanup happens only here */
+
+        data->state.pipe_broke = FALSE;
+
+        if(easy->easy_conn) {
+          /* if this has a connection, unsubscribe from the pipelines */
+          easy->easy_conn->writechannel_inuse = FALSE;
+          easy->easy_conn->readchannel_inuse = FALSE;
+          Curl_removeHandleFromPipeline(data,
+                                        easy->easy_conn->send_pipe);
+          Curl_removeHandleFromPipeline(data,
+                                        easy->easy_conn->recv_pipe);
+          Curl_removeHandleFromPipeline(data,
+                                        easy->easy_conn->done_pipe);
+          /* Check if we can move pending requests to send pipe */
+          checkPendPipeline(easy->easy_conn);
+
+          if(disconnect_conn) {
+            /* disconnect properly */
+            Curl_disconnect(easy->easy_conn, /* dead_connection */ FALSE);
+
+            /* This is where we make sure that the easy_conn pointer is reset.
+               We don't have to do this in every case block above where a
+               failure is detected */
+            easy->easy_conn = NULL;
+          }
+        }
+        else if(easy->state == CURLM_STATE_CONNECT) {
+          /* Curl_connect() failed */
+          (void)Curl_posttransfer(data);
+        }
+
+        multistate(easy, CURLM_STATE_COMPLETED);
+      }
+      /* if there's still a connection to use, call the progress function */
+      else if(easy->easy_conn && Curl_pgrsUpdate(easy->easy_conn)) {
+        /* aborted due to progress callback return code must close the
+           connection */
+        easy->easy_conn->bits.close = TRUE;
+
+        /* if not yet in DONE state, go there, otherwise COMPLETED */
+        multistate(easy, (easy->state < CURLM_STATE_DONE)?
+                   CURLM_STATE_DONE: CURLM_STATE_COMPLETED);
+        result = CURLM_CALL_MULTI_PERFORM;
+      }
+    }
+  } WHILE_FALSE; /* just to break out from! */
+
+  if(CURLM_STATE_COMPLETED == easy->state) {
+    /* now fill in the Curl_message with this info */
+    msg = &easy->msg;
+
+    msg->extmsg.msg = CURLMSG_DONE;
+    msg->extmsg.easy_handle = data;
+    msg->extmsg.data.result = easy->result;
+
+    result = multi_addmsg(multi, msg);
+
+    multistate(easy, CURLM_STATE_MSGSENT);
+  }
+
+  return result;
+}
+
+
+CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
+{
+  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
+  struct Curl_one_easy *easy;
+  CURLMcode returncode=CURLM_OK;
+  struct Curl_tree *t;
+  struct timeval now = Curl_tvnow();
+
+  if(!GOOD_MULTI_HANDLE(multi))
+    return CURLM_BAD_HANDLE;
+
+  easy=multi->easy.next;
+  while(easy != &multi->easy) {
+    CURLMcode result;
+    struct WildcardData *wc = &easy->easy_handle->wildcard;
+
+    if(easy->easy_handle->set.wildcardmatch) {
+      if(!wc->filelist) {
+        CURLcode ret = Curl_wildcard_init(wc); /* init wildcard structures */
+        if(ret)
+          return CURLM_OUT_OF_MEMORY;
+      }
+    }
+
+    do
+      result = multi_runsingle(multi, now, easy);
+    while(CURLM_CALL_MULTI_PERFORM == result);
+
+    if(easy->easy_handle->set.wildcardmatch) {
+      /* destruct wildcard structures if it is needed */
+      if(wc->state == CURLWC_DONE || result)
+        Curl_wildcard_dtor(wc);
+    }
+
+    if(result)
+      returncode = result;
+
+    easy = easy->next; /* operate on next handle */
+  }
+
+  /*
+   * Simply remove all expired timers from the splay since handles are dealt
+   * with unconditionally by this function and curl_multi_timeout() requires
+   * that already passed/handled expire times are removed from the splay.
+   *
+   * It is important that the 'now' value is set at the entry of this function
+   * and not for the current time as it may have ticked a little while since
+   * then and then we risk this loop to remove timers that actually have not
+   * been handled!
+   */
+  do {
+    multi->timetree = Curl_splaygetbest(now, multi->timetree, &t);
+    if(t)
+      /* the removed may have another timeout in queue */
+      (void)add_next_timeout(now, multi, t->payload);
+
+  } while(t);
+
+  *running_handles = multi->num_alive;
+
+  if(CURLM_OK >= returncode)
+    update_timer(multi);
+
+  return returncode;
+}
+
+static void close_all_connections(struct Curl_multi *multi)
+{
+  struct connectdata *conn;
+
+  conn = Curl_conncache_find_first_connection(multi->conn_cache);
+  while(conn) {
+    conn->data = multi->closure_handle;
+
+    /* This will remove the connection from the cache */
+    (void)Curl_disconnect(conn, FALSE);
+
+    conn = Curl_conncache_find_first_connection(multi->conn_cache);
+  }
+}
+
+CURLMcode curl_multi_cleanup(CURLM *multi_handle)
+{
+  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
+  struct Curl_one_easy *easy;
+  struct Curl_one_easy *nexteasy;
+
+  if(GOOD_MULTI_HANDLE(multi)) {
+    multi->type = 0; /* not good anymore */
+
+    /* Close all the connections in the connection cache */
+    close_all_connections(multi);
+
+    Curl_close(multi->closure_handle);
+    multi->closure_handle = NULL;
+
+    Curl_hash_destroy(multi->sockhash);
+    multi->sockhash = NULL;
+
+    Curl_conncache_destroy(multi->conn_cache);
+    multi->conn_cache = NULL;
+
+    /* remove the pending list of messages */
+    Curl_llist_destroy(multi->msglist, NULL);
+    multi->msglist = NULL;
+
+    /* remove all easy handles */
+    easy = multi->easy.next;
+    while(easy != &multi->easy) {
+      nexteasy=easy->next;
+      if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) {
+        /* clear out the usage of the shared DNS cache */
+        Curl_hostcache_clean(easy->easy_handle);
+        easy->easy_handle->dns.hostcache = NULL;
+        easy->easy_handle->dns.hostcachetype = HCACHE_NONE;
+      }
+
+      /* Clear the pointer to the connection cache */
+      easy->easy_handle->state.conn_cache = NULL;
+
+      Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association */
+
+      free(easy);
+      easy = nexteasy;
+    }
+
+    Curl_hash_destroy(multi->hostcache);
+    multi->hostcache = NULL;
+
+    free(multi);
+
+    return CURLM_OK;
+  }
+  else
+    return CURLM_BAD_HANDLE;
+}
+
+/*
+ * curl_multi_info_read()
+ *
+ * This function is the primary way for a multi/multi_socket application to
+ * figure out if a transfer has ended. We MUST make this function as fast as
+ * possible as it will be polled frequently and we MUST NOT scan any lists in
+ * here to figure out things. We must scale fine to thousands of handles and
+ * beyond. The current design is fully O(1).
+ */
+
+CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue)
+{
+  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
+  struct Curl_message *msg;
+
+  *msgs_in_queue = 0; /* default to none */
+
+  if(GOOD_MULTI_HANDLE(multi) && Curl_llist_count(multi->msglist)) {
+    /* there is one or more messages in the list */
+    struct curl_llist_element *e;
+
+    /* extract the head of the list to return */
+    e = multi->msglist->head;
+
+    msg = e->ptr;
+
+    /* remove the extracted entry */
+    Curl_llist_remove(multi->msglist, e, NULL);
+
+    *msgs_in_queue = curlx_uztosi(Curl_llist_count(multi->msglist));
+
+    return &msg->extmsg;
+  }
+  else
+    return NULL;
+}
+
+/*
+ * singlesocket() checks what sockets we deal with and their "action state"
+ * and if we have a different state in any of those sockets from last time we
+ * call the callback accordingly.
+ */
+static void singlesocket(struct Curl_multi *multi,
+                         struct Curl_one_easy *easy)
+{
+  curl_socket_t socks[MAX_SOCKSPEREASYHANDLE];
+  int i;
+  struct Curl_sh_entry *entry;
+  curl_socket_t s;
+  int num;
+  unsigned int curraction;
+  struct Curl_one_easy *easy_by_hash;
+  bool remove_sock_from_hash;
+
+  for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++)
+    socks[i] = CURL_SOCKET_BAD;
+
+  /* Fill in the 'current' struct with the state as it is now: what sockets to
+     supervise and for what actions */
+  curraction = multi_getsock(easy, socks, MAX_SOCKSPEREASYHANDLE);
+
+  /* We have 0 .. N sockets already and we get to know about the 0 .. M
+     sockets we should have from now on. Detect the differences, remove no
+     longer supervised ones and add new ones */
+
+  /* walk over the sockets we got right now */
+  for(i=0; (i< MAX_SOCKSPEREASYHANDLE) &&
+        (curraction & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i)));
+      i++) {
+    int action = CURL_POLL_NONE;
+
+    s = socks[i];
+
+    /* get it from the hash */
+    entry = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));
+
+    if(curraction & GETSOCK_READSOCK(i))
+      action |= CURL_POLL_IN;
+    if(curraction & GETSOCK_WRITESOCK(i))
+      action |= CURL_POLL_OUT;
+
+    if(entry) {
+      /* yeps, already present so check if it has the same action set */
+      if(entry->action == action)
+        /* same, continue */
+        continue;
+    }
+    else {
+      /* this is a socket we didn't have before, add it! */
+      entry = sh_addentry(multi->sockhash, s, easy->easy_handle);
+      if(!entry)
+        /* fatal */
+        return;
+    }
+
+    /* we know (entry != NULL) at this point, see the logic above */
+    if(multi->socket_cb)
+      multi->socket_cb(easy->easy_handle,
+                       s,
+                       action,
+                       multi->socket_userp,
+                       entry->socketp);
+
+    entry->action = action; /* store the current action state */
+  }
+
+  num = i; /* number of sockets */
+
+  /* when we've walked over all the sockets we should have right now, we must
+     make sure to detect sockets that are removed */
+  for(i=0; i< easy->numsocks; i++) {
+    int j;
+    s = easy->sockets[i];
+    for(j=0; j<num; j++) {
+      if(s == socks[j]) {
+        /* this is still supervised */
+        s = CURL_SOCKET_BAD;
+        break;
+      }
+    }
+    if(s != CURL_SOCKET_BAD) {
+
+      /* this socket has been removed. Tell the app to remove it */
+      remove_sock_from_hash = TRUE;
+
+      entry = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));
+      if(entry) {
+        /* check if the socket to be removed serves a connection which has
+           other easy-s in a pipeline. In this case the socket should not be
+           removed. */
+        struct connectdata *easy_conn;
+
+        easy_by_hash = entry->easy->multi_pos;
+        easy_conn = easy_by_hash->easy_conn;
+        if(easy_conn) {
+          if(easy_conn->recv_pipe && easy_conn->recv_pipe->size > 1) {
+            /* the handle should not be removed from the pipe yet */
+            remove_sock_from_hash = FALSE;
+
+            /* Update the sockhash entry to instead point to the next in line
+               for the recv_pipe, or the first (in case this particular easy
+               isn't already) */
+            if(entry->easy == easy->easy_handle) {
+              if(isHandleAtHead(easy->easy_handle, easy_conn->recv_pipe))
+                entry->easy = easy_conn->recv_pipe->head->next->ptr;
+              else
+                entry->easy = easy_conn->recv_pipe->head->ptr;
+            }
+          }
+          if(easy_conn->send_pipe  && easy_conn->send_pipe->size > 1) {
+            /* the handle should not be removed from the pipe yet */
+            remove_sock_from_hash = FALSE;
+
+            /* Update the sockhash entry to instead point to the next in line
+               for the send_pipe, or the first (in case this particular easy
+               isn't already) */
+            if(entry->easy == easy->easy_handle) {
+              if(isHandleAtHead(easy->easy_handle, easy_conn->send_pipe))
+                entry->easy = easy_conn->send_pipe->head->next->ptr;
+              else
+                entry->easy = easy_conn->send_pipe->head->ptr;
+            }
+          }
+          /* Don't worry about overwriting recv_pipe head with send_pipe_head,
+             when action will be asked on the socket (see multi_socket()), the
+             head of the correct pipe will be taken according to the
+             action. */
+        }
+      }
+      else
+        /* just a precaution, this socket really SHOULD be in the hash already
+           but in case it isn't, we don't have to tell the app to remove it
+           either since it never got to know about it */
+        remove_sock_from_hash = FALSE;
+
+      if(remove_sock_from_hash) {
+        /* in this case 'entry' is always non-NULL */
+        if(multi->socket_cb)
+          multi->socket_cb(easy->easy_handle,
+                           s,
+                           CURL_POLL_REMOVE,
+                           multi->socket_userp,
+                           entry->socketp);
+        sh_delentry(multi->sockhash, s);
+      }
+
+    }
+  }
+
+  memcpy(easy->sockets, socks, num*sizeof(curl_socket_t));
+  easy->numsocks = num;
+}
+
+/*
+ * add_next_timeout()
+ *
+ * Each SessionHandle has a list of timeouts. The add_next_timeout() is called
+ * when it has just been removed from the splay tree because the timeout has
+ * expired. This function is then to advance in the list to pick the next
+ * timeout to use (skip the already expired ones) and add this node back to
+ * the splay tree again.
+ *
+ * The splay tree only has each sessionhandle as a single node and the nearest
+ * timeout is used to sort it on.
+ */
+static CURLMcode add_next_timeout(struct timeval now,
+                                  struct Curl_multi *multi,
+                                  struct SessionHandle *d)
+{
+  struct timeval *tv = &d->state.expiretime;
+  struct curl_llist *list = d->state.timeoutlist;
+  struct curl_llist_element *e;
+
+  /* move over the timeout list for this specific handle and remove all
+     timeouts that are now passed tense and store the next pending
+     timeout in *tv */
+  for(e = list->head; e; ) {
+    struct curl_llist_element *n = e->next;
+    long diff = curlx_tvdiff(*(struct timeval *)e->ptr, now);
+    if(diff <= 0)
+      /* remove outdated entry */
+      Curl_llist_remove(list, e, NULL);
+    else
+      /* the list is sorted so get out on the first mismatch */
+      break;
+    e = n;
+  }
+  e = list->head;
+  if(!e) {
+    /* clear the expire times within the handles that we remove from the
+       splay tree */
+    tv->tv_sec = 0;
+    tv->tv_usec = 0;
+  }
+  else {
+    /* copy the first entry to 'tv' */
+    memcpy(tv, e->ptr, sizeof(*tv));
+
+    /* remove first entry from list */
+    Curl_llist_remove(list, e, NULL);
+
+    /* insert this node again into the splay */
+    multi->timetree = Curl_splayinsert(*tv, multi->timetree,
+                                       &d->state.timenode);
+  }
+  return CURLM_OK;
+}
+
+
+static CURLMcode multi_socket(struct Curl_multi *multi,
+                              bool checkall,
+                              curl_socket_t s,
+                              int ev_bitmask,
+                              int *running_handles)
+{
+  CURLMcode result = CURLM_OK;
+  struct SessionHandle *data = NULL;
+  struct Curl_tree *t;
+  struct timeval now = Curl_tvnow();
+
+  if(checkall) {
+    struct Curl_one_easy *easyp;
+    /* *perform() deals with running_handles on its own */
+    result = curl_multi_perform(multi, running_handles);
+
+    /* walk through each easy handle and do the socket state change magic
+       and callbacks */
+    easyp=multi->easy.next;
+    while(easyp != &multi->easy) {
+      singlesocket(multi, easyp);
+      easyp = easyp->next;
+    }
+
+    /* or should we fall-through and do the timer-based stuff? */
+    return result;
+  }
+  else if(s != CURL_SOCKET_TIMEOUT) {
+
+    struct Curl_sh_entry *entry =
+      Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));
+
+    if(!entry)
+      /* Unmatched socket, we can't act on it but we ignore this fact.  In
+         real-world tests it has been proved that libevent can in fact give
+         the application actions even though the socket was just previously
+         asked to get removed, so thus we better survive stray socket actions
+         and just move on. */
+      ;
+    else {
+      data = entry->easy;
+
+      if(data->magic != CURLEASY_MAGIC_NUMBER)
+        /* bad bad bad bad bad bad bad */
+        return CURLM_INTERNAL_ERROR;
+
+      /* If the pipeline is enabled, take the handle which is in the head of
+         the pipeline. If we should write into the socket, take the send_pipe
+         head.  If we should read from the socket, take the recv_pipe head. */
+      if(data->set.one_easy->easy_conn) {
+        if((ev_bitmask & CURL_POLL_OUT) &&
+           data->set.one_easy->easy_conn->send_pipe &&
+           data->set.one_easy->easy_conn->send_pipe->head)
+          data = data->set.one_easy->easy_conn->send_pipe->head->ptr;
+        else if((ev_bitmask & CURL_POLL_IN) &&
+                data->set.one_easy->easy_conn->recv_pipe &&
+                data->set.one_easy->easy_conn->recv_pipe->head)
+          data = data->set.one_easy->easy_conn->recv_pipe->head->ptr;
+      }
+
+      if(data->set.one_easy->easy_conn &&
+         !(data->set.one_easy->easy_conn->handler->flags & PROTOPT_DIRLOCK))
+        /* set socket event bitmask if they're not locked */
+        data->set.one_easy->easy_conn->cselect_bits = ev_bitmask;
+
+      do
+        result = multi_runsingle(multi, now, data->set.one_easy);
+      while(CURLM_CALL_MULTI_PERFORM == result);
+
+      if(data->set.one_easy->easy_conn &&
+         !(data->set.one_easy->easy_conn->handler->flags & PROTOPT_DIRLOCK))
+        /* clear the bitmask only if not locked */
+        data->set.one_easy->easy_conn->cselect_bits = 0;
+
+      if(CURLM_OK >= result)
+        /* get the socket(s) and check if the state has been changed since
+           last */
+        singlesocket(multi, data->set.one_easy);
+
+      /* Now we fall-through and do the timer-based stuff, since we don't want
+         to force the user to have to deal with timeouts as long as at least
+         one connection in fact has traffic. */
+
+      data = NULL; /* set data to NULL again to avoid calling
+                      multi_runsingle() in case there's no need to */
+    }
+  }
+
+  now.tv_usec += 40000; /* compensate for bad precision timers that might've
+                           triggered too early */
+  if(now.tv_usec >= 1000000) {
+    now.tv_sec++;
+    now.tv_usec -= 1000000;
+  }
+
+  /*
+   * The loop following here will go on as long as there are expire-times left
+   * to process in the splay and 'data' will be re-assigned for every expired
+   * handle we deal with.
+   */
+  do {
+    /* the first loop lap 'data' can be NULL */
+    if(data) {
+      do
+        result = multi_runsingle(multi, now, data->set.one_easy);
+      while(CURLM_CALL_MULTI_PERFORM == result);
+
+      if(CURLM_OK >= result)
+        /* get the socket(s) and check if the state has been changed since
+           last */
+        singlesocket(multi, data->set.one_easy);
+    }
+
+    /* Check if there's one (more) expired timer to deal with! This function
+       extracts a matching node if there is one */
+
+    multi->timetree = Curl_splaygetbest(now, multi->timetree, &t);
+    if(t) {
+      data = t->payload; /* assign this for next loop */
+      (void)add_next_timeout(now, multi, t->payload);
+    }
+
+  } while(t);
+
+  *running_handles = multi->num_alive;
+  return result;
+}
+
+#undef curl_multi_setopt
+CURLMcode curl_multi_setopt(CURLM *multi_handle,
+                            CURLMoption option, ...)
+{
+  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
+  CURLMcode res = CURLM_OK;
+  va_list param;
+
+  if(!GOOD_MULTI_HANDLE(multi))
+    return CURLM_BAD_HANDLE;
+
+  va_start(param, option);
+
+  switch(option) {
+  case CURLMOPT_SOCKETFUNCTION:
+    multi->socket_cb = va_arg(param, curl_socket_callback);
+    break;
+  case CURLMOPT_SOCKETDATA:
+    multi->socket_userp = va_arg(param, void *);
+    break;
+  case CURLMOPT_PIPELINING:
+    multi->pipelining_enabled = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    break;
+  case CURLMOPT_TIMERFUNCTION:
+    multi->timer_cb = va_arg(param, curl_multi_timer_callback);
+    break;
+  case CURLMOPT_TIMERDATA:
+    multi->timer_userp = va_arg(param, void *);
+    break;
+  case CURLMOPT_MAXCONNECTS:
+    multi->maxconnects = va_arg(param, long);
+    break;
+  default:
+    res = CURLM_UNKNOWN_OPTION;
+    break;
+  }
+  va_end(param);
+  return res;
+}
+
+/* we define curl_multi_socket() in the public multi.h header */
+#undef curl_multi_socket
+
+CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s,
+                            int *running_handles)
+{
+  CURLMcode result = multi_socket((struct Curl_multi *)multi_handle, FALSE, s,
+                                  0, running_handles);
+  if(CURLM_OK >= result)
+    update_timer((struct Curl_multi *)multi_handle);
+  return result;
+}
+
+CURLMcode curl_multi_socket_action(CURLM *multi_handle, curl_socket_t s,
+                                   int ev_bitmask, int *running_handles)
+{
+  CURLMcode result = multi_socket((struct Curl_multi *)multi_handle, FALSE, s,
+                                  ev_bitmask, running_handles);
+  if(CURLM_OK >= result)
+    update_timer((struct Curl_multi *)multi_handle);
+  return result;
+}
+
+CURLMcode curl_multi_socket_all(CURLM *multi_handle, int *running_handles)
+
+{
+  CURLMcode result = multi_socket((struct Curl_multi *)multi_handle,
+                                  TRUE, CURL_SOCKET_BAD, 0, running_handles);
+  if(CURLM_OK >= result)
+    update_timer((struct Curl_multi *)multi_handle);
+  return result;
+}
+
+static CURLMcode multi_timeout(struct Curl_multi *multi,
+                               long *timeout_ms)
+{
+  static struct timeval tv_zero = {0,0};
+
+  if(multi->timetree) {
+    /* we have a tree of expire times */
+    struct timeval now = Curl_tvnow();
+
+    /* splay the lowest to the bottom */
+    multi->timetree = Curl_splay(tv_zero, multi->timetree);
+
+    if(Curl_splaycomparekeys(multi->timetree->key, now) > 0) {
+      /* some time left before expiration */
+      *timeout_ms = curlx_tvdiff(multi->timetree->key, now);
+      if(!*timeout_ms)
+        /*
+         * Since we only provide millisecond resolution on the returned value
+         * and the diff might be less than one millisecond here, we don't
+         * return zero as that may cause short bursts of busyloops on fast
+         * processors while the diff is still present but less than one
+         * millisecond! instead we return 1 until the time is ripe.
+         */
+        *timeout_ms=1;
+    }
+    else
+      /* 0 means immediately */
+      *timeout_ms = 0;
+  }
+  else
+    *timeout_ms = -1;
+
+  return CURLM_OK;
+}
+
+CURLMcode curl_multi_timeout(CURLM *multi_handle,
+                             long *timeout_ms)
+{
+  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
+
+  /* First, make some basic checks that the CURLM handle is a good handle */
+  if(!GOOD_MULTI_HANDLE(multi))
+    return CURLM_BAD_HANDLE;
+
+  return multi_timeout(multi, timeout_ms);
+}
+
+/*
+ * Tell the application it should update its timers, if it subscribes to the
+ * update timer callback.
+ */
+static int update_timer(struct Curl_multi *multi)
+{
+  long timeout_ms;
+
+  if(!multi->timer_cb)
+    return 0;
+  if(multi_timeout(multi, &timeout_ms)) {
+    return -1;
+  }
+  if(timeout_ms < 0) {
+    static const struct timeval none={0,0};
+    if(Curl_splaycomparekeys(none, multi->timer_lastcall)) {
+      multi->timer_lastcall = none;
+      /* there's no timeout now but there was one previously, tell the app to
+         disable it */
+      return multi->timer_cb((CURLM*)multi, -1, multi->timer_userp);
+    }
+    return 0;
+  }
+
+  /* When multi_timeout() is done, multi->timetree points to the node with the
+   * timeout we got the (relative) time-out time for. We can thus easily check
+   * if this is the same (fixed) time as we got in a previous call and then
+   * avoid calling the callback again. */
+  if(Curl_splaycomparekeys(multi->timetree->key, multi->timer_lastcall) == 0)
+    return 0;
+
+  multi->timer_lastcall = multi->timetree->key;
+
+  return multi->timer_cb((CURLM*)multi, timeout_ms, multi->timer_userp);
+}
+
+static CURLcode addHandleToSendOrPendPipeline(struct SessionHandle *handle,
+                                              struct connectdata *conn)
+{
+  size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size;
+  struct curl_llist_element *sendhead = conn->send_pipe->head;
+  struct curl_llist *pipeline;
+  CURLcode rc;
+
+  if(!Curl_isPipeliningEnabled(handle) ||
+     pipeLen == 0)
+    pipeline = conn->send_pipe;
+  else {
+    if(conn->server_supports_pipelining &&
+       pipeLen < MAX_PIPELINE_LENGTH)
+      pipeline = conn->send_pipe;
+    else
+      pipeline = conn->pend_pipe;
+  }
+
+  rc = Curl_addHandleToPipeline(handle, pipeline);
+
+  if(pipeline == conn->send_pipe && sendhead != conn->send_pipe->head) {
+    /* this is a new one as head, expire it */
+    conn->writechannel_inuse = FALSE; /* not in use yet */
+#ifdef DEBUGBUILD
+    infof(conn->data, "%p is at send pipe head!\n",
+          conn->send_pipe->head->ptr);
+#endif
+    Curl_expire(conn->send_pipe->head->ptr, 1);
+  }
+
+  return rc;
+}
+
+static int checkPendPipeline(struct connectdata *conn)
+{
+  int result = 0;
+  struct curl_llist_element *sendhead = conn->send_pipe->head;
+
+  size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size;
+  if(conn->server_supports_pipelining || pipeLen == 0) {
+    struct curl_llist_element *curr = conn->pend_pipe->head;
+    const size_t maxPipeLen =
+      conn->server_supports_pipelining ? MAX_PIPELINE_LENGTH : 1;
+
+    while(pipeLen < maxPipeLen && curr) {
+      Curl_llist_move(conn->pend_pipe, curr,
+                      conn->send_pipe, conn->send_pipe->tail);
+      Curl_pgrsTime(curr->ptr, TIMER_PRETRANSFER);
+      ++result; /* count how many handles we moved */
+      curr = conn->pend_pipe->head;
+      ++pipeLen;
+    }
+  }
+
+  if(result) {
+    conn->now = Curl_tvnow();
+    /* something moved, check for a new send pipeline leader */
+    if(sendhead != conn->send_pipe->head) {
+      /* this is a new one as head, expire it */
+      conn->writechannel_inuse = FALSE; /* not in use yet */
+#ifdef DEBUGBUILD
+      infof(conn->data, "%p is at send pipe head!\n",
+            conn->send_pipe->head->ptr);
+#endif
+      Curl_expire(conn->send_pipe->head->ptr, 1);
+    }
+  }
+
+  return result;
+}
+
+/* Move this transfer from the sending list to the receiving list.
+
+   Pay special attention to the new sending list "leader" as it needs to get
+   checked to update what sockets it acts on.
+
+*/
+static void moveHandleFromSendToRecvPipeline(struct SessionHandle *handle,
+                                             struct connectdata *conn)
+{
+  struct curl_llist_element *curr;
+
+  curr = conn->send_pipe->head;
+  while(curr) {
+    if(curr->ptr == handle) {
+      Curl_llist_move(conn->send_pipe, curr,
+                      conn->recv_pipe, conn->recv_pipe->tail);
+
+      if(conn->send_pipe->head) {
+        /* Since there's a new easy handle at the start of the send pipeline,
+           set its timeout value to 1ms to make it trigger instantly */
+        conn->writechannel_inuse = FALSE; /* not used now */
+#ifdef DEBUGBUILD
+        infof(conn->data, "%p is at send pipe head B!\n",
+              conn->send_pipe->head->ptr);
+#endif
+        Curl_expire(conn->send_pipe->head->ptr, 1);
+      }
+
+      /* The receiver's list is not really interesting here since either this
+         handle is now first in the list and we'll deal with it soon, or
+         another handle is already first and thus is already taken care of */
+
+      break; /* we're done! */
+    }
+    curr = curr->next;
+  }
+}
+
+static void moveHandleFromRecvToDonePipeline(struct SessionHandle *handle,
+                                            struct connectdata *conn)
+{
+  struct curl_llist_element *curr;
+
+  curr = conn->recv_pipe->head;
+  while(curr) {
+    if(curr->ptr == handle) {
+      Curl_llist_move(conn->recv_pipe, curr,
+                      conn->done_pipe, conn->done_pipe->tail);
+      break;
+    }
+    curr = curr->next;
+  }
+}
+static bool isHandleAtHead(struct SessionHandle *handle,
+                           struct curl_llist *pipeline)
+{
+  struct curl_llist_element *curr = pipeline->head;
+  if(curr)
+    return (curr->ptr == handle) ? TRUE : FALSE;
+
+  return FALSE;
+}
+
+/*
+ * multi_freetimeout()
+ *
+ * Callback used by the llist system when a single timeout list entry is
+ * destroyed.
+ */
+static void multi_freetimeout(void *user, void *entryptr)
+{
+  (void)user;
+
+  /* the entry was plain malloc()'ed */
+  free(entryptr);
+}
+
+/*
+ * multi_addtimeout()
+ *
+ * Add a timestamp to the list of timeouts. Keep the list sorted so that head
+ * of list is always the timeout nearest in time.
+ *
+ */
+static CURLMcode
+multi_addtimeout(struct curl_llist *timeoutlist,
+                 struct timeval *stamp)
+{
+  struct curl_llist_element *e;
+  struct timeval *timedup;
+  struct curl_llist_element *prev = NULL;
+
+  timedup = malloc(sizeof(*timedup));
+  if(!timedup)
+    return CURLM_OUT_OF_MEMORY;
+
+  /* copy the timestamp */
+  memcpy(timedup, stamp, sizeof(*timedup));
+
+  if(Curl_llist_count(timeoutlist)) {
+    /* find the correct spot in the list */
+    for(e = timeoutlist->head; e; e = e->next) {
+      struct timeval *checktime = e->ptr;
+      long diff = curlx_tvdiff(*checktime, *timedup);
+      if(diff > 0)
+        break;
+      prev = e;
+    }
+
+  }
+  /* else
+     this is the first timeout on the list */
+
+  if(!Curl_llist_insert_next(timeoutlist, prev, timedup)) {
+    free(timedup);
+    return CURLM_OUT_OF_MEMORY;
+  }
+
+  return CURLM_OK;
+}
+
+/*
+ * Curl_expire()
+ *
+ * given a number of milliseconds from now to use to set the 'act before
+ * this'-time for the transfer, to be extracted by curl_multi_timeout()
+ *
+ * Note that the timeout will be added to a queue of timeouts if it defines a
+ * moment in time that is later than the current head of queue.
+ *
+ * Pass zero to clear all timeout values for this handle.
+*/
+void Curl_expire(struct SessionHandle *data, long milli)
+{
+  struct Curl_multi *multi = data->multi;
+  struct timeval *nowp = &data->state.expiretime;
+  int rc;
+
+  /* this is only interesting for multi-interface using libcurl, and only
+     while there is still a multi interface struct remaining! */
+  if(!multi)
+    return;
+
+  if(!milli) {
+    /* No timeout, clear the time data. */
+    if(nowp->tv_sec || nowp->tv_usec) {
+      /* Since this is an cleared time, we must remove the previous entry from
+         the splay tree */
+      struct curl_llist *list = data->state.timeoutlist;
+
+      rc = Curl_splayremovebyaddr(multi->timetree,
+                                  &data->state.timenode,
+                                  &multi->timetree);
+      if(rc)
+        infof(data, "Internal error clearing splay node = %d\n", rc);
+
+      /* flush the timeout list too */
+      while(list->size > 0)
+        Curl_llist_remove(list, list->tail, NULL);
+
+#ifdef DEBUGBUILD
+      infof(data, "Expire cleared\n");
+#endif
+      nowp->tv_sec = 0;
+      nowp->tv_usec = 0;
+    }
+  }
+  else {
+    struct timeval set;
+
+    set = Curl_tvnow();
+    set.tv_sec += milli/1000;
+    set.tv_usec += (milli%1000)*1000;
+
+    if(set.tv_usec >= 1000000) {
+      set.tv_sec++;
+      set.tv_usec -= 1000000;
+    }
+
+    if(nowp->tv_sec || nowp->tv_usec) {
+      /* This means that the struct is added as a node in the splay tree.
+         Compare if the new time is earlier, and only remove-old/add-new if it
+         is. */
+      long diff = curlx_tvdiff(set, *nowp);
+      if(diff > 0) {
+        /* the new expire time was later so just add it to the queue
+           and get out */
+        multi_addtimeout(data->state.timeoutlist, &set);
+        return;
+      }
+
+      /* the new time is newer than the presently set one, so add the current
+         to the queue and update the head */
+      multi_addtimeout(data->state.timeoutlist, nowp);
+
+      /* Since this is an updated time, we must remove the previous entry from
+         the splay tree first and then re-add the new value */
+      rc = Curl_splayremovebyaddr(multi->timetree,
+                                  &data->state.timenode,
+                                  &multi->timetree);
+      if(rc)
+        infof(data, "Internal error removing splay node = %d\n", rc);
+    }
+
+    *nowp = set;
+    data->state.timenode.payload = data;
+    multi->timetree = Curl_splayinsert(*nowp,
+                                       multi->timetree,
+                                       &data->state.timenode);
+  }
+#if 0
+  Curl_splayprint(multi->timetree, 0, TRUE);
+#endif
+}
+
+CURLMcode curl_multi_assign(CURLM *multi_handle,
+                            curl_socket_t s, void *hashp)
+{
+  struct Curl_sh_entry *there = NULL;
+  struct Curl_multi *multi = (struct Curl_multi *)multi_handle;
+
+  if(s != CURL_SOCKET_BAD)
+    there = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(curl_socket_t));
+
+  if(!there)
+    return CURLM_BAD_SOCKET;
+
+  there->socketp = hashp;
+
+  return CURLM_OK;
+}
+
+#ifdef DEBUGBUILD
+void Curl_multi_dump(const struct Curl_multi *multi_handle)
+{
+  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
+  struct Curl_one_easy *easy;
+  int i;
+  fprintf(stderr, "* Multi status: %d handles, %d alive\n",
+          multi->num_easy, multi->num_alive);
+  for(easy=multi->easy.next; easy != &multi->easy; easy = easy->next) {
+    if(easy->state < CURLM_STATE_COMPLETED) {
+      /* only display handles that are not completed */
+      fprintf(stderr, "handle %p, state %s, %d sockets\n",
+              (void *)easy->easy_handle,
+              statename[easy->state], easy->numsocks);
+      for(i=0; i < easy->numsocks; i++) {
+        curl_socket_t s = easy->sockets[i];
+        struct Curl_sh_entry *entry =
+          Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));
+
+        fprintf(stderr, "%d ", (int)s);
+        if(!entry) {
+          fprintf(stderr, "INTERNAL CONFUSION\n");
+          continue;
+        }
+        fprintf(stderr, "[%s %s] ",
+                entry->action&CURL_POLL_IN?"RECVING":"",
+                entry->action&CURL_POLL_OUT?"SENDING":"");
+      }
+      if(easy->numsocks)
+        fprintf(stderr, "\n");
+    }
+  }
+}
+#endif
diff --git a/lib/curl_netrc.c b/lib/curl_netrc.c
new file mode 100644 (file)
index 0000000..10853d3
--- /dev/null
@@ -0,0 +1,186 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef __VMS
+#include <unixlib.h>
+#endif
+
+#include <curl/curl.h>
+#include "curl_netrc.h"
+
+#include "curl_strequal.h"
+#include "curl_strtok.h"
+#include "curl_memory.h"
+#include "curl_rawstr.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/* Get user and password from .netrc when given a machine name */
+
+enum host_lookup_state {
+  NOTHING,
+  HOSTFOUND,    /* the 'machine' keyword was found */
+  HOSTVALID     /* this is "our" machine! */
+};
+
+/*
+ * @unittest: 1304
+ */
+int Curl_parsenetrc(const char *host,
+                    char *login,
+                    char *password,
+                    char *netrcfile)
+{
+  FILE *file;
+  int retcode=1;
+  int specific_login = (login[0] != 0);
+  char *home = NULL;
+  bool home_alloc = FALSE;
+  bool netrc_alloc = FALSE;
+  enum host_lookup_state state=NOTHING;
+
+  char state_login=0;      /* Found a login keyword */
+  char state_password=0;   /* Found a password keyword */
+  int state_our_login=FALSE;  /* With specific_login, found *our* login name */
+
+#define NETRC DOT_CHAR "netrc"
+
+  if(!netrcfile) {
+    home = curl_getenv("HOME"); /* portable environment reader */
+    if(home) {
+      home_alloc = TRUE;
+#if defined(HAVE_GETPWUID) && defined(HAVE_GETEUID)
+    }
+    else {
+      struct passwd *pw;
+      pw= getpwuid(geteuid());
+      if(pw) {
+#ifdef __VMS
+        home = decc_translate_vms(pw->pw_dir);
+#else
+        home = pw->pw_dir;
+#endif
+      }
+#endif
+    }
+
+    if(!home)
+      return -1;
+
+    netrcfile = curl_maprintf("%s%s%s", home, DIR_CHAR, NETRC);
+    if(!netrcfile) {
+      if(home_alloc)
+        free(home);
+      return -1;
+    }
+    netrc_alloc = TRUE;
+  }
+
+  file = fopen(netrcfile, "r");
+  if(file) {
+    char *tok;
+    char *tok_buf;
+    bool done=FALSE;
+    char netrcbuffer[256];
+    int  netrcbuffsize = (int)sizeof(netrcbuffer);
+
+    while(!done && fgets(netrcbuffer, netrcbuffsize, file)) {
+      tok=strtok_r(netrcbuffer, " \t\n", &tok_buf);
+      while(!done && tok) {
+
+        if(login[0] && password[0]) {
+          done=TRUE;
+          break;
+        }
+
+        switch(state) {
+        case NOTHING:
+          if(Curl_raw_equal("machine", tok)) {
+            /* the next tok is the machine name, this is in itself the
+               delimiter that starts the stuff entered for this machine,
+               after this we need to search for 'login' and
+               'password'. */
+            state=HOSTFOUND;
+          }
+          break;
+        case HOSTFOUND:
+          if(Curl_raw_equal(host, tok)) {
+            /* and yes, this is our host! */
+            state=HOSTVALID;
+            retcode=0; /* we did find our host */
+          }
+          else
+            /* not our host */
+            state=NOTHING;
+          break;
+        case HOSTVALID:
+          /* we are now parsing sub-keywords concerning "our" host */
+          if(state_login) {
+            if(specific_login) {
+              state_our_login = Curl_raw_equal(login, tok);
+            }
+            else {
+              strncpy(login, tok, LOGINSIZE-1);
+            }
+            state_login=0;
+          }
+          else if(state_password) {
+            if(state_our_login || !specific_login) {
+              strncpy(password, tok, PASSWORDSIZE-1);
+            }
+            state_password=0;
+          }
+          else if(Curl_raw_equal("login", tok))
+            state_login=1;
+          else if(Curl_raw_equal("password", tok))
+            state_password=1;
+          else if(Curl_raw_equal("machine", tok)) {
+            /* ok, there's machine here go => */
+            state = HOSTFOUND;
+            state_our_login = FALSE;
+          }
+          break;
+        } /* switch (state) */
+
+        tok = strtok_r(NULL, " \t\n", &tok_buf);
+      } /* while(tok) */
+    } /* while fgets() */
+
+    fclose(file);
+  }
+
+  if(home_alloc)
+    free(home);
+  if(netrc_alloc)
+    free(netrcfile);
+
+  return retcode;
+}
diff --git a/lib/curl_non-ascii.h b/lib/curl_non-ascii.h
deleted file mode 100644 (file)
index 552a513..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-#ifndef HEADER_CURL_NON_ASCII_H
-#define HEADER_CURL_NON_ASCII_H
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-#include "curl_setup.h"
-
-#ifdef CURL_DOES_CONVERSIONS
-
-#include "curl_urldata.h"
-
-/*
- * Curl_convert_clone() returns a malloced copy of the source string (if
- * returning CURLE_OK), with the data converted to network format.
- *
- * If no conversion was needed *outbuf may be NULL.
- */
-CURLcode Curl_convert_clone(struct SessionHandle *data,
-                            const char *indata,
-                            size_t insize,
-                            char **outbuf);
-
-void Curl_convert_init(struct SessionHandle *data);
-void Curl_convert_setup(struct SessionHandle *data);
-void Curl_convert_close(struct SessionHandle *data);
-
-CURLcode Curl_convert_to_network(struct SessionHandle *data,
-                                 char *buffer, size_t length);
-CURLcode Curl_convert_from_network(struct SessionHandle *data,
-                                 char *buffer, size_t length);
-CURLcode Curl_convert_from_utf8(struct SessionHandle *data,
-                                 char *buffer, size_t length);
-CURLcode Curl_convert_form(struct SessionHandle *data, struct FormData *form);
-#else
-#define Curl_convert_clone(a,b,c,d) ((void)a, CURLE_OK)
-#define Curl_convert_init(x) Curl_nop_stmt
-#define Curl_convert_setup(x) Curl_nop_stmt
-#define Curl_convert_close(x) Curl_nop_stmt
-#define Curl_convert_to_network(a,b,c) ((void)a, CURLE_OK)
-#define Curl_convert_from_network(a,b,c) ((void)a, CURLE_OK)
-#define Curl_convert_from_utf8(a,b,c) ((void)a, CURLE_OK)
-#define Curl_convert_form(a,b) CURLE_OK
-#endif
-
-#endif /* HEADER_CURL_NON_ASCII_H */
diff --git a/lib/curl_non_ascii.c b/lib/curl_non_ascii.c
new file mode 100644 (file)
index 0000000..68b33a9
--- /dev/null
@@ -0,0 +1,343 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef CURL_DOES_CONVERSIONS
+
+#include <curl/curl.h>
+
+#include "curl_non_ascii.h"
+#include "curl_formdata.h"
+#include "curl_sendf.h"
+#include "curl_urldata.h"
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+#ifdef HAVE_ICONV
+#include <iconv.h>
+/* set default codesets for iconv */
+#ifndef CURL_ICONV_CODESET_OF_NETWORK
+#define CURL_ICONV_CODESET_OF_NETWORK "ISO8859-1"
+#endif
+#ifndef CURL_ICONV_CODESET_FOR_UTF8
+#define CURL_ICONV_CODESET_FOR_UTF8   "UTF-8"
+#endif
+#define ICONV_ERROR  (size_t)-1
+#endif /* HAVE_ICONV */
+
+/*
+ * Curl_convert_clone() returns a malloced copy of the source string (if
+ * returning CURLE_OK), with the data converted to network format.
+ */
+CURLcode Curl_convert_clone(struct SessionHandle *data,
+                           const char *indata,
+                           size_t insize,
+                           char **outbuf)
+{
+  char *convbuf;
+  CURLcode result;
+
+  convbuf = malloc(insize);
+  if(!convbuf)
+    return CURLE_OUT_OF_MEMORY;
+
+  memcpy(convbuf, indata, insize);
+  result = Curl_convert_to_network(data, convbuf, insize);
+  if(result) {
+    free(convbuf);
+    return result;
+  }
+
+  *outbuf = convbuf; /* return the converted buffer */
+
+  return CURLE_OK;
+}
+
+/*
+ * Curl_convert_to_network() is an internal function for performing ASCII
+ * conversions on non-ASCII platforms. It convers the buffer _in place_.
+ */
+CURLcode Curl_convert_to_network(struct SessionHandle *data,
+                                 char *buffer, size_t length)
+{
+  CURLcode rc;
+
+  if(data->set.convtonetwork) {
+    /* use translation callback */
+    rc = data->set.convtonetwork(buffer, length);
+    if(rc != CURLE_OK) {
+      failf(data,
+            "CURLOPT_CONV_TO_NETWORK_FUNCTION callback returned %d: %s",
+            (int)rc, curl_easy_strerror(rc));
+    }
+    return rc;
+  }
+  else {
+#ifdef HAVE_ICONV
+    /* do the translation ourselves */
+    char *input_ptr, *output_ptr;
+    size_t in_bytes, out_bytes, rc;
+    int error;
+
+    /* open an iconv conversion descriptor if necessary */
+    if(data->outbound_cd == (iconv_t)-1) {
+      data->outbound_cd = iconv_open(CURL_ICONV_CODESET_OF_NETWORK,
+                                     CURL_ICONV_CODESET_OF_HOST);
+      if(data->outbound_cd == (iconv_t)-1) {
+        error = ERRNO;
+        failf(data,
+              "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s",
+              CURL_ICONV_CODESET_OF_NETWORK,
+              CURL_ICONV_CODESET_OF_HOST,
+              error, strerror(error));
+        return CURLE_CONV_FAILED;
+      }
+    }
+    /* call iconv */
+    input_ptr = output_ptr = buffer;
+    in_bytes = out_bytes = length;
+    rc = iconv(data->outbound_cd, (const char**)&input_ptr, &in_bytes,
+               &output_ptr, &out_bytes);
+    if((rc == ICONV_ERROR) || (in_bytes != 0)) {
+      error = ERRNO;
+      failf(data,
+            "The Curl_convert_to_network iconv call failed with errno %i: %s",
+            error, strerror(error));
+      return CURLE_CONV_FAILED;
+    }
+#else
+    failf(data, "CURLOPT_CONV_TO_NETWORK_FUNCTION callback required");
+    return CURLE_CONV_REQD;
+#endif /* HAVE_ICONV */
+  }
+
+  return CURLE_OK;
+}
+
+/*
+ * Curl_convert_from_network() is an internal function for performing ASCII
+ * conversions on non-ASCII platforms. It convers the buffer _in place_.
+ */
+CURLcode Curl_convert_from_network(struct SessionHandle *data,
+                                   char *buffer, size_t length)
+{
+  CURLcode rc;
+
+  if(data->set.convfromnetwork) {
+    /* use translation callback */
+    rc = data->set.convfromnetwork(buffer, length);
+    if(rc != CURLE_OK) {
+      failf(data,
+            "CURLOPT_CONV_FROM_NETWORK_FUNCTION callback returned %d: %s",
+            (int)rc, curl_easy_strerror(rc));
+    }
+    return rc;
+  }
+  else {
+#ifdef HAVE_ICONV
+    /* do the translation ourselves */
+    char *input_ptr, *output_ptr;
+    size_t in_bytes, out_bytes, rc;
+    int error;
+
+    /* open an iconv conversion descriptor if necessary */
+    if(data->inbound_cd == (iconv_t)-1) {
+      data->inbound_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
+                                    CURL_ICONV_CODESET_OF_NETWORK);
+      if(data->inbound_cd == (iconv_t)-1) {
+        error = ERRNO;
+        failf(data,
+              "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s",
+              CURL_ICONV_CODESET_OF_HOST,
+              CURL_ICONV_CODESET_OF_NETWORK,
+              error, strerror(error));
+        return CURLE_CONV_FAILED;
+      }
+    }
+    /* call iconv */
+    input_ptr = output_ptr = buffer;
+    in_bytes = out_bytes = length;
+    rc = iconv(data->inbound_cd, (const char **)&input_ptr, &in_bytes,
+               &output_ptr, &out_bytes);
+    if((rc == ICONV_ERROR) || (in_bytes != 0)) {
+      error = ERRNO;
+      failf(data,
+            "Curl_convert_from_network iconv call failed with errno %i: %s",
+            error, strerror(error));
+      return CURLE_CONV_FAILED;
+    }
+#else
+    failf(data, "CURLOPT_CONV_FROM_NETWORK_FUNCTION callback required");
+    return CURLE_CONV_REQD;
+#endif /* HAVE_ICONV */
+  }
+
+  return CURLE_OK;
+}
+
+/*
+ * Curl_convert_from_utf8() is an internal function for performing UTF-8
+ * conversions on non-ASCII platforms.
+ */
+CURLcode Curl_convert_from_utf8(struct SessionHandle *data,
+                                char *buffer, size_t length)
+{
+  CURLcode rc;
+
+  if(data->set.convfromutf8) {
+    /* use translation callback */
+    rc = data->set.convfromutf8(buffer, length);
+    if(rc != CURLE_OK) {
+      failf(data,
+            "CURLOPT_CONV_FROM_UTF8_FUNCTION callback returned %d: %s",
+            (int)rc, curl_easy_strerror(rc));
+    }
+    return rc;
+  }
+  else {
+#ifdef HAVE_ICONV
+    /* do the translation ourselves */
+    const char *input_ptr;
+    char *output_ptr;
+    size_t in_bytes, out_bytes, rc;
+    int error;
+
+    /* open an iconv conversion descriptor if necessary */
+    if(data->utf8_cd == (iconv_t)-1) {
+      data->utf8_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
+                                 CURL_ICONV_CODESET_FOR_UTF8);
+      if(data->utf8_cd == (iconv_t)-1) {
+        error = ERRNO;
+        failf(data,
+              "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s",
+              CURL_ICONV_CODESET_OF_HOST,
+              CURL_ICONV_CODESET_FOR_UTF8,
+              error, strerror(error));
+        return CURLE_CONV_FAILED;
+      }
+    }
+    /* call iconv */
+    input_ptr = output_ptr = buffer;
+    in_bytes = out_bytes = length;
+    rc = iconv(data->utf8_cd, &input_ptr, &in_bytes,
+               &output_ptr, &out_bytes);
+    if((rc == ICONV_ERROR) || (in_bytes != 0)) {
+      error = ERRNO;
+      failf(data,
+            "The Curl_convert_from_utf8 iconv call failed with errno %i: %s",
+            error, strerror(error));
+      return CURLE_CONV_FAILED;
+    }
+    if(output_ptr < input_ptr) {
+      /* null terminate the now shorter output string */
+      *output_ptr = 0x00;
+    }
+#else
+    failf(data, "CURLOPT_CONV_FROM_UTF8_FUNCTION callback required");
+    return CURLE_CONV_REQD;
+#endif /* HAVE_ICONV */
+  }
+
+  return CURLE_OK;
+}
+
+/*
+ * Init conversion stuff for a SessionHandle
+ */
+void Curl_convert_init(struct SessionHandle *data)
+{
+#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
+  /* conversion descriptors for iconv calls */
+  data->outbound_cd = (iconv_t)-1;
+  data->inbound_cd  = (iconv_t)-1;
+  data->utf8_cd     = (iconv_t)-1;
+#else
+  (void)data;
+#endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */
+}
+
+/*
+ * Setup conversion stuff for a SessionHandle
+ */
+void Curl_convert_setup(struct SessionHandle *data)
+{
+  data->inbound_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
+                                CURL_ICONV_CODESET_OF_NETWORK);
+  data->outbound_cd = iconv_open(CURL_ICONV_CODESET_OF_NETWORK,
+                                 CURL_ICONV_CODESET_OF_HOST);
+  data->utf8_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
+                             CURL_ICONV_CODESET_FOR_UTF8);
+}
+
+/*
+ * Close conversion stuff for a SessionHandle
+ */
+
+void Curl_convert_close(struct SessionHandle *data)
+{
+#ifdef HAVE_ICONV
+  /* close iconv conversion descriptors */
+  if(data->inbound_cd != (iconv_t)-1) {
+    iconv_close(data->inbound_cd);
+  }
+  if(data->outbound_cd != (iconv_t)-1) {
+    iconv_close(data->outbound_cd);
+  }
+  if(data->utf8_cd != (iconv_t)-1) {
+    iconv_close(data->utf8_cd);
+  }
+#else
+  (void)data;
+#endif /* HAVE_ICONV */
+}
+
+/*
+ * Curl_convert_form() is used from curl_http.c, this converts any form items
+ * that need to be sent in the network encoding.  Returns CURLE_OK on success.
+ */
+CURLcode Curl_convert_form(struct SessionHandle *data, struct FormData *form)
+{
+  struct FormData *next;
+  CURLcode rc;
+
+  if(!form)
+    return CURLE_OK;
+
+  if(!data)
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+
+  do {
+    next=form->next;  /* the following form line */
+    if(form->type == FORM_DATA) {
+      rc = Curl_convert_to_network(data, form->line, form->length);
+      /* Curl_convert_to_network calls failf if unsuccessful */
+      if(rc != CURLE_OK)
+        return rc;
+    }
+  } while((form = next) != NULL); /* continue */
+  return CURLE_OK;
+}
+
+#endif /* CURL_DOES_CONVERSIONS */
diff --git a/lib/curl_non_ascii.h b/lib/curl_non_ascii.h
new file mode 100644 (file)
index 0000000..552a513
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef HEADER_CURL_NON_ASCII_H
+#define HEADER_CURL_NON_ASCII_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#ifdef CURL_DOES_CONVERSIONS
+
+#include "curl_urldata.h"
+
+/*
+ * Curl_convert_clone() returns a malloced copy of the source string (if
+ * returning CURLE_OK), with the data converted to network format.
+ *
+ * If no conversion was needed *outbuf may be NULL.
+ */
+CURLcode Curl_convert_clone(struct SessionHandle *data,
+                            const char *indata,
+                            size_t insize,
+                            char **outbuf);
+
+void Curl_convert_init(struct SessionHandle *data);
+void Curl_convert_setup(struct SessionHandle *data);
+void Curl_convert_close(struct SessionHandle *data);
+
+CURLcode Curl_convert_to_network(struct SessionHandle *data,
+                                 char *buffer, size_t length);
+CURLcode Curl_convert_from_network(struct SessionHandle *data,
+                                 char *buffer, size_t length);
+CURLcode Curl_convert_from_utf8(struct SessionHandle *data,
+                                 char *buffer, size_t length);
+CURLcode Curl_convert_form(struct SessionHandle *data, struct FormData *form);
+#else
+#define Curl_convert_clone(a,b,c,d) ((void)a, CURLE_OK)
+#define Curl_convert_init(x) Curl_nop_stmt
+#define Curl_convert_setup(x) Curl_nop_stmt
+#define Curl_convert_close(x) Curl_nop_stmt
+#define Curl_convert_to_network(a,b,c) ((void)a, CURLE_OK)
+#define Curl_convert_from_network(a,b,c) ((void)a, CURLE_OK)
+#define Curl_convert_from_utf8(a,b,c) ((void)a, CURLE_OK)
+#define Curl_convert_form(a,b) CURLE_OK
+#endif
+
+#endif /* HEADER_CURL_NON_ASCII_H */
diff --git a/lib/curl_nonblock.c b/lib/curl_nonblock.c
new file mode 100644 (file)
index 0000000..6e6d128
--- /dev/null
@@ -0,0 +1,91 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#if (defined(HAVE_IOCTL_FIONBIO) && defined(NETWARE))
+#include <sys/filio.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#include "curl_nonblock.h"
+
+/*
+ * curlx_nonblock() set the given socket to either blocking or non-blocking
+ * mode based on the 'nonblock' boolean argument. This function is highly
+ * portable.
+ */
+int curlx_nonblock(curl_socket_t sockfd,    /* operate on this */
+                   int nonblock   /* TRUE or FALSE */)
+{
+#if defined(USE_BLOCKING_SOCKETS)
+
+  return 0; /* returns success */
+
+#elif defined(HAVE_FCNTL_O_NONBLOCK)
+
+  /* most recent unix versions */
+  int flags;
+  flags = sfcntl(sockfd, F_GETFL, 0);
+  if(nonblock)
+    return sfcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
+  else
+    return sfcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK));
+
+#elif defined(HAVE_IOCTL_FIONBIO)
+
+  /* older unix versions */
+  int flags = nonblock ? 1 : 0;
+  return ioctl(sockfd, FIONBIO, &flags);
+
+#elif defined(HAVE_IOCTLSOCKET_FIONBIO)
+
+  /* Windows */
+  unsigned long flags = nonblock ? 1UL : 0UL;
+  return ioctlsocket(sockfd, FIONBIO, &flags);
+
+#elif defined(HAVE_IOCTLSOCKET_CAMEL_FIONBIO)
+
+  /* Amiga */
+  long flags = nonblock ? 1L : 0L;
+  return IoctlSocket(sockfd, FIONBIO, flags);
+
+#elif defined(HAVE_SETSOCKOPT_SO_NONBLOCK)
+
+  /* BeOS */
+  long b = nonblock ? 1L : 0L;
+  return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b));
+
+#else
+#  error "no non-blocking method was found/used/set"
+#endif
+}
diff --git a/lib/curl_nss.c b/lib/curl_nss.c
new file mode 100644 (file)
index 0000000..15e92a7
--- /dev/null
@@ -0,0 +1,1572 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/*
+ * Source file for all NSS-specific code for the TLS/SSL layer. No code
+ * but curl_sslgen.c should ever call or use these functions.
+ */
+
+#include "curl_setup.h"
+
+#ifdef USE_NSS
+
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_formdata.h" /* for the boundary function */
+#include "curl_url.h" /* for the ssl config check function */
+#include "curl_connect.h"
+#include "curl_strequal.h"
+#include "curl_select.h"
+#include "curl_sslgen.h"
+#include "curl_llist.h"
+
+#define _MPRINTF_REPLACE /* use the internal *printf() functions */
+#include <curl/mprintf.h>
+
+#include "curl_nssg.h"
+#include <nspr.h>
+#include <nss.h>
+#include <ssl.h>
+#include <sslerr.h>
+#include <secerr.h>
+#include <secmod.h>
+#include <sslproto.h>
+#include <prtypes.h>
+#include <pk11pub.h>
+#include <prio.h>
+#include <secitem.h>
+#include <secport.h>
+#include <certdb.h>
+#include <base64.h>
+#include <cert.h>
+#include <prerror.h>
+
+#include "curl_memory.h"
+#include "curl_rawstr.h"
+#include "curl_warnless.h"
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+#define SSL_DIR "/etc/pki/nssdb"
+
+/* enough to fit the string "PEM Token #[0|1]" */
+#define SLOTSIZE 13
+
+PRFileDesc *PR_ImportTCPSocket(PRInt32 osfd);
+
+PRLock * nss_initlock = NULL;
+PRLock * nss_crllock = NULL;
+#ifdef HAVE_NSS_INITCONTEXT
+NSSInitContext * nss_context = NULL;
+#endif
+
+volatile int initialized = 0;
+
+typedef struct {
+  const char *name;
+  int num;
+} cipher_s;
+
+#define PK11_SETATTRS(_attr, _idx, _type, _val, _len) do {  \
+  CK_ATTRIBUTE *ptr = (_attr) + ((_idx)++);                 \
+  ptr->type = (_type);                                      \
+  ptr->pValue = (_val);                                     \
+  ptr->ulValueLen = (_len);                                 \
+} WHILE_FALSE
+
+#define CERT_NewTempCertificate __CERT_NewTempCertificate
+
+#define NUM_OF_CIPHERS sizeof(cipherlist)/sizeof(cipherlist[0])
+static const cipher_s cipherlist[] = {
+  /* SSL2 cipher suites */
+  {"rc4",                        SSL_EN_RC4_128_WITH_MD5},
+  {"rc4-md5",                    SSL_EN_RC4_128_WITH_MD5},
+  {"rc4export",                  SSL_EN_RC4_128_EXPORT40_WITH_MD5},
+  {"rc2",                        SSL_EN_RC2_128_CBC_WITH_MD5},
+  {"rc2export",                  SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5},
+  {"des",                        SSL_EN_DES_64_CBC_WITH_MD5},
+  {"desede3",                    SSL_EN_DES_192_EDE3_CBC_WITH_MD5},
+  /* SSL3/TLS cipher suites */
+  {"rsa_rc4_128_md5",            SSL_RSA_WITH_RC4_128_MD5},
+  {"rsa_rc4_128_sha",            SSL_RSA_WITH_RC4_128_SHA},
+  {"rsa_3des_sha",               SSL_RSA_WITH_3DES_EDE_CBC_SHA},
+  {"rsa_des_sha",                SSL_RSA_WITH_DES_CBC_SHA},
+  {"rsa_rc4_40_md5",             SSL_RSA_EXPORT_WITH_RC4_40_MD5},
+  {"rsa_rc2_40_md5",             SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5},
+  {"rsa_null_md5",               SSL_RSA_WITH_NULL_MD5},
+  {"rsa_null_sha",               SSL_RSA_WITH_NULL_SHA},
+  {"fips_3des_sha",              SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA},
+  {"fips_des_sha",               SSL_RSA_FIPS_WITH_DES_CBC_SHA},
+  {"fortezza",                   SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA},
+  {"fortezza_rc4_128_sha",       SSL_FORTEZZA_DMS_WITH_RC4_128_SHA},
+  {"fortezza_null",              SSL_FORTEZZA_DMS_WITH_NULL_SHA},
+  /* TLS 1.0: Exportable 56-bit Cipher Suites. */
+  {"rsa_des_56_sha",             TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA},
+  {"rsa_rc4_56_sha",             TLS_RSA_EXPORT1024_WITH_RC4_56_SHA},
+  /* AES ciphers. */
+  {"rsa_aes_128_sha",            TLS_RSA_WITH_AES_128_CBC_SHA},
+  {"rsa_aes_256_sha",            TLS_RSA_WITH_AES_256_CBC_SHA},
+#ifdef NSS_ENABLE_ECC
+  /* ECC ciphers. */
+  {"ecdh_ecdsa_null_sha",        TLS_ECDH_ECDSA_WITH_NULL_SHA},
+  {"ecdh_ecdsa_rc4_128_sha",     TLS_ECDH_ECDSA_WITH_RC4_128_SHA},
+  {"ecdh_ecdsa_3des_sha",        TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA},
+  {"ecdh_ecdsa_aes_128_sha",     TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA},
+  {"ecdh_ecdsa_aes_256_sha",     TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA},
+  {"ecdhe_ecdsa_null_sha",       TLS_ECDHE_ECDSA_WITH_NULL_SHA},
+  {"ecdhe_ecdsa_rc4_128_sha",    TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
+  {"ecdhe_ecdsa_3des_sha",       TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA},
+  {"ecdhe_ecdsa_aes_128_sha",    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
+  {"ecdhe_ecdsa_aes_256_sha",    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
+  {"ecdh_rsa_null_sha",          TLS_ECDH_RSA_WITH_NULL_SHA},
+  {"ecdh_rsa_128_sha",           TLS_ECDH_RSA_WITH_RC4_128_SHA},
+  {"ecdh_rsa_3des_sha",          TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA},
+  {"ecdh_rsa_aes_128_sha",       TLS_ECDH_RSA_WITH_AES_128_CBC_SHA},
+  {"ecdh_rsa_aes_256_sha",       TLS_ECDH_RSA_WITH_AES_256_CBC_SHA},
+  {"echde_rsa_null",             TLS_ECDHE_RSA_WITH_NULL_SHA},
+  {"ecdhe_rsa_rc4_128_sha",      TLS_ECDHE_RSA_WITH_RC4_128_SHA},
+  {"ecdhe_rsa_3des_sha",         TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA},
+  {"ecdhe_rsa_aes_128_sha",      TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
+  {"ecdhe_rsa_aes_256_sha",      TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
+  {"ecdh_anon_null_sha",         TLS_ECDH_anon_WITH_NULL_SHA},
+  {"ecdh_anon_rc4_128sha",       TLS_ECDH_anon_WITH_RC4_128_SHA},
+  {"ecdh_anon_3des_sha",         TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA},
+  {"ecdh_anon_aes_128_sha",      TLS_ECDH_anon_WITH_AES_128_CBC_SHA},
+  {"ecdh_anon_aes_256_sha",      TLS_ECDH_anon_WITH_AES_256_CBC_SHA},
+#endif
+};
+
+/* following ciphers are new in NSS 3.4 and not enabled by default, therefore
+   they are enabled explicitly */
+static const int enable_ciphers_by_default[] = {
+  TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
+  TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
+  TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
+  TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+  TLS_RSA_WITH_AES_128_CBC_SHA,
+  TLS_RSA_WITH_AES_256_CBC_SHA,
+  SSL_NULL_WITH_NULL_NULL
+};
+
+static const char* pem_library = "libnsspem.so";
+SECMODModule* mod = NULL;
+
+static const char* nss_error_to_name(PRErrorCode code)
+{
+  const char *name = PR_ErrorToName(code);
+  if(name)
+    return name;
+
+  return "unknown error";
+}
+
+static void nss_print_error_message(struct SessionHandle *data, PRUint32 err)
+{
+  failf(data, "%s", PR_ErrorToString(err, PR_LANGUAGE_I_DEFAULT));
+}
+
+static SECStatus set_ciphers(struct SessionHandle *data, PRFileDesc * model,
+                             char *cipher_list)
+{
+  unsigned int i;
+  PRBool cipher_state[NUM_OF_CIPHERS];
+  PRBool found;
+  char *cipher;
+  SECStatus rv;
+
+  /* First disable all ciphers. This uses a different max value in case
+   * NSS adds more ciphers later we don't want them available by
+   * accident
+   */
+  for(i=0; i<SSL_NumImplementedCiphers; i++) {
+    SSL_CipherPrefSet(model, SSL_ImplementedCiphers[i], SSL_NOT_ALLOWED);
+  }
+
+  /* Set every entry in our list to false */
+  for(i=0; i<NUM_OF_CIPHERS; i++) {
+    cipher_state[i] = PR_FALSE;
+  }
+
+  cipher = cipher_list;
+
+  while(cipher_list && (cipher_list[0])) {
+    while((*cipher) && (ISSPACE(*cipher)))
+      ++cipher;
+
+    if((cipher_list = strchr(cipher, ','))) {
+      *cipher_list++ = '\0';
+    }
+
+    found = PR_FALSE;
+
+    for(i=0; i<NUM_OF_CIPHERS; i++) {
+      if(Curl_raw_equal(cipher, cipherlist[i].name)) {
+        cipher_state[i] = PR_TRUE;
+        found = PR_TRUE;
+        break;
+      }
+    }
+
+    if(found == PR_FALSE) {
+      failf(data, "Unknown cipher in list: %s", cipher);
+      return SECFailure;
+    }
+
+    if(cipher_list) {
+      cipher = cipher_list;
+    }
+  }
+
+  /* Finally actually enable the selected ciphers */
+  for(i=0; i<NUM_OF_CIPHERS; i++) {
+    rv = SSL_CipherPrefSet(model, cipherlist[i].num, cipher_state[i]);
+    if(rv != SECSuccess) {
+      failf(data, "cipher-suite not supported by NSS: %s", cipherlist[i].name);
+      return SECFailure;
+    }
+  }
+
+  return SECSuccess;
+}
+
+/*
+ * Get the number of ciphers that are enabled. We use this to determine
+ * if we need to call NSS_SetDomesticPolicy() to enable the default ciphers.
+ */
+static int num_enabled_ciphers(void)
+{
+  PRInt32 policy = 0;
+  int count = 0;
+  unsigned int i;
+
+  for(i=0; i<NUM_OF_CIPHERS; i++) {
+    SSL_CipherPolicyGet(cipherlist[i].num, &policy);
+    if(policy)
+      count++;
+  }
+  return count;
+}
+
+/*
+ * Determine whether the nickname passed in is a filename that needs to
+ * be loaded as a PEM or a regular NSS nickname.
+ *
+ * returns 1 for a file
+ * returns 0 for not a file (NSS nickname)
+ */
+static int is_file(const char *filename)
+{
+  struct_stat st;
+
+  if(filename == NULL)
+    return 0;
+
+  if(stat(filename, &st) == 0)
+    if(S_ISREG(st.st_mode))
+      return 1;
+
+  return 0;
+}
+
+/* Check if the given string is filename or nickname of a certificate.  If the
+ * given string is recognized as filename, return NULL.  If the given string is
+ * recognized as nickname, return a duplicated string.  The returned string
+ * should be later deallocated using free().  If the OOM failure occurs, we
+ * return NULL, too.
+ */
+static char* dup_nickname(struct SessionHandle *data, enum dupstring cert_kind)
+{
+  const char *str = data->set.str[cert_kind];
+  const char *n;
+
+  if(!is_file(str))
+    /* no such file exists, use the string as nickname */
+    return strdup(str);
+
+  /* search the last slash; we require at least one slash in a file name */
+  n = strrchr(str, '/');
+  if(!n) {
+    infof(data, "warning: certificate file name \"%s\" handled as nickname; "
+          "please use \"./%s\" to force file name\n", str, str);
+    return strdup(str);
+  }
+
+  /* we'll use the PEM reader to read the certificate from file */
+  return NULL;
+}
+
+/* Call PK11_CreateGenericObject() with the given obj_class and filename.  If
+ * the call succeeds, append the object handle to the list of objects so that
+ * the object can be destroyed in Curl_nss_close(). */
+static CURLcode nss_create_object(struct ssl_connect_data *ssl,
+                                  CK_OBJECT_CLASS obj_class,
+                                  const char *filename, bool cacert)
+{
+  PK11SlotInfo *slot;
+  PK11GenericObject *obj;
+  CK_BBOOL cktrue = CK_TRUE;
+  CK_BBOOL ckfalse = CK_FALSE;
+  CK_ATTRIBUTE attrs[/* max count of attributes */ 4];
+  int attr_cnt = 0;
+  CURLcode err = (cacert)
+    ? CURLE_SSL_CACERT_BADFILE
+    : CURLE_SSL_CERTPROBLEM;
+
+  const int slot_id = (cacert) ? 0 : 1;
+  char *slot_name = aprintf("PEM Token #%d", slot_id);
+  if(!slot_name)
+    return CURLE_OUT_OF_MEMORY;
+
+  slot = PK11_FindSlotByName(slot_name);
+  free(slot_name);
+  if(!slot)
+    return err;
+
+  PK11_SETATTRS(attrs, attr_cnt, CKA_CLASS, &obj_class, sizeof(obj_class));
+  PK11_SETATTRS(attrs, attr_cnt, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL));
+  PK11_SETATTRS(attrs, attr_cnt, CKA_LABEL, (unsigned char *)filename,
+                strlen(filename) + 1);
+
+  if(CKO_CERTIFICATE == obj_class) {
+    CK_BBOOL *pval = (cacert) ? (&cktrue) : (&ckfalse);
+    PK11_SETATTRS(attrs, attr_cnt, CKA_TRUST, pval, sizeof(*pval));
+  }
+
+  obj = PK11_CreateGenericObject(slot, attrs, attr_cnt, PR_FALSE);
+  PK11_FreeSlot(slot);
+  if(!obj)
+    return err;
+
+  if(!Curl_llist_insert_next(ssl->obj_list, ssl->obj_list->tail, obj)) {
+    PK11_DestroyGenericObject(obj);
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  if(!cacert && CKO_CERTIFICATE == obj_class)
+    /* store reference to a client certificate */
+    ssl->obj_clicert = obj;
+
+  return CURLE_OK;
+}
+
+/* Destroy the NSS object whose handle is given by ptr.  This function is
+ * a callback of Curl_llist_alloc() used by Curl_llist_destroy() to destroy
+ * NSS objects in Curl_nss_close() */
+static void nss_destroy_object(void *user, void *ptr)
+{
+  PK11GenericObject *obj = (PK11GenericObject *)ptr;
+  (void) user;
+  PK11_DestroyGenericObject(obj);
+}
+
+static CURLcode nss_load_cert(struct ssl_connect_data *ssl,
+                              const char *filename, PRBool cacert)
+{
+  CURLcode err = (cacert)
+    ? CURLE_SSL_CACERT_BADFILE
+    : CURLE_SSL_CERTPROBLEM;
+
+  /* libnsspem.so leaks memory if the requested file does not exist.  For more
+   * details, go to <https://bugzilla.redhat.com/734760>. */
+  if(is_file(filename))
+    err = nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert);
+
+  if(CURLE_OK == err && !cacert) {
+    /* we have successfully loaded a client certificate */
+    CERTCertificate *cert;
+    char *nickname = NULL;
+    char *n = strrchr(filename, '/');
+    if(n)
+      n++;
+
+    /* The following undocumented magic helps to avoid a SIGSEGV on call
+     * of PK11_ReadRawAttribute() from SelectClientCert() when using an
+     * immature version of libnsspem.so.  For more details, go to
+     * <https://bugzilla.redhat.com/733685>. */
+    nickname = aprintf("PEM Token #1:%s", n);
+    if(nickname) {
+      cert = PK11_FindCertFromNickname(nickname, NULL);
+      if(cert)
+        CERT_DestroyCertificate(cert);
+
+      free(nickname);
+    }
+  }
+
+  return err;
+}
+
+/* add given CRL to cache if it is not already there */
+static SECStatus nss_cache_crl(SECItem *crlDER)
+{
+  CERTCertDBHandle *db = CERT_GetDefaultCertDB();
+  CERTSignedCrl *crl = SEC_FindCrlByDERCert(db, crlDER, 0);
+  if(crl) {
+    /* CRL already cached */
+    SEC_DestroyCrl(crl);
+    SECITEM_FreeItem(crlDER, PR_FALSE);
+    return SECSuccess;
+  }
+
+  /* acquire lock before call of CERT_CacheCRL() */
+  PR_Lock(nss_crllock);
+  if(SECSuccess != CERT_CacheCRL(db, crlDER)) {
+    /* unable to cache CRL */
+    PR_Unlock(nss_crllock);
+    SECITEM_FreeItem(crlDER, PR_FALSE);
+    return SECFailure;
+  }
+
+  /* we need to clear session cache, so that the CRL could take effect */
+  SSL_ClearSessionCache();
+  PR_Unlock(nss_crllock);
+  return SECSuccess;
+}
+
+static SECStatus nss_load_crl(const char* crlfilename)
+{
+  PRFileDesc *infile;
+  PRFileInfo  info;
+  SECItem filedata = { 0, NULL, 0 };
+  SECItem crlDER = { 0, NULL, 0 };
+  char *body;
+
+  infile = PR_Open(crlfilename, PR_RDONLY, 0);
+  if(!infile)
+    return SECFailure;
+
+  if(PR_SUCCESS != PR_GetOpenFileInfo(infile, &info))
+    goto fail;
+
+  if(!SECITEM_AllocItem(NULL, &filedata, info.size + /* zero ended */ 1))
+    goto fail;
+
+  if(info.size != PR_Read(infile, filedata.data, info.size))
+    goto fail;
+
+  /* place a trailing zero right after the visible data */
+  body = (char*)filedata.data;
+  body[--filedata.len] = '\0';
+
+  body = strstr(body, "-----BEGIN");
+  if(body) {
+    /* assume ASCII */
+    char *trailer;
+    char *begin = PORT_Strchr(body, '\n');
+    if(!begin)
+      begin = PORT_Strchr(body, '\r');
+    if(!begin)
+      goto fail;
+
+    trailer = strstr(++begin, "-----END");
+    if(!trailer)
+      goto fail;
+
+    /* retrieve DER from ASCII */
+    *trailer = '\0';
+    if(ATOB_ConvertAsciiToItem(&crlDER, begin))
+      goto fail;
+
+    SECITEM_FreeItem(&filedata, PR_FALSE);
+  }
+  else
+    /* assume DER */
+    crlDER = filedata;
+
+  PR_Close(infile);
+  return nss_cache_crl(&crlDER);
+
+fail:
+  PR_Close(infile);
+  SECITEM_FreeItem(&filedata, PR_FALSE);
+  return SECFailure;
+}
+
+static CURLcode nss_load_key(struct connectdata *conn, int sockindex,
+                             char *key_file)
+{
+  PK11SlotInfo *slot;
+  SECStatus status;
+  CURLcode rv;
+  struct ssl_connect_data *ssl = conn->ssl;
+  (void)sockindex; /* unused */
+
+  rv = nss_create_object(ssl, CKO_PRIVATE_KEY, key_file, FALSE);
+  if(CURLE_OK != rv) {
+    PR_SetError(SEC_ERROR_BAD_KEY, 0);
+    return rv;
+  }
+
+  slot = PK11_FindSlotByName("PEM Token #1");
+  if(!slot)
+    return CURLE_SSL_CERTPROBLEM;
+
+  /* This will force the token to be seen as re-inserted */
+  SECMOD_WaitForAnyTokenEvent(mod, 0, 0);
+  PK11_IsPresent(slot);
+
+  status = PK11_Authenticate(slot, PR_TRUE,
+                             conn->data->set.str[STRING_KEY_PASSWD]);
+  PK11_FreeSlot(slot);
+  return (SECSuccess == status)
+    ? CURLE_OK
+    : CURLE_SSL_CERTPROBLEM;
+}
+
+static int display_error(struct connectdata *conn, PRInt32 err,
+                         const char *filename)
+{
+  switch(err) {
+  case SEC_ERROR_BAD_PASSWORD:
+    failf(conn->data, "Unable to load client key: Incorrect password");
+    return 1;
+  case SEC_ERROR_UNKNOWN_CERT:
+    failf(conn->data, "Unable to load certificate %s", filename);
+    return 1;
+  default:
+    break;
+  }
+  return 0; /* The caller will print a generic error */
+}
+
+static CURLcode cert_stuff(struct connectdata *conn, int sockindex,
+                           char *cert_file, char *key_file)
+{
+  struct SessionHandle *data = conn->data;
+  CURLcode rv;
+
+  if(cert_file) {
+    rv = nss_load_cert(&conn->ssl[sockindex], cert_file, PR_FALSE);
+    if(CURLE_OK != rv) {
+      const PRErrorCode err = PR_GetError();
+      if(!display_error(conn, err, cert_file)) {
+        const char *err_name = nss_error_to_name(err);
+        failf(data, "unable to load client cert: %d (%s)", err, err_name);
+      }
+
+      return rv;
+    }
+  }
+
+  if(key_file || (is_file(cert_file))) {
+    if(key_file)
+      rv = nss_load_key(conn, sockindex, key_file);
+    else
+      /* In case the cert file also has the key */
+      rv = nss_load_key(conn, sockindex, cert_file);
+    if(CURLE_OK != rv) {
+      const PRErrorCode err = PR_GetError();
+      if(!display_error(conn, err, key_file)) {
+        const char *err_name = nss_error_to_name(err);
+        failf(data, "unable to load client key: %d (%s)", err, err_name);
+      }
+
+      return rv;
+    }
+  }
+
+  return CURLE_OK;
+}
+
+static char * nss_get_password(PK11SlotInfo * slot, PRBool retry, void *arg)
+{
+  (void)slot; /* unused */
+  if(retry || NULL == arg)
+    return NULL;
+  else
+    return (char *)PORT_Strdup((char *)arg);
+}
+
+/* bypass the default SSL_AuthCertificate() hook in case we do not want to
+ * verify peer */
+static SECStatus nss_auth_cert_hook(void *arg, PRFileDesc *fd, PRBool checksig,
+                                    PRBool isServer)
+{
+  struct connectdata *conn = (struct connectdata *)arg;
+  if(!conn->data->set.ssl.verifypeer) {
+    infof(conn->data, "skipping SSL peer certificate verification\n");
+    return SECSuccess;
+  }
+
+  return SSL_AuthCertificate(CERT_GetDefaultCertDB(), fd, checksig, isServer);
+}
+
+/**
+ * Inform the application that the handshake is complete.
+ */
+static void HandshakeCallback(PRFileDesc *sock, void *arg)
+{
+  (void)sock;
+  (void)arg;
+}
+
+static void display_cert_info(struct SessionHandle *data,
+                              CERTCertificate *cert)
+{
+  char *subject, *issuer, *common_name;
+  PRExplodedTime printableTime;
+  char timeString[256];
+  PRTime notBefore, notAfter;
+
+  subject = CERT_NameToAscii(&cert->subject);
+  issuer = CERT_NameToAscii(&cert->issuer);
+  common_name = CERT_GetCommonName(&cert->subject);
+  infof(data, "\tsubject: %s\n", subject);
+
+  CERT_GetCertTimes(cert, &notBefore, &notAfter);
+  PR_ExplodeTime(notBefore, PR_GMTParameters, &printableTime);
+  PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime);
+  infof(data, "\tstart date: %s\n", timeString);
+  PR_ExplodeTime(notAfter, PR_GMTParameters, &printableTime);
+  PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime);
+  infof(data, "\texpire date: %s\n", timeString);
+  infof(data, "\tcommon name: %s\n", common_name);
+  infof(data, "\tissuer: %s\n", issuer);
+
+  PR_Free(subject);
+  PR_Free(issuer);
+  PR_Free(common_name);
+}
+
+static void display_conn_info(struct connectdata *conn, PRFileDesc *sock)
+{
+  SSLChannelInfo channel;
+  SSLCipherSuiteInfo suite;
+  CERTCertificate *cert;
+
+  if(SSL_GetChannelInfo(sock, &channel, sizeof channel) ==
+     SECSuccess && channel.length == sizeof channel &&
+     channel.cipherSuite) {
+    if(SSL_GetCipherSuiteInfo(channel.cipherSuite,
+                              &suite, sizeof suite) == SECSuccess) {
+      infof(conn->data, "SSL connection using %s\n", suite.cipherSuiteName);
+    }
+  }
+
+  infof(conn->data, "Server certificate:\n");
+
+  cert = SSL_PeerCertificate(sock);
+  display_cert_info(conn->data, cert);
+  CERT_DestroyCertificate(cert);
+
+  return;
+}
+
+static SECStatus BadCertHandler(void *arg, PRFileDesc *sock)
+{
+  struct connectdata *conn = (struct connectdata *)arg;
+  struct SessionHandle *data = conn->data;
+  PRErrorCode err = PR_GetError();
+  CERTCertificate *cert;
+
+  /* remember the cert verification result */
+  data->set.ssl.certverifyresult = err;
+
+  if(err == SSL_ERROR_BAD_CERT_DOMAIN && !data->set.ssl.verifyhost)
+    /* we are asked not to verify the host name */
+    return SECSuccess;
+
+  /* print only info about the cert, the error is printed off the callback */
+  cert = SSL_PeerCertificate(sock);
+  if(cert) {
+    infof(data, "Server certificate:\n");
+    display_cert_info(data, cert);
+    CERT_DestroyCertificate(cert);
+  }
+
+  return SECFailure;
+}
+
+/**
+ *
+ * Check that the Peer certificate's issuer certificate matches the one found
+ * by issuer_nickname.  This is not exactly the way OpenSSL and GNU TLS do the
+ * issuer check, so we provide comments that mimic the OpenSSL
+ * X509_check_issued function (in x509v3/v3_purp.c)
+ */
+static SECStatus check_issuer_cert(PRFileDesc *sock,
+                                   char *issuer_nickname)
+{
+  CERTCertificate *cert,*cert_issuer,*issuer;
+  SECStatus res=SECSuccess;
+  void *proto_win = NULL;
+
+  /*
+    PRArenaPool   *tmpArena = NULL;
+    CERTAuthKeyID *authorityKeyID = NULL;
+    SECITEM       *caname = NULL;
+  */
+
+  cert = SSL_PeerCertificate(sock);
+  cert_issuer = CERT_FindCertIssuer(cert,PR_Now(),certUsageObjectSigner);
+
+  proto_win = SSL_RevealPinArg(sock);
+  issuer = PK11_FindCertFromNickname(issuer_nickname, proto_win);
+
+  if((!cert_issuer) || (!issuer))
+    res = SECFailure;
+  else if(SECITEM_CompareItem(&cert_issuer->derCert,
+                              &issuer->derCert)!=SECEqual)
+    res = SECFailure;
+
+  CERT_DestroyCertificate(cert);
+  CERT_DestroyCertificate(issuer);
+  CERT_DestroyCertificate(cert_issuer);
+  return res;
+}
+
+/**
+ *
+ * Callback to pick the SSL client certificate.
+ */
+static SECStatus SelectClientCert(void *arg, PRFileDesc *sock,
+                                  struct CERTDistNamesStr *caNames,
+                                  struct CERTCertificateStr **pRetCert,
+                                  struct SECKEYPrivateKeyStr **pRetKey)
+{
+  struct ssl_connect_data *connssl = (struct ssl_connect_data *)arg;
+  struct SessionHandle *data = connssl->data;
+  const char *nickname = connssl->client_nickname;
+
+  if(connssl->obj_clicert) {
+    /* use the cert/key provided by PEM reader */
+    static const char pem_slotname[] = "PEM Token #1";
+    SECItem cert_der = { 0, NULL, 0 };
+    void *proto_win = SSL_RevealPinArg(sock);
+    struct CERTCertificateStr *cert;
+    struct SECKEYPrivateKeyStr *key;
+
+    PK11SlotInfo *slot = PK11_FindSlotByName(pem_slotname);
+    if(NULL == slot) {
+      failf(data, "NSS: PK11 slot not found: %s", pem_slotname);
+      return SECFailure;
+    }
+
+    if(PK11_ReadRawAttribute(PK11_TypeGeneric, connssl->obj_clicert, CKA_VALUE,
+                             &cert_der) != SECSuccess) {
+      failf(data, "NSS: CKA_VALUE not found in PK11 generic object");
+      PK11_FreeSlot(slot);
+      return SECFailure;
+    }
+
+    cert = PK11_FindCertFromDERCertItem(slot, &cert_der, proto_win);
+    SECITEM_FreeItem(&cert_der, PR_FALSE);
+    if(NULL == cert) {
+      failf(data, "NSS: client certificate from file not found");
+      PK11_FreeSlot(slot);
+      return SECFailure;
+    }
+
+    key = PK11_FindPrivateKeyFromCert(slot, cert, NULL);
+    PK11_FreeSlot(slot);
+    if(NULL == key) {
+      failf(data, "NSS: private key from file not found");
+      CERT_DestroyCertificate(cert);
+      return SECFailure;
+    }
+
+    infof(data, "NSS: client certificate from file\n");
+    display_cert_info(data, cert);
+
+    *pRetCert = cert;
+    *pRetKey = key;
+    return SECSuccess;
+  }
+
+  /* use the default NSS hook */
+  if(SECSuccess != NSS_GetClientAuthData((void *)nickname, sock, caNames,
+                                          pRetCert, pRetKey)
+      || NULL == *pRetCert) {
+
+    if(NULL == nickname)
+      failf(data, "NSS: client certificate not found (nickname not "
+            "specified)");
+    else
+      failf(data, "NSS: client certificate not found: %s", nickname);
+
+    return SECFailure;
+  }
+
+  /* get certificate nickname if any */
+  nickname = (*pRetCert)->nickname;
+  if(NULL == nickname)
+    nickname = "[unknown]";
+
+  if(NULL == *pRetKey) {
+    failf(data, "NSS: private key not found for certificate: %s", nickname);
+    return SECFailure;
+  }
+
+  infof(data, "NSS: using client certificate: %s\n", nickname);
+  display_cert_info(data, *pRetCert);
+  return SECSuccess;
+}
+
+/* This function is supposed to decide, which error codes should be used
+ * to conclude server is TLS intolerant.
+ *
+ * taken from xulrunner - nsNSSIOLayer.cpp
+ */
+static PRBool
+isTLSIntoleranceError(PRInt32 err)
+{
+  switch (err) {
+  case SSL_ERROR_BAD_MAC_ALERT:
+  case SSL_ERROR_BAD_MAC_READ:
+  case SSL_ERROR_HANDSHAKE_FAILURE_ALERT:
+  case SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT:
+  case SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE:
+  case SSL_ERROR_ILLEGAL_PARAMETER_ALERT:
+  case SSL_ERROR_NO_CYPHER_OVERLAP:
+  case SSL_ERROR_BAD_SERVER:
+  case SSL_ERROR_BAD_BLOCK_PADDING:
+  case SSL_ERROR_UNSUPPORTED_VERSION:
+  case SSL_ERROR_PROTOCOL_VERSION_ALERT:
+  case SSL_ERROR_RX_MALFORMED_FINISHED:
+  case SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE:
+  case SSL_ERROR_DECODE_ERROR_ALERT:
+  case SSL_ERROR_RX_UNKNOWN_ALERT:
+    return PR_TRUE;
+  default:
+    return PR_FALSE;
+  }
+}
+
+static CURLcode nss_init_core(struct SessionHandle *data, const char *cert_dir)
+{
+#ifdef HAVE_NSS_INITCONTEXT
+  NSSInitParameters initparams;
+
+  if(nss_context != NULL)
+    return CURLE_OK;
+
+  memset((void *) &initparams, '\0', sizeof(initparams));
+  initparams.length = sizeof(initparams);
+#else /* HAVE_NSS_INITCONTEXT */
+  SECStatus rv;
+
+  if(NSS_IsInitialized())
+    return CURLE_OK;
+#endif
+
+  if(cert_dir) {
+    const bool use_sql = NSS_VersionCheck("3.12.0");
+    char *certpath = aprintf("%s%s", use_sql ? "sql:" : "", cert_dir);
+    if(!certpath)
+      return CURLE_OUT_OF_MEMORY;
+
+    infof(data, "Initializing NSS with certpath: %s\n", certpath);
+#ifdef HAVE_NSS_INITCONTEXT
+    nss_context = NSS_InitContext(certpath, "", "", "", &initparams,
+            NSS_INIT_READONLY | NSS_INIT_PK11RELOAD);
+    free(certpath);
+
+    if(nss_context != NULL)
+      return CURLE_OK;
+#else /* HAVE_NSS_INITCONTEXT */
+    rv = NSS_Initialize(certpath, "", "", "", NSS_INIT_READONLY);
+    free(certpath);
+
+    if(rv == SECSuccess)
+      return CURLE_OK;
+#endif
+
+    infof(data, "Unable to initialize NSS database\n");
+  }
+
+  infof(data, "Initializing NSS with certpath: none\n");
+#ifdef HAVE_NSS_INITCONTEXT
+  nss_context = NSS_InitContext("", "", "", "", &initparams, NSS_INIT_READONLY
+         | NSS_INIT_NOCERTDB   | NSS_INIT_NOMODDB       | NSS_INIT_FORCEOPEN
+         | NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD);
+  if(nss_context != NULL)
+    return CURLE_OK;
+#else /* HAVE_NSS_INITCONTEXT */
+  if(NSS_NoDB_Init(NULL) == SECSuccess)
+    return CURLE_OK;
+#endif
+
+  infof(data, "Unable to initialize NSS\n");
+  return CURLE_SSL_CACERT_BADFILE;
+}
+
+static CURLcode nss_init(struct SessionHandle *data)
+{
+  char *cert_dir;
+  struct_stat st;
+  CURLcode rv;
+
+  if(initialized)
+    return CURLE_OK;
+
+  /* First we check if $SSL_DIR points to a valid dir */
+  cert_dir = getenv("SSL_DIR");
+  if(cert_dir) {
+    if((stat(cert_dir, &st) != 0) ||
+        (!S_ISDIR(st.st_mode))) {
+      cert_dir = NULL;
+    }
+  }
+
+  /* Now we check if the default location is a valid dir */
+  if(!cert_dir) {
+    if((stat(SSL_DIR, &st) == 0) &&
+        (S_ISDIR(st.st_mode))) {
+      cert_dir = (char *)SSL_DIR;
+    }
+  }
+
+  rv = nss_init_core(data, cert_dir);
+  if(rv)
+    return rv;
+
+  if(num_enabled_ciphers() == 0)
+    NSS_SetDomesticPolicy();
+
+  initialized = 1;
+  return CURLE_OK;
+}
+
+/**
+ * Global SSL init
+ *
+ * @retval 0 error initializing SSL
+ * @retval 1 SSL initialized successfully
+ */
+int Curl_nss_init(void)
+{
+  /* curl_global_init() is not thread-safe so this test is ok */
+  if(nss_initlock == NULL) {
+    PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 256);
+    nss_initlock = PR_NewLock();
+    nss_crllock = PR_NewLock();
+  }
+
+  /* We will actually initialize NSS later */
+
+  return 1;
+}
+
+CURLcode Curl_nss_force_init(struct SessionHandle *data)
+{
+  CURLcode rv;
+  if(!nss_initlock) {
+    failf(data,
+          "unable to initialize NSS, curl_global_init() should have been "
+          "called with CURL_GLOBAL_SSL or CURL_GLOBAL_ALL");
+    return CURLE_FAILED_INIT;
+  }
+
+  PR_Lock(nss_initlock);
+  rv = nss_init(data);
+  PR_Unlock(nss_initlock);
+  return rv;
+}
+
+/* Global cleanup */
+void Curl_nss_cleanup(void)
+{
+  /* This function isn't required to be threadsafe and this is only done
+   * as a safety feature.
+   */
+  PR_Lock(nss_initlock);
+  if(initialized) {
+    /* Free references to client certificates held in the SSL session cache.
+     * Omitting this hampers destruction of the security module owning
+     * the certificates. */
+    SSL_ClearSessionCache();
+
+    if(mod && SECSuccess == SECMOD_UnloadUserModule(mod)) {
+      SECMOD_DestroyModule(mod);
+      mod = NULL;
+    }
+#ifdef HAVE_NSS_INITCONTEXT
+    NSS_ShutdownContext(nss_context);
+    nss_context = NULL;
+#else /* HAVE_NSS_INITCONTEXT */
+    NSS_Shutdown();
+#endif
+  }
+  PR_Unlock(nss_initlock);
+
+  PR_DestroyLock(nss_initlock);
+  PR_DestroyLock(nss_crllock);
+  nss_initlock = NULL;
+
+  initialized = 0;
+}
+
+/*
+ * This function uses SSL_peek to determine connection status.
+ *
+ * Return codes:
+ *     1 means the connection is still in place
+ *     0 means the connection has been closed
+ *    -1 means the connection status is unknown
+ */
+int
+Curl_nss_check_cxn(struct connectdata *conn)
+{
+  int rc;
+  char buf;
+
+  rc =
+    PR_Recv(conn->ssl[FIRSTSOCKET].handle, (void *)&buf, 1, PR_MSG_PEEK,
+            PR_SecondsToInterval(1));
+  if(rc > 0)
+    return 1; /* connection still in place */
+
+  if(rc == 0)
+    return 0; /* connection has been closed */
+
+  return -1;  /* connection status unknown */
+}
+
+/*
+ * This function is called when an SSL connection is closed.
+ */
+void Curl_nss_close(struct connectdata *conn, int sockindex)
+{
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+
+  if(connssl->handle) {
+    /* NSS closes the socket we previously handed to it, so we must mark it
+       as closed to avoid double close */
+    fake_sclose(conn->sock[sockindex]);
+    conn->sock[sockindex] = CURL_SOCKET_BAD;
+    if(connssl->client_nickname != NULL) {
+      free(connssl->client_nickname);
+      connssl->client_nickname = NULL;
+
+      /* force NSS to ask again for a client cert when connecting
+       * next time to the same server */
+      SSL_InvalidateSession(connssl->handle);
+    }
+    /* destroy all NSS objects in order to avoid failure of NSS shutdown */
+    Curl_llist_destroy(connssl->obj_list, NULL);
+    connssl->obj_list = NULL;
+    connssl->obj_clicert = NULL;
+
+    PR_Close(connssl->handle);
+    connssl->handle = NULL;
+  }
+}
+
+/*
+ * This function is called when the 'data' struct is going away. Close
+ * down everything and free all resources!
+ */
+int Curl_nss_close_all(struct SessionHandle *data)
+{
+  (void)data;
+  return 0;
+}
+
+/* return true if NSS can provide error code (and possibly msg) for the
+   error */
+static bool is_nss_error(CURLcode err)
+{
+  switch(err) {
+  case CURLE_PEER_FAILED_VERIFICATION:
+  case CURLE_SSL_CACERT:
+  case CURLE_SSL_CACERT_BADFILE:
+  case CURLE_SSL_CERTPROBLEM:
+  case CURLE_SSL_CONNECT_ERROR:
+  case CURLE_SSL_CRL_BADFILE:
+  case CURLE_SSL_ISSUER_ERROR:
+    return true;
+
+  default:
+    return false;
+  }
+}
+
+/* return true if the given error code is related to a client certificate */
+static bool is_cc_error(PRInt32 err)
+{
+  switch(err) {
+  case SSL_ERROR_BAD_CERT_ALERT:
+  case SSL_ERROR_EXPIRED_CERT_ALERT:
+  case SSL_ERROR_REVOKED_CERT_ALERT:
+    return true;
+
+  default:
+    return false;
+  }
+}
+
+static Curl_recv nss_recv;
+static Curl_send nss_send;
+
+static CURLcode nss_load_ca_certificates(struct connectdata *conn,
+                                         int sockindex)
+{
+  struct SessionHandle *data = conn->data;
+  const char *cafile = data->set.ssl.CAfile;
+  const char *capath = data->set.ssl.CApath;
+
+  if(cafile) {
+    CURLcode rv = nss_load_cert(&conn->ssl[sockindex], cafile, PR_TRUE);
+    if(CURLE_OK != rv)
+      return rv;
+  }
+
+  if(capath) {
+    struct_stat st;
+    if(stat(capath, &st) == -1)
+      return CURLE_SSL_CACERT_BADFILE;
+
+    if(S_ISDIR(st.st_mode)) {
+      PRDirEntry *entry;
+      PRDir *dir = PR_OpenDir(capath);
+      if(!dir)
+        return CURLE_SSL_CACERT_BADFILE;
+
+      while((entry = PR_ReadDir(dir, PR_SKIP_BOTH | PR_SKIP_HIDDEN))) {
+        char *fullpath = aprintf("%s/%s", capath, entry->name);
+        if(!fullpath) {
+          PR_CloseDir(dir);
+          return CURLE_OUT_OF_MEMORY;
+        }
+
+        if(CURLE_OK != nss_load_cert(&conn->ssl[sockindex], fullpath, PR_TRUE))
+          /* This is purposefully tolerant of errors so non-PEM files can
+           * be in the same directory */
+          infof(data, "failed to load '%s' from CURLOPT_CAPATH\n", fullpath);
+
+        free(fullpath);
+      }
+
+      PR_CloseDir(dir);
+    }
+    else
+      infof(data, "warning: CURLOPT_CAPATH not a directory (%s)\n", capath);
+  }
+
+  infof(data, "  CAfile: %s\n  CApath: %s\n",
+      cafile ? cafile : "none",
+      capath ? capath : "none");
+
+  return CURLE_OK;
+}
+
+CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
+{
+  PRErrorCode err = 0;
+  PRFileDesc *model = NULL;
+  PRBool ssl2 = PR_FALSE;
+  PRBool ssl3 = PR_FALSE;
+  PRBool tlsv1 = PR_FALSE;
+  PRBool ssl_no_cache;
+  PRBool ssl_cbc_random_iv;
+  struct SessionHandle *data = conn->data;
+  curl_socket_t sockfd = conn->sock[sockindex];
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  CURLcode curlerr;
+  const int *cipher_to_enable;
+  PRSocketOptionData sock_opt;
+  long time_left;
+  PRUint32 timeout;
+
+  if(connssl->state == ssl_connection_complete)
+    return CURLE_OK;
+
+  connssl->data = data;
+
+  /* list of all NSS objects we need to destroy in Curl_nss_close() */
+  connssl->obj_list = Curl_llist_alloc(nss_destroy_object);
+  if(!connssl->obj_list)
+    return CURLE_OUT_OF_MEMORY;
+
+  /* FIXME. NSS doesn't support multiple databases open at the same time. */
+  PR_Lock(nss_initlock);
+  curlerr = nss_init(conn->data);
+  if(CURLE_OK != curlerr) {
+    PR_Unlock(nss_initlock);
+    goto error;
+  }
+
+  curlerr = CURLE_SSL_CONNECT_ERROR;
+
+  if(!mod) {
+    char *configstring = aprintf("library=%s name=PEM", pem_library);
+    if(!configstring) {
+      PR_Unlock(nss_initlock);
+      goto error;
+    }
+    mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE);
+    free(configstring);
+
+    if(!mod || !mod->loaded) {
+      if(mod) {
+        SECMOD_DestroyModule(mod);
+        mod = NULL;
+      }
+      infof(data, "WARNING: failed to load NSS PEM library %s. Using "
+            "OpenSSL PEM certificates will not work.\n", pem_library);
+    }
+  }
+
+  PK11_SetPasswordFunc(nss_get_password);
+  PR_Unlock(nss_initlock);
+
+  model = PR_NewTCPSocket();
+  if(!model)
+    goto error;
+  model = SSL_ImportFD(NULL, model);
+
+  /* make the socket nonblocking */
+  sock_opt.option = PR_SockOpt_Nonblocking;
+  sock_opt.value.non_blocking = PR_TRUE;
+  if(PR_SetSocketOption(model, &sock_opt) != PR_SUCCESS)
+    goto error;
+
+  if(SSL_OptionSet(model, SSL_SECURITY, PR_TRUE) != SECSuccess)
+    goto error;
+  if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != SECSuccess)
+    goto error;
+  if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != SECSuccess)
+    goto error;
+
+  /* do not use SSL cache if we are not going to verify peer */
+  ssl_no_cache = (data->set.ssl.verifypeer) ? PR_FALSE : PR_TRUE;
+  if(SSL_OptionSet(model, SSL_NO_CACHE, ssl_no_cache) != SECSuccess)
+    goto error;
+
+  switch (data->set.ssl.version) {
+  default:
+  case CURL_SSLVERSION_DEFAULT:
+    ssl3 = PR_TRUE;
+    if(data->state.ssl_connect_retry)
+      infof(data, "TLS disabled due to previous handshake failure\n");
+    else
+      tlsv1 = PR_TRUE;
+    break;
+  case CURL_SSLVERSION_TLSv1:
+    tlsv1 = PR_TRUE;
+    break;
+  case CURL_SSLVERSION_SSLv2:
+    ssl2 = PR_TRUE;
+    break;
+  case CURL_SSLVERSION_SSLv3:
+    ssl3 = PR_TRUE;
+    break;
+  }
+
+  if(SSL_OptionSet(model, SSL_ENABLE_SSL2, ssl2) != SECSuccess)
+    goto error;
+  if(SSL_OptionSet(model, SSL_ENABLE_SSL3, ssl3) != SECSuccess)
+    goto error;
+  if(SSL_OptionSet(model, SSL_ENABLE_TLS, tlsv1) != SECSuccess)
+    goto error;
+
+  if(SSL_OptionSet(model, SSL_V2_COMPATIBLE_HELLO, ssl2) != SECSuccess)
+    goto error;
+
+  ssl_cbc_random_iv = !data->set.ssl_enable_beast;
+#ifdef SSL_CBC_RANDOM_IV
+  /* unless the user explicitly asks to allow the protocol vulnerability, we
+     use the work-around */
+  if(SSL_OptionSet(model, SSL_CBC_RANDOM_IV, ssl_cbc_random_iv) != SECSuccess)
+    infof(data, "warning: failed to set SSL_CBC_RANDOM_IV = %d\n",
+          ssl_cbc_random_iv);
+#else
+  if(ssl_cbc_random_iv)
+    infof(data, "warning: support for SSL_CBC_RANDOM_IV not compiled in\n");
+#endif
+
+  /* reset the flag to avoid an infinite loop */
+  data->state.ssl_connect_retry = FALSE;
+
+  /* enable all ciphers from enable_ciphers_by_default */
+  cipher_to_enable = enable_ciphers_by_default;
+  while(SSL_NULL_WITH_NULL_NULL != *cipher_to_enable) {
+    if(SSL_CipherPrefSet(model, *cipher_to_enable, PR_TRUE) != SECSuccess) {
+      curlerr = CURLE_SSL_CIPHER;
+      goto error;
+    }
+    cipher_to_enable++;
+  }
+
+  if(data->set.ssl.cipher_list) {
+    if(set_ciphers(data, model, data->set.ssl.cipher_list) != SECSuccess) {
+      curlerr = CURLE_SSL_CIPHER;
+      goto error;
+    }
+  }
+
+  if(!data->set.ssl.verifypeer && data->set.ssl.verifyhost)
+    infof(data, "warning: ignoring value of ssl.verifyhost\n");
+
+  /* bypass the default SSL_AuthCertificate() hook in case we do not want to
+   * verify peer */
+  if(SSL_AuthCertificateHook(model, nss_auth_cert_hook, conn) != SECSuccess)
+    goto error;
+
+  data->set.ssl.certverifyresult=0; /* not checked yet */
+  if(SSL_BadCertHook(model, BadCertHandler, conn) != SECSuccess)
+    goto error;
+
+  if(SSL_HandshakeCallback(model, HandshakeCallback, NULL) != SECSuccess)
+    goto error;
+
+  if(data->set.ssl.verifypeer) {
+    const CURLcode rv = nss_load_ca_certificates(conn, sockindex);
+    if(CURLE_OK != rv) {
+      curlerr = rv;
+      goto error;
+    }
+  }
+
+  if(data->set.ssl.CRLfile) {
+    if(SECSuccess != nss_load_crl(data->set.ssl.CRLfile)) {
+      curlerr = CURLE_SSL_CRL_BADFILE;
+      goto error;
+    }
+    infof(data,
+          "  CRLfile: %s\n",
+          data->set.ssl.CRLfile ? data->set.ssl.CRLfile : "none");
+  }
+
+  if(data->set.str[STRING_CERT]) {
+    char *nickname = dup_nickname(data, STRING_CERT);
+    if(nickname) {
+      /* we are not going to use libnsspem.so to read the client cert */
+      connssl->obj_clicert = NULL;
+    }
+    else {
+      CURLcode rv = cert_stuff(conn, sockindex, data->set.str[STRING_CERT],
+                               data->set.str[STRING_KEY]);
+      if(CURLE_OK != rv) {
+        /* failf() is already done in cert_stuff() */
+        curlerr = rv;
+        goto error;
+      }
+    }
+
+    /* store the nickname for SelectClientCert() called during handshake */
+    connssl->client_nickname = nickname;
+  }
+  else
+    connssl->client_nickname = NULL;
+
+  if(SSL_GetClientAuthDataHook(model, SelectClientCert,
+                               (void *)connssl) != SECSuccess) {
+    curlerr = CURLE_SSL_CERTPROBLEM;
+    goto error;
+  }
+
+  /* Import our model socket  onto the existing file descriptor */
+  connssl->handle = PR_ImportTCPSocket(sockfd);
+  connssl->handle = SSL_ImportFD(model, connssl->handle);
+  if(!connssl->handle)
+    goto error;
+
+  PR_Close(model); /* We don't need this any more */
+  model = NULL;
+
+  /* This is the password associated with the cert that we're using */
+  if(data->set.str[STRING_KEY_PASSWD]) {
+    SSL_SetPKCS11PinArg(connssl->handle, data->set.str[STRING_KEY_PASSWD]);
+  }
+
+  /* Force handshake on next I/O */
+  SSL_ResetHandshake(connssl->handle, /* asServer */ PR_FALSE);
+
+  SSL_SetURL(connssl->handle, conn->host.name);
+
+  /* check timeout situation */
+  time_left = Curl_timeleft(data, NULL, TRUE);
+  if(time_left < 0L) {
+    failf(data, "timed out before SSL handshake");
+    curlerr = CURLE_OPERATION_TIMEDOUT;
+    goto error;
+  }
+  timeout = PR_MillisecondsToInterval((PRUint32) time_left);
+
+  /* Force the handshake now */
+  if(SSL_ForceHandshakeWithTimeout(connssl->handle, timeout) != SECSuccess) {
+    if(conn->data->set.ssl.certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN)
+      curlerr = CURLE_PEER_FAILED_VERIFICATION;
+    else if(conn->data->set.ssl.certverifyresult!=0)
+      curlerr = CURLE_SSL_CACERT;
+    goto error;
+  }
+
+  connssl->state = ssl_connection_complete;
+  conn->recv[sockindex] = nss_recv;
+  conn->send[sockindex] = nss_send;
+
+  display_conn_info(conn, connssl->handle);
+
+  if(data->set.str[STRING_SSL_ISSUERCERT]) {
+    SECStatus ret = SECFailure;
+    char *nickname = dup_nickname(data, STRING_SSL_ISSUERCERT);
+    if(nickname) {
+      /* we support only nicknames in case of STRING_SSL_ISSUERCERT for now */
+      ret = check_issuer_cert(connssl->handle, nickname);
+      free(nickname);
+    }
+
+    if(SECFailure == ret) {
+      infof(data,"SSL certificate issuer check failed\n");
+      curlerr = CURLE_SSL_ISSUER_ERROR;
+      goto error;
+    }
+    else {
+      infof(data, "SSL certificate issuer check ok\n");
+    }
+  }
+
+  return CURLE_OK;
+
+  error:
+  /* reset the flag to avoid an infinite loop */
+  data->state.ssl_connect_retry = FALSE;
+
+  if(is_nss_error(curlerr)) {
+    /* read NSPR error code */
+    err = PR_GetError();
+    if(is_cc_error(err))
+      curlerr = CURLE_SSL_CERTPROBLEM;
+
+    /* print the error number and error string */
+    infof(data, "NSS error %d (%s)\n", err, nss_error_to_name(err));
+
+    /* print a human-readable message describing the error if available */
+    nss_print_error_message(data, err);
+  }
+
+  if(model)
+    PR_Close(model);
+
+    /* cleanup on connection failure */
+    Curl_llist_destroy(connssl->obj_list, NULL);
+    connssl->obj_list = NULL;
+
+  if(ssl3 && tlsv1 && isTLSIntoleranceError(err)) {
+    /* schedule reconnect through Curl_retry_request() */
+    data->state.ssl_connect_retry = TRUE;
+    infof(data, "Error in TLS handshake, trying SSLv3...\n");
+    return CURLE_OK;
+  }
+
+  return curlerr;
+}
+
+static ssize_t nss_send(struct connectdata *conn,  /* connection data */
+                        int sockindex,             /* socketindex */
+                        const void *mem,           /* send this data */
+                        size_t len,                /* amount to write */
+                        CURLcode *curlcode)
+{
+  int rc;
+
+  rc = PR_Send(conn->ssl[sockindex].handle, mem, (int)len, 0, -1);
+
+  if(rc < 0) {
+    PRInt32 err = PR_GetError();
+    if(err == PR_WOULD_BLOCK_ERROR)
+      *curlcode = CURLE_AGAIN;
+    else {
+      /* print the error number and error string */
+      const char *err_name = nss_error_to_name(err);
+      infof(conn->data, "SSL write: error %d (%s)\n", err, err_name);
+
+      /* print a human-readable message describing the error if available */
+      nss_print_error_message(conn->data, err);
+
+      *curlcode = (is_cc_error(err))
+        ? CURLE_SSL_CERTPROBLEM
+        : CURLE_SEND_ERROR;
+    }
+    return -1;
+  }
+  return rc; /* number of bytes */
+}
+
+static ssize_t nss_recv(struct connectdata * conn, /* connection data */
+                        int num,                   /* socketindex */
+                        char *buf,                 /* store read data here */
+                        size_t buffersize,         /* max amount to read */
+                        CURLcode *curlcode)
+{
+  ssize_t nread;
+
+  nread = PR_Recv(conn->ssl[num].handle, buf, (int)buffersize, 0, -1);
+  if(nread < 0) {
+    /* failed SSL read */
+    PRInt32 err = PR_GetError();
+
+    if(err == PR_WOULD_BLOCK_ERROR)
+      *curlcode = CURLE_AGAIN;
+    else {
+      /* print the error number and error string */
+      const char *err_name = nss_error_to_name(err);
+      infof(conn->data, "SSL read: errno %d (%s)\n", err, err_name);
+
+      /* print a human-readable message describing the error if available */
+      nss_print_error_message(conn->data, err);
+
+      *curlcode = (is_cc_error(err))
+        ? CURLE_SSL_CERTPROBLEM
+        : CURLE_RECV_ERROR;
+    }
+    return -1;
+  }
+  return nread;
+}
+
+size_t Curl_nss_version(char *buffer, size_t size)
+{
+  return snprintf(buffer, size, "NSS/%s", NSS_VERSION);
+}
+
+int Curl_nss_seed(struct SessionHandle *data)
+{
+  /* TODO: implement? */
+  (void) data;
+  return 0;
+}
+
+void Curl_nss_random(struct SessionHandle *data,
+                     unsigned char *entropy,
+                     size_t length)
+{
+  Curl_nss_seed(data);  /* Initiate the seed if not already done */
+  PK11_GenerateRandom(entropy, curlx_uztosi(length));
+}
+
+void Curl_nss_md5sum(unsigned char *tmp, /* input */
+                     size_t tmplen,
+                     unsigned char *md5sum, /* output */
+                     size_t md5len)
+{
+  PK11Context *MD5pw = PK11_CreateDigestContext(SEC_OID_MD5);
+  unsigned int MD5out;
+  PK11_DigestOp(MD5pw, tmp, curlx_uztoui(tmplen));
+  PK11_DigestFinal(MD5pw, md5sum, &MD5out, curlx_uztoui(md5len));
+  PK11_DestroyContext(MD5pw, PR_TRUE);
+}
+
+#endif /* USE_NSS */
diff --git a/lib/curl_nwlib.c b/lib/curl_nwlib.c
new file mode 100644 (file)
index 0000000..f63f16b
--- /dev/null
@@ -0,0 +1,329 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef NETWARE /* Novell NetWare */
+
+#ifdef __NOVELL_LIBC__
+/* For native LibC-based NLM we need to register as a real lib. */
+#include <library.h>
+#include <netware.h>
+#include <screen.h>
+#include <nks/thread.h>
+#include <nks/synch.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+typedef struct
+{
+  int     _errno;
+  void    *twentybytes;
+} libthreaddata_t;
+
+typedef struct
+{
+  int         x;
+  int         y;
+  int         z;
+  void        *tenbytes;
+  NXKey_t     perthreadkey;   /* if -1, no key obtained... */
+  NXMutex_t   *lock;
+} libdata_t;
+
+int         gLibId      = -1;
+void        *gLibHandle = (void *) NULL;
+rtag_t      gAllocTag   = (rtag_t) NULL;
+NXMutex_t   *gLibLock   = (NXMutex_t *) NULL;
+
+/* internal library function prototypes... */
+int  DisposeLibraryData( void * );
+void DisposeThreadData( void * );
+int  GetOrSetUpData( int id, libdata_t **data, libthreaddata_t **threaddata );
+
+
+int _NonAppStart( void        *NLMHandle,
+                  void        *errorScreen,
+                  const char  *cmdLine,
+                  const char  *loadDirPath,
+                  size_t      uninitializedDataLength,
+                  void        *NLMFileHandle,
+                  int         (*readRoutineP)( int conn,
+                                               void *fileHandle, size_t offset,
+                                               size_t nbytes,
+                                               size_t *bytesRead,
+                                               void *buffer ),
+                  size_t      customDataOffset,
+                  size_t      customDataSize,
+                  int         messageCount,
+                  const char  **messages )
+{
+  NX_LOCK_INFO_ALLOC(liblock, "Per-Application Data Lock", 0);
+
+#ifndef __GNUC__
+#pragma unused(cmdLine)
+#pragma unused(loadDirPath)
+#pragma unused(uninitializedDataLength)
+#pragma unused(NLMFileHandle)
+#pragma unused(readRoutineP)
+#pragma unused(customDataOffset)
+#pragma unused(customDataSize)
+#pragma unused(messageCount)
+#pragma unused(messages)
+#endif
+
+  /*
+   * Here we process our command line, post errors (to the error screen),
+   * perform initializations and anything else we need to do before being able
+   * to accept calls into us. If we succeed, we return non-zero and the NetWare
+   * Loader will leave us up, otherwise we fail to load and get dumped.
+   */
+  gAllocTag = AllocateResourceTag(NLMHandle,
+                                  "<library-name> memory allocations",
+                                  AllocSignature);
+
+  if(!gAllocTag) {
+    OutputToScreen(errorScreen, "Unable to allocate resource tag for "
+                   "library memory allocations.\n");
+    return -1;
+  }
+
+  gLibId = register_library(DisposeLibraryData);
+
+  if(gLibId < -1) {
+    OutputToScreen(errorScreen, "Unable to register library with kernel.\n");
+    return -1;
+  }
+
+  gLibHandle = NLMHandle;
+
+  gLibLock = NXMutexAlloc(0, 0, &liblock);
+
+  if(!gLibLock) {
+    OutputToScreen(errorScreen, "Unable to allocate library data lock.\n");
+    return -1;
+  }
+
+  return 0;
+}
+
+/*
+ * Here we clean up any resources we allocated. Resource tags is a big part
+ * of what we created, but NetWare doesn't ask us to free those.
+ */
+void _NonAppStop( void )
+{
+  (void) unregister_library(gLibId);
+  NXMutexFree(gLibLock);
+}
+
+/*
+ * This function cannot be the first in the file for if the file is linked
+ * first, then the check-unload function's offset will be nlmname.nlm+0
+ * which is how to tell that there isn't one. When the check function is
+ * first in the linked objects, it is ambiguous. For this reason, we will
+ * put it inside this file after the stop function.
+ *
+ * Here we check to see if it's alright to ourselves to be unloaded. If not,
+ * we return a non-zero value. Right now, there isn't any reason not to allow
+ * it.
+ */
+int _NonAppCheckUnload( void )
+{
+    return 0;
+}
+
+int GetOrSetUpData(int id, libdata_t **appData,
+                   libthreaddata_t **threadData )
+{
+  int                 err;
+  libdata_t           *app_data;
+  libthreaddata_t *thread_data;
+  NXKey_t             key;
+  NX_LOCK_INFO_ALLOC(liblock, "Application Data Lock", 0);
+
+  err         = 0;
+  thread_data = (libthreaddata_t *) NULL;
+
+  /*
+   * Attempt to get our data for the application calling us. This is where we
+   * store whatever application-specific information we need to carry in
+   * support of calling applications.
+   */
+  app_data = (libdata_t *) get_app_data(id);
+
+  if(!app_data) {
+    /*
+     * This application hasn't called us before; set up application AND
+     * per-thread data. Of course, just in case a thread from this same
+     * application is calling us simultaneously, we better lock our application
+     * data-creation mutex. We also need to recheck for data after we acquire
+     * the lock because WE might be that other thread that was too late to
+     * create the data and the first thread in will have created it.
+     */
+    NXLock(gLibLock);
+
+    if(!(app_data = (libdata_t *) get_app_data(id))) {
+      app_data = malloc(sizeof(libdata_t));
+
+      if(app_data) {
+        memset(app_data, 0, sizeof(libdata_t));
+
+        app_data->tenbytes = malloc(10);
+        app_data->lock     = NXMutexAlloc(0, 0, &liblock);
+
+        if(!app_data->tenbytes || !app_data->lock) {
+          if(app_data->lock)
+            NXMutexFree(app_data->lock);
+
+          free(app_data);
+          app_data = (libdata_t *) NULL;
+          err      = ENOMEM;
+        }
+
+        if(app_data) {
+          /*
+           * Here we burn in the application data that we were trying to get
+           * by calling get_app_data(). Next time we call the first function,
+           * we'll get this data we're just now setting. We also go on here to
+           * establish the per-thread data for the calling thread, something
+           * we'll have to do on each application thread the first time
+           * it calls us.
+           */
+          err = set_app_data(gLibId, app_data);
+
+          if(err) {
+            free(app_data);
+            app_data = (libdata_t *) NULL;
+            err      = ENOMEM;
+          }
+          else {
+            /* create key for thread-specific data... */
+            err = NXKeyCreate(DisposeThreadData, (void *) NULL, &key);
+
+            if(err)                /* (no more keys left?) */
+              key = -1;
+
+            app_data->perthreadkey = key;
+          }
+        }
+      }
+    }
+
+    NXUnlock(gLibLock);
+  }
+
+  if(app_data) {
+    key = app_data->perthreadkey;
+
+    if(key != -1 /* couldn't create a key? no thread data */
+        && !(err = NXKeyGetValue(key, (void **) &thread_data))
+        && !thread_data) {
+      /*
+       * Allocate the per-thread data for the calling thread. Regardless of
+       * whether there was already application data or not, this may be the
+       * first call by a new thread. The fact that we allocation 20 bytes on
+       * a pointer is not very important, this just helps to demonstrate that
+       * we can have arbitrarily complex per-thread data.
+       */
+      thread_data = malloc(sizeof(libthreaddata_t));
+
+      if(thread_data) {
+        thread_data->_errno      = 0;
+        thread_data->twentybytes = malloc(20);
+
+        if(!thread_data->twentybytes) {
+          free(thread_data);
+          thread_data = (libthreaddata_t *) NULL;
+          err         = ENOMEM;
+        }
+
+        if((err = NXKeySetValue(key, thread_data))) {
+          free(thread_data->twentybytes);
+          free(thread_data);
+          thread_data = (libthreaddata_t *) NULL;
+        }
+      }
+    }
+  }
+
+  if(appData)
+    *appData = app_data;
+
+  if(threadData)
+    *threadData = thread_data;
+
+  return err;
+}
+
+int DisposeLibraryData( void *data )
+{
+  if(data) {
+    void *tenbytes = ((libdata_t *) data)->tenbytes;
+
+    if(tenbytes)
+      free(tenbytes);
+
+    free(data);
+  }
+
+  return 0;
+}
+
+void DisposeThreadData( void *data )
+{
+  if(data) {
+    void *twentybytes = ((libthreaddata_t *) data)->twentybytes;
+
+    if(twentybytes)
+      free(twentybytes);
+
+    free(data);
+  }
+}
+
+#else /* __NOVELL_LIBC__ */
+/* For native CLib-based NLM seems we can do a bit more simple. */
+#include <nwthread.h>
+
+int main ( void )
+{
+  /* initialize any globals here... */
+
+  /* do this if any global initializing was done
+  SynchronizeStart();
+  */
+  ExitThread (TSR_THREAD, 0);
+  return 0;
+}
+
+#endif /* __NOVELL_LIBC__ */
+
+#else /* NETWARE */
+
+#ifdef __POCC__
+#  pragma warn(disable:2024)  /* Disable warning #2024: Empty input file */
+#endif
+
+#endif /* NETWARE */
diff --git a/lib/curl_nwos.c b/lib/curl_nwos.c
new file mode 100644 (file)
index 0000000..23ff2a7
--- /dev/null
@@ -0,0 +1,88 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef NETWARE /* Novell NetWare */
+
+#ifdef __NOVELL_LIBC__
+/* For native LibC-based NLM we need to do nothing. */
+int netware_init ( void )
+{
+  return 0;
+}
+
+#else /* __NOVELL_LIBC__ */
+
+/* For native CLib-based NLM we need to initialize the LONG namespace. */
+#include <nwnspace.h>
+#include <nwthread.h>
+#include <nwadv.h>
+/* Make the CLIB Ctx stuff link */
+#include <netdb.h>
+NETDB_DEFINE_CONTEXT
+/* Make the CLIB Inet stuff link */
+#include <netinet/in.h>
+#include <arpa/inet.h>
+NETINET_DEFINE_CONTEXT
+
+int netware_init ( void )
+{
+  int rc = 0;
+  unsigned int myHandle = GetNLMHandle();
+  /* import UnAugmentAsterisk dynamically for NW4.x compatibility */
+  void (*pUnAugmentAsterisk)(int) = (void(*)(int))
+          ImportSymbol(myHandle, "UnAugmentAsterisk");
+  /* import UseAccurateCaseForPaths dynamically for NW3.x compatibility */
+  void (*pUseAccurateCaseForPaths)(int) = (void(*)(int))
+          ImportSymbol(myHandle, "UseAccurateCaseForPaths");
+  if(pUnAugmentAsterisk)
+    pUnAugmentAsterisk(1);
+  if(pUseAccurateCaseForPaths)
+    pUseAccurateCaseForPaths(1);
+  UnimportSymbol(myHandle, "UnAugmentAsterisk");
+  UnimportSymbol(myHandle, "UseAccurateCaseForPaths");
+  /* set long name space */
+  if((SetCurrentNameSpace(4) == 255)) {
+    rc = 1;
+  }
+  if((SetTargetNameSpace(4) == 255)) {
+    rc = rc + 2;
+  }
+  return rc;
+}
+
+/* dummy function to satisfy newer prelude */
+int __init_environment ( void )
+{
+  return 0;
+}
+
+/* dummy function to satisfy newer prelude */
+int __deinit_environment ( void )
+{
+  return 0;
+}
+
+#endif /* __NOVELL_LIBC__ */
+
+#endif /* NETWARE */
diff --git a/lib/curl_openldap.c b/lib/curl_openldap.c
new file mode 100644 (file)
index 0000000..b10d31e
--- /dev/null
@@ -0,0 +1,652 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2010, Howard Chu, <hyc@openldap.org>
+ * Copyright (C) 2011 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)
+
+/*
+ * Notice that USE_OPENLDAP is only a source code selection switch. When
+ * libcurl is built with USE_OPENLDAP defined the libcurl source code that
+ * gets compiled is the code from curl_openldap.c, otherwise the code that
+ * gets compiled is the code from curl_ldap.c.
+ *
+ * When USE_OPENLDAP is defined a recent version of the OpenLDAP library
+ * might be required for compilation and runtime. In order to use ancient
+ * OpenLDAP library versions, USE_OPENLDAP shall not be defined.
+ */
+
+#include <ldap.h>
+
+#include "curl_urldata.h"
+#include <curl/curl.h>
+#include "curl_sendf.h"
+#include "curl_sslgen.h"
+#include "curl_transfer.h"
+#include "curl_ldap.h"
+#include "curl_memory.h"
+#include "curl_base64.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memdebug.h"
+
+#ifndef _LDAP_PVT_H
+extern int ldap_pvt_url_scheme2proto(const char *);
+extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url,
+                        LDAP **ld);
+#endif
+
+static CURLcode ldap_setup(struct connectdata *conn);
+static CURLcode ldap_do(struct connectdata *conn, bool *done);
+static CURLcode ldap_done(struct connectdata *conn, CURLcode, bool);
+static CURLcode ldap_connect(struct connectdata *conn, bool *done);
+static CURLcode ldap_connecting(struct connectdata *conn, bool *done);
+static CURLcode ldap_disconnect(struct connectdata *conn, bool dead);
+
+static Curl_recv ldap_recv;
+
+/*
+ * LDAP protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_ldap = {
+  "LDAP",                               /* scheme */
+  ldap_setup,                           /* setup_connection */
+  ldap_do,                              /* do_it */
+  ldap_done,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  ldap_connect,                         /* connect_it */
+  ldap_connecting,                      /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  ldap_disconnect,                      /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  PORT_LDAP,                            /* defport */
+  CURLPROTO_LDAP,                       /* protocol */
+  PROTOPT_NONE                          /* flags */
+};
+
+#ifdef USE_SSL
+/*
+ * LDAPS protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_ldaps = {
+  "LDAPS",                              /* scheme */
+  ldap_setup,                           /* setup_connection */
+  ldap_do,                              /* do_it */
+  ldap_done,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  ldap_connect,                         /* connect_it */
+  ldap_connecting,                      /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  ldap_disconnect,                      /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  PORT_LDAPS,                           /* defport */
+  CURLPROTO_LDAP,                       /* protocol */
+  PROTOPT_SSL                           /* flags */
+};
+#endif
+
+static const char *url_errs[] = {
+  "success",
+  "out of memory",
+  "bad parameter",
+  "unrecognized scheme",
+  "unbalanced delimiter",
+  "bad URL",
+  "bad host or port",
+  "bad or missing attributes",
+  "bad or missing scope",
+  "bad or missing filter",
+  "bad or missing extensions"
+};
+
+typedef struct ldapconninfo {
+  LDAP *ld;
+  Curl_recv *recv;  /* for stacking SSL handler */
+  Curl_send *send;
+  int proto;
+  int msgid;
+  bool ssldone;
+  bool sslinst;
+  bool didbind;
+} ldapconninfo;
+
+typedef struct ldapreqinfo {
+  int msgid;
+  int nument;
+} ldapreqinfo;
+
+static CURLcode ldap_setup(struct connectdata *conn)
+{
+  ldapconninfo *li;
+  LDAPURLDesc *lud;
+  struct SessionHandle *data=conn->data;
+  int rc, proto;
+  CURLcode status;
+
+  rc = ldap_url_parse(data->change.url, &lud);
+  if(rc != LDAP_URL_SUCCESS) {
+    const char *msg = "url parsing problem";
+    status = CURLE_URL_MALFORMAT;
+    if(rc > LDAP_URL_SUCCESS && rc <= LDAP_URL_ERR_BADEXTS) {
+      if(rc == LDAP_URL_ERR_MEM)
+        status = CURLE_OUT_OF_MEMORY;
+      msg = url_errs[rc];
+    }
+    failf(conn->data, "LDAP local: %s", msg);
+    return status;
+  }
+  proto = ldap_pvt_url_scheme2proto(lud->lud_scheme);
+  ldap_free_urldesc(lud);
+
+  li = calloc(1, sizeof(ldapconninfo));
+  if(!li)
+    return CURLE_OUT_OF_MEMORY;
+  li->proto = proto;
+  conn->proto.generic = li;
+  conn->bits.close = FALSE;
+  /* TODO:
+   * - provide option to choose SASL Binds instead of Simple
+   */
+  return CURLE_OK;
+}
+
+#ifdef USE_SSL
+static Sockbuf_IO ldapsb_tls;
+#endif
+
+static CURLcode ldap_connect(struct connectdata *conn, bool *done)
+{
+  ldapconninfo *li = conn->proto.generic;
+  struct SessionHandle *data=conn->data;
+  int rc, proto = LDAP_VERSION3;
+  char hosturl[1024], *ptr;
+
+  strcpy(hosturl, "ldap");
+  ptr = hosturl+4;
+  if(conn->handler->flags & PROTOPT_SSL)
+    *ptr++ = 's';
+  snprintf(ptr, sizeof(hosturl)-(ptr-hosturl), "://%s:%d",
+    conn->host.name, conn->remote_port);
+
+  rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld);
+  if(rc) {
+    failf(data, "LDAP local: Cannot connect to %s, %s",
+          hosturl, ldap_err2string(rc));
+    return CURLE_COULDNT_CONNECT;
+  }
+
+  ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
+
+#ifdef USE_SSL
+  if(conn->handler->flags & PROTOPT_SSL) {
+    CURLcode res;
+    if(data->state.used_interface == Curl_if_easy) {
+      res = Curl_ssl_connect(conn, FIRSTSOCKET);
+      if(res)
+        return res;
+      li->ssldone = TRUE;
+    }
+    else {
+      res = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &li->ssldone);
+      if(res)
+        return res;
+    }
+  }
+#endif
+
+  if(data->state.used_interface == Curl_if_easy)
+    return ldap_connecting(conn, done);
+
+  return CURLE_OK;
+}
+
+static CURLcode ldap_connecting(struct connectdata *conn, bool *done)
+{
+  ldapconninfo *li = conn->proto.generic;
+  struct SessionHandle *data=conn->data;
+  LDAPMessage *result = NULL;
+  struct timeval tv = {0,1}, *tvp;
+  int rc, err;
+  char *info = NULL;
+
+#ifdef USE_SSL
+  if(conn->handler->flags & PROTOPT_SSL) {
+    /* Is the SSL handshake complete yet? */
+    if(!li->ssldone) {
+      CURLcode res = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET,
+                                                  &li->ssldone);
+      if(res || !li->ssldone)
+        return res;
+    }
+    /* Have we installed the libcurl SSL handlers into the sockbuf yet? */
+    if(!li->sslinst) {
+      Sockbuf *sb;
+      ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
+      ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, conn);
+      li->sslinst = TRUE;
+      li->recv = conn->recv[FIRSTSOCKET];
+      li->send = conn->send[FIRSTSOCKET];
+    }
+  }
+#endif
+
+  if(data->state.used_interface == Curl_if_easy)
+    tvp = NULL;    /* let ldap_result block indefinitely */
+  else
+    tvp = &tv;
+
+retry:
+  if(!li->didbind) {
+    char *binddn;
+    struct berval passwd;
+
+    if(conn->bits.user_passwd) {
+      binddn = conn->user;
+      passwd.bv_val = conn->passwd;
+      passwd.bv_len = strlen(passwd.bv_val);
+    }
+    else {
+      binddn = NULL;
+      passwd.bv_val = NULL;
+      passwd.bv_len = 0;
+    }
+    rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd,
+                        NULL, NULL, &li->msgid);
+    if(rc)
+      return CURLE_LDAP_CANNOT_BIND;
+    li->didbind = TRUE;
+    if(tvp)
+      return CURLE_OK;
+  }
+
+  rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, tvp, &result);
+  if(rc < 0) {
+    failf(data, "LDAP local: bind ldap_result %s", ldap_err2string(rc));
+    return CURLE_LDAP_CANNOT_BIND;
+  }
+  if(rc == 0) {
+    /* timed out */
+    return CURLE_OK;
+  }
+  rc = ldap_parse_result(li->ld, result, &err, NULL, &info, NULL, NULL, 1);
+  if(rc) {
+    failf(data, "LDAP local: bind ldap_parse_result %s", ldap_err2string(rc));
+    return CURLE_LDAP_CANNOT_BIND;
+  }
+  /* Try to fallback to LDAPv2? */
+  if(err == LDAP_PROTOCOL_ERROR) {
+    int proto;
+    ldap_get_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
+    if(proto == LDAP_VERSION3) {
+      if(info) {
+        ldap_memfree(info);
+        info = NULL;
+      }
+      proto = LDAP_VERSION2;
+      ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
+      li->didbind = FALSE;
+      goto retry;
+    }
+  }
+
+  if(err) {
+    failf(data, "LDAP remote: bind failed %s %s", ldap_err2string(rc),
+          info ? info : "");
+    if(info)
+      ldap_memfree(info);
+    return CURLE_LOGIN_DENIED;
+  }
+
+  if(info)
+    ldap_memfree(info);
+  conn->recv[FIRSTSOCKET] = ldap_recv;
+  *done = TRUE;
+  return CURLE_OK;
+}
+
+static CURLcode ldap_disconnect(struct connectdata *conn, bool dead_connection)
+{
+  ldapconninfo *li = conn->proto.generic;
+  (void) dead_connection;
+
+  if(li) {
+    if(li->ld) {
+      ldap_unbind_ext(li->ld, NULL, NULL);
+      li->ld = NULL;
+    }
+    conn->proto.generic = NULL;
+    free(li);
+  }
+  return CURLE_OK;
+}
+
+static CURLcode ldap_do(struct connectdata *conn, bool *done)
+{
+  ldapconninfo *li = conn->proto.generic;
+  ldapreqinfo *lr;
+  CURLcode status = CURLE_OK;
+  int rc = 0;
+  LDAPURLDesc *ludp = NULL;
+  int msgid;
+  struct SessionHandle *data=conn->data;
+
+  conn->bits.close = FALSE;
+
+  infof(data, "LDAP local: %s\n", data->change.url);
+
+  rc = ldap_url_parse(data->change.url, &ludp);
+  if(rc != LDAP_URL_SUCCESS) {
+    const char *msg = "url parsing problem";
+    status = CURLE_URL_MALFORMAT;
+    if(rc > LDAP_URL_SUCCESS && rc <= LDAP_URL_ERR_BADEXTS) {
+      if(rc == LDAP_URL_ERR_MEM)
+        status = CURLE_OUT_OF_MEMORY;
+      msg = url_errs[rc];
+    }
+    failf(conn->data, "LDAP local: %s", msg);
+    return status;
+  }
+
+  rc = ldap_search_ext(li->ld, ludp->lud_dn, ludp->lud_scope,
+                       ludp->lud_filter, ludp->lud_attrs, 0,
+                       NULL, NULL, NULL, 0, &msgid);
+  ldap_free_urldesc(ludp);
+  if(rc != LDAP_SUCCESS) {
+    failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
+    return CURLE_LDAP_SEARCH_FAILED;
+  }
+  lr = calloc(1,sizeof(ldapreqinfo));
+  if(!lr)
+    return CURLE_OUT_OF_MEMORY;
+  lr->msgid = msgid;
+  data->state.proto.generic = lr;
+  Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
+  *done = TRUE;
+  return CURLE_OK;
+}
+
+static CURLcode ldap_done(struct connectdata *conn, CURLcode res,
+                          bool premature)
+{
+  ldapreqinfo *lr = conn->data->state.proto.generic;
+  (void)res;
+  (void)premature;
+
+  if(lr) {
+    /* if there was a search in progress, abandon it */
+    if(lr->msgid) {
+      ldapconninfo *li = conn->proto.generic;
+      ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
+      lr->msgid = 0;
+    }
+    conn->data->state.proto.generic = NULL;
+    free(lr);
+  }
+  return CURLE_OK;
+}
+
+static ssize_t ldap_recv(struct connectdata *conn, int sockindex, char *buf,
+                         size_t len, CURLcode *err)
+{
+  ldapconninfo *li = conn->proto.generic;
+  struct SessionHandle *data=conn->data;
+  ldapreqinfo *lr = data->state.proto.generic;
+  int rc, ret;
+  LDAPMessage *result = NULL;
+  LDAPMessage *ent;
+  BerElement *ber = NULL;
+  struct timeval tv = {0,1};
+  (void)len;
+  (void)buf;
+  (void)sockindex;
+
+  rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_RECEIVED, &tv, &result);
+  if(rc < 0) {
+    failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc));
+    *err = CURLE_RECV_ERROR;
+    return -1;
+  }
+
+  *err = CURLE_AGAIN;
+  ret = -1;
+
+  /* timed out */
+  if(result == NULL)
+    return ret;
+
+  for(ent = ldap_first_message(li->ld, result); ent;
+    ent = ldap_next_message(li->ld, ent)) {
+    struct berval bv, *bvals, **bvp = &bvals;
+    int binary = 0, msgtype;
+
+    msgtype = ldap_msgtype(ent);
+    if(msgtype == LDAP_RES_SEARCH_RESULT) {
+      int code;
+      char *info = NULL;
+      rc = ldap_parse_result(li->ld, ent, &code, NULL, &info, NULL, NULL, 0);
+      if(rc) {
+        failf(data, "LDAP local: search ldap_parse_result %s",
+              ldap_err2string(rc));
+        *err = CURLE_LDAP_SEARCH_FAILED;
+      }
+      else if(code && code != LDAP_SIZELIMIT_EXCEEDED) {
+        failf(data, "LDAP remote: search failed %s %s", ldap_err2string(rc),
+              info ? info : "");
+        *err = CURLE_LDAP_SEARCH_FAILED;
+      }
+      else {
+        /* successful */
+        if(code == LDAP_SIZELIMIT_EXCEEDED)
+          infof(data, "There are more than %d entries\n", lr->nument);
+        data->req.size = data->req.bytecount;
+        *err = CURLE_OK;
+        ret = 0;
+      }
+      lr->msgid = 0;
+      ldap_memfree(info);
+      break;
+    }
+    else if(msgtype != LDAP_RES_SEARCH_ENTRY)
+      continue;
+
+    lr->nument++;
+    rc = ldap_get_dn_ber(li->ld, ent, &ber, &bv);
+    if(rc < 0) {
+      /* TODO: verify that this is really how this return code should be
+         handled */
+      *err = CURLE_RECV_ERROR;
+      return -1;
+    }
+    Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"DN: ", 4);
+    Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val, bv.bv_len);
+    Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
+    data->req.bytecount += bv.bv_len + 5;
+
+    for(rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, bvp);
+      rc == LDAP_SUCCESS;
+      rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, bvp)) {
+      int i;
+
+      if(bv.bv_val == NULL) break;
+
+      if(bv.bv_len > 7 && !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7))
+        binary = 1;
+      else
+        binary = 0;
+
+      for(i=0; bvals[i].bv_val != NULL; i++) {
+        int binval = 0;
+        Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1);
+        Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val,
+                          bv.bv_len);
+        Curl_client_write(conn, CLIENTWRITE_BODY, (char *)":", 1);
+        data->req.bytecount += bv.bv_len + 2;
+
+        if(!binary) {
+          /* check for leading or trailing whitespace */
+          if(ISSPACE(bvals[i].bv_val[0]) ||
+              ISSPACE(bvals[i].bv_val[bvals[i].bv_len-1]))
+            binval = 1;
+          else {
+            /* check for unprintable characters */
+            unsigned int j;
+            for(j=0; j<bvals[i].bv_len; j++)
+              if(!ISPRINT(bvals[i].bv_val[j])) {
+                binval = 1;
+                break;
+              }
+          }
+        }
+        if(binary || binval) {
+          char *val_b64 = NULL;
+          size_t val_b64_sz = 0;
+          /* Binary value, encode to base64. */
+          CURLcode error = Curl_base64_encode(data,
+                                              bvals[i].bv_val,
+                                              bvals[i].bv_len,
+                                              &val_b64,
+                                              &val_b64_sz);
+          if(error) {
+            ber_memfree(bvals);
+            ber_free(ber, 0);
+            ldap_msgfree(result);
+            *err = error;
+            return -1;
+          }
+          Curl_client_write(conn, CLIENTWRITE_BODY, (char *)": ", 2);
+          data->req.bytecount += 2;
+          if(val_b64_sz > 0) {
+            Curl_client_write(conn, CLIENTWRITE_BODY, val_b64, val_b64_sz);
+            free(val_b64);
+            data->req.bytecount += val_b64_sz;
+          }
+        }
+        else {
+          Curl_client_write(conn, CLIENTWRITE_BODY, (char *)" ", 1);
+          Curl_client_write(conn, CLIENTWRITE_BODY, bvals[i].bv_val,
+                            bvals[i].bv_len);
+          data->req.bytecount += bvals[i].bv_len + 1;
+        }
+        Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
+        data->req.bytecount++;
+      }
+      ber_memfree(bvals);
+      Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
+      data->req.bytecount++;
+    }
+    Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
+    data->req.bytecount++;
+    ber_free(ber, 0);
+  }
+  ldap_msgfree(result);
+  return ret;
+}
+
+#ifdef USE_SSL
+static int
+ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg)
+{
+  sbiod->sbiod_pvt = arg;
+  return 0;
+}
+
+static int
+ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod)
+{
+  sbiod->sbiod_pvt = NULL;
+  return 0;
+}
+
+/* We don't need to do anything because libcurl does it already */
+static int
+ldapsb_tls_close(Sockbuf_IO_Desc *sbiod)
+{
+  (void)sbiod;
+  return 0;
+}
+
+static int
+ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg)
+{
+  (void)arg;
+  if(opt == LBER_SB_OPT_DATA_READY) {
+    struct connectdata *conn = sbiod->sbiod_pvt;
+    return Curl_ssl_data_pending(conn, FIRSTSOCKET);
+  }
+  return 0;
+}
+
+static ber_slen_t
+ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
+{
+  struct connectdata *conn = sbiod->sbiod_pvt;
+  ldapconninfo *li = conn->proto.generic;
+  ber_slen_t ret;
+  CURLcode err = CURLE_RECV_ERROR;
+
+  ret = li->recv(conn, FIRSTSOCKET, buf, len, &err);
+  if(ret < 0 && err == CURLE_AGAIN) {
+    SET_SOCKERRNO(EWOULDBLOCK);
+  }
+  return ret;
+}
+
+static ber_slen_t
+ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
+{
+  struct connectdata *conn = sbiod->sbiod_pvt;
+  ldapconninfo *li = conn->proto.generic;
+  ber_slen_t ret;
+  CURLcode err = CURLE_SEND_ERROR;
+
+  ret = li->send(conn, FIRSTSOCKET, buf, len, &err);
+  if(ret < 0 && err == CURLE_AGAIN) {
+    SET_SOCKERRNO(EWOULDBLOCK);
+  }
+  return ret;
+}
+
+static Sockbuf_IO ldapsb_tls =
+{
+  ldapsb_tls_setup,
+  ldapsb_tls_remove,
+  ldapsb_tls_ctrl,
+  ldapsb_tls_read,
+  ldapsb_tls_write,
+  ldapsb_tls_close
+};
+#endif /* USE_SSL */
+
+#endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */
diff --git a/lib/curl_parsedate.c b/lib/curl_parsedate.c
new file mode 100644 (file)
index 0000000..a50b607
--- /dev/null
@@ -0,0 +1,580 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+/*
+  A brief summary of the date string formats this parser groks:
+
+  RFC 2616 3.3.1
+
+  Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
+  Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+  Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format
+
+  we support dates without week day name:
+
+  06 Nov 1994 08:49:37 GMT
+  06-Nov-94 08:49:37 GMT
+  Nov  6 08:49:37 1994
+
+  without the time zone:
+
+  06 Nov 1994 08:49:37
+  06-Nov-94 08:49:37
+
+  weird order:
+
+  1994 Nov 6 08:49:37  (GNU date fails)
+  GMT 08:49:37 06-Nov-94 Sunday
+  94 6 Nov 08:49:37    (GNU date fails)
+
+  time left out:
+
+  1994 Nov 6
+  06-Nov-94
+  Sun Nov 6 94
+
+  unusual separators:
+
+  1994.Nov.6
+  Sun/Nov/6/94/GMT
+
+  commonly used time zone names:
+
+  Sun, 06 Nov 1994 08:49:37 CET
+  06 Nov 1994 08:49:37 EST
+
+  time zones specified using RFC822 style:
+
+  Sun, 12 Sep 2004 15:05:58 -0700
+  Sat, 11 Sep 2004 21:32:11 +0200
+
+  compact numerical date strings:
+
+  20040912 15:05:58 -0700
+  20040911 +0200
+
+*/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include <curl/curl.h>
+#include "curl_rawstr.h"
+#include "curl_warnless.h"
+#include "curl_parsedate.h"
+
+const char * const Curl_wkday[] =
+{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
+static const char * const weekday[] =
+{ "Monday", "Tuesday", "Wednesday", "Thursday",
+  "Friday", "Saturday", "Sunday" };
+const char * const Curl_month[]=
+{ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+struct tzinfo {
+  char name[5];
+  int offset; /* +/- in minutes */
+};
+
+/*
+ * parsedate()
+ *
+ * Returns:
+ *
+ * PARSEDATE_OK     - a fine conversion
+ * PARSEDATE_FAIL   - failed to convert
+ * PARSEDATE_LATER  - time overflow at the far end of time_t
+ * PARSEDATE_SOONER - time underflow at the low end of time_t
+ */
+
+static int parsedate(const char *date, time_t *output);
+
+#define PARSEDATE_OK     0
+#define PARSEDATE_FAIL   -1
+#define PARSEDATE_LATER  1
+#define PARSEDATE_SOONER 2
+
+/* Here's a bunch of frequently used time zone names. These were supported
+   by the old getdate parser. */
+#define tDAYZONE -60       /* offset for daylight savings time */
+static const struct tzinfo tz[]= {
+  {"GMT", 0},              /* Greenwich Mean */
+  {"UTC", 0},              /* Universal (Coordinated) */
+  {"WET", 0},              /* Western European */
+  {"BST", 0 tDAYZONE},     /* British Summer */
+  {"WAT", 60},             /* West Africa */
+  {"AST", 240},            /* Atlantic Standard */
+  {"ADT", 240 tDAYZONE},   /* Atlantic Daylight */
+  {"EST", 300},            /* Eastern Standard */
+  {"EDT", 300 tDAYZONE},   /* Eastern Daylight */
+  {"CST", 360},            /* Central Standard */
+  {"CDT", 360 tDAYZONE},   /* Central Daylight */
+  {"MST", 420},            /* Mountain Standard */
+  {"MDT", 420 tDAYZONE},   /* Mountain Daylight */
+  {"PST", 480},            /* Pacific Standard */
+  {"PDT", 480 tDAYZONE},   /* Pacific Daylight */
+  {"YST", 540},            /* Yukon Standard */
+  {"YDT", 540 tDAYZONE},   /* Yukon Daylight */
+  {"HST", 600},            /* Hawaii Standard */
+  {"HDT", 600 tDAYZONE},   /* Hawaii Daylight */
+  {"CAT", 600},            /* Central Alaska */
+  {"AHST", 600},           /* Alaska-Hawaii Standard */
+  {"NT",  660},            /* Nome */
+  {"IDLW", 720},           /* International Date Line West */
+  {"CET", -60},            /* Central European */
+  {"MET", -60},            /* Middle European */
+  {"MEWT", -60},           /* Middle European Winter */
+  {"MEST", -60 tDAYZONE},  /* Middle European Summer */
+  {"CEST", -60 tDAYZONE},  /* Central European Summer */
+  {"MESZ", -60 tDAYZONE},  /* Middle European Summer */
+  {"FWT", -60},            /* French Winter */
+  {"FST", -60 tDAYZONE},   /* French Summer */
+  {"EET", -120},           /* Eastern Europe, USSR Zone 1 */
+  {"WAST", -420},          /* West Australian Standard */
+  {"WADT", -420 tDAYZONE}, /* West Australian Daylight */
+  {"CCT", -480},           /* China Coast, USSR Zone 7 */
+  {"JST", -540},           /* Japan Standard, USSR Zone 8 */
+  {"EAST", -600},          /* Eastern Australian Standard */
+  {"EADT", -600 tDAYZONE}, /* Eastern Australian Daylight */
+  {"GST", -600},           /* Guam Standard, USSR Zone 9 */
+  {"NZT", -720},           /* New Zealand */
+  {"NZST", -720},          /* New Zealand Standard */
+  {"NZDT", -720 tDAYZONE}, /* New Zealand Daylight */
+  {"IDLE", -720},          /* International Date Line East */
+  /* Next up: Military timezone names. RFC822 allowed these, but (as noted in
+     RFC 1123) had their signs wrong. Here we use the correct signs to match
+     actual military usage.
+   */
+  {"A",  +1 * 60},         /* Alpha */
+  {"B",  +2 * 60},         /* Bravo */
+  {"C",  +3 * 60},         /* Charlie */
+  {"D",  +4 * 60},         /* Delta */
+  {"E",  +5 * 60},         /* Echo */
+  {"F",  +6 * 60},         /* Foxtrot */
+  {"G",  +7 * 60},         /* Golf */
+  {"H",  +8 * 60},         /* Hotel */
+  {"I",  +9 * 60},         /* India */
+  /* "J", Juliet is not used as a timezone, to indicate the observer's local
+     time */
+  {"K", +10 * 60},         /* Kilo */
+  {"L", +11 * 60},         /* Lima */
+  {"M", +12 * 60},         /* Mike */
+  {"N",  -1 * 60},         /* November */
+  {"O",  -2 * 60},         /* Oscar */
+  {"P",  -3 * 60},         /* Papa */
+  {"Q",  -4 * 60},         /* Quebec */
+  {"R",  -5 * 60},         /* Romeo */
+  {"S",  -6 * 60},         /* Sierra */
+  {"T",  -7 * 60},         /* Tango */
+  {"U",  -8 * 60},         /* Uniform */
+  {"V",  -9 * 60},         /* Victor */
+  {"W", -10 * 60},         /* Whiskey */
+  {"X", -11 * 60},         /* X-ray */
+  {"Y", -12 * 60},         /* Yankee */
+  {"Z", 0},                /* Zulu, zero meridian, a.k.a. UTC */
+};
+
+/* returns:
+   -1 no day
+   0 monday - 6 sunday
+*/
+
+static int checkday(const char *check, size_t len)
+{
+  int i;
+  const char * const *what;
+  bool found= FALSE;
+  if(len > 3)
+    what = &weekday[0];
+  else
+    what = &Curl_wkday[0];
+  for(i=0; i<7; i++) {
+    if(Curl_raw_equal(check, what[0])) {
+      found=TRUE;
+      break;
+    }
+    what++;
+  }
+  return found?i:-1;
+}
+
+static int checkmonth(const char *check)
+{
+  int i;
+  const char * const *what;
+  bool found= FALSE;
+
+  what = &Curl_month[0];
+  for(i=0; i<12; i++) {
+    if(Curl_raw_equal(check, what[0])) {
+      found=TRUE;
+      break;
+    }
+    what++;
+  }
+  return found?i:-1; /* return the offset or -1, no real offset is -1 */
+}
+
+/* return the time zone offset between GMT and the input one, in number
+   of seconds or -1 if the timezone wasn't found/legal */
+
+static int checktz(const char *check)
+{
+  unsigned int i;
+  const struct tzinfo *what;
+  bool found= FALSE;
+
+  what = tz;
+  for(i=0; i< sizeof(tz)/sizeof(tz[0]); i++) {
+    if(Curl_raw_equal(check, what->name)) {
+      found=TRUE;
+      break;
+    }
+    what++;
+  }
+  return found?what->offset*60:-1;
+}
+
+static void skip(const char **date)
+{
+  /* skip everything that aren't letters or digits */
+  while(**date && !ISALNUM(**date))
+    (*date)++;
+}
+
+enum assume {
+  DATE_MDAY,
+  DATE_YEAR,
+  DATE_TIME
+};
+
+/* this is a clone of 'struct tm' but with all fields we don't need or use
+   cut out */
+struct my_tm {
+  int tm_sec;
+  int tm_min;
+  int tm_hour;
+  int tm_mday;
+  int tm_mon;
+  int tm_year;
+};
+
+/* struct tm to time since epoch in GMT time zone.
+ * This is similar to the standard mktime function but for GMT only, and
+ * doesn't suffer from the various bugs and portability problems that
+ * some systems' implementations have.
+ */
+static time_t my_timegm(struct my_tm *tm)
+{
+  static const int month_days_cumulative [12] =
+    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+  int month, year, leap_days;
+
+  if(tm->tm_year < 70)
+    /* we don't support years before 1970 as they will cause this function
+       to return a negative value */
+    return -1;
+
+  year = tm->tm_year + 1900;
+  month = tm->tm_mon;
+  if(month < 0) {
+    year += (11 - month) / 12;
+    month = 11 - (11 - month) % 12;
+  }
+  else if(month >= 12) {
+    year -= month / 12;
+    month = month % 12;
+  }
+
+  leap_days = year - (tm->tm_mon <= 1);
+  leap_days = ((leap_days / 4) - (leap_days / 100) + (leap_days / 400)
+               - (1969 / 4) + (1969 / 100) - (1969 / 400));
+
+  return ((((time_t) (year - 1970) * 365
+            + leap_days + month_days_cumulative [month] + tm->tm_mday - 1) * 24
+           + tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec;
+}
+
+/*
+ * parsedate()
+ *
+ * Returns:
+ *
+ * PARSEDATE_OK     - a fine conversion
+ * PARSEDATE_FAIL   - failed to convert
+ * PARSEDATE_LATER  - time overflow at the far end of time_t
+ * PARSEDATE_SOONER - time underflow at the low end of time_t
+ */
+
+static int parsedate(const char *date, time_t *output)
+{
+  time_t t = 0;
+  int wdaynum=-1;  /* day of the week number, 0-6 (mon-sun) */
+  int monnum=-1;   /* month of the year number, 0-11 */
+  int mdaynum=-1; /* day of month, 1 - 31 */
+  int hournum=-1;
+  int minnum=-1;
+  int secnum=-1;
+  int yearnum=-1;
+  int tzoff=-1;
+  struct my_tm tm;
+  enum assume dignext = DATE_MDAY;
+  const char *indate = date; /* save the original pointer */
+  int part = 0; /* max 6 parts */
+
+  while(*date && (part < 6)) {
+    bool found=FALSE;
+
+    skip(&date);
+
+    if(ISALPHA(*date)) {
+      /* a name coming up */
+      char buf[32]="";
+      size_t len;
+      sscanf(date, "%31[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]",
+             buf);
+      len = strlen(buf);
+
+      if(wdaynum == -1) {
+        wdaynum = checkday(buf, len);
+        if(wdaynum != -1)
+          found = TRUE;
+      }
+      if(!found && (monnum == -1)) {
+        monnum = checkmonth(buf);
+        if(monnum != -1)
+          found = TRUE;
+      }
+
+      if(!found && (tzoff == -1)) {
+        /* this just must be a time zone string */
+        tzoff = checktz(buf);
+        if(tzoff != -1)
+          found = TRUE;
+      }
+
+      if(!found)
+        return PARSEDATE_FAIL; /* bad string */
+
+      date += len;
+    }
+    else if(ISDIGIT(*date)) {
+      /* a digit */
+      int val;
+      char *end;
+      if((secnum == -1) &&
+         (3 == sscanf(date, "%02d:%02d:%02d", &hournum, &minnum, &secnum))) {
+        /* time stamp! */
+        date += 8;
+      }
+      else if((secnum == -1) &&
+              (2 == sscanf(date, "%02d:%02d", &hournum, &minnum))) {
+        /* time stamp without seconds */
+        date += 5;
+        secnum = 0;
+      }
+      else {
+        long lval;
+        int error;
+        int old_errno;
+
+        old_errno = ERRNO;
+        SET_ERRNO(0);
+        lval = strtol(date, &end, 10);
+        error = ERRNO;
+        if(error != old_errno)
+          SET_ERRNO(old_errno);
+
+        if(error)
+          return PARSEDATE_FAIL;
+
+        if((lval > (long)INT_MAX) || (lval < (long)INT_MIN))
+          return PARSEDATE_FAIL;
+
+        val = curlx_sltosi(lval);
+
+        if((tzoff == -1) &&
+           ((end - date) == 4) &&
+           (val <= 1400) &&
+           (indate< date) &&
+           ((date[-1] == '+' || date[-1] == '-'))) {
+          /* four digits and a value less than or equal to 1400 (to take into
+             account all sorts of funny time zone diffs) and it is preceded
+             with a plus or minus. This is a time zone indication.  1400 is
+             picked since +1300 is frequently used and +1400 is mentioned as
+             an edge number in the document "ISO C 200X Proposal: Timezone
+             Functions" at http://david.tribble.com/text/c0xtimezone.html If
+             anyone has a more authoritative source for the exact maximum time
+             zone offsets, please speak up! */
+          found = TRUE;
+          tzoff = (val/100 * 60 + val%100)*60;
+
+          /* the + and - prefix indicates the local time compared to GMT,
+             this we need ther reversed math to get what we want */
+          tzoff = date[-1]=='+'?-tzoff:tzoff;
+        }
+
+        if(((end - date) == 8) &&
+           (yearnum == -1) &&
+           (monnum == -1) &&
+           (mdaynum == -1)) {
+          /* 8 digits, no year, month or day yet. This is YYYYMMDD */
+          found = TRUE;
+          yearnum = val/10000;
+          monnum = (val%10000)/100-1; /* month is 0 - 11 */
+          mdaynum = val%100;
+        }
+
+        if(!found && (dignext == DATE_MDAY) && (mdaynum == -1)) {
+          if((val > 0) && (val<32)) {
+            mdaynum = val;
+            found = TRUE;
+          }
+          dignext = DATE_YEAR;
+        }
+
+        if(!found && (dignext == DATE_YEAR) && (yearnum == -1)) {
+          yearnum = val;
+          found = TRUE;
+          if(yearnum < 1900) {
+            if(yearnum > 70)
+              yearnum += 1900;
+            else
+              yearnum += 2000;
+          }
+          if(mdaynum == -1)
+            dignext = DATE_MDAY;
+        }
+
+        if(!found)
+          return PARSEDATE_FAIL;
+
+        date = end;
+      }
+    }
+
+    part++;
+  }
+
+  if(-1 == secnum)
+    secnum = minnum = hournum = 0; /* no time, make it zero */
+
+  if((-1 == mdaynum) ||
+     (-1 == monnum) ||
+     (-1 == yearnum))
+    /* lacks vital info, fail */
+    return PARSEDATE_FAIL;
+
+#if SIZEOF_TIME_T < 5
+  /* 32 bit time_t can only hold dates to the beginning of 2038 */
+  if(yearnum > 2037) {
+    *output = 0x7fffffff;
+    return PARSEDATE_LATER;
+  }
+#endif
+
+  if(yearnum < 1970) {
+    *output = 0;
+    return PARSEDATE_SOONER;
+  }
+
+  if((mdaynum > 31) || (monnum > 11) ||
+     (hournum > 23) || (minnum > 59) || (secnum > 60))
+    return PARSEDATE_FAIL; /* clearly an illegal date */
+
+  tm.tm_sec = secnum;
+  tm.tm_min = minnum;
+  tm.tm_hour = hournum;
+  tm.tm_mday = mdaynum;
+  tm.tm_mon = monnum;
+  tm.tm_year = yearnum - 1900;
+
+  /* my_timegm() returns a time_t. time_t is often 32 bits, even on many
+     architectures that feature 64 bit 'long'.
+
+     Some systems have 64 bit time_t and deal with years beyond 2038. However,
+     even on some of the systems with 64 bit time_t mktime() returns -1 for
+     dates beyond 03:14:07 UTC, January 19, 2038. (Such as AIX 5100-06)
+  */
+  t = my_timegm(&tm);
+
+  /* time zone adjust (cast t to int to compare to negative one) */
+  if(-1 != (int)t) {
+
+    /* Add the time zone diff between local time zone and GMT. */
+    long delta = (long)(tzoff!=-1?tzoff:0);
+
+    if((delta>0) && (t + delta < t))
+      return -1; /* time_t overflow */
+
+    t += delta;
+  }
+
+  *output = t;
+
+  return PARSEDATE_OK;
+}
+
+time_t curl_getdate(const char *p, const time_t *now)
+{
+  time_t parsed;
+  int rc = parsedate(p, &parsed);
+  (void)now; /* legacy argument from the past that we ignore */
+
+  switch(rc) {
+  case PARSEDATE_OK:
+  case PARSEDATE_LATER:
+  case PARSEDATE_SOONER:
+    return parsed;
+  }
+  /* everything else is fail */
+  return -1;
+}
+
+/*
+ * Curl_gmtime() is a gmtime() replacement for portability. Do not use the
+ * gmtime_r() or gmtime() functions anywhere else but here.
+ *
+ * To make sure no such function calls slip in, we define them to cause build
+ * errors, which is why we use the name within parentheses in this function.
+ *
+ */
+
+CURLcode Curl_gmtime(time_t intime, struct tm *store)
+{
+  const struct tm *tm;
+#ifdef HAVE_GMTIME_R
+  /* thread-safe version */
+  tm = (struct tm *)gmtime_r(&intime, store);
+#else
+  tm = gmtime(&intime);
+  if(tm)
+    *store = *tm; /* copy the pointed struct to the local copy */
+#endif
+
+  if(!tm)
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  return CURLE_OK;
+}
diff --git a/lib/curl_pingpong.c b/lib/curl_pingpong.c
new file mode 100644 (file)
index 0000000..d28e78a
--- /dev/null
@@ -0,0 +1,538 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ *   'pingpong' is for generic back-and-forth support functions used by FTP,
+ *   IMAP, POP3, SMTP and whatever more that likes them.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_select.h"
+#include "curl_progress.h"
+#include "curl_speedcheck.h"
+#include "curl_pingpong.h"
+#include "curl_multiif.h"
+#include "curl_non_ascii.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+#ifdef USE_PINGPONG
+
+/* Returns timeout in ms. 0 or negative number means the timeout has already
+   triggered */
+long Curl_pp_state_timeout(struct pingpong *pp)
+{
+  struct connectdata *conn = pp->conn;
+  struct SessionHandle *data=conn->data;
+  long timeout_ms; /* in milliseconds */
+  long timeout2_ms; /* in milliseconds */
+  long response_time= (data->set.server_response_timeout)?
+    data->set.server_response_timeout: pp->response_time;
+
+  /* if CURLOPT_SERVER_RESPONSE_TIMEOUT is set, use that to determine
+     remaining time, or use pp->response because SERVER_RESPONSE_TIMEOUT is
+     supposed to govern the response for any given server response, not for
+     the time from connect to the given server response. */
+
+  /* Without a requested timeout, we only wait 'response_time' seconds for the
+     full response to arrive before we bail out */
+  timeout_ms = response_time -
+    Curl_tvdiff(Curl_tvnow(), pp->response); /* spent time */
+
+  if(data->set.timeout) {
+    /* if timeout is requested, find out how much remaining time we have */
+    timeout2_ms = data->set.timeout - /* timeout time */
+      Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
+
+    /* pick the lowest number */
+    timeout_ms = CURLMIN(timeout_ms, timeout2_ms);
+  }
+
+  return timeout_ms;
+}
+
+
+/*
+ * Curl_pp_multi_statemach()
+ *
+ * called repeatedly until done when the multi interface is used.
+ */
+CURLcode Curl_pp_multi_statemach(struct pingpong *pp)
+{
+  struct connectdata *conn = pp->conn;
+  curl_socket_t sock = conn->sock[FIRSTSOCKET];
+  int rc;
+  struct SessionHandle *data=conn->data;
+  CURLcode result = CURLE_OK;
+  long timeout_ms = Curl_pp_state_timeout(pp);
+
+  if(timeout_ms <= 0) {
+    failf(data, "server response timeout");
+    return CURLE_OPERATION_TIMEDOUT;
+  }
+
+  rc = Curl_socket_ready(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
+                         pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
+                         0);
+
+  if(rc == -1) {
+    failf(data, "select/poll error");
+    return CURLE_OUT_OF_MEMORY;
+  }
+  else if(rc != 0)
+    result = pp->statemach_act(conn);
+
+  /* if rc == 0, then select() timed out */
+
+  return result;
+}
+
+/*
+ * Curl_pp_easy_statemach()
+ *
+ * called repeatedly until done when the easy interface is used.
+ */
+CURLcode Curl_pp_easy_statemach(struct pingpong *pp)
+{
+  struct connectdata *conn = pp->conn;
+  curl_socket_t sock = conn->sock[FIRSTSOCKET];
+  int rc;
+  long interval_ms;
+  long timeout_ms = Curl_pp_state_timeout(pp);
+  struct SessionHandle *data=conn->data;
+  CURLcode result;
+
+  if(timeout_ms <=0 ) {
+    failf(data, "server response timeout");
+    return CURLE_OPERATION_TIMEDOUT; /* already too little time */
+  }
+
+  interval_ms = 1000;  /* use 1 second timeout intervals */
+  if(timeout_ms < interval_ms)
+    interval_ms = timeout_ms;
+
+  rc = Curl_socket_ready(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
+                         pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
+                         interval_ms);
+
+  if(Curl_pgrsUpdate(conn))
+    result = CURLE_ABORTED_BY_CALLBACK;
+  else
+    result = Curl_speedcheck(data, Curl_tvnow());
+
+  if(result)
+    ;
+  else if(rc == -1) {
+    failf(data, "select/poll error");
+    result = CURLE_OUT_OF_MEMORY;
+  }
+  else if(rc)
+    result = pp->statemach_act(conn);
+
+  return result;
+}
+
+/* initialize stuff to prepare for reading a fresh new response */
+void Curl_pp_init(struct pingpong *pp)
+{
+  struct connectdata *conn = pp->conn;
+  pp->nread_resp = 0;
+  pp->linestart_resp = conn->data->state.buffer;
+  pp->pending_resp = TRUE;
+  pp->response = Curl_tvnow(); /* start response time-out now! */
+}
+
+
+
+/***********************************************************************
+ *
+ * Curl_pp_vsendf()
+ *
+ * Send the formated string as a command to a pingpong server. Note that
+ * the string should not have any CRLF appended, as this function will
+ * append the necessary things itself.
+ *
+ * made to never block
+ */
+CURLcode Curl_pp_vsendf(struct pingpong *pp,
+                        const char *fmt,
+                        va_list args)
+{
+  ssize_t bytes_written;
+  size_t write_len;
+  char *fmt_crlf;
+  char *s;
+  CURLcode error;
+  struct connectdata *conn = pp->conn;
+  struct SessionHandle *data = conn->data;
+
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+  enum protection_level data_sec = conn->data_prot;
+#endif
+
+  DEBUGASSERT(pp->sendleft == 0);
+  DEBUGASSERT(pp->sendsize == 0);
+  DEBUGASSERT(pp->sendthis == NULL);
+
+  fmt_crlf = aprintf("%s\r\n", fmt); /* append a trailing CRLF */
+  if(!fmt_crlf)
+    return CURLE_OUT_OF_MEMORY;
+
+  s = vaprintf(fmt_crlf, args); /* trailing CRLF appended */
+  free(fmt_crlf);
+  if(!s)
+    return CURLE_OUT_OF_MEMORY;
+
+  bytes_written = 0;
+  write_len = strlen(s);
+
+  Curl_pp_init(pp);
+
+  error = Curl_convert_to_network(data, s, write_len);
+  /* Curl_convert_to_network calls failf if unsuccessful */
+  if(error) {
+    free(s);
+    return error;
+  }
+
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+  conn->data_prot = PROT_CMD;
+#endif
+  error = Curl_write(conn, conn->sock[FIRSTSOCKET], s, write_len,
+                     &bytes_written);
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+  DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
+  conn->data_prot = data_sec;
+#endif
+
+  if(error) {
+    free(s);
+    return error;
+  }
+
+  if(conn->data->set.verbose)
+    Curl_debug(conn->data, CURLINFO_HEADER_OUT,
+               s, (size_t)bytes_written, conn);
+
+  if(bytes_written != (ssize_t)write_len) {
+    /* the whole chunk was not sent, keep it around and adjust sizes */
+    pp->sendthis = s;
+    pp->sendsize = write_len;
+    pp->sendleft = write_len - bytes_written;
+  }
+  else {
+    free(s);
+    pp->sendthis = NULL;
+    pp->sendleft = pp->sendsize = 0;
+    pp->response = Curl_tvnow();
+  }
+
+  return CURLE_OK;
+}
+
+
+/***********************************************************************
+ *
+ * Curl_pp_sendf()
+ *
+ * Send the formated string as a command to a pingpong server. Note that
+ * the string should not have any CRLF appended, as this function will
+ * append the necessary things itself.
+ *
+ * made to never block
+ */
+CURLcode Curl_pp_sendf(struct pingpong *pp,
+                       const char *fmt, ...)
+{
+  CURLcode res;
+  va_list ap;
+  va_start(ap, fmt);
+
+  res = Curl_pp_vsendf(pp, fmt, ap);
+
+  va_end(ap);
+
+  return res;
+}
+
+/*
+ * Curl_pp_readresp()
+ *
+ * Reads a piece of a server response.
+ */
+CURLcode Curl_pp_readresp(curl_socket_t sockfd,
+                          struct pingpong *pp,
+                          int *code, /* return the server code if done */
+                          size_t *size) /* size of the response */
+{
+  ssize_t perline; /* count bytes per line */
+  bool keepon=TRUE;
+  ssize_t gotbytes;
+  char *ptr;
+  struct connectdata *conn = pp->conn;
+  struct SessionHandle *data = conn->data;
+  char * const buf = data->state.buffer;
+  CURLcode result = CURLE_OK;
+
+  *code = 0; /* 0 for errors or not done */
+  *size = 0;
+
+  ptr=buf + pp->nread_resp;
+
+  /* number of bytes in the current line, so far */
+  perline = (ssize_t)(ptr-pp->linestart_resp);
+
+  keepon=TRUE;
+
+  while((pp->nread_resp<BUFSIZE) && (keepon && !result)) {
+
+    if(pp->cache) {
+      /* we had data in the "cache", copy that instead of doing an actual
+       * read
+       *
+       * pp->cache_size is cast to ssize_t here.  This should be safe, because
+       * it would have been populated with something of size int to begin
+       * with, even though its datatype may be larger than an int.
+       */
+      DEBUGASSERT((ptr+pp->cache_size) <= (buf+BUFSIZE+1));
+      memcpy(ptr, pp->cache, pp->cache_size);
+      gotbytes = (ssize_t)pp->cache_size;
+      free(pp->cache);    /* free the cache */
+      pp->cache = NULL;   /* clear the pointer */
+      pp->cache_size = 0; /* zero the size just in case */
+    }
+    else {
+      int res;
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+      enum protection_level prot = conn->data_prot;
+      conn->data_prot = PROT_CLEAR;
+#endif
+      DEBUGASSERT((ptr+BUFSIZE-pp->nread_resp) <= (buf+BUFSIZE+1));
+      res = Curl_read(conn, sockfd, ptr, BUFSIZE-pp->nread_resp,
+                      &gotbytes);
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+      DEBUGASSERT(prot  > PROT_NONE && prot < PROT_LAST);
+      conn->data_prot = prot;
+#endif
+      if(res == CURLE_AGAIN)
+        return CURLE_OK; /* return */
+
+      if((res == CURLE_OK) && (gotbytes > 0))
+        /* convert from the network encoding */
+        res = Curl_convert_from_network(data, ptr, gotbytes);
+      /* Curl_convert_from_network calls failf if unsuccessful */
+
+      if(CURLE_OK != res) {
+        result = (CURLcode)res; /* Set outer result variable to this error. */
+        keepon = FALSE;
+      }
+    }
+
+    if(!keepon)
+      ;
+    else if(gotbytes <= 0) {
+      keepon = FALSE;
+      result = CURLE_RECV_ERROR;
+      failf(data, "response reading failed");
+    }
+    else {
+      /* we got a whole chunk of data, which can be anything from one
+       * byte to a set of lines and possible just a piece of the last
+       * line */
+      ssize_t i;
+      ssize_t clipamount = 0;
+      bool restart = FALSE;
+
+      data->req.headerbytecount += (long)gotbytes;
+
+      pp->nread_resp += gotbytes;
+      for(i = 0; i < gotbytes; ptr++, i++) {
+        perline++;
+        if(*ptr=='\n') {
+          /* a newline is CRLF in pp-talk, so the CR is ignored as
+             the line isn't really terminated until the LF comes */
+
+          /* output debug output if that is requested */
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+          if(!conn->sec_complete)
+#endif
+            if(data->set.verbose)
+              Curl_debug(data, CURLINFO_HEADER_IN,
+                         pp->linestart_resp, (size_t)perline, conn);
+
+          /*
+           * We pass all response-lines to the callback function registered
+           * for "headers". The response lines can be seen as a kind of
+           * headers.
+           */
+          result = Curl_client_write(conn, CLIENTWRITE_HEADER,
+                                     pp->linestart_resp, perline);
+          if(result)
+            return result;
+
+          if(pp->endofresp(pp, code)) {
+            /* This is the end of the last line, copy the last line to the
+               start of the buffer and zero terminate, for old times sake (and
+               krb4)! */
+            char *meow;
+            int n;
+            for(meow=pp->linestart_resp, n=0; meow<ptr; meow++, n++)
+              buf[n] = *meow;
+            *meow=0; /* zero terminate */
+            keepon=FALSE;
+            pp->linestart_resp = ptr+1; /* advance pointer */
+            i++; /* skip this before getting out */
+
+            *size = pp->nread_resp; /* size of the response */
+            pp->nread_resp = 0; /* restart */
+            break;
+          }
+          perline=0; /* line starts over here */
+          pp->linestart_resp = ptr+1;
+        }
+      }
+
+      if(!keepon && (i != gotbytes)) {
+        /* We found the end of the response lines, but we didn't parse the
+           full chunk of data we have read from the server. We therefore need
+           to store the rest of the data to be checked on the next invoke as
+           it may actually contain another end of response already! */
+        clipamount = gotbytes - i;
+        restart = TRUE;
+        DEBUGF(infof(data, "Curl_pp_readresp_ %d bytes of trailing "
+                     "server response left\n",
+                     (int)clipamount));
+      }
+      else if(keepon) {
+
+        if((perline == gotbytes) && (gotbytes > BUFSIZE/2)) {
+          /* We got an excessive line without newlines and we need to deal
+             with it. We keep the first bytes of the line then we throw
+             away the rest. */
+          infof(data, "Excessive server response line length received, "
+                "%zd bytes. Stripping\n", gotbytes);
+          restart = TRUE;
+
+          /* we keep 40 bytes since all our pingpong protocols are only
+             interested in the first piece */
+          clipamount = 40;
+        }
+        else if(pp->nread_resp > BUFSIZE/2) {
+          /* We got a large chunk of data and there's potentially still
+             trailing data to take care of, so we put any such part in the
+             "cache", clear the buffer to make space and restart. */
+          clipamount = perline;
+          restart = TRUE;
+        }
+      }
+      else if(i == gotbytes)
+        restart = TRUE;
+
+      if(clipamount) {
+        pp->cache_size = clipamount;
+        pp->cache = malloc(pp->cache_size);
+        if(pp->cache)
+          memcpy(pp->cache, pp->linestart_resp, pp->cache_size);
+        else
+          return CURLE_OUT_OF_MEMORY;
+      }
+      if(restart) {
+        /* now reset a few variables to start over nicely from the start of
+           the big buffer */
+        pp->nread_resp = 0; /* start over from scratch in the buffer */
+        ptr = pp->linestart_resp = buf;
+        perline = 0;
+      }
+
+    } /* there was data */
+
+  } /* while there's buffer left and loop is requested */
+
+  pp->pending_resp = FALSE;
+
+  return result;
+}
+
+int Curl_pp_getsock(struct pingpong *pp,
+                    curl_socket_t *socks,
+                    int numsocks)
+{
+  struct connectdata *conn = pp->conn;
+
+  if(!numsocks)
+    return GETSOCK_BLANK;
+
+  socks[0] = conn->sock[FIRSTSOCKET];
+
+  if(pp->sendleft) {
+    /* write mode */
+    return GETSOCK_WRITESOCK(0);
+  }
+
+  /* read mode */
+  return GETSOCK_READSOCK(0);
+}
+
+CURLcode Curl_pp_flushsend(struct pingpong *pp)
+{
+  /* we have a piece of a command still left to send */
+  struct connectdata *conn = pp->conn;
+  ssize_t written;
+  CURLcode result = CURLE_OK;
+  curl_socket_t sock = conn->sock[FIRSTSOCKET];
+
+  result = Curl_write(conn, sock, pp->sendthis + pp->sendsize -
+                      pp->sendleft, pp->sendleft, &written);
+  if(result)
+    return result;
+
+  if(written != (ssize_t)pp->sendleft) {
+    /* only a fraction was sent */
+    pp->sendleft -= written;
+  }
+  else {
+    free(pp->sendthis);
+    pp->sendthis=NULL;
+    pp->sendleft = pp->sendsize = 0;
+    pp->response = Curl_tvnow();
+  }
+  return CURLE_OK;
+}
+
+CURLcode Curl_pp_disconnect(struct pingpong *pp)
+{
+  if(pp->cache) {
+    free(pp->cache);
+    pp->cache = NULL;
+  }
+  return CURLE_OK;
+}
+
+
+
+#endif
diff --git a/lib/curl_polarssl.c b/lib/curl_polarssl.c
new file mode 100644 (file)
index 0000000..81c7026
--- /dev/null
@@ -0,0 +1,596 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2010, 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com>
+ * Copyright (C) 2012 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/*
+ * Source file for all PolarSSL-specific code for the TLS/SSL layer. No code
+ * but curl_sslgen.c should ever call or use these functions.
+ *
+ */
+
+#include "curl_setup.h"
+
+#ifdef USE_POLARSSL
+
+#include <polarssl/net.h>
+#include <polarssl/ssl.h>
+#include <polarssl/havege.h>
+#include <polarssl/certs.h>
+#include <polarssl/x509.h>
+#include <polarssl/version.h>
+
+#include <polarssl/entropy.h>
+#include <polarssl/ctr_drbg.h>
+
+#if POLARSSL_VERSION_NUMBER<0x01000000
+/*
+  Earlier versions of polarssl had no WANT_READ or WANT_WRITE, only TRY_AGAIN
+*/
+#define POLARSSL_ERR_NET_WANT_READ  POLARSSL_ERR_NET_TRY_AGAIN
+#define POLARSSL_ERR_NET_WANT_WRITE POLARSSL_ERR_NET_TRY_AGAIN
+#endif
+
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_inet_pton.h"
+#include "curl_polarssl.h"
+#include "curl_sslgen.h"
+#include "curl_parsedate.h"
+#include "curl_connect.h" /* for the connect timeout */
+#include "curl_select.h"
+#include "curl_rawstr.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/* version dependent differences */
+#if POLARSSL_VERSION_NUMBER < 0x01010000
+/* the old way */
+#define HAVEGE_RANDOM havege_rand
+#else
+/* from 1.1.0 */
+#define HAVEGE_RANDOM havege_random
+#endif
+
+/* Define this to enable lots of debugging for PolarSSL */
+#undef POLARSSL_DEBUG
+
+#ifdef POLARSSL_DEBUG
+static void polarssl_debug(void *context, int level, char *line)
+{
+  struct SessionHandle *data = NULL;
+
+  if(!context)
+    return;
+
+  data = (struct SessionHandle *)context;
+
+  infof(data, "%s\n", line);
+}
+#else
+#endif
+
+static Curl_recv polarssl_recv;
+static Curl_send polarssl_send;
+
+
+static CURLcode
+polarssl_connect_step1(struct connectdata *conn,
+                     int sockindex)
+{
+  struct SessionHandle *data = conn->data;
+  struct ssl_connect_data* connssl = &conn->ssl[sockindex];
+
+  bool sni = TRUE; /* default is SNI enabled */
+  int ret = -1;
+#ifdef ENABLE_IPV6
+  struct in6_addr addr;
+#else
+  struct in_addr addr;
+#endif
+  void *old_session = NULL;
+  size_t old_session_size = 0;
+
+  /* PolarSSL only supports SSLv3 and TLSv1 */
+  if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) {
+    failf(data, "PolarSSL does not support SSLv2");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+  else if(data->set.ssl.version == CURL_SSLVERSION_SSLv3)
+    sni = FALSE; /* SSLv3 has no SNI */
+
+  havege_init(&connssl->hs);
+
+  /* Load the trusted CA */
+  memset(&connssl->cacert, 0, sizeof(x509_cert));
+
+  if(data->set.str[STRING_SSL_CAFILE]) {
+    ret = x509parse_crtfile(&connssl->cacert,
+                            data->set.str[STRING_SSL_CAFILE]);
+
+    if(ret<0) {
+      failf(data, "Error reading ca cert file %s: -0x%04X",
+            data->set.str[STRING_SSL_CAFILE], ret);
+
+      if(data->set.ssl.verifypeer)
+        return CURLE_SSL_CACERT_BADFILE;
+    }
+  }
+
+  /* Load the client certificate */
+  memset(&connssl->clicert, 0, sizeof(x509_cert));
+
+  if(data->set.str[STRING_CERT]) {
+    ret = x509parse_crtfile(&connssl->clicert,
+                            data->set.str[STRING_CERT]);
+
+    if(ret) {
+      failf(data, "Error reading client cert file %s: -0x%04X",
+            data->set.str[STRING_CERT], -ret);
+      return CURLE_SSL_CERTPROBLEM;
+    }
+  }
+
+  /* Load the client private key */
+  if(data->set.str[STRING_KEY]) {
+    ret = x509parse_keyfile(&connssl->rsa,
+                            data->set.str[STRING_KEY],
+                            data->set.str[STRING_KEY_PASSWD]);
+
+    if(ret) {
+      failf(data, "Error reading private key %s: -0x%04X",
+            data->set.str[STRING_KEY], -ret);
+      return CURLE_SSL_CERTPROBLEM;
+    }
+  }
+
+  /* Load the CRL */
+  memset(&connssl->crl, 0, sizeof(x509_crl));
+
+  if(data->set.str[STRING_SSL_CRLFILE]) {
+    ret = x509parse_crlfile(&connssl->crl,
+                            data->set.str[STRING_SSL_CRLFILE]);
+
+    if(ret) {
+      failf(data, "Error reading CRL file %s: -0x%04X",
+            data->set.str[STRING_SSL_CRLFILE], -ret);
+      return CURLE_SSL_CRL_BADFILE;
+    }
+  }
+
+  infof(data, "PolarSSL: Connecting to %s:%d\n",
+        conn->host.name, conn->remote_port);
+
+  if(ssl_init(&connssl->ssl)) {
+    failf(data, "PolarSSL: ssl_init failed");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  ssl_set_endpoint(&connssl->ssl, SSL_IS_CLIENT);
+  ssl_set_authmode(&connssl->ssl, SSL_VERIFY_OPTIONAL);
+
+  ssl_set_rng(&connssl->ssl, HAVEGE_RANDOM,
+              &connssl->hs);
+  ssl_set_bio(&connssl->ssl,
+              net_recv, &conn->sock[sockindex],
+              net_send, &conn->sock[sockindex]);
+
+
+#if POLARSSL_VERSION_NUMBER<0x01000000
+  ssl_set_ciphers(&connssl->ssl, ssl_default_ciphers);
+#else
+  ssl_set_ciphersuites(&connssl->ssl, ssl_default_ciphersuites);
+#endif
+  if(!Curl_ssl_getsessionid(conn, &old_session, &old_session_size)) {
+    memcpy(&connssl->ssn, old_session, old_session_size);
+    infof(data, "PolarSSL re-using session\n");
+  }
+
+/* PolarSSL SVN revision r1316 to r1317, matching <1.2.0 is to cover Ubuntu's
+   1.1.4 version and the like */
+#if POLARSSL_VERSION_NUMBER<0x01020000
+  ssl_set_session(&connssl->ssl, 1, 600,
+                  &connssl->ssn);
+#else
+  ssl_set_session(&connssl->ssl,
+                  &connssl->ssn);
+#endif
+
+  ssl_set_ca_chain(&connssl->ssl,
+                   &connssl->cacert,
+                   &connssl->crl,
+                   conn->host.name);
+
+  ssl_set_own_cert(&connssl->ssl,
+                   &connssl->clicert, &connssl->rsa);
+
+  if(!Curl_inet_pton(AF_INET, conn->host.name, &addr) &&
+#ifdef ENABLE_IPV6
+     !Curl_inet_pton(AF_INET6, conn->host.name, &addr) &&
+#endif
+     sni && ssl_set_hostname(&connssl->ssl, conn->host.name)) {
+     infof(data, "WARNING: failed to configure "
+                 "server name indication (SNI) TLS extension\n");
+  }
+
+#ifdef POLARSSL_DEBUG
+  ssl_set_dbg(&connssl->ssl, polarssl_debug, data);
+#endif
+
+  connssl->connecting_state = ssl_connect_2;
+
+  return CURLE_OK;
+}
+
+static CURLcode
+polarssl_connect_step2(struct connectdata *conn,
+                     int sockindex)
+{
+  int ret;
+  struct SessionHandle *data = conn->data;
+  struct ssl_connect_data* connssl = &conn->ssl[sockindex];
+  char buffer[1024];
+
+  conn->recv[sockindex] = polarssl_recv;
+  conn->send[sockindex] = polarssl_send;
+
+  for(;;) {
+    if(!(ret = ssl_handshake(&connssl->ssl)))
+      break;
+    else if(ret != POLARSSL_ERR_NET_WANT_READ &&
+            ret != POLARSSL_ERR_NET_WANT_WRITE) {
+      failf(data, "ssl_handshake returned -0x%04X", -ret);
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+    else {
+      if(ret == POLARSSL_ERR_NET_WANT_READ) {
+        connssl->connecting_state = ssl_connect_2_reading;
+        return CURLE_OK;
+      }
+      if(ret == POLARSSL_ERR_NET_WANT_WRITE) {
+        connssl->connecting_state = ssl_connect_2_writing;
+        return CURLE_OK;
+      }
+      failf(data, "SSL_connect failed with error %d.", ret);
+      return CURLE_SSL_CONNECT_ERROR;
+
+    }
+  }
+
+  infof(data, "PolarSSL: Handshake complete, cipher is %s\n",
+#if POLARSSL_VERSION_NUMBER<0x01000000
+        ssl_get_cipher(&conn->ssl[sockindex].ssl)
+#elif POLARSSL_VERSION_NUMBER >= 0x01010000
+        ssl_get_ciphersuite(&conn->ssl[sockindex].ssl)
+#else
+        ssl_get_ciphersuite_name(&conn->ssl[sockindex].ssl)
+#endif
+    );
+
+  ret = ssl_get_verify_result(&conn->ssl[sockindex].ssl);
+
+  if(ret && data->set.ssl.verifypeer) {
+    if(ret & BADCERT_EXPIRED)
+      failf(data, "Cert verify failed: BADCERT_EXPIRED");
+
+    if(ret & BADCERT_REVOKED) {
+      failf(data, "Cert verify failed: BADCERT_REVOKED");
+      return CURLE_SSL_CACERT;
+    }
+
+    if(ret & BADCERT_CN_MISMATCH)
+      failf(data, "Cert verify failed: BADCERT_CN_MISMATCH");
+
+    if(ret & BADCERT_NOT_TRUSTED)
+      failf(data, "Cert verify failed: BADCERT_NOT_TRUSTED");
+
+    return CURLE_PEER_FAILED_VERIFICATION;
+  }
+
+/* PolarSSL SVN revision r1316 to r1317, matching <1.2.0 is to cover Ubuntu's
+   1.1.4 version and the like */
+#if POLARSSL_VERSION_NUMBER<0x01020000
+  if(conn->ssl[sockindex].ssl.peer_cert) {
+#else
+  if(ssl_get_peer_cert(&(connssl->ssl))) {
+#endif
+    /* If the session was resumed, there will be no peer certs */
+    memset(buffer, 0, sizeof(buffer));
+
+/* PolarSSL SVN revision r1316 to r1317, matching <1.2.0 is to cover Ubuntu's
+   1.1.4 version and the like */
+#if POLARSSL_VERSION_NUMBER<0x01020000
+    if(x509parse_cert_info(buffer, sizeof(buffer), (char *)"* ",
+                           conn->ssl[sockindex].ssl.peer_cert) != -1)
+#else
+    if(x509parse_cert_info(buffer, sizeof(buffer), (char *)"* ",
+                           ssl_get_peer_cert(&(connssl->ssl))) != -1)
+#endif
+      infof(data, "Dumping cert info:\n%s\n", buffer);
+  }
+
+  connssl->connecting_state = ssl_connect_3;
+  infof(data, "SSL connected\n");
+
+  return CURLE_OK;
+}
+
+static CURLcode
+polarssl_connect_step3(struct connectdata *conn,
+                     int sockindex)
+{
+  CURLcode retcode = CURLE_OK;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  struct SessionHandle *data = conn->data;
+  void *old_ssl_sessionid = NULL;
+  ssl_session *our_ssl_sessionid = &conn->ssl[sockindex].ssn ;
+  int incache;
+
+  DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
+
+  /* Save the current session data for possible re-use */
+  incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL));
+  if(incache) {
+    if(old_ssl_sessionid != our_ssl_sessionid) {
+      infof(data, "old SSL session ID is stale, removing\n");
+      Curl_ssl_delsessionid(conn, old_ssl_sessionid);
+      incache = FALSE;
+    }
+  }
+  if(!incache) {
+    void *new_session = malloc(sizeof(ssl_session));
+
+    if(new_session) {
+      memcpy(new_session, our_ssl_sessionid,
+             sizeof(ssl_session));
+
+      retcode = Curl_ssl_addsessionid(conn, new_session,
+                                   sizeof(ssl_session));
+    }
+    else {
+      retcode = CURLE_OUT_OF_MEMORY;
+    }
+
+    if(retcode) {
+      failf(data, "failed to store ssl session");
+      return retcode;
+    }
+  }
+
+  connssl->connecting_state = ssl_connect_done;
+
+  return CURLE_OK;
+}
+
+static ssize_t polarssl_send(struct connectdata *conn,
+                             int sockindex,
+                             const void *mem,
+                             size_t len,
+                             CURLcode *curlcode)
+{
+  int ret = -1;
+
+  ret = ssl_write(&conn->ssl[sockindex].ssl,
+                  (unsigned char *)mem, len);
+
+  if(ret < 0) {
+    *curlcode = (ret == POLARSSL_ERR_NET_WANT_WRITE) ?
+      CURLE_AGAIN : CURLE_SEND_ERROR;
+    ret = -1;
+  }
+
+  return ret;
+}
+
+void Curl_polarssl_close_all(struct SessionHandle *data)
+{
+  (void)data;
+}
+
+void Curl_polarssl_close(struct connectdata *conn, int sockindex)
+{
+  rsa_free(&conn->ssl[sockindex].rsa);
+  x509_free(&conn->ssl[sockindex].clicert);
+  x509_free(&conn->ssl[sockindex].cacert);
+  x509_crl_free(&conn->ssl[sockindex].crl);
+  ssl_free(&conn->ssl[sockindex].ssl);
+}
+
+static ssize_t polarssl_recv(struct connectdata *conn,
+                             int num,
+                             char *buf,
+                             size_t buffersize,
+                             CURLcode *curlcode)
+{
+  int ret = -1;
+  ssize_t len = -1;
+
+  memset(buf, 0, buffersize);
+  ret = ssl_read(&conn->ssl[num].ssl, (unsigned char *)buf, buffersize);
+
+  if(ret <= 0) {
+    if(ret == POLARSSL_ERR_SSL_PEER_CLOSE_NOTIFY)
+      return 0;
+
+    *curlcode = (ret == POLARSSL_ERR_NET_WANT_READ) ?
+      CURLE_AGAIN : CURLE_RECV_ERROR;
+    return -1;
+  }
+
+  len = ret;
+
+  return len;
+}
+
+void Curl_polarssl_session_free(void *ptr)
+{
+  free(ptr);
+}
+
+size_t Curl_polarssl_version(char *buffer, size_t size)
+{
+  unsigned int version = version_get_number();
+  return snprintf(buffer, size, "PolarSSL/%d.%d.%d", version>>24,
+                  (version>>16)&0xff, (version>>8)&0xff);
+}
+
+static CURLcode
+polarssl_connect_common(struct connectdata *conn,
+                        int sockindex,
+                        bool nonblocking,
+                        bool *done)
+{
+  CURLcode retcode;
+  struct SessionHandle *data = conn->data;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  curl_socket_t sockfd = conn->sock[sockindex];
+  long timeout_ms;
+  int what;
+
+  /* check if the connection has already been established */
+  if(ssl_connection_complete == connssl->state) {
+    *done = TRUE;
+    return CURLE_OK;
+  }
+
+  if(ssl_connect_1==connssl->connecting_state) {
+    /* Find out how much more time we're allowed */
+    timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+    if(timeout_ms < 0) {
+      /* no need to continue if time already is up */
+      failf(data, "SSL connection timeout");
+      return CURLE_OPERATION_TIMEDOUT;
+    }
+    retcode = polarssl_connect_step1(conn, sockindex);
+    if(retcode)
+      return retcode;
+  }
+
+  while(ssl_connect_2 == connssl->connecting_state ||
+        ssl_connect_2_reading == connssl->connecting_state ||
+        ssl_connect_2_writing == connssl->connecting_state) {
+
+    /* check allowed time left */
+    timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+    if(timeout_ms < 0) {
+      /* no need to continue if time already is up */
+      failf(data, "SSL connection timeout");
+      return CURLE_OPERATION_TIMEDOUT;
+    }
+
+    /* if ssl is expecting something, check if it's available. */
+    if(connssl->connecting_state == ssl_connect_2_reading
+       || connssl->connecting_state == ssl_connect_2_writing) {
+
+      curl_socket_t writefd = ssl_connect_2_writing==
+        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+      curl_socket_t readfd = ssl_connect_2_reading==
+        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+
+      what = Curl_socket_ready(readfd, writefd, nonblocking?0:timeout_ms);
+      if(what < 0) {
+        /* fatal error */
+        failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+        return CURLE_SSL_CONNECT_ERROR;
+      }
+      else if(0 == what) {
+        if(nonblocking) {
+          *done = FALSE;
+          return CURLE_OK;
+        }
+        else {
+          /* timeout */
+          failf(data, "SSL connection timeout");
+          return CURLE_OPERATION_TIMEDOUT;
+        }
+      }
+      /* socket is readable or writable */
+    }
+
+    /* Run transaction, and return to the caller if it failed or if
+     * this connection is part of a multi handle and this loop would
+     * execute again. This permits the owner of a multi handle to
+     * abort a connection attempt before step2 has completed while
+     * ensuring that a client using select() or epoll() will always
+     * have a valid fdset to wait on.
+     */
+    retcode = polarssl_connect_step2(conn, sockindex);
+    if(retcode || (nonblocking &&
+                   (ssl_connect_2 == connssl->connecting_state ||
+                    ssl_connect_2_reading == connssl->connecting_state ||
+                    ssl_connect_2_writing == connssl->connecting_state)))
+      return retcode;
+
+  } /* repeat step2 until all transactions are done. */
+
+  if(ssl_connect_3==connssl->connecting_state) {
+    retcode = polarssl_connect_step3(conn, sockindex);
+    if(retcode)
+      return retcode;
+  }
+
+  if(ssl_connect_done==connssl->connecting_state) {
+    connssl->state = ssl_connection_complete;
+    conn->recv[sockindex] = polarssl_recv;
+    conn->send[sockindex] = polarssl_send;
+    *done = TRUE;
+  }
+  else
+    *done = FALSE;
+
+  /* Reset our connect state machine */
+  connssl->connecting_state = ssl_connect_1;
+
+  return CURLE_OK;
+}
+
+CURLcode
+Curl_polarssl_connect_nonblocking(struct connectdata *conn,
+                                int sockindex,
+                                bool *done)
+{
+  return polarssl_connect_common(conn, sockindex, TRUE, done);
+}
+
+
+CURLcode
+Curl_polarssl_connect(struct connectdata *conn,
+                    int sockindex)
+{
+  CURLcode retcode;
+  bool done = FALSE;
+
+  retcode = polarssl_connect_common(conn, sockindex, FALSE, &done);
+  if(retcode)
+    return retcode;
+
+  DEBUGASSERT(done);
+
+  return CURLE_OK;
+}
+
+#endif
diff --git a/lib/curl_pop3.c b/lib/curl_pop3.c
new file mode 100644 (file)
index 0000000..0d157f0
--- /dev/null
@@ -0,0 +1,1764 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * RFC1734 POP3 Authentication
+ * RFC1939 POP3 protocol
+ * RFC2195 CRAM-MD5 authentication
+ * RFC2384 POP URL Scheme
+ * RFC2449 POP3 Extension Mechanism
+ * RFC2595 Using TLS with IMAP, POP3 and ACAP
+ * RFC2831 DIGEST-MD5 authentication
+ * RFC4616 PLAIN authentication
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_POP3
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_UTSNAME_H
+#include <sys/utsname.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
+#undef in_addr_t
+#define in_addr_t unsigned long
+#endif
+
+#include <curl/curl.h>
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_if2ip.h"
+#include "curl_hostip.h"
+#include "curl_progress.h"
+#include "curl_transfer.h"
+#include "curl_escape.h"
+#include "curl_http.h" /* for HTTP proxy tunnel stuff */
+#include "curl_socks.h"
+#include "curl_pop3.h"
+
+#include "curl_strtoofft.h"
+#include "curl_strequal.h"
+#include "curl_sslgen.h"
+#include "curl_connect.h"
+#include "curl_strerror.h"
+#include "curl_select.h"
+#include "curl_multiif.h"
+#include "curl_url.h"
+#include "curl_rawstr.h"
+#include "curl_sasl.h"
+#include "curl_md5.h"
+#include "curl_warnless.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/* Local API functions */
+static CURLcode pop3_parse_url_path(struct connectdata *conn);
+static CURLcode pop3_parse_custom_request(struct connectdata *conn);
+static CURLcode pop3_regular_transfer(struct connectdata *conn, bool *done);
+static CURLcode pop3_do(struct connectdata *conn, bool *done);
+static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
+                          bool premature);
+static CURLcode pop3_connect(struct connectdata *conn, bool *done);
+static CURLcode pop3_disconnect(struct connectdata *conn, bool dead);
+static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done);
+static int pop3_getsock(struct connectdata *conn,
+                        curl_socket_t *socks,
+                        int numsocks);
+static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done);
+static CURLcode pop3_setup_connection(struct connectdata *conn);
+
+/*
+ * POP3 protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_pop3 = {
+  "POP3",                           /* scheme */
+  pop3_setup_connection,            /* setup_connection */
+  pop3_do,                          /* do_it */
+  pop3_done,                        /* done */
+  ZERO_NULL,                        /* do_more */
+  pop3_connect,                     /* connect_it */
+  pop3_multi_statemach,             /* connecting */
+  pop3_doing,                       /* doing */
+  pop3_getsock,                     /* proto_getsock */
+  pop3_getsock,                     /* doing_getsock */
+  ZERO_NULL,                        /* domore_getsock */
+  ZERO_NULL,                        /* perform_getsock */
+  pop3_disconnect,                  /* disconnect */
+  ZERO_NULL,                        /* readwrite */
+  PORT_POP3,                        /* defport */
+  CURLPROTO_POP3,                   /* protocol */
+  PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
+};
+
+#ifdef USE_SSL
+/*
+ * POP3S protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_pop3s = {
+  "POP3S",                          /* scheme */
+  pop3_setup_connection,            /* setup_connection */
+  pop3_do,                          /* do_it */
+  pop3_done,                        /* done */
+  ZERO_NULL,                        /* do_more */
+  pop3_connect,                     /* connect_it */
+  pop3_multi_statemach,             /* connecting */
+  pop3_doing,                       /* doing */
+  pop3_getsock,                     /* proto_getsock */
+  pop3_getsock,                     /* doing_getsock */
+  ZERO_NULL,                        /* domore_getsock */
+  ZERO_NULL,                        /* perform_getsock */
+  pop3_disconnect,                  /* disconnect */
+  ZERO_NULL,                        /* readwrite */
+  PORT_POP3S,                       /* defport */
+  CURLPROTO_POP3 | CURLPROTO_POP3S, /* protocol */
+  PROTOPT_CLOSEACTION | PROTOPT_SSL
+  | PROTOPT_NOURLQUERY              /* flags */
+};
+#endif
+
+#ifndef CURL_DISABLE_HTTP
+/*
+ * HTTP-proxyed POP3 protocol handler.
+ */
+
+static const struct Curl_handler Curl_handler_pop3_proxy = {
+  "POP3",                               /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  Curl_http,                            /* do_it */
+  Curl_http_done,                       /* done */
+  ZERO_NULL,                            /* do_more */
+  ZERO_NULL,                            /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  PORT_POP3,                            /* defport */
+  CURLPROTO_HTTP,                       /* protocol */
+  PROTOPT_NONE                          /* flags */
+};
+
+#ifdef USE_SSL
+/*
+ * HTTP-proxyed POP3S protocol handler.
+ */
+
+static const struct Curl_handler Curl_handler_pop3s_proxy = {
+  "POP3S",                              /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  Curl_http,                            /* do_it */
+  Curl_http_done,                       /* done */
+  ZERO_NULL,                            /* do_more */
+  ZERO_NULL,                            /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  PORT_POP3S,                           /* defport */
+  CURLPROTO_HTTP,                       /* protocol */
+  PROTOPT_NONE                          /* flags */
+};
+#endif
+#endif
+
+/* Function that checks for an ending pop3 status code at the start of the
+   given string, but also detects the APOP timestamp from the server greeting
+   as well as the supported authentication types and allowed SASL mechanisms
+   from the CAPA response. */
+static int pop3_endofresp(struct pingpong *pp, int *resp)
+{
+  char *line = pp->linestart_resp;
+  size_t len = strlen(pp->linestart_resp);
+  struct connectdata *conn = pp->conn;
+  struct pop3_conn *pop3c = &conn->proto.pop3c;
+  size_t wordlen;
+  size_t i;
+
+  /* Do we have an error response? */
+  if(len >= 4 && !memcmp("-ERR", line, 4)) {
+    *resp = '-';
+
+    return FALSE;
+  }
+
+  /* Are we processing servergreet responses */
+  if(pop3c->state == POP3_SERVERGREET) {
+    /* Look for the APOP timestamp */
+    if(len >= 3 && line[len - 3] == '>') {
+      for(i = 0; i < len - 3; ++i) {
+        if(line[i] == '<') {
+          /* Calculate the length of the timestamp */
+          size_t timestamplen = len - 2 - i;
+
+          /* Allocate some memory for the timestamp */
+          pop3c->apoptimestamp = (char *)calloc(1, timestamplen + 1);
+
+          if(!pop3c->apoptimestamp)
+            break;
+
+          /* Copy the timestamp */
+          memcpy(pop3c->apoptimestamp, line + i, timestamplen);
+          pop3c->apoptimestamp[timestamplen] = '\0';
+          break;
+        }
+      }
+    }
+  }
+  /* Are we processing CAPA command responses? */
+  else if(pop3c->state == POP3_CAPA) {
+
+    /* Do we have the terminating character? */
+    if(len >= 1 && !memcmp(line, ".", 1)) {
+      *resp = '+';
+
+      return TRUE;
+    }
+
+    /* Does the server support clear text? */
+    if(len >= 4 && !memcmp(line, "USER", 4)) {
+      pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
+      return FALSE;
+    }
+
+    /* Does the server support APOP? */
+    if(len >= 4 && !memcmp(line, "APOP", 4)) {
+      pop3c->authtypes |= POP3_TYPE_APOP;
+      return FALSE;
+    }
+
+    /* Does the server support SASL? */
+    if(len < 4 || memcmp(line, "SASL", 4))
+      return FALSE;
+
+    pop3c->authtypes |= POP3_TYPE_SASL;
+
+    /* Advance past the SASL keyword */
+    line += 4;
+    len -= 4;
+
+    /* Loop through the data line */
+    for(;;) {
+      while(len &&
+            (*line == ' ' || *line == '\t' ||
+             *line == '\r' || *line == '\n')) {
+
+        if(*line == '\n')
+          return FALSE;
+
+        line++;
+        len--;
+      }
+
+      if(!len)
+        break;
+
+      /* Extract the word */
+      for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
+            line[wordlen] != '\t' && line[wordlen] != '\r' &&
+            line[wordlen] != '\n';)
+        wordlen++;
+
+      /* Test the word for a matching authentication mechanism */
+      if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
+        pop3c->authmechs |= SASL_MECH_LOGIN;
+      else if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
+        pop3c->authmechs |= SASL_MECH_PLAIN;
+      else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
+        pop3c->authmechs |= SASL_MECH_CRAM_MD5;
+      else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
+        pop3c->authmechs |= SASL_MECH_DIGEST_MD5;
+      else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
+        pop3c->authmechs |= SASL_MECH_GSSAPI;
+      else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
+        pop3c->authmechs |= SASL_MECH_EXTERNAL;
+      else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
+        pop3c->authmechs |= SASL_MECH_NTLM;
+
+      line += wordlen;
+      len -= wordlen;
+    }
+  }
+
+  if((len < 1 || memcmp("+", line, 1)) &&
+     (len < 3 || memcmp("+OK", line, 3)))
+  return FALSE; /* Nothing for us */
+
+  /* Otherwise it's a positive response */
+  *resp = '+';
+
+  return TRUE;
+}
+
+/* This is the ONLY way to change POP3 state! */
+static void state(struct connectdata *conn, pop3state newstate)
+{
+  struct pop3_conn *pop3c = &conn->proto.pop3c;
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+  /* for debug purposes */
+  static const char * const names[] = {
+    "STOP",
+    "SERVERGREET",
+    "STARTTLS",
+    "CAPA",
+    "AUTH_PLAIN",
+    "AUTH_LOGIN",
+    "AUTH_LOGIN_PASSWD",
+    "AUTH_CRAMMD5",
+    "AUTH_DIGESTMD5",
+    "AUTH_DIGESTMD5_RESP",
+    "AUTH_NTLM",
+    "AUTH_NTLM_TYPE2MSG",
+    "AUTH",
+    "APOP",
+    "USER",
+    "PASS",
+    "COMMAND",
+    "QUIT",
+    /* LAST */
+  };
+
+  if(pop3c->state != newstate)
+    infof(conn->data, "POP3 %p state change from %s to %s\n",
+          pop3c, names[pop3c->state], names[newstate]);
+#endif
+
+  pop3c->state = newstate;
+}
+
+static CURLcode pop3_state_capa(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+  struct pop3_conn *pop3c = &conn->proto.pop3c;
+
+  pop3c->authmechs = 0;         /* No known authentication mechanisms yet */
+  pop3c->authused = 0;          /* Clear the authentication mechanism used */
+
+  /* Check we have a username and password to authenticate with and end the
+     connect phase if we don't */
+  if(!conn->bits.user_passwd) {
+    state(conn, POP3_STOP);
+
+    return result;
+  }
+
+  /* Send the CAPA command */
+  result = Curl_pp_sendf(&pop3c->pp, "CAPA");
+
+  if(result)
+    return result;
+
+  state(conn, POP3_CAPA);
+
+  return CURLE_OK;
+}
+
+static CURLcode pop3_state_user(struct connectdata *conn)
+{
+  CURLcode result;
+  struct FTP *pop3 = conn->data->state.proto.pop3;
+
+  /* Send the USER command */
+  result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s",
+                         pop3->user ? pop3->user : "");
+  if(result)
+    return result;
+
+  state(conn, POP3_USER);
+
+  return CURLE_OK;
+}
+
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+static CURLcode pop3_state_apop(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+  struct pop3_conn *pop3c = &conn->proto.pop3c;
+  size_t i;
+  MD5_context *ctxt;
+  unsigned char digest[MD5_DIGEST_LEN];
+  char secret[2 * MD5_DIGEST_LEN + 1];
+
+  ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
+  if(!ctxt)
+    return CURLE_OUT_OF_MEMORY;
+
+  Curl_MD5_update(ctxt, (const unsigned char *) pop3c->apoptimestamp,
+                  curlx_uztoui(strlen(pop3c->apoptimestamp)));
+
+  Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd,
+                  curlx_uztoui(strlen(conn->passwd)));
+
+  /* Finalise the digest */
+  Curl_MD5_final(ctxt, digest);
+
+  /* Convert the calculated 16 octet digest into a 32 byte hex string */
+  for(i = 0; i < MD5_DIGEST_LEN; i++)
+    snprintf(&secret[2 * i], 3, "%02x", digest[i]);
+
+  result = Curl_pp_sendf(&pop3c->pp, "APOP %s %s", conn->user, secret);
+
+  if(!result)
+    state(conn, POP3_APOP);
+
+  return result;
+}
+#endif
+
+static CURLcode pop3_authenticate(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+  struct pop3_conn *pop3c = &conn->proto.pop3c;
+  const char *mech = NULL;
+  pop3state authstate = POP3_STOP;
+
+  /* Check supported authentication mechanisms by decreasing order of
+     security */
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+  if(pop3c->authmechs & SASL_MECH_DIGEST_MD5) {
+    mech = "DIGEST-MD5";
+    authstate = POP3_AUTH_DIGESTMD5;
+    pop3c->authused = SASL_MECH_DIGEST_MD5;
+  }
+  else if(pop3c->authmechs & SASL_MECH_CRAM_MD5) {
+    mech = "CRAM-MD5";
+    authstate = POP3_AUTH_CRAMMD5;
+    pop3c->authused = SASL_MECH_CRAM_MD5;
+  }
+  else
+#endif
+#ifdef USE_NTLM
+  if(pop3c->authmechs & SASL_MECH_NTLM) {
+    mech = "NTLM";
+    authstate = POP3_AUTH_NTLM;
+    pop3c->authused = SASL_MECH_NTLM;
+  }
+  else
+#endif
+  if(pop3c->authmechs & SASL_MECH_LOGIN) {
+    mech = "LOGIN";
+    authstate = POP3_AUTH_LOGIN;
+    pop3c->authused = SASL_MECH_LOGIN;
+  }
+  else if(pop3c->authmechs & SASL_MECH_PLAIN) {
+    mech = "PLAIN";
+    authstate = POP3_AUTH_PLAIN;
+    pop3c->authused = SASL_MECH_PLAIN;
+  }
+  else {
+    infof(conn->data, "No known SASL authentication mechanisms supported!\n");
+    result = CURLE_LOGIN_DENIED; /* Other mechanisms not supported */
+  }
+
+  if(!result) {
+    result = Curl_pp_sendf(&pop3c->pp, "AUTH %s", mech);
+
+    if(!result)
+      state(conn, authstate);
+  }
+
+  return result;
+}
+
+/* For the POP3 "protocol connect" and "doing" phases only */
+static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
+                        int numsocks)
+{
+  return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks);
+}
+
+#ifdef USE_SSL
+static void pop3_to_pop3s(struct connectdata *conn)
+{
+  conn->handler = &Curl_handler_pop3s;
+}
+#else
+#define pop3_to_pop3s(x) Curl_nop_stmt
+#endif
+
+/* For the initial server greeting */
+static CURLcode pop3_state_servergreet_resp(struct connectdata *conn,
+                                            int pop3code,
+                                            pop3state instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  struct pop3_conn *pop3c = &conn->proto.pop3c;
+
+  (void)instate; /* no use for this yet */
+
+  if(pop3code != '+') {
+    failf(data, "Got unexpected pop3-server response");
+    return CURLE_FTP_WEIRD_SERVER_REPLY;
+  }
+
+  if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
+    /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
+       to TLS connection now */
+    result = Curl_pp_sendf(&pop3c->pp, "STLS");
+    state(conn, POP3_STARTTLS);
+  }
+  else
+    result = pop3_state_capa(conn);
+
+  return result;
+}
+
+/* For STARTTLS responses */
+static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
+                                         int pop3code,
+                                         pop3state instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+
+  (void)instate; /* no use for this yet */
+
+  if(pop3code != '+') {
+    if(data->set.use_ssl != CURLUSESSL_TRY) {
+      failf(data, "STARTTLS denied. %c", pop3code);
+      result = CURLE_USE_SSL_FAILED;
+    }
+    else
+      result = pop3_state_capa(conn);
+  }
+  else {
+    /* Curl_ssl_connect is BLOCKING */
+    result = Curl_ssl_connect(conn, FIRSTSOCKET);
+    if(CURLE_OK == result) {
+      pop3_to_pop3s(conn);
+      result = pop3_state_capa(conn);
+    }
+  }
+
+  return result;
+}
+
+/* For CAPA responses */
+static CURLcode pop3_state_capa_resp(struct connectdata *conn,
+                                     int pop3code,
+                                     pop3state instate)
+{
+  CURLcode result = CURLE_OK;
+
+  (void)instate; /* no use for this yet */
+
+  if(pop3code != '+')
+    result = pop3_state_user(conn);
+  else {
+    /* Check supported authentication types by decreasing order of security */
+    if(conn->proto.pop3c.authtypes & POP3_TYPE_SASL)
+      result = pop3_authenticate(conn);
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+    else if(conn->proto.pop3c.authtypes & POP3_TYPE_APOP)
+      result = pop3_state_apop(conn);
+#endif
+    else if(conn->proto.pop3c.authtypes & POP3_TYPE_CLEARTEXT)
+      result = pop3_state_user(conn);
+    else {
+      infof(conn->data, "No known authentication types supported!\n");
+      result = CURLE_LOGIN_DENIED; /* Other types not supported */
+    }
+  }
+
+  return result;
+}
+
+/* For AUTH PLAIN responses */
+static CURLcode pop3_state_auth_plain_resp(struct connectdata *conn,
+                                           int pop3code,
+                                           pop3state instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  size_t len = 0;
+  char *plainauth = NULL;
+
+  (void)instate; /* no use for this yet */
+
+  if(pop3code != '+') {
+    failf(data, "Access denied. %c", pop3code);
+    result = CURLE_LOGIN_DENIED;
+  }
+  else {
+    /* Create the authorisation message */
+    result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
+                                            &plainauth, &len);
+
+    /* Send the message */
+    if(!result) {
+      if(plainauth) {
+        result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", plainauth);
+
+        if(!result)
+          state(conn, POP3_AUTH);
+      }
+
+      Curl_safefree(plainauth);
+    }
+  }
+
+  return result;
+}
+
+/* For AUTH LOGIN responses */
+static CURLcode pop3_state_auth_login_resp(struct connectdata *conn,
+                                           int pop3code,
+                                           pop3state instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  size_t len = 0;
+  char *authuser = NULL;
+
+  (void)instate; /* no use for this yet */
+
+  if(pop3code != '+') {
+    failf(data, "Access denied: %d", pop3code);
+    result = CURLE_LOGIN_DENIED;
+  }
+  else {
+    /* Create the user message */
+    result = Curl_sasl_create_login_message(data, conn->user,
+                                            &authuser, &len);
+
+    /* Send the user */
+    if(!result) {
+      if(authuser) {
+        result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authuser);
+
+        if(!result)
+          state(conn, POP3_AUTH_LOGIN_PASSWD);
+      }
+
+      Curl_safefree(authuser);
+    }
+  }
+
+  return result;
+}
+
+/* For AUTH LOGIN user entry responses */
+static CURLcode pop3_state_auth_login_password_resp(struct connectdata *conn,
+                                                    int pop3code,
+                                                    pop3state instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  size_t len = 0;
+  char *authpasswd = NULL;
+
+  (void)instate; /* no use for this yet */
+
+  if(pop3code != '+') {
+    failf(data, "Access denied: %d", pop3code);
+    result = CURLE_LOGIN_DENIED;
+  }
+  else {
+    /* Create the password message */
+    result = Curl_sasl_create_login_message(data, conn->passwd,
+                                            &authpasswd, &len);
+
+    /* Send the password */
+    if(!result) {
+      if(authpasswd) {
+        result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authpasswd);
+
+        if(!result)
+          state(conn, POP3_AUTH);
+      }
+
+      Curl_safefree(authpasswd);
+    }
+  }
+
+  return result;
+}
+
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+/* For AUTH CRAM-MD5 responses */
+static CURLcode pop3_state_auth_cram_resp(struct connectdata *conn,
+                                          int pop3code,
+                                          pop3state instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  char *chlg64 = data->state.buffer;
+  size_t len = 0;
+  char *rplyb64 = NULL;
+
+  (void)instate; /* no use for this yet */
+
+  if(pop3code != '+') {
+    failf(data, "Access denied: %d", pop3code);
+    return CURLE_LOGIN_DENIED;
+  }
+
+  /* Get the challenge */
+  for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
+    ;
+
+  /* Terminate the challenge */
+  if(*chlg64 != '=') {
+    for(len = strlen(chlg64); len--;)
+      if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
+         chlg64[len] != '\t')
+        break;
+
+    if(++len) {
+      chlg64[len] = '\0';
+    }
+  }
+
+  /* Create the response message */
+  result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
+                                             conn->passwd, &rplyb64, &len);
+
+  /* Send the response */
+  if(!result) {
+    if(rplyb64) {
+      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64);
+
+      if(!result)
+        state(conn, POP3_AUTH);
+    }
+
+    Curl_safefree(rplyb64);
+  }
+
+  return result;
+}
+
+/* For AUTH DIGEST-MD5 challenge responses */
+static CURLcode pop3_state_auth_digest_resp(struct connectdata *conn,
+                                            int pop3code,
+                                            pop3state instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  char *chlg64 = data->state.buffer;
+  size_t len = 0;
+  char *rplyb64 = NULL;
+
+  (void)instate; /* no use for this yet */
+
+  if(pop3code != '+') {
+    failf(data, "Access denied: %d", pop3code);
+    return CURLE_LOGIN_DENIED;
+  }
+
+  /* Get the challenge */
+  for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
+    ;
+
+  /* Create the response message */
+  result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
+                                               conn->passwd, "pop",
+                                               &rplyb64, &len);
+
+  /* Send the response */
+  if(!result) {
+    if(rplyb64) {
+      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64);
+
+      if(!result)
+        state(conn, POP3_AUTH_DIGESTMD5_RESP);
+    }
+
+    Curl_safefree(rplyb64);
+  }
+
+  return result;
+}
+
+/* For AUTH DIGEST-MD5 challenge-response responses */
+static CURLcode pop3_state_auth_digest_resp_resp(struct connectdata *conn,
+                                                 int pop3code,
+                                                 pop3state instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+
+  (void)instate; /* no use for this yet */
+
+  if(pop3code != '+') {
+    failf(data, "Authentication failed: %d", pop3code);
+    result = CURLE_LOGIN_DENIED;
+  }
+  else {
+    /* Send an empty response */
+    result = Curl_pp_sendf(&conn->proto.pop3c.pp, "");
+
+    if(!result)
+      state(conn, POP3_AUTH);
+  }
+
+  return result;
+}
+#endif
+
+#ifdef USE_NTLM
+/* For AUTH NTLM responses */
+static CURLcode pop3_state_auth_ntlm_resp(struct connectdata *conn,
+                                          int pop3code,
+                                          pop3state instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  size_t len = 0;
+  char *type1msg = NULL;
+
+  (void)instate; /* no use for this yet */
+
+  if(pop3code != '+') {
+    failf(data, "Access denied: %d", pop3code);
+    result = CURLE_LOGIN_DENIED;
+  }
+  else {
+    /* Create the type-1 message */
+    result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
+                                                 &conn->ntlm,
+                                                 &type1msg, &len);
+
+    /* Send the message */
+    if(!result) {
+      if(type1msg) {
+        result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type1msg);
+
+        if(!result)
+          state(conn, POP3_AUTH_NTLM_TYPE2MSG);
+      }
+
+      Curl_safefree(type1msg);
+    }
+  }
+
+  return result;
+}
+
+/* For NTLM type-2 responses (sent in reponse to our type-1 message) */
+static CURLcode pop3_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
+                                                   int pop3code,
+                                                   pop3state instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  size_t len = 0;
+  char *type3msg = NULL;
+
+  (void)instate; /* no use for this yet */
+
+  if(pop3code != '+') {
+    failf(data, "Access denied: %d", pop3code);
+    result = CURLE_LOGIN_DENIED;
+  }
+  else {
+    /* Create the type-3 message */
+    result = Curl_sasl_create_ntlm_type3_message(data,
+                                                 data->state.buffer + 2,
+                                                 conn->user, conn->passwd,
+                                                 &conn->ntlm,
+                                                 &type3msg, &len);
+
+    /* Send the message */
+    if(!result) {
+      if(type3msg) {
+        result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type3msg);
+
+        if(!result)
+          state(conn, POP3_AUTH);
+      }
+
+      Curl_safefree(type3msg);
+    }
+  }
+
+  return result;
+}
+#endif
+
+/* For final responses to the AUTH sequence */
+static CURLcode pop3_state_auth_final_resp(struct connectdata *conn,
+                                           int pop3code,
+                                           pop3state instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+
+  (void)instate; /* no use for this yet */
+
+  if(pop3code != '+') {
+    failf(data, "Authentication failed: %d", pop3code);
+    result = CURLE_LOGIN_DENIED;
+  }
+
+  /* End of connect phase */
+  state(conn, POP3_STOP);
+
+  return result;
+}
+
+static CURLcode pop3_state_apop_resp(struct connectdata *conn,
+                                     int pop3code,
+                                     pop3state instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+
+  (void)instate; /* no use for this yet */
+
+  if(pop3code != '+') {
+    failf(data, "Authentication failed: %d", pop3code);
+    result = CURLE_LOGIN_DENIED;
+  }
+
+  /* End of connect phase */
+  state(conn, POP3_STOP);
+
+  return result;
+}
+
+/* For USER responses */
+static CURLcode pop3_state_user_resp(struct connectdata *conn,
+                                     int pop3code,
+                                     pop3state instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  struct FTP *pop3 = data->state.proto.pop3;
+
+  (void)instate; /* no use for this yet */
+
+  if(pop3code != '+') {
+    failf(data, "Access denied. %c", pop3code);
+    result = CURLE_LOGIN_DENIED;
+  }
+  else
+    /* Send the PASS command */
+    result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s",
+                           pop3->passwd ? pop3->passwd : "");
+  if(result)
+    return result;
+
+  state(conn, POP3_PASS);
+
+  return result;
+}
+
+/* For PASS responses */
+static CURLcode pop3_state_pass_resp(struct connectdata *conn,
+                                     int pop3code,
+                                     pop3state instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+
+  (void)instate; /* no use for this yet */
+
+  if(pop3code != '+') {
+    failf(data, "Access denied. %c", pop3code);
+    result = CURLE_LOGIN_DENIED;
+  }
+
+  /* End of connect phase */
+  state(conn, POP3_STOP);
+
+  return result;
+}
+
+/* Start the DO phase for the command */
+static CURLcode pop3_command(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+  struct pop3_conn *pop3c = &conn->proto.pop3c;
+  const char *command = NULL;
+
+  /* Calculate the default command */
+  if(pop3c->mailbox[0] == '\0' || conn->data->set.ftp_list_only) {
+    command = "LIST";
+
+    if(pop3c->mailbox[0] != '\0') {
+      /* Message specific LIST so skip the BODY transfer */
+      struct FTP *pop3 = conn->data->state.proto.pop3;
+      pop3->transfer = FTPTRANSFER_INFO;
+    }
+  }
+  else
+    command = "RETR";
+
+  /* Send the command */
+  if(pop3c->mailbox[0] != '\0')
+    result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s %s",
+                           (pop3c->custom && pop3c->custom[0] != '\0' ?
+                            pop3c->custom : command), pop3c->mailbox);
+  else
+    result = Curl_pp_sendf(&conn->proto.pop3c.pp,
+                           (pop3c->custom && pop3c->custom[0] != '\0' ?
+                            pop3c->custom : command));
+
+  if(result)
+    return result;
+
+  state(conn, POP3_COMMAND);
+
+  return result;
+}
+
+/* For command responses */
+static CURLcode pop3_state_command_resp(struct connectdata *conn,
+                                        int pop3code,
+                                        pop3state instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  struct FTP *pop3 = data->state.proto.pop3;
+  struct pop3_conn *pop3c = &conn->proto.pop3c;
+  struct pingpong *pp = &pop3c->pp;
+
+  (void)instate; /* no use for this yet */
+
+  if(pop3code != '+') {
+    state(conn, POP3_STOP);
+    return CURLE_RECV_ERROR;
+  }
+
+  /* This 'OK' line ends with a CR LF pair which is the two first bytes of the
+     EOB string so count this is two matching bytes. This is necessary to make
+     the code detect the EOB if the only data than comes now is %2e CR LF like
+     when there is no body to return. */
+  pop3c->eob = 2;
+
+  /* But since this initial CR LF pair is not part of the actual body, we set
+     the strip counter here so that these bytes won't be delivered. */
+  pop3c->strip = 2;
+
+  /* POP3 download */
+  Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, pop3->bytecountp,
+                      -1, NULL); /* no upload here */
+
+  if(pp->cache) {
+    /* The header "cache" contains a bunch of data that is actually body
+       content so send it as such. Note that there may even be additional
+       "headers" after the body */
+
+    if(!data->set.opt_no_body) {
+      result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
+      if(result)
+        return result;
+    }
+
+    /* Free the cache */
+    Curl_safefree(pp->cache);
+
+    /* Reset the cache size */
+    pp->cache_size = 0;
+  }
+
+  /* End of do phase */
+  state(conn, POP3_STOP);
+
+  return result;
+}
+
+static CURLcode pop3_statemach_act(struct connectdata *conn)
+{
+  CURLcode result;
+  curl_socket_t sock = conn->sock[FIRSTSOCKET];
+  int pop3code;
+  struct pop3_conn *pop3c = &conn->proto.pop3c;
+  struct pingpong *pp = &pop3c->pp;
+  size_t nread = 0;
+
+  /* Flush any data that needs to be sent */
+  if(pp->sendleft)
+    return Curl_pp_flushsend(pp);
+
+  /* Read the response from the server */
+  result = Curl_pp_readresp(sock, pp, &pop3code, &nread);
+  if(result)
+    return result;
+
+  if(pop3code) {
+    /* We have now received a full POP3 server response */
+    switch(pop3c->state) {
+    case POP3_SERVERGREET:
+      result = pop3_state_servergreet_resp(conn, pop3code, pop3c->state);
+      break;
+
+    case POP3_STARTTLS:
+      result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
+      break;
+
+    case POP3_CAPA:
+      result = pop3_state_capa_resp(conn, pop3code, pop3c->state);
+      break;
+
+    case POP3_AUTH_PLAIN:
+      result = pop3_state_auth_plain_resp(conn, pop3code, pop3c->state);
+      break;
+
+    case POP3_AUTH_LOGIN:
+      result = pop3_state_auth_login_resp(conn, pop3code, pop3c->state);
+      break;
+
+    case POP3_AUTH_LOGIN_PASSWD:
+      result = pop3_state_auth_login_password_resp(conn, pop3code,
+                                                   pop3c->state);
+      break;
+
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+    case POP3_AUTH_CRAMMD5:
+      result = pop3_state_auth_cram_resp(conn, pop3code, pop3c->state);
+      break;
+
+    case POP3_AUTH_DIGESTMD5:
+      result = pop3_state_auth_digest_resp(conn, pop3code, pop3c->state);
+      break;
+
+    case POP3_AUTH_DIGESTMD5_RESP:
+      result = pop3_state_auth_digest_resp_resp(conn, pop3code, pop3c->state);
+      break;
+#endif
+
+#ifdef USE_NTLM
+    case POP3_AUTH_NTLM:
+      result = pop3_state_auth_ntlm_resp(conn, pop3code, pop3c->state);
+      break;
+
+    case POP3_AUTH_NTLM_TYPE2MSG:
+      result = pop3_state_auth_ntlm_type2msg_resp(conn, pop3code,
+                                                  pop3c->state);
+      break;
+#endif
+
+    case POP3_AUTH:
+      result = pop3_state_auth_final_resp(conn, pop3code, pop3c->state);
+      break;
+
+    case POP3_APOP:
+      result = pop3_state_apop_resp(conn, pop3code, pop3c->state);
+      break;
+
+    case POP3_USER:
+      result = pop3_state_user_resp(conn, pop3code, pop3c->state);
+      break;
+
+    case POP3_PASS:
+      result = pop3_state_pass_resp(conn, pop3code, pop3c->state);
+      break;
+
+    case POP3_COMMAND:
+      result = pop3_state_command_resp(conn, pop3code, pop3c->state);
+      break;
+
+    case POP3_QUIT:
+      /* fallthrough, just stop! */
+    default:
+      /* internal error */
+      state(conn, POP3_STOP);
+      break;
+    }
+  }
+
+  return result;
+}
+
+/* Called repeatedly until done from curl_multi.c */
+static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done)
+{
+  struct pop3_conn *pop3c = &conn->proto.pop3c;
+  CURLcode result = Curl_pp_multi_statemach(&pop3c->pp);
+
+  *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE;
+
+  return result;
+}
+
+static CURLcode pop3_easy_statemach(struct connectdata *conn)
+{
+  struct pop3_conn *pop3c = &conn->proto.pop3c;
+  struct pingpong *pp = &pop3c->pp;
+  CURLcode result = CURLE_OK;
+
+  while(pop3c->state != POP3_STOP) {
+    result = Curl_pp_easy_statemach(pp);
+    if(result)
+      break;
+  }
+
+  return result;
+}
+
+/* Allocate and initialize the POP3 struct for the current SessionHandle if
+   required */
+static CURLcode pop3_init(struct connectdata *conn)
+{
+  struct SessionHandle *data = conn->data;
+  struct FTP *pop3 = data->state.proto.pop3;
+
+  if(!pop3) {
+    pop3 = data->state.proto.pop3 = calloc(sizeof(struct FTP), 1);
+    if(!pop3)
+      return CURLE_OUT_OF_MEMORY;
+  }
+
+  /* Get some initial data into the pop3 struct */
+  pop3->bytecountp = &data->req.bytecount;
+
+  /* No need to duplicate user+password, the connectdata struct won't change
+     during a session, but we re-init them here since on subsequent inits
+     since the conn struct may have changed or been replaced.
+  */
+  pop3->user = conn->user;
+  pop3->passwd = conn->passwd;
+
+  return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * pop3_connect()
+ *
+ * This function should do everything that is to be considered a part of the
+ * connection phase.
+ *
+ * The variable 'done' points to will be TRUE if the protocol-layer connect
+ * phase is done when this function returns, or FALSE is not. When called as
+ * a part of the easy interface, it will always be TRUE.
+ */
+static CURLcode pop3_connect(struct connectdata *conn, bool *done)
+{
+  CURLcode result;
+  struct pop3_conn *pop3c = &conn->proto.pop3c;
+  struct SessionHandle *data = conn->data;
+  struct pingpong *pp = &pop3c->pp;
+
+  *done = FALSE; /* default to not done yet */
+
+  /* If there already is a protocol-specific struct allocated for this
+     sessionhandle, deal with it */
+  Curl_reset_reqproto(conn);
+
+  result = pop3_init(conn);
+  if(CURLE_OK != result)
+    return result;
+
+  /* We always support persistent connections on pop3 */
+  conn->bits.close = FALSE;
+
+  pp->response_time = RESP_TIMEOUT; /* set default response time-out */
+  pp->statemach_act = pop3_statemach_act;
+  pp->endofresp = pop3_endofresp;
+  pp->conn = conn;
+
+  if(conn->handler->flags & PROTOPT_SSL) {
+    /* POP3S is simply pop3 with SSL for the control channel */
+    /* so perform the SSL initialization for this socket */
+    result = Curl_ssl_connect(conn, FIRSTSOCKET);
+    if(result)
+      return result;
+  }
+
+  /* Initialise the response reader stuff */
+  Curl_pp_init(pp);
+
+  /* Start off waiting for the server greeting response */
+  state(conn, POP3_SERVERGREET);
+
+  if(data->state.used_interface == Curl_if_multi)
+    result = pop3_multi_statemach(conn, done);
+  else {
+    result = pop3_easy_statemach(conn);
+    if(!result)
+      *done = TRUE;
+  }
+
+  return result;
+}
+
+/***********************************************************************
+ *
+ * pop3_done()
+ *
+ * The DONE function. This does what needs to be done after a single DO has
+ * performed.
+ *
+ * Input argument is already checked for validity.
+ */
+static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
+                          bool premature)
+{
+  struct SessionHandle *data = conn->data;
+  struct FTP *pop3 = data->state.proto.pop3;
+  struct pop3_conn *pop3c = &conn->proto.pop3c;
+  CURLcode result = CURLE_OK;
+
+  (void)premature;
+
+  if(!pop3)
+    /* When the easy handle is removed from the multi while libcurl is still
+     * trying to resolve the host name, it seems that the pop3 struct is not
+     * yet initialized, but the removal action calls Curl_done() which calls
+     * this function. So we simply return success if no pop3 pointer is set.
+     */
+    return CURLE_OK;
+
+  if(status) {
+    conn->bits.close = TRUE; /* marked for closure */
+    result = status;         /* use the already set error code */
+  }
+
+  /* Cleanup our do based variables */
+  Curl_safefree(pop3c->mailbox);
+  Curl_safefree(pop3c->custom);
+
+  /* Clear the transfer mode for the next connection */
+  pop3->transfer = FTPTRANSFER_BODY;
+
+  return result;
+}
+
+/***********************************************************************
+ *
+ * pop3_perform()
+ *
+ * This is the actual DO function for POP3. Get a file/directory according to
+ * the options previously setup.
+ */
+static CURLcode pop3_perform(struct connectdata *conn, bool *connected,
+                             bool *dophase_done)
+{
+  /* This is POP3 and no proxy */
+  CURLcode result = CURLE_OK;
+
+  DEBUGF(infof(conn->data, "DO phase starts\n"));
+
+  if(conn->data->set.opt_no_body) {
+    /* Requested no body means no transfer */
+    struct FTP *pop3 = conn->data->state.proto.pop3;
+    pop3->transfer = FTPTRANSFER_INFO;
+  }
+
+  *dophase_done = FALSE; /* not done yet */
+
+  /* Start the first command in the DO phase */
+  result = pop3_command(conn);
+  if(result)
+    return result;
+
+  /* Run the state-machine */
+  if(conn->data->state.used_interface == Curl_if_multi)
+    result = pop3_multi_statemach(conn, dophase_done);
+  else {
+    result = pop3_easy_statemach(conn);
+    *dophase_done = TRUE; /* with the easy interface we are done here */
+  }
+  *connected = conn->bits.tcpconnect[FIRSTSOCKET];
+
+  if(*dophase_done)
+    DEBUGF(infof(conn->data, "DO phase is complete\n"));
+
+  return result;
+}
+
+/***********************************************************************
+ *
+ * pop3_do()
+ *
+ * This function is registered as 'curl_do' function. It decodes the path
+ * parts etc as a wrapper to the actual DO function (pop3_perform).
+ *
+ * The input argument is already checked for validity.
+ */
+static CURLcode pop3_do(struct connectdata *conn, bool *done)
+{
+  CURLcode retcode = CURLE_OK;
+
+  *done = FALSE; /* default to false */
+
+  /*
+    Since connections can be re-used between SessionHandles, this might be a
+    connection already existing but on a fresh SessionHandle struct so we must
+    make sure we have a good 'struct POP3' to play with. For new connections,
+    the struct POP3 is allocated and setup in the pop3_connect() function.
+  */
+  Curl_reset_reqproto(conn);
+  retcode = pop3_init(conn);
+  if(retcode)
+    return retcode;
+
+  /* Parse the URL path */
+  retcode = pop3_parse_url_path(conn);
+  if(retcode)
+    return retcode;
+
+  /* Parse the custom request */
+  retcode = pop3_parse_custom_request(conn);
+  if(retcode)
+    return retcode;
+
+  retcode = pop3_regular_transfer(conn, done);
+
+  return retcode;
+}
+
+/***********************************************************************
+ *
+ * pop3_quit()
+ *
+ * This should be called before calling sclose().  We should then wait for the
+ * response from the server before returning. The calling code should then try
+ * to close the connection.
+ */
+static CURLcode pop3_quit(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+
+  result = Curl_pp_sendf(&conn->proto.pop3c.pp, "QUIT", NULL);
+  if(result)
+    return result;
+
+  state(conn, POP3_QUIT);
+
+  result = pop3_easy_statemach(conn);
+
+  return result;
+}
+
+/***********************************************************************
+ *
+ * pop3_disconnect()
+ *
+ * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
+ * resources. BLOCKING.
+ */
+static CURLcode pop3_disconnect(struct connectdata *conn,
+                                bool dead_connection)
+{
+  struct pop3_conn *pop3c = &conn->proto.pop3c;
+
+  /* We cannot send quit unconditionally. If this connection is stale or
+     bad in any way, sending quit and waiting around here will make the
+     disconnect wait in vain and cause more problems than we need to */
+
+  /* The POP3 session may or may not have been allocated/setup at this
+     point! */
+  if(!dead_connection && pop3c->pp.conn)
+    (void)pop3_quit(conn); /* ignore errors on the LOGOUT */
+
+  /* Disconnect from the server */
+  Curl_pp_disconnect(&pop3c->pp);
+
+  /* Cleanup the SASL module */
+  Curl_sasl_cleanup(conn, pop3c->authused);
+
+  /* Cleanup our connection based variables */
+  Curl_safefree(pop3c->apoptimestamp);
+
+  return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * pop3_parse_url_path()
+ *
+ * Parse the URL path into separate path components.
+ */
+static CURLcode pop3_parse_url_path(struct connectdata *conn)
+{
+  /* The POP3 struct is already initialised in pop3_connect() */
+  struct pop3_conn *pop3c = &conn->proto.pop3c;
+  struct SessionHandle *data = conn->data;
+  const char *path = data->state.path;
+
+  /* URL decode the path and use this mailbox */
+  return Curl_urldecode(data, path, 0, &pop3c->mailbox, NULL, TRUE);
+}
+
+static CURLcode pop3_parse_custom_request(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+  struct pop3_conn *pop3c = &conn->proto.pop3c;
+  struct SessionHandle *data = conn->data;
+  const char *custom = conn->data->set.str[STRING_CUSTOMREQUEST];
+
+  /* URL decode the custom request */
+  if(custom)
+    result = Curl_urldecode(data, custom, 0, &pop3c->custom, NULL, TRUE);
+
+  return result;
+}
+
+/* Call this when the DO phase has completed */
+static CURLcode pop3_dophase_done(struct connectdata *conn, bool connected)
+{
+  struct FTP *pop3 = conn->data->state.proto.pop3;
+
+  (void)connected;
+
+  if(pop3->transfer != FTPTRANSFER_BODY)
+    /* no data to transfer */
+    Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+
+  return CURLE_OK;
+}
+
+/* Called from curl_multi.c while DOing */
+static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done)
+{
+  CURLcode result = pop3_multi_statemach(conn, dophase_done);
+
+  if(result)
+    DEBUGF(infof(conn->data, "DO phase failed\n"));
+  else {
+    if(*dophase_done) {
+      result = pop3_dophase_done(conn, FALSE /* not connected */);
+
+      DEBUGF(infof(conn->data, "DO phase is complete\n"));
+    }
+  }
+
+  return result;
+}
+
+/***********************************************************************
+ *
+ * pop3_regular_transfer()
+ *
+ * The input argument is already checked for validity.
+ *
+ * Performs all commands done before a regular transfer between a local and a
+ * remote host.
+ */
+static CURLcode pop3_regular_transfer(struct connectdata *conn,
+                                      bool *dophase_done)
+{
+  CURLcode result = CURLE_OK;
+  bool connected = FALSE;
+  struct SessionHandle *data = conn->data;
+
+  /* Make sure size is unknown at this point */
+  data->req.size = -1;
+
+  Curl_pgrsSetUploadCounter(data, 0);
+  Curl_pgrsSetDownloadCounter(data, 0);
+  Curl_pgrsSetUploadSize(data, 0);
+  Curl_pgrsSetDownloadSize(data, 0);
+
+  result = pop3_perform(conn, &connected, dophase_done);
+
+  if(CURLE_OK == result) {
+    if(!*dophase_done)
+      /* The DO phase has not completed yet */
+      return CURLE_OK;
+
+    result = pop3_dophase_done(conn, connected);
+    if(result)
+      return result;
+  }
+
+  return result;
+}
+
+static CURLcode pop3_setup_connection(struct connectdata * conn)
+{
+  struct SessionHandle *data = conn->data;
+
+  if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
+    /* Unless we have asked to tunnel pop3 operations through the proxy, we
+       switch and use HTTP operations only */
+#ifndef CURL_DISABLE_HTTP
+    if(conn->handler == &Curl_handler_pop3)
+      conn->handler = &Curl_handler_pop3_proxy;
+    else {
+#ifdef USE_SSL
+      conn->handler = &Curl_handler_pop3s_proxy;
+#else
+      failf(data, "POP3S not supported!");
+      return CURLE_UNSUPPORTED_PROTOCOL;
+#endif
+    }
+
+    /* We explicitly mark this connection as persistent here as we're doing
+       POP3 over HTTP and thus we accidentally avoid setting this value
+       otherwise */
+    conn->bits.close = FALSE;
+#else
+    failf(data, "POP3 over http proxy requires HTTP support built-in!");
+    return CURLE_UNSUPPORTED_PROTOCOL;
+#endif
+  }
+
+  data->state.path++;   /* don't include the initial slash */
+
+  return CURLE_OK;
+}
+
+/* This function scans the body after the end-of-body and writes everything
+   until the end is found */
+CURLcode Curl_pop3_write(struct connectdata *conn, char *str, size_t nread)
+{
+  /* This code could be made into a special function in the handler struct */
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  struct SingleRequest *k = &data->req;
+
+  struct pop3_conn *pop3c = &conn->proto.pop3c;
+  bool strip_dot = FALSE;
+  size_t last = 0;
+  size_t i;
+
+  /* Search through the buffer looking for the end-of-body marker which is
+     5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
+     the eob so the server will have prefixed it with an extra dot which we
+     need to strip out. Additionally the marker could of course be spread out
+     over 5 different data chunks */
+  for(i = 0; i < nread; i++) {
+    size_t prev = pop3c->eob;
+
+    switch(str[i]) {
+    case 0x0d:
+      if(pop3c->eob == 0) {
+        pop3c->eob++;
+
+        if(i) {
+          /* Write out the body part that didn't match */
+          result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
+                                     i - last);
+
+          if(result)
+            return result;
+
+          last = i;
+        }
+      }
+      else if(pop3c->eob == 3)
+        pop3c->eob++;
+      else
+        /* If the character match wasn't at position 0 or 3 then restart the
+           pattern matching */
+        pop3c->eob = 1;
+      break;
+
+    case 0x0a:
+      if(pop3c->eob == 1 || pop3c->eob == 4)
+        pop3c->eob++;
+      else
+        /* If the character match wasn't at position 1 or 4 then start the
+           search again */
+        pop3c->eob = 0;
+      break;
+
+    case 0x2e:
+      if(pop3c->eob == 2)
+        pop3c->eob++;
+      else if(pop3c->eob == 3) {
+        /* We have an extra dot after the CRLF which we need to strip off */
+        strip_dot = TRUE;
+        pop3c->eob = 0;
+      }
+      else
+        /* If the character match wasn't at position 2 then start the search
+           again */
+        pop3c->eob = 0;
+      break;
+
+    default:
+      pop3c->eob = 0;
+      break;
+    }
+
+    /* Did we have a partial match which has subsequently failed? */
+    if(prev && prev >= pop3c->eob) {
+      /* Strip can only be non-zero for the very first mismatch after CRLF
+         and then both prev and strip are equal and nothing will be output
+         below */
+      while(prev && pop3c->strip) {
+        prev--;
+        pop3c->strip--;
+      }
+
+      if(prev) {
+        /* If the partial match was the CRLF and dot then only write the CRLF
+           as the server would have inserted the dot */
+        result = Curl_client_write(conn, CLIENTWRITE_BODY, (char*)POP3_EOB,
+                                   strip_dot ? prev - 1 : prev);
+
+        if(result)
+          return result;
+
+        last = i;
+        strip_dot = FALSE;
+      }
+    }
+  }
+
+  if(pop3c->eob == POP3_EOB_LEN) {
+    /* We have a full match so the transfer is done, however we must transfer
+    the CRLF at the start of the EOB as this is considered to be part of the
+    message as per RFC-1939, sect. 3 */
+    result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
+
+    k->keepon &= ~KEEP_RECV;
+    pop3c->eob = 0;
+
+    return result;
+  }
+
+  if(pop3c->eob)
+    /* While EOB is matching nothing should be output */
+    return CURLE_OK;
+
+  if(nread - last) {
+    result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
+                               nread - last);
+  }
+
+  return result;
+}
+
+#endif /* CURL_DISABLE_POP3 */
diff --git a/lib/curl_progress.c b/lib/curl_progress.c
new file mode 100644 (file)
index 0000000..88f802d
--- /dev/null
@@ -0,0 +1,474 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_progress.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* Provide a string that is 2 + 1 + 2 + 1 + 2 = 8 letters long (plus the zero
+   byte) */
+static void time2str(char *r, curl_off_t seconds)
+{
+  curl_off_t d, h, m, s;
+  if(seconds <= 0) {
+    strcpy(r, "--:--:--");
+    return;
+  }
+  h = seconds / CURL_OFF_T_C(3600);
+  if(h <= CURL_OFF_T_C(99)) {
+    m = (seconds - (h*CURL_OFF_T_C(3600))) / CURL_OFF_T_C(60);
+    s = (seconds - (h*CURL_OFF_T_C(3600))) - (m*CURL_OFF_T_C(60));
+    snprintf(r, 9, "%2" FORMAT_OFF_T ":%02" FORMAT_OFF_T ":%02" FORMAT_OFF_T,
+             h, m, s);
+  }
+  else {
+    /* this equals to more than 99 hours, switch to a more suitable output
+       format to fit within the limits. */
+    d = seconds / CURL_OFF_T_C(86400);
+    h = (seconds - (d*CURL_OFF_T_C(86400))) / CURL_OFF_T_C(3600);
+    if(d <= CURL_OFF_T_C(999))
+      snprintf(r, 9, "%3" FORMAT_OFF_T "d %02" FORMAT_OFF_T "h", d, h);
+    else
+      snprintf(r, 9, "%7" FORMAT_OFF_T "d", d);
+  }
+}
+
+/* The point of this function would be to return a string of the input data,
+   but never longer than 5 columns (+ one zero byte).
+   Add suffix k, M, G when suitable... */
+static char *max5data(curl_off_t bytes, char *max5)
+{
+#define ONE_KILOBYTE  CURL_OFF_T_C(1024)
+#define ONE_MEGABYTE (CURL_OFF_T_C(1024) * ONE_KILOBYTE)
+#define ONE_GIGABYTE (CURL_OFF_T_C(1024) * ONE_MEGABYTE)
+#define ONE_TERABYTE (CURL_OFF_T_C(1024) * ONE_GIGABYTE)
+#define ONE_PETABYTE (CURL_OFF_T_C(1024) * ONE_TERABYTE)
+
+  if(bytes < CURL_OFF_T_C(100000))
+    snprintf(max5, 6, "%5" FORMAT_OFF_T, bytes);
+
+  else if(bytes < CURL_OFF_T_C(10000) * ONE_KILOBYTE)
+    snprintf(max5, 6, "%4" FORMAT_OFF_T "k", bytes/ONE_KILOBYTE);
+
+  else if(bytes < CURL_OFF_T_C(100) * ONE_MEGABYTE)
+    /* 'XX.XM' is good as long as we're less than 100 megs */
+    snprintf(max5, 6, "%2" FORMAT_OFF_T ".%0" FORMAT_OFF_T "M",
+              bytes/ONE_MEGABYTE,
+             (bytes%ONE_MEGABYTE) / (ONE_MEGABYTE/CURL_OFF_T_C(10)) );
+
+#if (CURL_SIZEOF_CURL_OFF_T > 4)
+
+  else if(bytes < CURL_OFF_T_C(10000) * ONE_MEGABYTE)
+    /* 'XXXXM' is good until we're at 10000MB or above */
+    snprintf(max5, 6, "%4" FORMAT_OFF_T "M", bytes/ONE_MEGABYTE);
+
+  else if(bytes < CURL_OFF_T_C(100) * ONE_GIGABYTE)
+    /* 10000 MB - 100 GB, we show it as XX.XG */
+    snprintf(max5, 6, "%2" FORMAT_OFF_T ".%0" FORMAT_OFF_T "G",
+              bytes/ONE_GIGABYTE,
+             (bytes%ONE_GIGABYTE) / (ONE_GIGABYTE/CURL_OFF_T_C(10)) );
+
+  else if(bytes < CURL_OFF_T_C(10000) * ONE_GIGABYTE)
+    /* up to 10000GB, display without decimal: XXXXG */
+    snprintf(max5, 6, "%4" FORMAT_OFF_T "G", bytes/ONE_GIGABYTE);
+
+  else if(bytes < CURL_OFF_T_C(10000) * ONE_TERABYTE)
+    /* up to 10000TB, display without decimal: XXXXT */
+    snprintf(max5, 6, "%4" FORMAT_OFF_T "T", bytes/ONE_TERABYTE);
+
+  else
+    /* up to 10000PB, display without decimal: XXXXP */
+    snprintf(max5, 6, "%4" FORMAT_OFF_T "P", bytes/ONE_PETABYTE);
+
+    /* 16384 petabytes (16 exabytes) is the maximum a 64 bit unsigned number
+       can hold, but our data type is signed so 8192PB will be the maximum. */
+
+#else
+
+  else
+    snprintf(max5, 6, "%4" FORMAT_OFF_T "M", bytes/ONE_MEGABYTE);
+
+#endif
+
+  return max5;
+}
+
+/*
+
+   New proposed interface, 9th of February 2000:
+
+   pgrsStartNow() - sets start time
+   pgrsSetDownloadSize(x) - known expected download size
+   pgrsSetUploadSize(x) - known expected upload size
+   pgrsSetDownloadCounter() - amount of data currently downloaded
+   pgrsSetUploadCounter() - amount of data currently uploaded
+   pgrsUpdate() - show progress
+   pgrsDone() - transfer complete
+
+*/
+
+int Curl_pgrsDone(struct connectdata *conn)
+{
+  int rc;
+  struct SessionHandle *data = conn->data;
+  data->progress.lastshow=0;
+  rc = Curl_pgrsUpdate(conn); /* the final (forced) update */
+  if(rc)
+    return rc;
+
+  if(!(data->progress.flags & PGRS_HIDE) &&
+     !data->progress.callback)
+    /* only output if we don't use a progress callback and we're not
+     * hidden */
+    fprintf(data->set.err, "\n");
+
+  data->progress.speeder_c = 0; /* reset the progress meter display */
+  return 0;
+}
+
+/* reset all times except redirect, and reset the known transfer sizes */
+void Curl_pgrsResetTimesSizes(struct SessionHandle *data)
+{
+  data->progress.t_nslookup = 0.0;
+  data->progress.t_connect = 0.0;
+  data->progress.t_pretransfer = 0.0;
+  data->progress.t_starttransfer = 0.0;
+
+  Curl_pgrsSetDownloadSize(data, 0);
+  Curl_pgrsSetUploadSize(data, 0);
+}
+
+void Curl_pgrsTime(struct SessionHandle *data, timerid timer)
+{
+  struct timeval now = Curl_tvnow();
+
+  switch(timer) {
+  default:
+  case TIMER_NONE:
+    /* mistake filter */
+    break;
+  case TIMER_STARTSINGLE:
+    /* This is set at the start of a single fetch */
+    data->progress.t_startsingle = now;
+    break;
+
+  case TIMER_STARTACCEPT:
+    data->progress.t_acceptdata = Curl_tvnow();
+    break;
+
+  case TIMER_NAMELOOKUP:
+    data->progress.t_nslookup =
+      Curl_tvdiff_secs(now, data->progress.t_startsingle);
+    break;
+  case TIMER_CONNECT:
+    data->progress.t_connect =
+      Curl_tvdiff_secs(now, data->progress.t_startsingle);
+    break;
+  case TIMER_APPCONNECT:
+    data->progress.t_appconnect =
+      Curl_tvdiff_secs(now, data->progress.t_startsingle);
+    break;
+  case TIMER_PRETRANSFER:
+    data->progress.t_pretransfer =
+      Curl_tvdiff_secs(now, data->progress.t_startsingle);
+    break;
+  case TIMER_STARTTRANSFER:
+    data->progress.t_starttransfer =
+      Curl_tvdiff_secs(now, data->progress.t_startsingle);
+    break;
+  case TIMER_POSTRANSFER:
+    /* this is the normal end-of-transfer thing */
+    break;
+  case TIMER_REDIRECT:
+    data->progress.t_redirect = Curl_tvdiff_secs(now, data->progress.start);
+    break;
+  }
+}
+
+void Curl_pgrsStartNow(struct SessionHandle *data)
+{
+  data->progress.speeder_c = 0; /* reset the progress meter display */
+  data->progress.start = Curl_tvnow();
+  /* clear all bits except HIDE and HEADERS_OUT */
+  data->progress.flags &= PGRS_HIDE|PGRS_HEADERS_OUT;
+}
+
+void Curl_pgrsSetDownloadCounter(struct SessionHandle *data, curl_off_t size)
+{
+  data->progress.downloaded = size;
+}
+
+void Curl_pgrsSetUploadCounter(struct SessionHandle *data, curl_off_t size)
+{
+  data->progress.uploaded = size;
+}
+
+void Curl_pgrsSetDownloadSize(struct SessionHandle *data, curl_off_t size)
+{
+  data->progress.size_dl = size;
+  if(size >= 0)
+    data->progress.flags |= PGRS_DL_SIZE_KNOWN;
+  else
+    data->progress.flags &= ~PGRS_DL_SIZE_KNOWN;
+}
+
+void Curl_pgrsSetUploadSize(struct SessionHandle *data, curl_off_t size)
+{
+  data->progress.size_ul = size;
+  if(size >= 0)
+    data->progress.flags |= PGRS_UL_SIZE_KNOWN;
+  else
+    data->progress.flags &= ~PGRS_UL_SIZE_KNOWN;
+}
+
+/*
+ * Curl_pgrsUpdate() returns 0 for success or the value returned by the
+ * progress callback!
+ */
+int Curl_pgrsUpdate(struct connectdata *conn)
+{
+  struct timeval now;
+  int result;
+  char max5[6][10];
+  curl_off_t dlpercen=0;
+  curl_off_t ulpercen=0;
+  curl_off_t total_percen=0;
+  curl_off_t total_transfer;
+  curl_off_t total_expected_transfer;
+  curl_off_t timespent;
+  struct SessionHandle *data = conn->data;
+  int nowindex = data->progress.speeder_c% CURR_TIME;
+  int checkindex;
+  int countindex; /* amount of seconds stored in the speeder array */
+  char time_left[10];
+  char time_total[10];
+  char time_spent[10];
+  curl_off_t ulestimate=0;
+  curl_off_t dlestimate=0;
+  curl_off_t total_estimate;
+  bool shownow=FALSE;
+
+  now = Curl_tvnow(); /* what time is it */
+
+  /* The time spent so far (from the start) */
+  data->progress.timespent =
+    (double)(now.tv_sec - data->progress.start.tv_sec) +
+    (double)(now.tv_usec - data->progress.start.tv_usec)/1000000.0;
+  timespent = (curl_off_t)data->progress.timespent;
+
+  /* The average download speed this far */
+  data->progress.dlspeed = (curl_off_t)
+    ((double)data->progress.downloaded/
+     (data->progress.timespent>0?data->progress.timespent:1));
+
+  /* The average upload speed this far */
+  data->progress.ulspeed = (curl_off_t)
+    ((double)data->progress.uploaded/
+     (data->progress.timespent>0?data->progress.timespent:1));
+
+  /* Calculations done at most once a second, unless end is reached */
+  if(data->progress.lastshow != (long)now.tv_sec) {
+    shownow = TRUE;
+
+    data->progress.lastshow = now.tv_sec;
+
+    /* Let's do the "current speed" thing, which should use the fastest
+       of the dl/ul speeds. Store the faster speed at entry 'nowindex'. */
+    data->progress.speeder[ nowindex ] =
+      data->progress.downloaded>data->progress.uploaded?
+      data->progress.downloaded:data->progress.uploaded;
+
+    /* remember the exact time for this moment */
+    data->progress.speeder_time [ nowindex ] = now;
+
+    /* advance our speeder_c counter, which is increased every time we get
+       here and we expect it to never wrap as 2^32 is a lot of seconds! */
+    data->progress.speeder_c++;
+
+    /* figure out how many index entries of data we have stored in our speeder
+       array. With N_ENTRIES filled in, we have about N_ENTRIES-1 seconds of
+       transfer. Imagine, after one second we have filled in two entries,
+       after two seconds we've filled in three entries etc. */
+    countindex = ((data->progress.speeder_c>=CURR_TIME)?
+                  CURR_TIME:data->progress.speeder_c) - 1;
+
+    /* first of all, we don't do this if there's no counted seconds yet */
+    if(countindex) {
+      long span_ms;
+
+      /* Get the index position to compare with the 'nowindex' position.
+         Get the oldest entry possible. While we have less than CURR_TIME
+         entries, the first entry will remain the oldest. */
+      checkindex = (data->progress.speeder_c>=CURR_TIME)?
+        data->progress.speeder_c%CURR_TIME:0;
+
+      /* Figure out the exact time for the time span */
+      span_ms = Curl_tvdiff(now,
+                            data->progress.speeder_time[checkindex]);
+      if(0 == span_ms)
+        span_ms=1; /* at least one millisecond MUST have passed */
+
+      /* Calculate the average speed the last 'span_ms' milliseconds */
+      {
+        curl_off_t amount = data->progress.speeder[nowindex]-
+          data->progress.speeder[checkindex];
+
+        if(amount > CURL_OFF_T_C(4294967) /* 0xffffffff/1000 */)
+          /* the 'amount' value is bigger than would fit in 32 bits if
+             multiplied with 1000, so we use the double math for this */
+          data->progress.current_speed = (curl_off_t)
+            ((double)amount/((double)span_ms/1000.0));
+        else
+          /* the 'amount' value is small enough to fit within 32 bits even
+             when multiplied with 1000 */
+          data->progress.current_speed = amount*CURL_OFF_T_C(1000)/span_ms;
+      }
+    }
+    else
+      /* the first second we use the main average */
+      data->progress.current_speed =
+        (data->progress.ulspeed>data->progress.dlspeed)?
+        data->progress.ulspeed:data->progress.dlspeed;
+
+  } /* Calculations end */
+
+  if(!(data->progress.flags & PGRS_HIDE)) {
+
+    /* progress meter has not been shut off */
+
+    if(data->set.fprogress) {
+      /* There's a callback set, so we call that instead of writing
+         anything ourselves. This really is the way to go. */
+      result= data->set.fprogress(data->set.progress_client,
+                                  (double)data->progress.size_dl,
+                                  (double)data->progress.downloaded,
+                                  (double)data->progress.size_ul,
+                                  (double)data->progress.uploaded);
+      if(result)
+        failf(data, "Callback aborted");
+      return result;
+    }
+
+    if(!shownow)
+      /* only show the internal progress meter once per second */
+      return 0;
+
+    /* If there's no external callback set, use internal code to show
+       progress */
+
+    if(!(data->progress.flags & PGRS_HEADERS_OUT)) {
+      if(data->state.resume_from) {
+        fprintf(data->set.err,
+                "** Resuming transfer from byte position %" FORMAT_OFF_T "\n",
+                data->state.resume_from);
+      }
+      fprintf(data->set.err,
+              "  %% Total    %% Received %% Xferd  Average Speed   "
+              "Time    Time     Time  Current\n"
+              "                                 Dload  Upload   "
+              "Total   Spent    Left  Speed\n");
+      data->progress.flags |= PGRS_HEADERS_OUT; /* headers are shown */
+    }
+
+    /* Figure out the estimated time of arrival for the upload */
+    if((data->progress.flags & PGRS_UL_SIZE_KNOWN) &&
+       (data->progress.ulspeed > CURL_OFF_T_C(0))) {
+      ulestimate = data->progress.size_ul / data->progress.ulspeed;
+
+      if(data->progress.size_ul > CURL_OFF_T_C(10000))
+        ulpercen = data->progress.uploaded /
+          (data->progress.size_ul/CURL_OFF_T_C(100));
+      else if(data->progress.size_ul > CURL_OFF_T_C(0))
+        ulpercen = (data->progress.uploaded*100) /
+          data->progress.size_ul;
+    }
+
+    /* ... and the download */
+    if((data->progress.flags & PGRS_DL_SIZE_KNOWN) &&
+       (data->progress.dlspeed > CURL_OFF_T_C(0))) {
+      dlestimate = data->progress.size_dl / data->progress.dlspeed;
+
+      if(data->progress.size_dl > CURL_OFF_T_C(10000))
+        dlpercen = data->progress.downloaded /
+          (data->progress.size_dl/CURL_OFF_T_C(100));
+      else if(data->progress.size_dl > CURL_OFF_T_C(0))
+        dlpercen = (data->progress.downloaded*100) /
+          data->progress.size_dl;
+    }
+
+    /* Now figure out which of them is slower and use that one for the
+       total estimate! */
+    total_estimate = ulestimate>dlestimate?ulestimate:dlestimate;
+
+    /* create the three time strings */
+    time2str(time_left, total_estimate > 0?(total_estimate - timespent):0);
+    time2str(time_total, total_estimate);
+    time2str(time_spent, timespent);
+
+    /* Get the total amount of data expected to get transferred */
+    total_expected_transfer =
+      (data->progress.flags & PGRS_UL_SIZE_KNOWN?
+       data->progress.size_ul:data->progress.uploaded)+
+      (data->progress.flags & PGRS_DL_SIZE_KNOWN?
+       data->progress.size_dl:data->progress.downloaded);
+
+    /* We have transferred this much so far */
+    total_transfer = data->progress.downloaded + data->progress.uploaded;
+
+    /* Get the percentage of data transferred so far */
+    if(total_expected_transfer > CURL_OFF_T_C(10000))
+      total_percen = total_transfer /
+        (total_expected_transfer/CURL_OFF_T_C(100));
+    else if(total_expected_transfer > CURL_OFF_T_C(0))
+      total_percen = (total_transfer*100) / total_expected_transfer;
+
+    fprintf(data->set.err,
+            "\r"
+            "%3" FORMAT_OFF_T " %s  "
+            "%3" FORMAT_OFF_T " %s  "
+            "%3" FORMAT_OFF_T " %s  %s  %s %s %s %s %s",
+            total_percen,  /* 3 letters */                /* total % */
+            max5data(total_expected_transfer, max5[2]),   /* total size */
+            dlpercen,      /* 3 letters */                /* rcvd % */
+            max5data(data->progress.downloaded, max5[0]), /* rcvd size */
+            ulpercen,      /* 3 letters */                /* xfer % */
+            max5data(data->progress.uploaded, max5[1]),   /* xfer size */
+            max5data(data->progress.dlspeed, max5[3]),    /* avrg dl speed */
+            max5data(data->progress.ulspeed, max5[4]),    /* avrg ul speed */
+            time_total,    /* 8 letters */                /* total time */
+            time_spent,    /* 8 letters */                /* time spent */
+            time_left,     /* 8 letters */                /* time left */
+            max5data(data->progress.current_speed, max5[5]) /* current speed */
+            );
+
+    /* we flush the output stream to make it appear as soon as possible */
+    fflush(data->set.err);
+
+  } /* !(data->progress.flags & PGRS_HIDE) */
+
+  return 0;
+}
diff --git a/lib/curl_qssl.c b/lib/curl_qssl.c
new file mode 100644 (file)
index 0000000..d140dc9
--- /dev/null
@@ -0,0 +1,501 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_QSOSSL
+
+#include <qsossl.h>
+
+#ifdef HAVE_LIMITS_H
+#  include <limits.h>
+#endif
+
+#include <curl/curl.h>
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_qssl.h"
+#include "curl_sslgen.h"
+#include "curl_connect.h" /* for the connect timeout */
+#include "curl_select.h"
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+
+int Curl_qsossl_init(void)
+
+{
+  /* Nothing to do here. We must have connection data to initialize ssl, so
+   * defer.
+   */
+
+  return 1;
+}
+
+
+void Curl_qsossl_cleanup(void)
+
+{
+  /* Nothing to do. */
+}
+
+
+static CURLcode Curl_qsossl_init_session(struct SessionHandle * data)
+
+{
+  int rc;
+  char * certname;
+  SSLInit initstr;
+  SSLInitApp initappstr;
+
+  /* Initialize the job for SSL according to the current parameters.
+   * QsoSSL offers two ways to do it: SSL_Init_Application() that uses an
+   *  application identifier to select certificates in the main certificate
+   *  store, and SSL_Init() that uses named keyring files and a password.
+   * It is not possible to have different keyrings for the CAs and the
+   *  local certificate. We thus use the certificate name to identify the
+   *  keyring if given, else the CA file name.
+   * If the key file name is given, it is taken as the password for the
+   *  keyring in certificate file.
+   * We first try to SSL_Init_Application(), then SSL_Init() if it failed.
+   */
+
+  certname = data->set.str[STRING_CERT];
+
+  if(!certname) {
+    certname = data->set.str[STRING_SSL_CAFILE];
+
+    if(!certname)
+      return CURLE_OK;          /* Use previous setup. */
+    }
+
+  memset((char *) &initappstr, 0, sizeof initappstr);
+  initappstr.applicationID = certname;
+  initappstr.applicationIDLen = strlen(certname);
+  initappstr.protocol = SSL_VERSION_CURRENT;    /* TLSV1 compat. SSLV[23]. */
+  initappstr.sessionType = SSL_REGISTERED_AS_CLIENT;
+  rc = SSL_Init_Application(&initappstr);
+
+  if(rc == SSL_ERROR_NOT_REGISTERED) {
+    initstr.keyringFileName = certname;
+    initstr.keyringPassword = data->set.str[STRING_KEY];
+    initstr.cipherSuiteList = NULL;    /* Use default. */
+    initstr.cipherSuiteListLen = 0;
+    rc = SSL_Init(&initstr);
+    }
+
+  switch (rc) {
+
+  case 0:                             /* No error. */
+    break;
+
+  case SSL_ERROR_IO:
+    failf(data, "SSL_Init() I/O error: %s", strerror(errno));
+    return CURLE_SSL_CONNECT_ERROR;
+
+  case SSL_ERROR_BAD_CIPHER_SUITE:
+    return CURLE_SSL_CIPHER;
+
+  case SSL_ERROR_KEYPASSWORD_EXPIRED:
+  case SSL_ERROR_NOT_REGISTERED:
+    return CURLE_SSL_CONNECT_ERROR;
+
+  case SSL_ERROR_NO_KEYRING:
+    return CURLE_SSL_CACERT;
+
+  case SSL_ERROR_CERT_EXPIRED:
+    return CURLE_SSL_CERTPROBLEM;
+
+  default:
+    failf(data, "SSL_Init(): %s", SSL_Strerror(rc, NULL));
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  return CURLE_OK;
+}
+
+
+static CURLcode Curl_qsossl_create(struct connectdata * conn, int sockindex)
+
+{
+  SSLHandle * h;
+  struct ssl_connect_data * connssl = &conn->ssl[sockindex];
+
+  h = SSL_Create(conn->sock[sockindex], SSL_ENCRYPT);
+
+  if(!h) {
+    failf(conn->data, "SSL_Create() I/O error: %s", strerror(errno));
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  connssl->handle = h;
+  return CURLE_OK;
+}
+
+
+static int Curl_qsossl_trap_cert(SSLHandle * h)
+
+{
+  return 1;       /* Accept certificate. */
+}
+
+
+static CURLcode Curl_qsossl_handshake(struct connectdata * conn, int sockindex)
+
+{
+  int rc;
+  struct SessionHandle * data = conn->data;
+  struct ssl_connect_data * connssl = &conn->ssl[sockindex];
+  SSLHandle * h = connssl->handle;
+  long timeout_ms;
+
+  h->exitPgm = NULL;
+
+  if(!data->set.ssl.verifyhost)
+    h->exitPgm = Curl_qsossl_trap_cert;
+
+  /* figure out how long time we should wait at maximum */
+  timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+  if(timeout_ms < 0) {
+    /* time-out, bail out, go home */
+    failf(data, "Connection time-out");
+    return CURLE_OPERATION_TIMEDOUT;
+  }
+
+  /* SSL_Handshake() timeout resolution is second, so round up. */
+  h->timeout = (timeout_ms + 1000 - 1) / 1000;
+
+  /* Set-up protocol. */
+
+  switch (data->set.ssl.version) {
+
+  default:
+  case CURL_SSLVERSION_DEFAULT:
+    h->protocol = SSL_VERSION_CURRENT;          /* TLSV1 compat. SSLV[23]. */
+    break;
+
+  case CURL_SSLVERSION_TLSv1:
+    h->protocol = TLS_VERSION_1;
+    break;
+
+  case CURL_SSLVERSION_SSLv2:
+    h->protocol = SSL_VERSION_2;
+    break;
+
+  case CURL_SSLVERSION_SSLv3:
+    h->protocol = SSL_VERSION_3;
+    break;
+  }
+
+  rc = SSL_Handshake(h, SSL_HANDSHAKE_AS_CLIENT);
+
+  switch (rc) {
+
+  case 0:                             /* No error. */
+    break;
+
+  case SSL_ERROR_BAD_CERTIFICATE:
+  case SSL_ERROR_BAD_CERT_SIG:
+  case SSL_ERROR_NOT_TRUSTED_ROOT:
+    return CURLE_PEER_FAILED_VERIFICATION;
+
+  case SSL_ERROR_BAD_CIPHER_SUITE:
+  case SSL_ERROR_NO_CIPHERS:
+    return CURLE_SSL_CIPHER;
+
+  case SSL_ERROR_CERTIFICATE_REJECTED:
+  case SSL_ERROR_CERT_EXPIRED:
+  case SSL_ERROR_NO_CERTIFICATE:
+    return CURLE_SSL_CERTPROBLEM;
+
+  case SSL_ERROR_IO:
+    failf(data, "SSL_Handshake() I/O error: %s", strerror(errno));
+    return CURLE_SSL_CONNECT_ERROR;
+
+  default:
+    failf(data, "SSL_Handshake(): %s", SSL_Strerror(rc, NULL));
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  return CURLE_OK;
+}
+
+
+static Curl_recv qsossl_recv;
+static Curl_send qsossl_send;
+
+CURLcode Curl_qsossl_connect(struct connectdata * conn, int sockindex)
+
+{
+  struct SessionHandle * data = conn->data;
+  struct ssl_connect_data * connssl = &conn->ssl[sockindex];
+  int rc;
+
+  rc = Curl_qsossl_init_session(data);
+
+  if(rc == CURLE_OK) {
+    rc = Curl_qsossl_create(conn, sockindex);
+
+    if(rc == CURLE_OK)
+      rc = Curl_qsossl_handshake(conn, sockindex);
+    else {
+      SSL_Destroy(connssl->handle);
+      connssl->handle = NULL;
+      connssl->use = FALSE;
+      connssl->state = ssl_connection_none;
+    }
+  }
+  if(rc == CURLE_OK) {
+    connssl->state = ssl_connection_complete;
+    conn->recv[sockindex] = qsossl_recv;
+    conn->send[sockindex] = qsossl_send;
+  }
+
+  return rc;
+}
+
+
+static int Curl_qsossl_close_one(struct ssl_connect_data * conn,
+                                 struct SessionHandle * data)
+
+{
+  int rc;
+
+  if(!conn->handle)
+    return 0;
+
+  rc = SSL_Destroy(conn->handle);
+
+  if(rc) {
+    if(rc == SSL_ERROR_IO) {
+      failf(data, "SSL_Destroy() I/O error: %s", strerror(errno));
+      return -1;
+    }
+
+    /* An SSL error. */
+    failf(data, "SSL_Destroy() returned error %s", SSL_Strerror(rc, NULL));
+    return -1;
+  }
+
+  conn->handle = NULL;
+  return 0;
+}
+
+
+void Curl_qsossl_close(struct connectdata *conn, int sockindex)
+
+{
+  struct SessionHandle *data = conn->data;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+
+  if(connssl->use)
+    (void) Curl_qsossl_close_one(connssl, data);
+}
+
+
+int Curl_qsossl_close_all(struct SessionHandle * data)
+
+{
+  /* Unimplemented. */
+  (void) data;
+  return 0;
+}
+
+
+int Curl_qsossl_shutdown(struct connectdata * conn, int sockindex)
+
+{
+  struct ssl_connect_data * connssl = &conn->ssl[sockindex];
+  struct SessionHandle *data = conn->data;
+  ssize_t nread;
+  int what;
+  int rc;
+  char buf[120];
+
+  if(!connssl->handle)
+    return 0;
+
+  if(data->set.ftp_ccc != CURLFTPSSL_CCC_ACTIVE)
+    return 0;
+
+  if(Curl_qsossl_close_one(connssl, data))
+    return -1;
+
+  rc = 0;
+
+  what = Curl_socket_ready(conn->sock[sockindex],
+                           CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT);
+
+  for(;;) {
+    if(what < 0) {
+      /* anything that gets here is fatally bad */
+      failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+      rc = -1;
+      break;
+    }
+
+    if(!what) {                                /* timeout */
+      failf(data, "SSL shutdown timeout");
+      break;
+    }
+
+    /* Something to read, let's do it and hope that it is the close
+       notify alert from the server. No way to SSL_Read now, so use read(). */
+
+    nread = read(conn->sock[sockindex], buf, sizeof(buf));
+
+    if(nread < 0) {
+      failf(data, "read: %s", strerror(errno));
+      rc = -1;
+    }
+
+    if(nread <= 0)
+      break;
+
+    what = Curl_socket_ready(conn->sock[sockindex], CURL_SOCKET_BAD, 0);
+  }
+
+  return rc;
+}
+
+
+static ssize_t qsossl_send(struct connectdata * conn, int sockindex,
+                           const void * mem, size_t len, CURLcode * curlcode)
+
+{
+  /* SSL_Write() is said to return 'int' while write() and send() returns
+     'size_t' */
+  int rc;
+
+  rc = SSL_Write(conn->ssl[sockindex].handle, (void *) mem, (int) len);
+
+  if(rc < 0) {
+    switch(rc) {
+
+    case SSL_ERROR_BAD_STATE:
+      /* The operation did not complete; the same SSL I/O function
+         should be called again later. This is basically an EWOULDBLOCK
+         equivalent. */
+      *curlcode = CURLE_AGAIN;
+      return -1;
+
+    case SSL_ERROR_IO:
+      switch (errno) {
+      case EWOULDBLOCK:
+      case EINTR:
+        *curlcode = CURLE_AGAIN;
+        return -1;
+        }
+
+      failf(conn->data, "SSL_Write() I/O error: %s", strerror(errno));
+      *curlcode = CURLE_SEND_ERROR;
+      return -1;
+    }
+
+    /* An SSL error. */
+    failf(conn->data, "SSL_Write() returned error %s",
+          SSL_Strerror(rc, NULL));
+    *curlcode = CURLE_SEND_ERROR;
+    return -1;
+  }
+
+  return (ssize_t) rc; /* number of bytes */
+}
+
+
+static ssize_t qsossl_recv(struct connectdata * conn, int num, char * buf,
+                           size_t buffersize, CURLcode * curlcode)
+
+{
+  char error_buffer[120]; /* OpenSSL documents that this must be at
+                             least 120 bytes long. */
+  unsigned long sslerror;
+  int buffsize;
+  int nread;
+
+  buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
+  nread = SSL_Read(conn->ssl[num].handle, buf, buffsize);
+
+  if(nread < 0) {
+    /* failed SSL_read */
+
+    switch (nread) {
+
+    case SSL_ERROR_BAD_STATE:
+      /* there's data pending, re-invoke SSL_Read(). */
+      *curlcode = CURLE_AGAIN;
+      return -1;
+
+    case SSL_ERROR_IO:
+      switch (errno) {
+      case EWOULDBLOCK:
+        *curlcode = CURLE_AGAIN;
+        return -1;
+        }
+
+      failf(conn->data, "SSL_Read() I/O error: %s", strerror(errno));
+      *curlcode = CURLE_RECV_ERROR;
+      return -1;
+
+    default:
+      failf(conn->data, "SSL read error: %s", SSL_Strerror(nread, NULL));
+      *curlcode = CURLE_RECV_ERROR;
+      return -1;
+    }
+  }
+  return (ssize_t) nread;
+}
+
+
+size_t Curl_qsossl_version(char * buffer, size_t size)
+
+{
+  strncpy(buffer, "IBM OS/400 SSL", size);
+  return strlen(buffer);
+}
+
+
+int Curl_qsossl_check_cxn(struct connectdata * cxn)
+
+{
+  int err;
+  int errlen;
+
+  /* The only thing that can be tested here is at the socket level. */
+
+  if(!cxn->ssl[FIRSTSOCKET].handle)
+    return 0; /* connection has been closed */
+
+  err = 0;
+  errlen = sizeof err;
+
+  if(getsockopt(cxn->sock[FIRSTSOCKET], SOL_SOCKET, SO_ERROR,
+                 (unsigned char *) &err, &errlen) ||
+      errlen != sizeof err || err)
+    return 0; /* connection has been closed */
+
+  return -1;  /* connection status unknown */
+}
+
+#endif /* USE_QSOSSL */
diff --git a/lib/curl_rawstr.c b/lib/curl_rawstr.c
new file mode 100644 (file)
index 0000000..17fd1f3
--- /dev/null
@@ -0,0 +1,142 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include "curl_rawstr.h"
+
+/* Portable, consistent toupper (remember EBCDIC). Do not use toupper() because
+   its behavior is altered by the current locale. */
+char Curl_raw_toupper(char in)
+{
+  switch (in) {
+  case 'a':
+    return 'A';
+  case 'b':
+    return 'B';
+  case 'c':
+    return 'C';
+  case 'd':
+    return 'D';
+  case 'e':
+    return 'E';
+  case 'f':
+    return 'F';
+  case 'g':
+    return 'G';
+  case 'h':
+    return 'H';
+  case 'i':
+    return 'I';
+  case 'j':
+    return 'J';
+  case 'k':
+    return 'K';
+  case 'l':
+    return 'L';
+  case 'm':
+    return 'M';
+  case 'n':
+    return 'N';
+  case 'o':
+    return 'O';
+  case 'p':
+    return 'P';
+  case 'q':
+    return 'Q';
+  case 'r':
+    return 'R';
+  case 's':
+    return 'S';
+  case 't':
+    return 'T';
+  case 'u':
+    return 'U';
+  case 'v':
+    return 'V';
+  case 'w':
+    return 'W';
+  case 'x':
+    return 'X';
+  case 'y':
+    return 'Y';
+  case 'z':
+    return 'Z';
+  }
+  return in;
+}
+
+/*
+ * Curl_raw_equal() is for doing "raw" case insensitive strings. This is meant
+ * to be locale independent and only compare strings we know are safe for
+ * this.  See http://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/ for
+ * some further explanation to why this function is necessary.
+ *
+ * The function is capable of comparing a-z case insensitively even for
+ * non-ascii.
+ */
+
+int Curl_raw_equal(const char *first, const char *second)
+{
+  while(*first && *second) {
+    if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second))
+      /* get out of the loop as soon as they don't match */
+      break;
+    first++;
+    second++;
+  }
+  /* we do the comparison here (possibly again), just to make sure that if the
+     loop above is skipped because one of the strings reached zero, we must not
+     return this as a successful match */
+  return (Curl_raw_toupper(*first) == Curl_raw_toupper(*second));
+}
+
+int Curl_raw_nequal(const char *first, const char *second, size_t max)
+{
+  while(*first && *second && max) {
+    if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second)) {
+      break;
+    }
+    max--;
+    first++;
+    second++;
+  }
+  if(0 == max)
+    return 1; /* they are equal this far */
+
+  return Curl_raw_toupper(*first) == Curl_raw_toupper(*second);
+}
+
+/* Copy an upper case version of the string from src to dest.  The
+ * strings may overlap.  No more than n characters of the string are copied
+ * (including any NUL) and the destination string will NOT be
+ * NUL-terminated if that limit is reached.
+ */
+void Curl_strntoupper(char *dest, const char *src, size_t n)
+{
+  if(n < 1)
+    return;
+
+  do {
+    *dest++ = Curl_raw_toupper(*src);
+  } while(*src++ && --n);
+}
diff --git a/lib/curl_rtsp.c b/lib/curl_rtsp.c
new file mode 100644 (file)
index 0000000..71e434c
--- /dev/null
@@ -0,0 +1,807 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_RTSP
+
+#include "curl_urldata.h"
+#include <curl/curl.h>
+#include "curl_transfer.h"
+#include "curl_sendf.h"
+#include "curl_multiif.h"
+#include "curl_http.h"
+#include "curl_url.h"
+#include "curl_progress.h"
+#include "curl_rtsp.h"
+#include "curl_rawstr.h"
+#include "curl_memory.h"
+#include "curl_select.h"
+#include "curl_connect.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/*
+ * TODO (general)
+ *  -incoming server requests
+ *      -server CSeq counter
+ *  -digest authentication
+ *  -connect thru proxy
+ *  -pipelining?
+ */
+
+
+#define RTP_PKT_CHANNEL(p)   ((int)((unsigned char)((p)[1])))
+
+#define RTP_PKT_LENGTH(p)  ((((int)((unsigned char)((p)[2]))) << 8) | \
+                             ((int)((unsigned char)((p)[3]))))
+
+/* protocol-specific functions set up to be called by the main engine */
+static CURLcode rtsp_do(struct connectdata *conn, bool *done);
+static CURLcode rtsp_done(struct connectdata *conn, CURLcode, bool premature);
+static CURLcode rtsp_connect(struct connectdata *conn, bool *done);
+static CURLcode rtsp_disconnect(struct connectdata *conn, bool dead);
+
+static int rtsp_getsock_do(struct connectdata *conn,
+                           curl_socket_t *socks,
+                           int numsocks);
+
+/*
+ * Parse and write out any available RTP data.
+ *
+ * nread: amount of data left after k->str. will be modified if RTP
+ *        data is parsed and k->str is moved up
+ * readmore: whether or not the RTP parser needs more data right away
+ */
+static CURLcode rtsp_rtp_readwrite(struct SessionHandle *data,
+                                   struct connectdata *conn,
+                                   ssize_t *nread,
+                                   bool *readmore);
+
+
+/* this returns the socket to wait for in the DO and DOING state for the multi
+   interface and then we're always _sending_ a request and thus we wait for
+   the single socket to become writable only */
+static int rtsp_getsock_do(struct connectdata *conn,
+                           curl_socket_t *socks,
+                           int numsocks)
+{
+  /* write mode */
+  (void)numsocks; /* unused, we trust it to be at least 1 */
+  socks[0] = conn->sock[FIRSTSOCKET];
+  return GETSOCK_WRITESOCK(0);
+}
+
+static
+CURLcode rtp_client_write(struct connectdata *conn, char *ptr, size_t len);
+
+
+/*
+ * RTSP handler interface.
+ */
+const struct Curl_handler Curl_handler_rtsp = {
+  "RTSP",                               /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  rtsp_do,                              /* do_it */
+  rtsp_done,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  rtsp_connect,                         /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  rtsp_getsock_do,                      /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  rtsp_disconnect,                      /* disconnect */
+  rtsp_rtp_readwrite,                   /* readwrite */
+  PORT_RTSP,                            /* defport */
+  CURLPROTO_RTSP,                       /* protocol */
+  PROTOPT_NONE                          /* flags */
+};
+
+/*
+ * The server may send us RTP data at any point, and RTSPREQ_RECEIVE does not
+ * want to block the application forever while receiving a stream. Therefore,
+ * we cannot assume that an RTSP socket is dead just because it is readable.
+ *
+ * Instead, if it is readable, run Curl_getconnectinfo() to peek at the socket
+ * and distinguish between closed and data.
+ */
+bool Curl_rtsp_connisdead(struct connectdata *check)
+{
+  int sval;
+  bool ret_val = TRUE;
+
+  sval = Curl_socket_ready(check->sock[FIRSTSOCKET], CURL_SOCKET_BAD, 0);
+  if(sval == 0) {
+    /* timeout */
+    ret_val = FALSE;
+  }
+  else if(sval & CURL_CSELECT_ERR) {
+    /* socket is in an error state */
+    ret_val = TRUE;
+  }
+  else if((sval & CURL_CSELECT_IN) && check->data) {
+    /* readable with no error. could be closed or could be alive but we can
+       only check if we have a proper SessionHandle for the connection */
+    curl_socket_t connectinfo = Curl_getconnectinfo(check->data, &check);
+    if(connectinfo != CURL_SOCKET_BAD)
+      ret_val = FALSE;
+  }
+
+  return ret_val;
+}
+
+static CURLcode rtsp_connect(struct connectdata *conn, bool *done)
+{
+  CURLcode httpStatus;
+  struct SessionHandle *data = conn->data;
+
+  httpStatus = Curl_http_connect(conn, done);
+
+  /* Initialize the CSeq if not already done */
+  if(data->state.rtsp_next_client_CSeq == 0)
+    data->state.rtsp_next_client_CSeq = 1;
+  if(data->state.rtsp_next_server_CSeq == 0)
+    data->state.rtsp_next_server_CSeq = 1;
+
+  conn->proto.rtspc.rtp_channel = -1;
+
+  return httpStatus;
+}
+
+static CURLcode rtsp_disconnect(struct connectdata *conn, bool dead)
+{
+  (void) dead;
+  Curl_safefree(conn->proto.rtspc.rtp_buf);
+  return CURLE_OK;
+}
+
+
+static CURLcode rtsp_done(struct connectdata *conn,
+                          CURLcode status, bool premature)
+{
+  struct SessionHandle *data = conn->data;
+  struct RTSP *rtsp = data->state.proto.rtsp;
+  CURLcode httpStatus;
+  long CSeq_sent;
+  long CSeq_recv;
+
+  /* Bypass HTTP empty-reply checks on receive */
+  if(data->set.rtspreq == RTSPREQ_RECEIVE)
+    premature = TRUE;
+
+  httpStatus = Curl_http_done(conn, status, premature);
+
+  if(rtsp) {
+    /* Check the sequence numbers */
+    CSeq_sent = rtsp->CSeq_sent;
+    CSeq_recv = rtsp->CSeq_recv;
+    if((data->set.rtspreq != RTSPREQ_RECEIVE) && (CSeq_sent != CSeq_recv)) {
+      failf(data,
+            "The CSeq of this request %ld did not match the response %ld",
+            CSeq_sent, CSeq_recv);
+      return CURLE_RTSP_CSEQ_ERROR;
+    }
+    else if(data->set.rtspreq == RTSPREQ_RECEIVE &&
+            (conn->proto.rtspc.rtp_channel == -1)) {
+      infof(data, "Got an RTP Receive with a CSeq of %ld\n", CSeq_recv);
+      /* TODO CPC: Server -> Client logic here */
+    }
+  }
+
+  return httpStatus;
+}
+
+static CURLcode rtsp_do(struct connectdata *conn, bool *done)
+{
+  struct SessionHandle *data = conn->data;
+  CURLcode result=CURLE_OK;
+  Curl_RtspReq rtspreq = data->set.rtspreq;
+  struct RTSP *rtsp;
+  struct HTTP *http;
+  Curl_send_buffer *req_buffer;
+  curl_off_t postsize = 0; /* for ANNOUNCE and SET_PARAMETER */
+  curl_off_t putsize = 0; /* for ANNOUNCE and SET_PARAMETER */
+
+  const char *p_request = NULL;
+  const char *p_session_id = NULL;
+  const char *p_accept = NULL;
+  const char *p_accept_encoding = NULL;
+  const char *p_range = NULL;
+  const char *p_referrer = NULL;
+  const char *p_stream_uri = NULL;
+  const char *p_transport = NULL;
+  const char *p_uagent = NULL;
+
+  *done = TRUE;
+
+  Curl_reset_reqproto(conn);
+
+  if(!data->state.proto.rtsp) {
+    /* Only allocate this struct if we don't already have it! */
+
+    rtsp = calloc(1, sizeof(struct RTSP));
+    if(!rtsp)
+      return CURLE_OUT_OF_MEMORY;
+    data->state.proto.rtsp = rtsp;
+  }
+  else {
+    rtsp = data->state.proto.rtsp;
+  }
+
+  http = &(rtsp->http_wrapper);
+  /* Assert that no one has changed the RTSP struct in an evil way */
+  DEBUGASSERT((void *)http == (void *)rtsp);
+
+  rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq;
+  rtsp->CSeq_recv = 0;
+
+  /* Setup the 'p_request' pointer to the proper p_request string
+   * Since all RTSP requests are included here, there is no need to
+   * support custom requests like HTTP.
+   **/
+  DEBUGASSERT((rtspreq > RTSPREQ_NONE && rtspreq < RTSPREQ_LAST));
+  data->set.opt_no_body = TRUE; /* most requests don't contain a body */
+  switch(rtspreq) {
+  case RTSPREQ_NONE:
+    failf(data, "Got invalid RTSP request: RTSPREQ_NONE");
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  case RTSPREQ_OPTIONS:
+    p_request = "OPTIONS";
+    break;
+  case RTSPREQ_DESCRIBE:
+    p_request = "DESCRIBE";
+    data->set.opt_no_body = FALSE;
+    break;
+  case RTSPREQ_ANNOUNCE:
+    p_request = "ANNOUNCE";
+    break;
+  case RTSPREQ_SETUP:
+    p_request = "SETUP";
+    break;
+  case RTSPREQ_PLAY:
+    p_request = "PLAY";
+    break;
+  case RTSPREQ_PAUSE:
+    p_request = "PAUSE";
+    break;
+  case RTSPREQ_TEARDOWN:
+    p_request = "TEARDOWN";
+    break;
+  case RTSPREQ_GET_PARAMETER:
+    /* GET_PARAMETER's no_body status is determined later */
+    p_request = "GET_PARAMETER";
+    data->set.opt_no_body = FALSE;
+    break;
+  case RTSPREQ_SET_PARAMETER:
+    p_request = "SET_PARAMETER";
+    break;
+  case RTSPREQ_RECORD:
+    p_request = "RECORD";
+    break;
+  case RTSPREQ_RECEIVE:
+    p_request = "";
+    /* Treat interleaved RTP as body*/
+    data->set.opt_no_body = FALSE;
+    break;
+  case RTSPREQ_LAST:
+    failf(data, "Got invalid RTSP request: RTSPREQ_LAST");
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  }
+
+  if(rtspreq == RTSPREQ_RECEIVE) {
+    Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
+                        &http->readbytecount, -1, NULL);
+
+    return result;
+  }
+
+  p_session_id = data->set.str[STRING_RTSP_SESSION_ID];
+  if(!p_session_id &&
+     (rtspreq & ~(RTSPREQ_OPTIONS | RTSPREQ_DESCRIBE | RTSPREQ_SETUP))) {
+    failf(data, "Refusing to issue an RTSP request [%s] without a session ID.",
+          p_request ? p_request : "");
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  }
+
+  /* TODO: auth? */
+  /* TODO: proxy? */
+
+  /* Stream URI. Default to server '*' if not specified */
+  if(data->set.str[STRING_RTSP_STREAM_URI]) {
+    p_stream_uri = data->set.str[STRING_RTSP_STREAM_URI];
+  }
+  else {
+    p_stream_uri = "*";
+  }
+
+  /* Transport Header for SETUP requests */
+  p_transport = Curl_checkheaders(data, "Transport:");
+  if(rtspreq == RTSPREQ_SETUP && !p_transport) {
+    /* New Transport: setting? */
+    if(data->set.str[STRING_RTSP_TRANSPORT]) {
+      Curl_safefree(conn->allocptr.rtsp_transport);
+
+      conn->allocptr.rtsp_transport =
+        aprintf("Transport: %s\r\n",
+                data->set.str[STRING_RTSP_TRANSPORT]);
+      if(!conn->allocptr.rtsp_transport)
+        return CURLE_OUT_OF_MEMORY;
+    }
+    else {
+      failf(data,
+            "Refusing to issue an RTSP SETUP without a Transport: header.");
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    }
+
+    p_transport = conn->allocptr.rtsp_transport;
+  }
+
+  /* Accept Headers for DESCRIBE requests */
+  if(rtspreq == RTSPREQ_DESCRIBE) {
+    /* Accept Header */
+    p_accept = Curl_checkheaders(data, "Accept:")?
+      NULL:"Accept: application/sdp\r\n";
+
+    /* Accept-Encoding header */
+    if(!Curl_checkheaders(data, "Accept-Encoding:") &&
+       data->set.str[STRING_ENCODING]) {
+      Curl_safefree(conn->allocptr.accept_encoding);
+      conn->allocptr.accept_encoding =
+        aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
+
+      if(!conn->allocptr.accept_encoding)
+        return CURLE_OUT_OF_MEMORY;
+
+      p_accept_encoding = conn->allocptr.accept_encoding;
+    }
+  }
+
+  /* The User-Agent string might have been allocated in curl_url.c already,
+     because it might have been used in the proxy connect, but if we have
+     got a header with the user-agent string specified, we erase the
+     previously made string here. */
+  if(Curl_checkheaders(data, "User-Agent:") && conn->allocptr.uagent) {
+    Curl_safefree(conn->allocptr.uagent);
+    conn->allocptr.uagent = NULL;
+  }
+  else if(!Curl_checkheaders(data, "User-Agent:") &&
+          data->set.str[STRING_USERAGENT]) {
+    p_uagent = conn->allocptr.uagent;
+  }
+
+  /* Referrer */
+  Curl_safefree(conn->allocptr.ref);
+  if(data->change.referer && !Curl_checkheaders(data, "Referer:"))
+    conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer);
+  else
+    conn->allocptr.ref = NULL;
+
+  p_referrer = conn->allocptr.ref;
+
+  /*
+   * Range Header
+   * Only applies to PLAY, PAUSE, RECORD
+   *
+   * Go ahead and use the Range stuff supplied for HTTP
+   */
+  if(data->state.use_range &&
+     (rtspreq  & (RTSPREQ_PLAY | RTSPREQ_PAUSE | RTSPREQ_RECORD))) {
+
+    /* Check to see if there is a range set in the custom headers */
+    if(!Curl_checkheaders(data, "Range:") && data->state.range) {
+      Curl_safefree(conn->allocptr.rangeline);
+      conn->allocptr.rangeline = aprintf("Range: %s\r\n", data->state.range);
+      p_range = conn->allocptr.rangeline;
+    }
+  }
+
+  /*
+   * Sanity check the custom headers
+   */
+  if(Curl_checkheaders(data, "CSeq:")) {
+    failf(data, "CSeq cannot be set as a custom header.");
+    return CURLE_RTSP_CSEQ_ERROR;
+  }
+  if(Curl_checkheaders(data, "Session:")) {
+    failf(data, "Session ID cannot be set as a custom header.");
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  }
+
+  /* Initialize a dynamic send buffer */
+  req_buffer = Curl_add_buffer_init();
+
+  if(!req_buffer)
+    return CURLE_OUT_OF_MEMORY;
+
+  result =
+    Curl_add_bufferf(req_buffer,
+                     "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */
+                     "CSeq: %ld\r\n", /* CSeq */
+                     (p_request ? p_request : ""), p_stream_uri,
+                     rtsp->CSeq_sent);
+  if(result)
+    return result;
+
+  /*
+   * Rather than do a normal alloc line, keep the session_id unformatted
+   * to make comparison easier
+   */
+  if(p_session_id) {
+    result = Curl_add_bufferf(req_buffer, "Session: %s\r\n", p_session_id);
+    if(result)
+      return result;
+  }
+
+  /*
+   * Shared HTTP-like options
+   */
+  result = Curl_add_bufferf(req_buffer,
+                            "%s" /* transport */
+                            "%s" /* accept */
+                            "%s" /* accept-encoding */
+                            "%s" /* range */
+                            "%s" /* referrer */
+                            "%s" /* user-agent */
+                            ,
+                            p_transport ? p_transport : "",
+                            p_accept ? p_accept : "",
+                            p_accept_encoding ? p_accept_encoding : "",
+                            p_range ? p_range : "",
+                            p_referrer ? p_referrer : "",
+                            p_uagent ? p_uagent : "");
+  if(result)
+    return result;
+
+  if((rtspreq == RTSPREQ_SETUP) || (rtspreq == RTSPREQ_DESCRIBE)) {
+    result = Curl_add_timecondition(data, req_buffer);
+    if(result)
+      return result;
+  }
+
+  result = Curl_add_custom_headers(conn, req_buffer);
+  if(result)
+    return result;
+
+  if(rtspreq == RTSPREQ_ANNOUNCE ||
+     rtspreq == RTSPREQ_SET_PARAMETER ||
+     rtspreq == RTSPREQ_GET_PARAMETER) {
+
+    if(data->set.upload) {
+      putsize = data->set.infilesize;
+      data->set.httpreq = HTTPREQ_PUT;
+
+    }
+    else {
+      postsize = (data->set.postfieldsize != -1)?
+        data->set.postfieldsize:
+        (data->set.postfields? (curl_off_t)strlen(data->set.postfields):0);
+      data->set.httpreq = HTTPREQ_POST;
+    }
+
+    if(putsize > 0 || postsize > 0) {
+      /* As stated in the http comments, it is probably not wise to
+       * actually set a custom Content-Length in the headers */
+      if(!Curl_checkheaders(data, "Content-Length:")) {
+        result = Curl_add_bufferf(req_buffer,
+            "Content-Length: %" FORMAT_OFF_T"\r\n",
+            (data->set.upload ? putsize : postsize));
+        if(result)
+          return result;
+      }
+
+      if(rtspreq == RTSPREQ_SET_PARAMETER ||
+         rtspreq == RTSPREQ_GET_PARAMETER) {
+        if(!Curl_checkheaders(data, "Content-Type:")) {
+          result = Curl_add_bufferf(req_buffer,
+              "Content-Type: text/parameters\r\n");
+          if(result)
+            return result;
+        }
+      }
+
+      if(rtspreq == RTSPREQ_ANNOUNCE) {
+        if(!Curl_checkheaders(data, "Content-Type:")) {
+          result = Curl_add_bufferf(req_buffer,
+              "Content-Type: application/sdp\r\n");
+          if(result)
+            return result;
+        }
+      }
+
+      data->state.expect100header = FALSE; /* RTSP posts are simple/small */
+    }
+    else if(rtspreq == RTSPREQ_GET_PARAMETER) {
+      /* Check for an empty GET_PARAMETER (heartbeat) request */
+      data->set.httpreq = HTTPREQ_HEAD;
+      data->set.opt_no_body = TRUE;
+    }
+  }
+
+  /* RTSP never allows chunked transfer */
+  data->req.forbidchunk = TRUE;
+  /* Finish the request buffer */
+  result = Curl_add_buffer(req_buffer, "\r\n", 2);
+  if(result)
+    return result;
+
+  if(postsize > 0) {
+    result = Curl_add_buffer(req_buffer, data->set.postfields,
+                             (size_t)postsize);
+    if(result)
+      return result;
+  }
+
+  /* issue the request */
+  result = Curl_add_buffer_send(req_buffer, conn,
+                                &data->info.request_size, 0, FIRSTSOCKET);
+  if(result) {
+    failf(data, "Failed sending RTSP request");
+    return result;
+  }
+
+  Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount,
+                      putsize?FIRSTSOCKET:-1,
+                      putsize?&http->writebytecount:NULL);
+
+  /* Increment the CSeq on success */
+  data->state.rtsp_next_client_CSeq++;
+
+  if(http->writebytecount) {
+    /* if a request-body has been sent off, we make sure this progress is
+       noted properly */
+    Curl_pgrsSetUploadCounter(data, http->writebytecount);
+    if(Curl_pgrsUpdate(conn))
+      result = CURLE_ABORTED_BY_CALLBACK;
+  }
+
+  return result;
+}
+
+
+static CURLcode rtsp_rtp_readwrite(struct SessionHandle *data,
+                                   struct connectdata *conn,
+                                   ssize_t *nread,
+                                   bool *readmore) {
+  struct SingleRequest *k = &data->req;
+  struct rtsp_conn *rtspc = &(conn->proto.rtspc);
+
+  char *rtp; /* moving pointer to rtp data */
+  ssize_t rtp_dataleft; /* how much data left to parse in this round */
+  char *scratch;
+  CURLcode result;
+
+  if(rtspc->rtp_buf) {
+    /* There was some leftover data the last time. Merge buffers */
+    char *newptr = realloc(rtspc->rtp_buf, rtspc->rtp_bufsize + *nread);
+    if(!newptr) {
+      Curl_safefree(rtspc->rtp_buf);
+      rtspc->rtp_buf = NULL;
+      rtspc->rtp_bufsize = 0;
+      return CURLE_OUT_OF_MEMORY;
+    }
+    rtspc->rtp_buf = newptr;
+    memcpy(rtspc->rtp_buf + rtspc->rtp_bufsize, k->str, *nread);
+    rtspc->rtp_bufsize += *nread;
+    rtp = rtspc->rtp_buf;
+    rtp_dataleft = rtspc->rtp_bufsize;
+  }
+  else {
+    /* Just parse the request buffer directly */
+    rtp = k->str;
+    rtp_dataleft = *nread;
+  }
+
+  while((rtp_dataleft > 0) &&
+        (rtp[0] == '$')) {
+    if(rtp_dataleft > 4) {
+      int rtp_length;
+
+      /* Parse the header */
+      /* The channel identifier immediately follows and is 1 byte */
+      rtspc->rtp_channel = RTP_PKT_CHANNEL(rtp);
+
+      /* The length is two bytes */
+      rtp_length = RTP_PKT_LENGTH(rtp);
+
+      if(rtp_dataleft < rtp_length + 4) {
+        /* Need more - incomplete payload*/
+        *readmore = TRUE;
+        break;
+      }
+      else {
+        /* We have the full RTP interleaved packet
+         * Write out the header including the leading '$' */
+        DEBUGF(infof(data, "RTP write channel %d rtp_length %d\n",
+              rtspc->rtp_channel, rtp_length));
+        result = rtp_client_write(conn, &rtp[0], rtp_length + 4);
+        if(result) {
+          failf(data, "Got an error writing an RTP packet");
+          *readmore = FALSE;
+          Curl_safefree(rtspc->rtp_buf);
+          rtspc->rtp_buf = NULL;
+          rtspc->rtp_bufsize = 0;
+          return result;
+        }
+
+        /* Move forward in the buffer */
+        rtp_dataleft -= rtp_length + 4;
+        rtp += rtp_length + 4;
+
+        if(data->set.rtspreq == RTSPREQ_RECEIVE) {
+          /* If we are in a passive receive, give control back
+           * to the app as often as we can.
+           */
+          k->keepon &= ~KEEP_RECV;
+        }
+      }
+    }
+    else {
+      /* Need more - incomplete header */
+      *readmore = TRUE;
+      break;
+    }
+  }
+
+  if(rtp_dataleft != 0 && rtp[0] == '$') {
+    DEBUGF(infof(data, "RTP Rewinding %zu %s\n", rtp_dataleft,
+          *readmore ? "(READMORE)" : ""));
+
+    /* Store the incomplete RTP packet for a "rewind" */
+    scratch = malloc(rtp_dataleft);
+    if(!scratch) {
+      Curl_safefree(rtspc->rtp_buf);
+      rtspc->rtp_buf = NULL;
+      rtspc->rtp_bufsize = 0;
+      return CURLE_OUT_OF_MEMORY;
+    }
+    memcpy(scratch, rtp, rtp_dataleft);
+    Curl_safefree(rtspc->rtp_buf);
+    rtspc->rtp_buf = scratch;
+    rtspc->rtp_bufsize = rtp_dataleft;
+
+    /* As far as the transfer is concerned, this data is consumed */
+    *nread = 0;
+    return CURLE_OK;
+  }
+  else {
+    /* Fix up k->str to point just after the last RTP packet */
+    k->str += *nread - rtp_dataleft;
+
+    /* either all of the data has been read or...
+     * rtp now points at the next byte to parse
+     */
+    if(rtp_dataleft > 0)
+      DEBUGASSERT(k->str[0] == rtp[0]);
+
+    DEBUGASSERT(rtp_dataleft <= *nread); /* sanity check */
+
+    *nread = rtp_dataleft;
+  }
+
+  /* If we get here, we have finished with the leftover/merge buffer */
+  Curl_safefree(rtspc->rtp_buf);
+  rtspc->rtp_buf = NULL;
+  rtspc->rtp_bufsize = 0;
+
+  return CURLE_OK;
+}
+
+static
+CURLcode rtp_client_write(struct connectdata *conn, char *ptr, size_t len)
+{
+  struct SessionHandle *data = conn->data;
+  size_t wrote;
+  curl_write_callback writeit;
+
+  if(len == 0) {
+    failf (data, "Cannot write a 0 size RTP packet.");
+    return CURLE_WRITE_ERROR;
+  }
+
+  writeit = data->set.fwrite_rtp?data->set.fwrite_rtp:data->set.fwrite_func;
+  wrote = writeit(ptr, 1, len, data->set.rtp_out);
+
+  if(CURL_WRITEFUNC_PAUSE == wrote) {
+    failf (data, "Cannot pause RTP");
+    return CURLE_WRITE_ERROR;
+  }
+
+  if(wrote != len) {
+    failf (data, "Failed writing RTP data");
+    return CURLE_WRITE_ERROR;
+  }
+
+  return CURLE_OK;
+}
+
+CURLcode Curl_rtsp_parseheader(struct connectdata *conn,
+                               char *header)
+{
+  struct SessionHandle *data = conn->data;
+  long CSeq = 0;
+
+  if(checkprefix("CSeq:", header)) {
+    /* Store the received CSeq. Match is verified in rtsp_done */
+    int nc = sscanf(&header[4], ": %ld", &CSeq);
+    if(nc == 1) {
+      data->state.proto.rtsp->CSeq_recv = CSeq; /* mark the request */
+      data->state.rtsp_CSeq_recv = CSeq; /* update the handle */
+    }
+    else {
+      failf(data, "Unable to read the CSeq header: [%s]", header);
+      return CURLE_RTSP_CSEQ_ERROR;
+    }
+  }
+  else if(checkprefix("Session:", header)) {
+    char *start;
+
+    /* Find the first non-space letter */
+    start = header + 9;
+    while(*start && ISSPACE(*start))
+      start++;
+
+    if(!*start) {
+      failf(data, "Got a blank Session ID");
+    }
+    else if(data->set.str[STRING_RTSP_SESSION_ID]) {
+      /* If the Session ID is set, then compare */
+      if(strncmp(start, data->set.str[STRING_RTSP_SESSION_ID],
+                 strlen(data->set.str[STRING_RTSP_SESSION_ID]))  != 0) {
+        failf(data, "Got RTSP Session ID Line [%s], but wanted ID [%s]",
+              start, data->set.str[STRING_RTSP_SESSION_ID]);
+        return CURLE_RTSP_SESSION_ERROR;
+      }
+    }
+    else {
+      /* If the Session ID is not set, and we find it in a response, then
+         set it */
+
+      /* The session ID can be an alphanumeric or a 'safe' character
+       *
+       * RFC 2326 15.1 Base Syntax:
+       * safe =  "\$" | "-" | "_" | "." | "+"
+       * */
+      char *end = start;
+      while(*end &&
+            (ISALNUM(*end) || *end == '-' || *end == '_' || *end == '.' ||
+             *end == '+' ||
+             (*end == '\\' && *(end + 1) && *(end + 1) == '$' && (++end, 1))))
+        end++;
+
+      /* Copy the id substring into a new buffer */
+      data->set.str[STRING_RTSP_SESSION_ID] = malloc(end - start + 1);
+      if(data->set.str[STRING_RTSP_SESSION_ID] == NULL)
+        return CURLE_OUT_OF_MEMORY;
+      memcpy(data->set.str[STRING_RTSP_SESSION_ID], start, end - start);
+      (data->set.str[STRING_RTSP_SESSION_ID])[end - start] = '\0';
+    }
+  }
+  return CURLE_OK;
+}
+
+#endif /* CURL_DISABLE_RTSP */
diff --git a/lib/curl_security.c b/lib/curl_security.c
new file mode 100644 (file)
index 0000000..b7544ff
--- /dev/null
@@ -0,0 +1,604 @@
+/* This source code was modified by Martin Hedenfalk <mhe@stacken.kth.se> for
+ * use in Curl. His latest changes were done 2000-09-18.
+ *
+ * It has since been patched and modified a lot by Daniel Stenberg
+ * <daniel@haxx.se> to make it better applied to curl conditions, and to make
+ * it not use globals, pollute name space and more. This source code awaits a
+ * rewrite to work around the paragraph 2 in the BSD licenses as explained
+ * below.
+ *
+ * Copyright (c) 1998, 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ *
+ * Copyright (C) 2001 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.  */
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_FTP
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include "curl_urldata.h"
+#include "curl_base64.h"
+#include "curl_memory.h"
+#include "curl_krb4.h"
+#include "curl_ftp.h"
+#include "curl_sendf.h"
+#include "curl_rawstr.h"
+#include "curl_warnless.h"
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+static const struct {
+  enum protection_level level;
+  const char *name;
+} level_names[] = {
+  { PROT_CLEAR, "clear" },
+  { PROT_SAFE, "safe" },
+  { PROT_CONFIDENTIAL, "confidential" },
+  { PROT_PRIVATE, "private" }
+};
+
+static enum protection_level
+name_to_level(const char *name)
+{
+  int i;
+  for(i = 0; i < (int)sizeof(level_names)/(int)sizeof(level_names[0]); i++)
+    if(checkprefix(name, level_names[i].name))
+      return level_names[i].level;
+  return PROT_NONE;
+}
+
+/* Convert a protocol |level| to its char representation.
+   We take an int to catch programming mistakes. */
+static char level_to_char(int level) {
+  switch(level) {
+  case PROT_CLEAR:
+    return 'C';
+  case PROT_SAFE:
+    return 'S';
+  case PROT_CONFIDENTIAL:
+    return 'E';
+  case PROT_PRIVATE:
+    return 'P';
+  case PROT_CMD:
+    /* Fall through */
+  default:
+    /* Those 2 cases should not be reached! */
+    break;
+  }
+  DEBUGASSERT(0);
+  /* Default to the most secure alternative. */
+  return 'P';
+}
+
+static const struct Curl_sec_client_mech * const mechs[] = {
+#if defined(HAVE_GSSAPI)
+  &Curl_krb5_client_mech,
+#endif
+#if defined(HAVE_KRB4)
+  &Curl_krb4_client_mech,
+#endif
+  NULL
+};
+
+/* Send an FTP command defined by |message| and the optional arguments. The
+   function returns the ftp_code. If an error occurs, -1 is returned. */
+static int ftp_send_command(struct connectdata *conn, const char *message, ...)
+{
+  int ftp_code;
+  ssize_t nread;
+  va_list args;
+  char print_buffer[50];
+
+  va_start(args, message);
+  vsnprintf(print_buffer, sizeof(print_buffer), message, args);
+  va_end(args);
+
+  if(Curl_ftpsendf(conn, print_buffer) != CURLE_OK) {
+    ftp_code = -1;
+  }
+  else {
+    if(Curl_GetFTPResponse(&nread, conn, &ftp_code) != CURLE_OK)
+      ftp_code = -1;
+  }
+
+  (void)nread; /* Unused */
+  return ftp_code;
+}
+
+/* Read |len| from the socket |fd| and store it in |to|. Return a CURLcode
+   saying whether an error occurred or CURLE_OK if |len| was read. */
+static CURLcode
+socket_read(curl_socket_t fd, void *to, size_t len)
+{
+  char *to_p = to;
+  CURLcode code;
+  ssize_t nread;
+
+  while(len > 0) {
+    code = Curl_read_plain(fd, to_p, len, &nread);
+    if(code == CURLE_OK) {
+      len -= nread;
+      to_p += nread;
+    }
+    else {
+      /* FIXME: We are doing a busy wait */
+      if(code == CURLE_AGAIN)
+        continue;
+      return code;
+    }
+  }
+  return CURLE_OK;
+}
+
+
+/* Write |len| bytes from the buffer |to| to the socket |fd|. Return a
+   CURLcode saying whether an error occurred or CURLE_OK if |len| was
+   written. */
+static CURLcode
+socket_write(struct connectdata *conn, curl_socket_t fd, const void *to,
+             size_t len)
+{
+  const char *to_p = to;
+  CURLcode code;
+  ssize_t written;
+
+  while(len > 0) {
+    code = Curl_write_plain(conn, fd, to_p, len, &written);
+    if(code == CURLE_OK) {
+      len -= written;
+      to_p += written;
+    }
+    else {
+      /* FIXME: We are doing a busy wait */
+      if(code == CURLE_AGAIN)
+        continue;
+      return code;
+    }
+  }
+  return CURLE_OK;
+}
+
+static CURLcode read_data(struct connectdata *conn,
+                          curl_socket_t fd,
+                          struct krb4buffer *buf)
+{
+  int len;
+  void* tmp;
+  CURLcode ret;
+
+  ret = socket_read(fd, &len, sizeof(len));
+  if(ret != CURLE_OK)
+    return ret;
+
+  len = ntohl(len);
+  tmp = realloc(buf->data, len);
+  if(tmp == NULL)
+    return CURLE_OUT_OF_MEMORY;
+
+  buf->data = tmp;
+  ret = socket_read(fd, buf->data, len);
+  if(ret != CURLE_OK)
+    return ret;
+  buf->size = conn->mech->decode(conn->app_data, buf->data, len,
+                                 conn->data_prot, conn);
+  buf->index = 0;
+  return CURLE_OK;
+}
+
+static size_t
+buffer_read(struct krb4buffer *buf, void *data, size_t len)
+{
+  if(buf->size - buf->index < len)
+    len = buf->size - buf->index;
+  memcpy(data, (char*)buf->data + buf->index, len);
+  buf->index += len;
+  return len;
+}
+
+/* Matches Curl_recv signature */
+static ssize_t sec_recv(struct connectdata *conn, int sockindex,
+                        char *buffer, size_t len, CURLcode *err)
+{
+  size_t bytes_read;
+  size_t total_read = 0;
+  curl_socket_t fd = conn->sock[sockindex];
+
+  *err = CURLE_OK;
+
+  /* Handle clear text response. */
+  if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR)
+      return read(fd, buffer, len);
+
+  if(conn->in_buffer.eof_flag) {
+    conn->in_buffer.eof_flag = 0;
+    return 0;
+  }
+
+  bytes_read = buffer_read(&conn->in_buffer, buffer, len);
+  len -= bytes_read;
+  total_read += bytes_read;
+  buffer += bytes_read;
+
+  while(len > 0) {
+    if(read_data(conn, fd, &conn->in_buffer) != CURLE_OK)
+      return -1;
+    if(conn->in_buffer.size == 0) {
+      if(bytes_read > 0)
+        conn->in_buffer.eof_flag = 1;
+      return bytes_read;
+    }
+    bytes_read = buffer_read(&conn->in_buffer, buffer, len);
+    len -= bytes_read;
+    total_read += bytes_read;
+    buffer += bytes_read;
+  }
+  /* FIXME: Check for overflow */
+  return total_read;
+}
+
+/* Send |length| bytes from |from| to the |fd| socket taking care of encoding
+   and negociating with the server. |from| can be NULL. */
+/* FIXME: We don't check for errors nor report any! */
+static void do_sec_send(struct connectdata *conn, curl_socket_t fd,
+                        const char *from, int length)
+{
+  int bytes, htonl_bytes; /* 32-bit integers for htonl */
+  char *buffer = NULL;
+  char *cmd_buffer;
+  size_t cmd_size = 0;
+  CURLcode error;
+  enum protection_level prot_level = conn->data_prot;
+  bool iscmd = (prot_level == PROT_CMD)?TRUE:FALSE;
+
+  DEBUGASSERT(prot_level > PROT_NONE && prot_level < PROT_LAST);
+
+  if(iscmd) {
+    if(!strncmp(from, "PASS ", 5) || !strncmp(from, "ACCT ", 5))
+      prot_level = PROT_PRIVATE;
+    else
+      prot_level = conn->command_prot;
+  }
+  bytes = conn->mech->encode(conn->app_data, from, length, prot_level,
+                             (void**)&buffer, conn);
+  if(!buffer || bytes <= 0)
+    return; /* error */
+
+  if(iscmd) {
+    error = Curl_base64_encode(conn->data, buffer, curlx_sitouz(bytes),
+                               &cmd_buffer, &cmd_size);
+    if(error) {
+      free(buffer);
+      return; /* error */
+    }
+    if(cmd_size > 0) {
+      static const char *enc = "ENC ";
+      static const char *mic = "MIC ";
+      if(prot_level == PROT_PRIVATE)
+        socket_write(conn, fd, enc, 4);
+      else
+        socket_write(conn, fd, mic, 4);
+
+      socket_write(conn, fd, cmd_buffer, cmd_size);
+      socket_write(conn, fd, "\r\n", 2);
+      infof(conn->data, "Send: %s%s\n", prot_level == PROT_PRIVATE?enc:mic,
+            cmd_buffer);
+      free(cmd_buffer);
+    }
+  }
+  else {
+    htonl_bytes = htonl(bytes);
+    socket_write(conn, fd, &htonl_bytes, sizeof(htonl_bytes));
+    socket_write(conn, fd, buffer, curlx_sitouz(bytes));
+  }
+  free(buffer);
+}
+
+static ssize_t sec_write(struct connectdata *conn, curl_socket_t fd,
+                         const char *buffer, size_t length)
+{
+  /* FIXME: Check for overflow */
+  ssize_t tx = 0, len = conn->buffer_size;
+
+  len -= conn->mech->overhead(conn->app_data, conn->data_prot,
+                              curlx_sztosi(len));
+  if(len <= 0)
+    len = length;
+  while(length) {
+    if(len >= 0 || length < (size_t)len) {
+      /* FIXME: Check for overflow. */
+      len = length;
+    }
+    do_sec_send(conn, fd, buffer, curlx_sztosi(len));
+    length -= len;
+    buffer += len;
+    tx += len;
+  }
+  return tx;
+}
+
+/* Matches Curl_send signature */
+static ssize_t sec_send(struct connectdata *conn, int sockindex,
+                        const void *buffer, size_t len, CURLcode *err)
+{
+  curl_socket_t fd = conn->sock[sockindex];
+  *err = CURLE_OK;
+  return sec_write(conn, fd, buffer, len);
+}
+
+int Curl_sec_read_msg(struct connectdata *conn, char *buffer,
+                      enum protection_level level)
+{
+  /* decoded_len should be size_t or ssize_t but conn->mech->decode returns an
+     int */
+  int decoded_len;
+  char *buf;
+  int ret_code;
+  size_t decoded_sz = 0;
+  CURLcode error;
+
+  DEBUGASSERT(level > PROT_NONE && level < PROT_LAST);
+
+  error = Curl_base64_decode(buffer + 4, (unsigned char **)&buf, &decoded_sz);
+  if(error || decoded_sz == 0)
+    return -1;
+
+  if(decoded_sz > (size_t)INT_MAX) {
+    free(buf);
+    return -1;
+  }
+  decoded_len = curlx_uztosi(decoded_sz);
+
+  decoded_len = conn->mech->decode(conn->app_data, buf, decoded_len,
+                                   level, conn);
+  if(decoded_len <= 0) {
+    free(buf);
+    return -1;
+  }
+
+  if(conn->data->set.verbose) {
+    buf[decoded_len] = '\n';
+    Curl_debug(conn->data, CURLINFO_HEADER_IN, buf, decoded_len + 1, conn);
+  }
+
+  buf[decoded_len] = '\0';
+  DEBUGASSERT(decoded_len > 3);
+  if(buf[3] == '-')
+    ret_code = 0;
+  else {
+    /* Check for error? */
+    sscanf(buf, "%d", &ret_code);
+  }
+
+  if(buf[decoded_len - 1] == '\n')
+    buf[decoded_len - 1] = '\0';
+  /* FIXME: Is |buffer| length always greater than |decoded_len|? */
+  strcpy(buffer, buf);
+  free(buf);
+  return ret_code;
+}
+
+/* FIXME: The error code returned here is never checked. */
+static int sec_set_protection_level(struct connectdata *conn)
+{
+  int code;
+  char* pbsz;
+  static unsigned int buffer_size = 1 << 20; /* 1048576 */
+  enum protection_level level = conn->request_data_prot;
+
+  DEBUGASSERT(level > PROT_NONE && level < PROT_LAST);
+
+  if(!conn->sec_complete) {
+    infof(conn->data, "Trying to change the protection level after the"
+                      "completion of the data exchange.\n");
+    return -1;
+  }
+
+  /* Bail out if we try to set up the same level */
+  if(conn->data_prot == level)
+    return 0;
+
+  if(level) {
+    code = ftp_send_command(conn, "PBSZ %u", buffer_size);
+    if(code < 0)
+      return -1;
+
+    if(code/100 != 2) {
+      failf(conn->data, "Failed to set the protection's buffer size.");
+      return -1;
+    }
+    conn->buffer_size = buffer_size;
+
+    pbsz = strstr(conn->data->state.buffer, "PBSZ=");
+    if(pbsz) {
+      /* FIXME: Checks for errors in sscanf? */
+      sscanf(pbsz, "PBSZ=%u", &buffer_size);
+      if(buffer_size < conn->buffer_size)
+        conn->buffer_size = buffer_size;
+    }
+  }
+
+  /* Now try to negiociate the protection level. */
+  code = ftp_send_command(conn, "PROT %c", level_to_char(level));
+
+  if(code < 0)
+    return -1;
+
+  if(code/100 != 2) {
+    failf(conn->data, "Failed to set the protection level.");
+    return -1;
+  }
+
+  conn->data_prot = level;
+  if(level == PROT_PRIVATE)
+    conn->command_prot = level;
+
+  return 0;
+}
+
+int
+Curl_sec_request_prot(struct connectdata *conn, const char *level)
+{
+  enum protection_level l = name_to_level(level);
+  if(l == PROT_NONE)
+    return -1;
+  DEBUGASSERT(l > PROT_NONE && l < PROT_LAST);
+  conn->request_data_prot = l;
+  return 0;
+}
+
+static CURLcode choose_mech(struct connectdata *conn)
+{
+  int ret;
+  struct SessionHandle *data = conn->data;
+  const struct Curl_sec_client_mech * const *mech;
+  void *tmp_allocation;
+  const char *mech_name;
+
+  for(mech = mechs; (*mech); ++mech) {
+    mech_name = (*mech)->name;
+    /* We have no mechanism with a NULL name but keep this check */
+    DEBUGASSERT(mech_name != NULL);
+    if(mech_name == NULL) {
+      infof(data, "Skipping mechanism with empty name (%p)\n", mech);
+      continue;
+    }
+    tmp_allocation = realloc(conn->app_data, (*mech)->size);
+    if(tmp_allocation == NULL) {
+      failf(data, "Failed realloc of size %u", (*mech)->size);
+      mech = NULL;
+      return CURLE_OUT_OF_MEMORY;
+    }
+    conn->app_data = tmp_allocation;
+
+    if((*mech)->init) {
+      ret = (*mech)->init(conn->app_data);
+      if(ret != 0) {
+        infof(data, "Failed initialization for %s. Skipping it.\n", mech_name);
+        continue;
+      }
+    }
+
+    infof(data, "Trying mechanism %s...\n", mech_name);
+    ret = ftp_send_command(conn, "AUTH %s", mech_name);
+    if(ret < 0)
+      /* FIXME: This error is too generic but it is OK for now. */
+      return CURLE_COULDNT_CONNECT;
+
+    if(ret/100 != 3) {
+      switch(ret) {
+      case 504:
+        infof(data, "Mechanism %s is not supported by the server (server "
+                    "returned ftp code: 504).\n", mech_name);
+        break;
+      case 534:
+        infof(data, "Mechanism %s was rejected by the server (server returned "
+                    "ftp code: 534).\n", mech_name);
+        break;
+      default:
+        if(ret/100 == 5) {
+          infof(data, "server does not support the security extensions\n");
+          return CURLE_USE_SSL_FAILED;
+        }
+        break;
+      }
+      continue;
+    }
+
+    /* Authenticate */
+    ret = (*mech)->auth(conn->app_data, conn);
+
+    if(ret == AUTH_CONTINUE)
+      continue;
+    else if(ret != AUTH_OK) {
+      /* Mechanism has dumped the error to stderr, don't error here. */
+      return -1;
+    }
+    DEBUGASSERT(ret == AUTH_OK);
+
+    conn->mech = *mech;
+    conn->sec_complete = 1;
+    conn->recv[FIRSTSOCKET] = sec_recv;
+    conn->send[FIRSTSOCKET] = sec_send;
+    conn->recv[SECONDARYSOCKET] = sec_recv;
+    conn->send[SECONDARYSOCKET] = sec_send;
+    conn->command_prot = PROT_SAFE;
+    /* Set the requested protection level */
+    /* BLOCKING */
+    (void)sec_set_protection_level(conn);
+    break;
+  }
+
+  return mech != NULL ? CURLE_OK : CURLE_FAILED_INIT;
+}
+
+CURLcode
+Curl_sec_login(struct connectdata *conn)
+{
+  return choose_mech(conn);
+}
+
+
+void
+Curl_sec_end(struct connectdata *conn)
+{
+  if(conn->mech != NULL && conn->mech->end)
+    conn->mech->end(conn->app_data);
+  if(conn->app_data) {
+    free(conn->app_data);
+    conn->app_data = NULL;
+  }
+  if(conn->in_buffer.data) {
+    free(conn->in_buffer.data);
+    conn->in_buffer.data = NULL;
+    conn->in_buffer.size = 0;
+    conn->in_buffer.index = 0;
+    /* FIXME: Is this really needed? */
+    conn->in_buffer.eof_flag = 0;
+  }
+  conn->sec_complete = 0;
+  conn->data_prot = PROT_CLEAR;
+  conn->mech = NULL;
+}
+
+#endif /* HAVE_KRB4 || HAVE_GSSAPI */
+
+#endif /* CURL_DISABLE_FTP */
diff --git a/lib/curl_select.c b/lib/curl_select.c
new file mode 100644 (file)
index 0000000..d4519d3
--- /dev/null
@@ -0,0 +1,529 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#if !defined(HAVE_SELECT) && !defined(HAVE_POLL_FINE)
+#error "We can't compile without select() or poll() support."
+#endif
+
+#if defined(__BEOS__) && !defined(__HAIKU__)
+/* BeOS has FD_SET defined in socket.h */
+#include <socket.h>
+#endif
+
+#ifdef MSDOS
+#include <dos.h>  /* delay() */
+#endif
+
+#include <curl/curl.h>
+
+#include "curl_urldata.h"
+#include "curl_connect.h"
+#include "curl_select.h"
+#include "curl_warnless.h"
+
+/* Convenience local macros */
+
+#define elapsed_ms  (int)curlx_tvdiff(curlx_tvnow(), initial_tv)
+
+#ifdef CURL_ACKNOWLEDGE_EINTR
+#define error_not_EINTR (1)
+#else
+#define error_not_EINTR (error != EINTR)
+#endif
+
+/*
+ * Internal function used for waiting a specific amount of ms
+ * in Curl_socket_ready() and Curl_poll() when no file descriptor
+ * is provided to wait on, just being used to delay execution.
+ * WinSock select() and poll() timeout mechanisms need a valid
+ * socket descriptor in a not null file descriptor set to work.
+ * Waiting indefinitely with this function is not allowed, a
+ * zero or negative timeout value will return immediately.
+ * Timeout resolution, accuracy, as well as maximum supported
+ * value is system dependent, neither factor is a citical issue
+ * for the intended use of this function in the library.
+ * On non-DOS and non-Winsock platforms, when compiled with
+ * CURL_ACKNOWLEDGE_EINTR defined, EINTR condition is honored
+ * and function might exit early without awaiting full timeout,
+ * otherwise EINTR will be ignored and full timeout will elapse.
+ *
+ * Return values:
+ *   -1 = system call error, invalid timeout value, or interrupted
+ *    0 = specified timeout has elapsed
+ */
+int Curl_wait_ms(int timeout_ms)
+{
+#if !defined(MSDOS) && !defined(USE_WINSOCK)
+#ifndef HAVE_POLL_FINE
+  struct timeval pending_tv;
+#endif
+  struct timeval initial_tv;
+  int pending_ms;
+  int error;
+#endif
+  int r = 0;
+
+  if(!timeout_ms)
+    return 0;
+  if(timeout_ms < 0) {
+    SET_SOCKERRNO(EINVAL);
+    return -1;
+  }
+#if defined(MSDOS)
+  delay(timeout_ms);
+#elif defined(USE_WINSOCK)
+  Sleep(timeout_ms);
+#else
+  pending_ms = timeout_ms;
+  initial_tv = curlx_tvnow();
+  do {
+#if defined(HAVE_POLL_FINE)
+    r = poll(NULL, 0, pending_ms);
+#else
+    pending_tv.tv_sec = pending_ms / 1000;
+    pending_tv.tv_usec = (pending_ms % 1000) * 1000;
+    r = select(0, NULL, NULL, NULL, &pending_tv);
+#endif /* HAVE_POLL_FINE */
+    if(r != -1)
+      break;
+    error = SOCKERRNO;
+    if(error && error_not_EINTR)
+      break;
+    pending_ms = timeout_ms - elapsed_ms;
+    if(pending_ms <= 0)
+      break;
+  } while(r == -1);
+#endif /* USE_WINSOCK */
+  if(r)
+    r = -1;
+  return r;
+}
+
+/*
+ * Wait for read or write events on a set of file descriptors. It uses poll()
+ * when a fine poll() is available, in order to avoid limits with FD_SETSIZE,
+ * otherwise select() is used.  An error is returned if select() is being used
+ * and a file descriptor is too large for FD_SETSIZE.
+ *
+ * A negative timeout value makes this function wait indefinitely,
+ * unles no valid file descriptor is given, when this happens the
+ * negative timeout is ignored and the function times out immediately.
+ * When compiled with CURL_ACKNOWLEDGE_EINTR defined, EINTR condition
+ * is honored and function might exit early without awaiting timeout,
+ * otherwise EINTR will be ignored.
+ *
+ * Return values:
+ *   -1 = system call error or fd >= FD_SETSIZE
+ *    0 = timeout
+ *    [bitmask] = action as described below
+ *
+ * CURL_CSELECT_IN - first socket is readable
+ * CURL_CSELECT_IN2 - second socket is readable
+ * CURL_CSELECT_OUT - write socket is writable
+ * CURL_CSELECT_ERR - an error condition occurred
+ */
+int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */
+                      curl_socket_t readfd1,
+                      curl_socket_t writefd, /* socket to write to */
+                      long timeout_ms)       /* milliseconds to wait */
+{
+#ifdef HAVE_POLL_FINE
+  struct pollfd pfd[3];
+  int num;
+#else
+  struct timeval pending_tv;
+  struct timeval *ptimeout;
+  fd_set fds_read;
+  fd_set fds_write;
+  fd_set fds_err;
+  curl_socket_t maxfd;
+#endif
+  struct timeval initial_tv = {0,0};
+  int pending_ms = 0;
+  int error;
+  int r;
+  int ret;
+
+  if((readfd0 == CURL_SOCKET_BAD) && (readfd1 == CURL_SOCKET_BAD) &&
+     (writefd == CURL_SOCKET_BAD)) {
+    /* no sockets, just wait */
+    r = Curl_wait_ms((int)timeout_ms);
+    return r;
+  }
+
+  /* Avoid initial timestamp, avoid curlx_tvnow() call, when elapsed
+     time in this function does not need to be measured. This happens
+     when function is called with a zero timeout or a negative timeout
+     value indicating a blocking call should be performed. */
+
+  if(timeout_ms > 0) {
+    pending_ms = (int)timeout_ms;
+    initial_tv = curlx_tvnow();
+  }
+
+#ifdef HAVE_POLL_FINE
+
+  num = 0;
+  if(readfd0 != CURL_SOCKET_BAD) {
+    pfd[num].fd = readfd0;
+    pfd[num].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI;
+    pfd[num].revents = 0;
+    num++;
+  }
+  if(readfd1 != CURL_SOCKET_BAD) {
+    pfd[num].fd = readfd1;
+    pfd[num].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI;
+    pfd[num].revents = 0;
+    num++;
+  }
+  if(writefd != CURL_SOCKET_BAD) {
+    pfd[num].fd = writefd;
+    pfd[num].events = POLLWRNORM|POLLOUT;
+    pfd[num].revents = 0;
+    num++;
+  }
+
+  do {
+    if(timeout_ms < 0)
+      pending_ms = -1;
+    else if(!timeout_ms)
+      pending_ms = 0;
+    r = poll(pfd, num, pending_ms);
+    if(r != -1)
+      break;
+    error = SOCKERRNO;
+    if(error && error_not_EINTR)
+      break;
+    if(timeout_ms > 0) {
+      pending_ms = (int)(timeout_ms - elapsed_ms);
+      if(pending_ms <= 0) {
+        r = 0;  /* Simulate a "call timed out" case */
+        break;
+      }
+    }
+  } while(r == -1);
+
+  if(r < 0)
+    return -1;
+  if(r == 0)
+    return 0;
+
+  ret = 0;
+  num = 0;
+  if(readfd0 != CURL_SOCKET_BAD) {
+    if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP))
+      ret |= CURL_CSELECT_IN;
+    if(pfd[num].revents & (POLLRDBAND|POLLPRI|POLLNVAL))
+      ret |= CURL_CSELECT_ERR;
+    num++;
+  }
+  if(readfd1 != CURL_SOCKET_BAD) {
+    if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP))
+      ret |= CURL_CSELECT_IN2;
+    if(pfd[num].revents & (POLLRDBAND|POLLPRI|POLLNVAL))
+      ret |= CURL_CSELECT_ERR;
+    num++;
+  }
+  if(writefd != CURL_SOCKET_BAD) {
+    if(pfd[num].revents & (POLLWRNORM|POLLOUT))
+      ret |= CURL_CSELECT_OUT;
+    if(pfd[num].revents & (POLLERR|POLLHUP|POLLNVAL))
+      ret |= CURL_CSELECT_ERR;
+  }
+
+  return ret;
+
+#else  /* HAVE_POLL_FINE */
+
+  FD_ZERO(&fds_err);
+  maxfd = (curl_socket_t)-1;
+
+  FD_ZERO(&fds_read);
+  if(readfd0 != CURL_SOCKET_BAD) {
+    VERIFY_SOCK(readfd0);
+    FD_SET(readfd0, &fds_read);
+    FD_SET(readfd0, &fds_err);
+    maxfd = readfd0;
+  }
+  if(readfd1 != CURL_SOCKET_BAD) {
+    VERIFY_SOCK(readfd1);
+    FD_SET(readfd1, &fds_read);
+    FD_SET(readfd1, &fds_err);
+    if(readfd1 > maxfd)
+      maxfd = readfd1;
+  }
+
+  FD_ZERO(&fds_write);
+  if(writefd != CURL_SOCKET_BAD) {
+    VERIFY_SOCK(writefd);
+    FD_SET(writefd, &fds_write);
+    FD_SET(writefd, &fds_err);
+    if(writefd > maxfd)
+      maxfd = writefd;
+  }
+
+  ptimeout = (timeout_ms < 0) ? NULL : &pending_tv;
+
+  do {
+    if(timeout_ms > 0) {
+      pending_tv.tv_sec = pending_ms / 1000;
+      pending_tv.tv_usec = (pending_ms % 1000) * 1000;
+    }
+    else if(!timeout_ms) {
+      pending_tv.tv_sec = 0;
+      pending_tv.tv_usec = 0;
+    }
+    r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, ptimeout);
+    if(r != -1)
+      break;
+    error = SOCKERRNO;
+    if(error && error_not_EINTR)
+      break;
+    if(timeout_ms > 0) {
+      pending_ms = timeout_ms - elapsed_ms;
+      if(pending_ms <= 0) {
+        r = 0;  /* Simulate a "call timed out" case */
+        break;
+      }
+    }
+  } while(r == -1);
+
+  if(r < 0)
+    return -1;
+  if(r == 0)
+    return 0;
+
+  ret = 0;
+  if(readfd0 != CURL_SOCKET_BAD) {
+    if(FD_ISSET(readfd0, &fds_read))
+      ret |= CURL_CSELECT_IN;
+    if(FD_ISSET(readfd0, &fds_err))
+      ret |= CURL_CSELECT_ERR;
+  }
+  if(readfd1 != CURL_SOCKET_BAD) {
+    if(FD_ISSET(readfd1, &fds_read))
+      ret |= CURL_CSELECT_IN2;
+    if(FD_ISSET(readfd1, &fds_err))
+      ret |= CURL_CSELECT_ERR;
+  }
+  if(writefd != CURL_SOCKET_BAD) {
+    if(FD_ISSET(writefd, &fds_write))
+      ret |= CURL_CSELECT_OUT;
+    if(FD_ISSET(writefd, &fds_err))
+      ret |= CURL_CSELECT_ERR;
+  }
+
+  return ret;
+
+#endif  /* HAVE_POLL_FINE */
+
+}
+
+/*
+ * This is a wrapper around poll().  If poll() does not exist, then
+ * select() is used instead.  An error is returned if select() is
+ * being used and a file descriptor is too large for FD_SETSIZE.
+ * A negative timeout value makes this function wait indefinitely,
+ * unles no valid file descriptor is given, when this happens the
+ * negative timeout is ignored and the function times out immediately.
+ * When compiled with CURL_ACKNOWLEDGE_EINTR defined, EINTR condition
+ * is honored and function might exit early without awaiting timeout,
+ * otherwise EINTR will be ignored.
+ *
+ * Return values:
+ *   -1 = system call error or fd >= FD_SETSIZE
+ *    0 = timeout
+ *    N = number of structures with non zero revent fields
+ */
+int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms)
+{
+#ifndef HAVE_POLL_FINE
+  struct timeval pending_tv;
+  struct timeval *ptimeout;
+  fd_set fds_read;
+  fd_set fds_write;
+  fd_set fds_err;
+  curl_socket_t maxfd;
+#endif
+  struct timeval initial_tv = {0,0};
+  bool fds_none = TRUE;
+  unsigned int i;
+  int pending_ms = 0;
+  int error;
+  int r;
+
+  if(ufds) {
+    for(i = 0; i < nfds; i++) {
+      if(ufds[i].fd != CURL_SOCKET_BAD) {
+        fds_none = FALSE;
+        break;
+      }
+    }
+  }
+  if(fds_none) {
+    r = Curl_wait_ms(timeout_ms);
+    return r;
+  }
+
+  /* Avoid initial timestamp, avoid curlx_tvnow() call, when elapsed
+     time in this function does not need to be measured. This happens
+     when function is called with a zero timeout or a negative timeout
+     value indicating a blocking call should be performed. */
+
+  if(timeout_ms > 0) {
+    pending_ms = timeout_ms;
+    initial_tv = curlx_tvnow();
+  }
+
+#ifdef HAVE_POLL_FINE
+
+  do {
+    if(timeout_ms < 0)
+      pending_ms = -1;
+    else if(!timeout_ms)
+      pending_ms = 0;
+    r = poll(ufds, nfds, pending_ms);
+    if(r != -1)
+      break;
+    error = SOCKERRNO;
+    if(error && error_not_EINTR)
+      break;
+    if(timeout_ms > 0) {
+      pending_ms = timeout_ms - elapsed_ms;
+      if(pending_ms <= 0)
+        break;
+    }
+  } while(r == -1);
+
+  if(r < 0)
+    return -1;
+  if(r == 0)
+    return 0;
+
+  for(i = 0; i < nfds; i++) {
+    if(ufds[i].fd == CURL_SOCKET_BAD)
+      continue;
+    if(ufds[i].revents & POLLHUP)
+      ufds[i].revents |= POLLIN;
+    if(ufds[i].revents & POLLERR)
+      ufds[i].revents |= (POLLIN|POLLOUT);
+  }
+
+#else  /* HAVE_POLL_FINE */
+
+  FD_ZERO(&fds_read);
+  FD_ZERO(&fds_write);
+  FD_ZERO(&fds_err);
+  maxfd = (curl_socket_t)-1;
+
+  for(i = 0; i < nfds; i++) {
+    ufds[i].revents = 0;
+    if(ufds[i].fd == CURL_SOCKET_BAD)
+      continue;
+    VERIFY_SOCK(ufds[i].fd);
+    if(ufds[i].events & (POLLIN|POLLOUT|POLLPRI|
+                          POLLRDNORM|POLLWRNORM|POLLRDBAND)) {
+      if(ufds[i].fd > maxfd)
+        maxfd = ufds[i].fd;
+      if(ufds[i].events & (POLLRDNORM|POLLIN))
+        FD_SET(ufds[i].fd, &fds_read);
+      if(ufds[i].events & (POLLWRNORM|POLLOUT))
+        FD_SET(ufds[i].fd, &fds_write);
+      if(ufds[i].events & (POLLRDBAND|POLLPRI))
+        FD_SET(ufds[i].fd, &fds_err);
+    }
+  }
+
+  ptimeout = (timeout_ms < 0) ? NULL : &pending_tv;
+
+  do {
+    if(timeout_ms > 0) {
+      pending_tv.tv_sec = pending_ms / 1000;
+      pending_tv.tv_usec = (pending_ms % 1000) * 1000;
+    }
+    else if(!timeout_ms) {
+      pending_tv.tv_sec = 0;
+      pending_tv.tv_usec = 0;
+    }
+    r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, ptimeout);
+    if(r != -1)
+      break;
+    error = SOCKERRNO;
+    if(error && error_not_EINTR)
+      break;
+    if(timeout_ms > 0) {
+      pending_ms = timeout_ms - elapsed_ms;
+      if(pending_ms <= 0)
+        break;
+    }
+  } while(r == -1);
+
+  if(r < 0)
+    return -1;
+  if(r == 0)
+    return 0;
+
+  r = 0;
+  for(i = 0; i < nfds; i++) {
+    ufds[i].revents = 0;
+    if(ufds[i].fd == CURL_SOCKET_BAD)
+      continue;
+    if(FD_ISSET(ufds[i].fd, &fds_read))
+      ufds[i].revents |= POLLIN;
+    if(FD_ISSET(ufds[i].fd, &fds_write))
+      ufds[i].revents |= POLLOUT;
+    if(FD_ISSET(ufds[i].fd, &fds_err))
+      ufds[i].revents |= POLLPRI;
+    if(ufds[i].revents != 0)
+      r++;
+  }
+
+#endif  /* HAVE_POLL_FINE */
+
+  return r;
+}
+
+#ifdef TPF
+/*
+ * This is a replacement for select() on the TPF platform.
+ * It is used whenever libcurl calls select().
+ * The call below to tpf_process_signals() is required because
+ * TPF's select calls are not signal interruptible.
+ *
+ * Return values are the same as select's.
+ */
+int tpf_select_libcurl(int maxfds, fd_set* reads, fd_set* writes,
+                       fd_set* excepts, struct timeval* tv)
+{
+   int rc;
+
+   rc = tpf_select_bsd(maxfds, reads, writes, excepts, tv);
+   tpf_process_signals();
+   return(rc);
+}
+#endif /* TPF */
diff --git a/lib/curl_sendf.c b/lib/curl_sendf.c
new file mode 100644 (file)
index 0000000..a1ec509
--- /dev/null
@@ -0,0 +1,687 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_connect.h"
+#include "curl_sslgen.h"
+#include "curl_ssh.h"
+#include "curl_multiif.h"
+#include "curl_non_ascii.h"
+
+#define _MPRINTF_REPLACE /* use the internal *printf() functions */
+#include <curl/mprintf.h>
+
+/* the krb4 functions only exists for FTP and if krb4 or gssapi is defined */
+#if !defined(CURL_DISABLE_FTP) && (defined(HAVE_KRB4) || defined(HAVE_GSSAPI))
+#include "curl_krb4.h"
+#else
+#define Curl_sec_send(a,b,c,d) -1
+#define Curl_sec_read(a,b,c,d) -1
+#endif
+
+#include "curl_memory.h"
+#include "curl_strerror.h"
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+#ifdef CURL_DO_LINEEND_CONV
+/*
+ * convert_lineends() changes CRLF (\r\n) end-of-line markers to a single LF
+ * (\n), with special processing for CRLF sequences that are split between two
+ * blocks of data.  Remaining, bare CRs are changed to LFs.  The possibly new
+ * size of the data is returned.
+ */
+static size_t convert_lineends(struct SessionHandle *data,
+                               char *startPtr, size_t size)
+{
+  char *inPtr, *outPtr;
+
+  /* sanity check */
+  if((startPtr == NULL) || (size < 1)) {
+    return(size);
+  }
+
+  if(data->state.prev_block_had_trailing_cr) {
+    /* The previous block of incoming data
+       had a trailing CR, which was turned into a LF. */
+    if(*startPtr == '\n') {
+      /* This block of incoming data starts with the
+         previous block's LF so get rid of it */
+      memmove(startPtr, startPtr+1, size-1);
+      size--;
+      /* and it wasn't a bare CR but a CRLF conversion instead */
+      data->state.crlf_conversions++;
+    }
+    data->state.prev_block_had_trailing_cr = FALSE; /* reset the flag */
+  }
+
+  /* find 1st CR, if any */
+  inPtr = outPtr = memchr(startPtr, '\r', size);
+  if(inPtr) {
+    /* at least one CR, now look for CRLF */
+    while(inPtr < (startPtr+size-1)) {
+      /* note that it's size-1, so we'll never look past the last byte */
+      if(memcmp(inPtr, "\r\n", 2) == 0) {
+        /* CRLF found, bump past the CR and copy the NL */
+        inPtr++;
+        *outPtr = *inPtr;
+        /* keep track of how many CRLFs we converted */
+        data->state.crlf_conversions++;
+      }
+      else {
+        if(*inPtr == '\r') {
+          /* lone CR, move LF instead */
+          *outPtr = '\n';
+        }
+        else {
+          /* not a CRLF nor a CR, just copy whatever it is */
+          *outPtr = *inPtr;
+        }
+      }
+      outPtr++;
+      inPtr++;
+    } /* end of while loop */
+
+    if(inPtr < startPtr+size) {
+      /* handle last byte */
+      if(*inPtr == '\r') {
+        /* deal with a CR at the end of the buffer */
+        *outPtr = '\n'; /* copy a NL instead */
+        /* note that a CRLF might be split across two blocks */
+        data->state.prev_block_had_trailing_cr = TRUE;
+      }
+      else {
+        /* copy last byte */
+        *outPtr = *inPtr;
+      }
+      outPtr++;
+    }
+    if(outPtr < startPtr+size)
+      /* tidy up by null terminating the now shorter data */
+      *outPtr = '\0';
+
+    return(outPtr - startPtr);
+  }
+  return(size);
+}
+#endif /* CURL_DO_LINEEND_CONV */
+
+/* Curl_infof() is for info message along the way */
+
+void Curl_infof(struct SessionHandle *data, const char *fmt, ...)
+{
+  if(data && data->set.verbose) {
+    va_list ap;
+    size_t len;
+    char print_buffer[2048 + 1];
+    va_start(ap, fmt);
+    vsnprintf(print_buffer, sizeof(print_buffer), fmt, ap);
+    va_end(ap);
+    len = strlen(print_buffer);
+    Curl_debug(data, CURLINFO_TEXT, print_buffer, len, NULL);
+  }
+}
+
+/* Curl_failf() is for messages stating why we failed.
+ * The message SHALL NOT include any LF or CR.
+ */
+
+void Curl_failf(struct SessionHandle *data, const char *fmt, ...)
+{
+  va_list ap;
+  size_t len;
+  va_start(ap, fmt);
+
+  vsnprintf(data->state.buffer, BUFSIZE, fmt, ap);
+
+  if(data->set.errorbuffer && !data->state.errorbuf) {
+    snprintf(data->set.errorbuffer, CURL_ERROR_SIZE, "%s", data->state.buffer);
+    data->state.errorbuf = TRUE; /* wrote error string */
+  }
+  if(data->set.verbose) {
+    len = strlen(data->state.buffer);
+    if(len < BUFSIZE - 1) {
+      data->state.buffer[len] = '\n';
+      data->state.buffer[++len] = '\0';
+    }
+    Curl_debug(data, CURLINFO_TEXT, data->state.buffer, len, NULL);
+  }
+
+  va_end(ap);
+}
+
+/* Curl_sendf() sends formated data to the server */
+CURLcode Curl_sendf(curl_socket_t sockfd, struct connectdata *conn,
+                    const char *fmt, ...)
+{
+  struct SessionHandle *data = conn->data;
+  ssize_t bytes_written;
+  size_t write_len;
+  CURLcode res = CURLE_OK;
+  char *s;
+  char *sptr;
+  va_list ap;
+  va_start(ap, fmt);
+  s = vaprintf(fmt, ap); /* returns an allocated string */
+  va_end(ap);
+  if(!s)
+    return CURLE_OUT_OF_MEMORY; /* failure */
+
+  bytes_written=0;
+  write_len = strlen(s);
+  sptr = s;
+
+  for(;;) {
+    /* Write the buffer to the socket */
+    res = Curl_write(conn, sockfd, sptr, write_len, &bytes_written);
+
+    if(CURLE_OK != res)
+      break;
+
+    if(data->set.verbose)
+      Curl_debug(data, CURLINFO_DATA_OUT, sptr, (size_t)bytes_written, conn);
+
+    if((size_t)bytes_written != write_len) {
+      /* if not all was written at once, we must advance the pointer, decrease
+         the size left and try again! */
+      write_len -= bytes_written;
+      sptr += bytes_written;
+    }
+    else
+      break;
+  }
+
+  free(s); /* free the output string */
+
+  return res;
+}
+
+/*
+ * Curl_write() is an internal write function that sends data to the
+ * server. Works with plain sockets, SCP, SSL or kerberos.
+ *
+ * If the write would block (CURLE_AGAIN), we return CURLE_OK and
+ * (*written == 0). Otherwise we return regular CURLcode value.
+ */
+CURLcode Curl_write(struct connectdata *conn,
+                    curl_socket_t sockfd,
+                    const void *mem,
+                    size_t len,
+                    ssize_t *written)
+{
+  ssize_t bytes_written;
+  CURLcode curlcode = CURLE_OK;
+  int num = (sockfd == conn->sock[SECONDARYSOCKET]);
+
+  bytes_written = conn->send[num](conn, num, mem, len, &curlcode);
+
+  *written = bytes_written;
+  if(bytes_written >= 0)
+    /* we completely ignore the curlcode value when subzero is not returned */
+    return CURLE_OK;
+
+  /* handle CURLE_AGAIN or a send failure */
+  switch(curlcode) {
+  case CURLE_AGAIN:
+    *written = 0;
+    return CURLE_OK;
+
+  case CURLE_OK:
+    /* general send failure */
+    return CURLE_SEND_ERROR;
+
+  default:
+    /* we got a specific curlcode, forward it */
+    return curlcode;
+  }
+}
+
+ssize_t Curl_send_plain(struct connectdata *conn, int num,
+                        const void *mem, size_t len, CURLcode *code)
+{
+  curl_socket_t sockfd = conn->sock[num];
+  ssize_t bytes_written = swrite(sockfd, mem, len);
+
+  *code = CURLE_OK;
+  if(-1 == bytes_written) {
+    int err = SOCKERRNO;
+
+    if(
+#ifdef WSAEWOULDBLOCK
+      /* This is how Windows does it */
+      (WSAEWOULDBLOCK == err)
+#else
+      /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
+         due to its inability to send off data without blocking. We therefor
+         treat both error codes the same here */
+      (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err)
+#endif
+      ) {
+      /* this is just a case of EWOULDBLOCK */
+      bytes_written=0;
+      *code = CURLE_AGAIN;
+    }
+    else {
+      failf(conn->data, "Send failure: %s",
+            Curl_strerror(conn, err));
+      conn->data->state.os_errno = err;
+      *code = CURLE_SEND_ERROR;
+    }
+  }
+  return bytes_written;
+}
+
+/*
+ * Curl_write_plain() is an internal write function that sends data to the
+ * server using plain sockets only. Otherwise meant to have the exact same
+ * proto as Curl_write()
+ */
+CURLcode Curl_write_plain(struct connectdata *conn,
+                          curl_socket_t sockfd,
+                          const void *mem,
+                          size_t len,
+                          ssize_t *written)
+{
+  ssize_t bytes_written;
+  CURLcode retcode;
+  int num = (sockfd == conn->sock[SECONDARYSOCKET]);
+
+  bytes_written = Curl_send_plain(conn, num, mem, len, &retcode);
+
+  *written = bytes_written;
+
+  return retcode;
+}
+
+ssize_t Curl_recv_plain(struct connectdata *conn, int num, char *buf,
+                        size_t len, CURLcode *code)
+{
+  curl_socket_t sockfd = conn->sock[num];
+  ssize_t nread = sread(sockfd, buf, len);
+
+  *code = CURLE_OK;
+  if(-1 == nread) {
+    int err = SOCKERRNO;
+
+    if(
+#ifdef WSAEWOULDBLOCK
+      /* This is how Windows does it */
+      (WSAEWOULDBLOCK == err)
+#else
+      /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
+         due to its inability to send off data without blocking. We therefor
+         treat both error codes the same here */
+      (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err)
+#endif
+      ) {
+      /* this is just a case of EWOULDBLOCK */
+      *code = CURLE_AGAIN;
+    }
+    else {
+      failf(conn->data, "Recv failure: %s",
+            Curl_strerror(conn, err));
+      conn->data->state.os_errno = err;
+      *code = CURLE_RECV_ERROR;
+    }
+  }
+  return nread;
+}
+
+static CURLcode pausewrite(struct SessionHandle *data,
+                           int type, /* what type of data */
+                           const char *ptr,
+                           size_t len)
+{
+  /* signalled to pause sending on this connection, but since we have data
+     we want to send we need to dup it to save a copy for when the sending
+     is again enabled */
+  struct SingleRequest *k = &data->req;
+  char *dupl = malloc(len);
+  if(!dupl)
+    return CURLE_OUT_OF_MEMORY;
+
+  memcpy(dupl, ptr, len);
+
+  /* store this information in the state struct for later use */
+  data->state.tempwrite = dupl;
+  data->state.tempwritesize = len;
+  data->state.tempwritetype = type;
+
+  /* mark the connection as RECV paused */
+  k->keepon |= KEEP_RECV_PAUSE;
+
+  DEBUGF(infof(data, "Pausing with %zu bytes in buffer for type %02x\n",
+               len, type));
+
+  return CURLE_OK;
+}
+
+
+/* Curl_client_write() sends data to the write callback(s)
+
+   The bit pattern defines to what "streams" to write to. Body and/or header.
+   The defines are in curl_sendf.h of course.
+
+   If CURL_DO_LINEEND_CONV is enabled, data is converted IN PLACE to the
+   local character encoding.  This is a problem and should be changed in
+   the future to leave the original data alone.
+ */
+CURLcode Curl_client_write(struct connectdata *conn,
+                           int type,
+                           char *ptr,
+                           size_t len)
+{
+  struct SessionHandle *data = conn->data;
+  size_t wrote;
+
+  if(0 == len)
+    len = strlen(ptr);
+
+  /* If reading is actually paused, we're forced to append this chunk of data
+     to the already held data, but only if it is the same type as otherwise it
+     can't work and it'll return error instead. */
+  if(data->req.keepon & KEEP_RECV_PAUSE) {
+    size_t newlen;
+    char *newptr;
+    if(type != data->state.tempwritetype)
+      /* major internal confusion */
+      return CURLE_RECV_ERROR;
+
+    DEBUGASSERT(data->state.tempwrite);
+
+    /* figure out the new size of the data to save */
+    newlen = len + data->state.tempwritesize;
+    /* allocate the new memory area */
+    newptr = realloc(data->state.tempwrite, newlen);
+    if(!newptr)
+      return CURLE_OUT_OF_MEMORY;
+    /* copy the new data to the end of the new area */
+    memcpy(newptr + data->state.tempwritesize, ptr, len);
+    /* update the pointer and the size */
+    data->state.tempwrite = newptr;
+    data->state.tempwritesize = newlen;
+
+    return CURLE_OK;
+  }
+
+  if(type & CLIENTWRITE_BODY) {
+    if((conn->handler->protocol&CURLPROTO_FTP) &&
+       conn->proto.ftpc.transfertype == 'A') {
+      /* convert from the network encoding */
+      CURLcode rc = Curl_convert_from_network(data, ptr, len);
+      /* Curl_convert_from_network calls failf if unsuccessful */
+      if(rc)
+        return rc;
+
+#ifdef CURL_DO_LINEEND_CONV
+      /* convert end-of-line markers */
+      len = convert_lineends(data, ptr, len);
+#endif /* CURL_DO_LINEEND_CONV */
+    }
+    /* If the previous block of data ended with CR and this block of data is
+       just a NL, then the length might be zero */
+    if(len) {
+      wrote = data->set.fwrite_func(ptr, 1, len, data->set.out);
+    }
+    else {
+      wrote = len;
+    }
+
+    if(CURL_WRITEFUNC_PAUSE == wrote)
+      return pausewrite(data, type, ptr, len);
+
+    if(wrote != len) {
+      failf(data, "Failed writing body (%zu != %zu)", wrote, len);
+      return CURLE_WRITE_ERROR;
+    }
+  }
+
+  if((type & CLIENTWRITE_HEADER) &&
+     (data->set.fwrite_header || data->set.writeheader) ) {
+    /*
+     * Write headers to the same callback or to the especially setup
+     * header callback function (added after version 7.7.1).
+     */
+    curl_write_callback writeit=
+      data->set.fwrite_header?data->set.fwrite_header:data->set.fwrite_func;
+
+    /* Note: The header is in the host encoding
+       regardless of the ftp transfer mode (ASCII/Image) */
+
+    wrote = writeit(ptr, 1, len, data->set.writeheader);
+    if(CURL_WRITEFUNC_PAUSE == wrote)
+      /* here we pass in the HEADER bit only since if this was body as well
+         then it was passed already and clearly that didn't trigger the pause,
+         so this is saved for later with the HEADER bit only */
+      return pausewrite(data, CLIENTWRITE_HEADER, ptr, len);
+
+    if(wrote != len) {
+      failf (data, "Failed writing header");
+      return CURLE_WRITE_ERROR;
+    }
+  }
+
+  return CURLE_OK;
+}
+
+CURLcode Curl_read_plain(curl_socket_t sockfd,
+                         char *buf,
+                         size_t bytesfromsocket,
+                         ssize_t *n)
+{
+  ssize_t nread = sread(sockfd, buf, bytesfromsocket);
+
+  if(-1 == nread) {
+    int err = SOCKERRNO;
+#ifdef USE_WINSOCK
+    if(WSAEWOULDBLOCK == err)
+#else
+    if((EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err))
+#endif
+      return CURLE_AGAIN;
+    else
+      return CURLE_RECV_ERROR;
+  }
+
+  /* we only return number of bytes read when we return OK */
+  *n = nread;
+  return CURLE_OK;
+}
+
+/*
+ * Internal read-from-socket function. This is meant to deal with plain
+ * sockets, SSL sockets and kerberos sockets.
+ *
+ * Returns a regular CURLcode value.
+ */
+CURLcode Curl_read(struct connectdata *conn, /* connection data */
+                   curl_socket_t sockfd,     /* read from this socket */
+                   char *buf,                /* store read data here */
+                   size_t sizerequested,     /* max amount to read */
+                   ssize_t *n)               /* amount bytes read */
+{
+  CURLcode curlcode = CURLE_RECV_ERROR;
+  ssize_t nread = 0;
+  size_t bytesfromsocket = 0;
+  char *buffertofill = NULL;
+  bool pipelining = (conn->data->multi &&
+                     Curl_multi_canPipeline(conn->data->multi)) ? TRUE : FALSE;
+
+  /* Set 'num' to 0 or 1, depending on which socket that has been sent here.
+     If it is the second socket, we set num to 1. Otherwise to 0. This lets
+     us use the correct ssl handle. */
+  int num = (sockfd == conn->sock[SECONDARYSOCKET]);
+
+  *n=0; /* reset amount to zero */
+
+  /* If session can pipeline, check connection buffer  */
+  if(pipelining) {
+    size_t bytestocopy = CURLMIN(conn->buf_len - conn->read_pos,
+                                 sizerequested);
+
+    /* Copy from our master buffer first if we have some unread data there*/
+    if(bytestocopy > 0) {
+      memcpy(buf, conn->master_buffer + conn->read_pos, bytestocopy);
+      conn->read_pos += bytestocopy;
+      conn->bits.stream_was_rewound = FALSE;
+
+      *n = (ssize_t)bytestocopy;
+      return CURLE_OK;
+    }
+    /* If we come here, it means that there is no data to read from the buffer,
+     * so we read from the socket */
+    bytesfromsocket = CURLMIN(sizerequested, BUFSIZE * sizeof (char));
+    buffertofill = conn->master_buffer;
+  }
+  else {
+    bytesfromsocket = CURLMIN((long)sizerequested,
+                              conn->data->set.buffer_size ?
+                              conn->data->set.buffer_size : BUFSIZE);
+    buffertofill = buf;
+  }
+
+  nread = conn->recv[num](conn, num, buffertofill, bytesfromsocket, &curlcode);
+  if(nread < 0)
+    return curlcode;
+
+  if(pipelining) {
+    memcpy(buf, conn->master_buffer, nread);
+    conn->buf_len = nread;
+    conn->read_pos = nread;
+  }
+
+  *n += nread;
+
+  return CURLE_OK;
+}
+
+/* return 0 on success */
+static int showit(struct SessionHandle *data, curl_infotype type,
+                  char *ptr, size_t size)
+{
+  static const char s_infotype[CURLINFO_END][3] = {
+    "* ", "< ", "> ", "{ ", "} ", "{ ", "} " };
+
+#ifdef CURL_DOES_CONVERSIONS
+  char buf[BUFSIZE+1];
+  size_t conv_size = 0;
+
+  switch(type) {
+  case CURLINFO_HEADER_OUT:
+    /* assume output headers are ASCII */
+    /* copy the data into my buffer so the original is unchanged */
+    if(size > BUFSIZE) {
+      size = BUFSIZE; /* truncate if necessary */
+      buf[BUFSIZE] = '\0';
+    }
+    conv_size = size;
+    memcpy(buf, ptr, size);
+    /* Special processing is needed for this block if it
+     * contains both headers and data (separated by CRLFCRLF).
+     * We want to convert just the headers, leaving the data as-is.
+     */
+    if(size > 4) {
+      size_t i;
+      for(i = 0; i < size-4; i++) {
+        if(memcmp(&buf[i], "\x0d\x0a\x0d\x0a", 4) == 0) {
+          /* convert everything through this CRLFCRLF but no further */
+          conv_size = i + 4;
+          break;
+        }
+      }
+    }
+
+    Curl_convert_from_network(data, buf, conv_size);
+    /* Curl_convert_from_network calls failf if unsuccessful */
+    /* we might as well continue even if it fails...   */
+    ptr = buf; /* switch pointer to use my buffer instead */
+    break;
+  default:
+    /* leave everything else as-is */
+    break;
+  }
+#endif /* CURL_DOES_CONVERSIONS */
+
+  if(data->set.fdebug)
+    return (*data->set.fdebug)(data, type, ptr, size,
+                               data->set.debugdata);
+
+  switch(type) {
+  case CURLINFO_TEXT:
+  case CURLINFO_HEADER_OUT:
+  case CURLINFO_HEADER_IN:
+    fwrite(s_infotype[type], 2, 1, data->set.err);
+    fwrite(ptr, size, 1, data->set.err);
+#ifdef CURL_DOES_CONVERSIONS
+    if(size != conv_size) {
+      /* we had untranslated data so we need an explicit newline */
+      fwrite("\n", 1, 1, data->set.err);
+    }
+#endif
+    break;
+  default: /* nada */
+    break;
+  }
+  return 0;
+}
+
+int Curl_debug(struct SessionHandle *data, curl_infotype type,
+               char *ptr, size_t size,
+               struct connectdata *conn)
+{
+  int rc;
+  if(data->set.printhost && conn && conn->host.dispname) {
+    char buffer[160];
+    const char *t=NULL;
+    const char *w="Data";
+    switch (type) {
+    case CURLINFO_HEADER_IN:
+      w = "Header";
+    case CURLINFO_DATA_IN:
+      t = "from";
+      break;
+    case CURLINFO_HEADER_OUT:
+      w = "Header";
+    case CURLINFO_DATA_OUT:
+      t = "to";
+      break;
+    default:
+      break;
+    }
+
+    if(t) {
+      snprintf(buffer, sizeof(buffer), "[%s %s %s]", w, t,
+               conn->host.dispname);
+      rc = showit(data, CURLINFO_TEXT, buffer, strlen(buffer));
+      if(rc)
+        return rc;
+    }
+  }
+  rc = showit(data, type, ptr, size);
+  return rc;
+}
diff --git a/lib/curl_share.c b/lib/curl_share.c
new file mode 100644 (file)
index 0000000..182e6e9
--- /dev/null
@@ -0,0 +1,254 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+#include "curl_urldata.h"
+#include "curl_share.h"
+#include "curl_sslgen.h"
+#include "curl_memory.h"
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+CURLSH *
+curl_share_init(void)
+{
+  struct Curl_share *share = calloc(1, sizeof(struct Curl_share));
+  if(share)
+    share->specifier |= (1<<CURL_LOCK_DATA_SHARE);
+
+  return share;
+}
+
+#undef curl_share_setopt
+CURLSHcode
+curl_share_setopt(CURLSH *sh, CURLSHoption option, ...)
+{
+  struct Curl_share *share = (struct Curl_share *)sh;
+  va_list param;
+  int type;
+  curl_lock_function lockfunc;
+  curl_unlock_function unlockfunc;
+  void *ptr;
+  CURLSHcode res = CURLSHE_OK;
+
+  if(share->dirty)
+    /* don't allow setting options while one or more handles are already
+       using this share */
+    return CURLSHE_IN_USE;
+
+  va_start(param, option);
+
+  switch(option) {
+  case CURLSHOPT_SHARE:
+    /* this is a type this share will share */
+    type = va_arg(param, int);
+    share->specifier |= (1<<type);
+    switch( type ) {
+    case CURL_LOCK_DATA_DNS:
+      if(!share->hostcache) {
+        share->hostcache = Curl_mk_dnscache();
+        if(!share->hostcache)
+          res = CURLSHE_NOMEM;
+      }
+      break;
+
+    case CURL_LOCK_DATA_COOKIE:
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
+      if(!share->cookies) {
+        share->cookies = Curl_cookie_init(NULL, NULL, NULL, TRUE );
+        if(!share->cookies)
+          res = CURLSHE_NOMEM;
+      }
+#else   /* CURL_DISABLE_HTTP */
+      res = CURLSHE_NOT_BUILT_IN;
+#endif
+      break;
+
+    case CURL_LOCK_DATA_SSL_SESSION:
+#ifdef USE_SSL
+      if(!share->sslsession) {
+        share->max_ssl_sessions = 8;
+        share->sslsession = calloc(share->max_ssl_sessions,
+                                   sizeof(struct curl_ssl_session));
+        share->sessionage = 0;
+        if(!share->sslsession)
+          res = CURLSHE_NOMEM;
+      }
+#else
+      res = CURLSHE_NOT_BUILT_IN;
+#endif
+      break;
+
+    case CURL_LOCK_DATA_CONNECT:     /* not supported (yet) */
+      break;
+
+    default:
+      res = CURLSHE_BAD_OPTION;
+    }
+    break;
+
+  case CURLSHOPT_UNSHARE:
+    /* this is a type this share will no longer share */
+    type = va_arg(param, int);
+    share->specifier &= ~(1<<type);
+    switch( type ) {
+    case CURL_LOCK_DATA_DNS:
+      if(share->hostcache) {
+        Curl_hash_destroy(share->hostcache);
+        share->hostcache = NULL;
+      }
+      break;
+
+    case CURL_LOCK_DATA_COOKIE:
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
+      if(share->cookies) {
+        Curl_cookie_cleanup(share->cookies);
+        share->cookies = NULL;
+      }
+#else   /* CURL_DISABLE_HTTP */
+      res = CURLSHE_NOT_BUILT_IN;
+#endif
+      break;
+
+    case CURL_LOCK_DATA_SSL_SESSION:
+#ifdef USE_SSL
+      Curl_safefree(share->sslsession);
+#else
+      res = CURLSHE_NOT_BUILT_IN;
+#endif
+      break;
+
+    case CURL_LOCK_DATA_CONNECT:
+      break;
+
+    default:
+      res = CURLSHE_BAD_OPTION;
+      break;
+    }
+    break;
+
+  case CURLSHOPT_LOCKFUNC:
+    lockfunc = va_arg(param, curl_lock_function);
+    share->lockfunc = lockfunc;
+    break;
+
+  case CURLSHOPT_UNLOCKFUNC:
+    unlockfunc = va_arg(param, curl_unlock_function);
+    share->unlockfunc = unlockfunc;
+    break;
+
+  case CURLSHOPT_USERDATA:
+    ptr = va_arg(param, void *);
+    share->clientdata = ptr;
+    break;
+
+  default:
+    res = CURLSHE_BAD_OPTION;
+    break;
+  }
+
+  va_end(param);
+
+  return res;
+}
+
+CURLSHcode
+curl_share_cleanup(CURLSH *sh)
+{
+  struct Curl_share *share = (struct Curl_share *)sh;
+
+  if(share == NULL)
+    return CURLSHE_INVALID;
+
+  if(share->lockfunc)
+    share->lockfunc(NULL, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE,
+                    share->clientdata);
+
+  if(share->dirty) {
+    if(share->unlockfunc)
+      share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata);
+    return CURLSHE_IN_USE;
+  }
+
+  if(share->hostcache) {
+    Curl_hash_destroy(share->hostcache);
+    share->hostcache = NULL;
+  }
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
+  if(share->cookies)
+    Curl_cookie_cleanup(share->cookies);
+#endif
+
+#ifdef USE_SSL
+  if(share->sslsession) {
+    size_t i;
+    for(i = 0; i < share->max_ssl_sessions; i++)
+      Curl_ssl_kill_session(&(share->sslsession[i]));
+    free(share->sslsession);
+  }
+#endif
+
+  if(share->unlockfunc)
+    share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata);
+  free(share);
+
+  return CURLSHE_OK;
+}
+
+
+CURLSHcode
+Curl_share_lock(struct SessionHandle *data, curl_lock_data type,
+                curl_lock_access accesstype)
+{
+  struct Curl_share *share = data->share;
+
+  if(share == NULL)
+    return CURLSHE_INVALID;
+
+  if(share->specifier & (1<<type)) {
+    if(share->lockfunc) /* only call this if set! */
+      share->lockfunc(data, type, accesstype, share->clientdata);
+  }
+  /* else if we don't share this, pretend successful lock */
+
+  return CURLSHE_OK;
+}
+
+CURLSHcode
+Curl_share_unlock(struct SessionHandle *data, curl_lock_data type)
+{
+  struct Curl_share *share = data->share;
+
+  if(share == NULL)
+    return CURLSHE_INVALID;
+
+  if(share->specifier & (1<<type)) {
+    if(share->unlockfunc) /* only call this if set! */
+      share->unlockfunc (data, type, share->clientdata);
+  }
+
+  return CURLSHE_OK;
+}
diff --git a/lib/curl_slist.c b/lib/curl_slist.c
new file mode 100644 (file)
index 0000000..2a30ea6
--- /dev/null
@@ -0,0 +1,127 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include "curl_memory.h"
+#include "curl_slist.h"
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/* returns last node in linked list */
+static struct curl_slist *slist_get_last(struct curl_slist *list)
+{
+  struct curl_slist     *item;
+
+  /* if caller passed us a NULL, return now */
+  if(!list)
+    return NULL;
+
+  /* loop through to find the last item */
+  item = list;
+  while(item->next) {
+    item = item->next;
+  }
+  return item;
+}
+
+/*
+ * curl_slist_append() appends a string to the linked list. It always returns
+ * the address of the first record, so that you can use this function as an
+ * initialization function as well as an append function. If you find this
+ * bothersome, then simply create a separate _init function and call it
+ * appropriately from within the program.
+ */
+struct curl_slist *curl_slist_append(struct curl_slist *list,
+                                     const char *data)
+{
+  struct curl_slist     *last;
+  struct curl_slist     *new_item;
+
+  new_item = malloc(sizeof(struct curl_slist));
+  if(new_item) {
+    char *dupdata = strdup(data);
+    if(dupdata) {
+      new_item->next = NULL;
+      new_item->data = dupdata;
+    }
+    else {
+      free(new_item);
+      return NULL;
+    }
+  }
+  else
+    return NULL;
+
+  if(list) {
+    last = slist_get_last(list);
+    last->next = new_item;
+    return list;
+  }
+
+  /* if this is the first item, then new_item *is* the list */
+  return new_item;
+}
+
+/*
+ * Curl_slist_duplicate() duplicates a linked list. It always returns the
+ * address of the first record of the cloned list or NULL in case of an
+ * error (or if the input list was NULL).
+ */
+struct curl_slist *Curl_slist_duplicate(struct curl_slist *inlist)
+{
+  struct curl_slist *outlist = NULL;
+  struct curl_slist *tmp;
+
+  while(inlist) {
+    tmp = curl_slist_append(outlist, inlist->data);
+
+    if(!tmp) {
+      curl_slist_free_all(outlist);
+      return NULL;
+    }
+
+    outlist = tmp;
+    inlist = inlist->next;
+  }
+  return outlist;
+}
+
+/* be nice and clean up resources */
+void curl_slist_free_all(struct curl_slist *list)
+{
+  struct curl_slist     *next;
+  struct curl_slist     *item;
+
+  if(!list)
+    return;
+
+  item = list;
+  do {
+    next = item->next;
+    Curl_safefree(item->data);
+    free(item);
+    item = next;
+  } while(next);
+}
+
diff --git a/lib/curl_smtp.c b/lib/curl_smtp.c
new file mode 100644 (file)
index 0000000..5c4c25c
--- /dev/null
@@ -0,0 +1,1750 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * RFC2821 SMTP protocol
+ * RFC3207 SMTP over TLS
+ * RFC4954 SMTP Authentication
+ * RFC2195 CRAM-MD5 authentication
+ * RFC2831 DIGEST-MD5 authentication
+ * RFC4616 PLAIN authentication
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_SMTP
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_UTSNAME_H
+#include <sys/utsname.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
+#undef in_addr_t
+#define in_addr_t unsigned long
+#endif
+
+#include <curl/curl.h>
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_if2ip.h"
+#include "curl_hostip.h"
+#include "curl_progress.h"
+#include "curl_transfer.h"
+#include "curl_escape.h"
+#include "curl_http.h" /* for HTTP proxy tunnel stuff */
+#include "curl_socks.h"
+#include "curl_smtp.h"
+
+#include "curl_strtoofft.h"
+#include "curl_strequal.h"
+#include "curl_sslgen.h"
+#include "curl_connect.h"
+#include "curl_strerror.h"
+#include "curl_select.h"
+#include "curl_multiif.h"
+#include "curl_url.h"
+#include "curl_rawstr.h"
+#include "curl_gethostname.h"
+#include "curl_sasl.h"
+#include "curl_warnless.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/* Local API functions */
+static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
+static CURLcode smtp_do(struct connectdata *conn, bool *done);
+static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
+                          bool premature);
+static CURLcode smtp_connect(struct connectdata *conn, bool *done);
+static CURLcode smtp_disconnect(struct connectdata *conn, bool dead);
+static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
+static int smtp_getsock(struct connectdata *conn,
+                        curl_socket_t *socks,
+                        int numsocks);
+static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done);
+static CURLcode smtp_setup_connection(struct connectdata *conn);
+static CURLcode smtp_state_upgrade_tls(struct connectdata *conn);
+
+/*
+ * SMTP protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_smtp = {
+  "SMTP",                           /* scheme */
+  smtp_setup_connection,            /* setup_connection */
+  smtp_do,                          /* do_it */
+  smtp_done,                        /* done */
+  ZERO_NULL,                        /* do_more */
+  smtp_connect,                     /* connect_it */
+  smtp_multi_statemach,             /* connecting */
+  smtp_doing,                       /* doing */
+  smtp_getsock,                     /* proto_getsock */
+  smtp_getsock,                     /* doing_getsock */
+  ZERO_NULL,                        /* domore_getsock */
+  ZERO_NULL,                        /* perform_getsock */
+  smtp_disconnect,                  /* disconnect */
+  ZERO_NULL,                        /* readwrite */
+  PORT_SMTP,                        /* defport */
+  CURLPROTO_SMTP,                   /* protocol */
+  PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
+};
+
+#ifdef USE_SSL
+/*
+ * SMTPS protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_smtps = {
+  "SMTPS",                          /* scheme */
+  smtp_setup_connection,            /* setup_connection */
+  smtp_do,                          /* do_it */
+  smtp_done,                        /* done */
+  ZERO_NULL,                        /* do_more */
+  smtp_connect,                     /* connect_it */
+  smtp_multi_statemach,             /* connecting */
+  smtp_doing,                       /* doing */
+  smtp_getsock,                     /* proto_getsock */
+  smtp_getsock,                     /* doing_getsock */
+  ZERO_NULL,                        /* domore_getsock */
+  ZERO_NULL,                        /* perform_getsock */
+  smtp_disconnect,                  /* disconnect */
+  ZERO_NULL,                        /* readwrite */
+  PORT_SMTPS,                       /* defport */
+  CURLPROTO_SMTP | CURLPROTO_SMTPS, /* protocol */
+  PROTOPT_CLOSEACTION | PROTOPT_SSL
+  | PROTOPT_NOURLQUERY              /* flags */
+};
+#endif
+
+#ifndef CURL_DISABLE_HTTP
+/*
+ * HTTP-proxyed SMTP protocol handler.
+ */
+
+static const struct Curl_handler Curl_handler_smtp_proxy = {
+  "SMTP",                               /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  Curl_http,                            /* do_it */
+  Curl_http_done,                       /* done */
+  ZERO_NULL,                            /* do_more */
+  ZERO_NULL,                            /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  PORT_SMTP,                            /* defport */
+  CURLPROTO_HTTP,                       /* protocol */
+  PROTOPT_NONE                          /* flags */
+};
+
+#ifdef USE_SSL
+/*
+ * HTTP-proxyed SMTPS protocol handler.
+ */
+
+static const struct Curl_handler Curl_handler_smtps_proxy = {
+  "SMTPS",                              /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  Curl_http,                            /* do_it */
+  Curl_http_done,                       /* done */
+  ZERO_NULL,                            /* do_more */
+  ZERO_NULL,                            /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  PORT_SMTPS,                           /* defport */
+  CURLPROTO_HTTP,                       /* protocol */
+  PROTOPT_NONE                          /* flags */
+};
+#endif
+#endif
+
+/* Function that checks for an ending smtp status code at the start of the
+   given string, but also detects the supported authentication mechanisms
+   from  the EHLO AUTH response. */
+static int smtp_endofresp(struct pingpong *pp, int *resp)
+{
+  char *line = pp->linestart_resp;
+  size_t len = pp->nread_resp;
+  struct connectdata *conn = pp->conn;
+  struct smtp_conn *smtpc = &conn->proto.smtpc;
+  int result;
+  size_t wordlen;
+
+  if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
+    return FALSE;       /* Nothing for us */
+
+  if((result = (line[3] == ' ')) != 0)
+    *resp = curlx_sltosi(strtol(line, NULL, 10));
+
+  line += 4;
+  len -= 4;
+
+  if(smtpc->state == SMTP_EHLO && len >= 4 && !memcmp(line, "SIZE", 4)) {
+    DEBUGF(infof(conn->data, "Server supports SIZE extension.\n"));
+    smtpc->size_supported = true;
+  }
+
+  if(smtpc->state == SMTP_EHLO && len >= 5 && !memcmp(line, "AUTH ", 5)) {
+    line += 5;
+    len -= 5;
+
+    for(;;) {
+      while(len &&
+            (*line == ' ' || *line == '\t' ||
+             *line == '\r' || *line == '\n')) {
+        line++;
+        len--;
+      }
+
+      if(!len)
+        break;
+
+      for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
+            line[wordlen] != '\t' && line[wordlen] != '\r' &&
+            line[wordlen] != '\n';)
+        wordlen++;
+
+      if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
+        smtpc->authmechs |= SASL_MECH_LOGIN;
+      else if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
+        smtpc->authmechs |= SASL_MECH_PLAIN;
+      else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
+        smtpc->authmechs |= SASL_MECH_CRAM_MD5;
+      else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
+        smtpc->authmechs |= SASL_MECH_DIGEST_MD5;
+      else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
+        smtpc->authmechs |= SASL_MECH_GSSAPI;
+      else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
+        smtpc->authmechs |= SASL_MECH_EXTERNAL;
+      else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
+        smtpc->authmechs |= SASL_MECH_NTLM;
+
+      line += wordlen;
+      len -= wordlen;
+    }
+  }
+
+  return result;
+}
+
+/* This is the ONLY way to change SMTP state! */
+static void state(struct connectdata *conn, smtpstate newstate)
+{
+  struct smtp_conn *smtpc = &conn->proto.smtpc;
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+  /* for debug purposes */
+  static const char * const names[] = {
+    "STOP",
+    "SERVERGREET",
+    "EHLO",
+    "HELO",
+    "STARTTLS",
+    "UPGRADETLS",
+    "AUTH_PLAIN",
+    "AUTH_LOGIN",
+    "AUTH_PASSWD",
+    "AUTH_CRAMMD5",
+    "AUTH_DIGESTMD5",
+    "AUTH_DIGESTMD5_RESP",
+    "AUTH_NTLM",
+    "AUTH_NTLM_TYPE2MSG",
+    "AUTH",
+    "MAIL",
+    "RCPT",
+    "DATA",
+    "POSTDATA",
+    "QUIT",
+    /* LAST */
+  };
+
+  if(smtpc->state != newstate)
+    infof(conn->data, "SMTP %p state change from %s to %s\n",
+          smtpc, names[smtpc->state], names[newstate]);
+#endif
+
+  smtpc->state = newstate;
+}
+
+static CURLcode smtp_state_ehlo(struct connectdata *conn)
+{
+  CURLcode result;
+  struct smtp_conn *smtpc = &conn->proto.smtpc;
+
+  smtpc->authmechs = 0;         /* No known authentication mechanisms yet */
+  smtpc->authused = 0;          /* Clear the authentication mechanism used
+                                   for esmtp connections */
+
+  /* send EHLO */
+  result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
+
+  if(result)
+    return result;
+
+  state(conn, SMTP_EHLO);
+
+  return CURLE_OK;
+}
+
+static CURLcode smtp_state_helo(struct connectdata *conn)
+{
+  CURLcode result;
+  struct smtp_conn *smtpc = &conn->proto.smtpc;
+
+  smtpc->authused = 0;          /* No authentication mechanism used in smtp
+                                   connections */
+
+  /* send HELO */
+  result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
+
+  if(result)
+    return result;
+
+  state(conn, SMTP_HELO);
+
+  return CURLE_OK;
+}
+
+static CURLcode smtp_authenticate(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+  struct smtp_conn *smtpc = &conn->proto.smtpc;
+  char *initresp = NULL;
+  const char *mech = NULL;
+  size_t len = 0;
+  smtpstate state1 = SMTP_STOP;
+  smtpstate state2 = SMTP_STOP;
+
+  /* Check we have a username and password to authenticate with and end the
+     connect phase if we don't */
+  if(!conn->bits.user_passwd) {
+    state(conn, SMTP_STOP);
+
+    return result;
+  }
+
+  /* Check supported authentication mechanisms by decreasing order of
+     security */
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+  if(smtpc->authmechs & SASL_MECH_DIGEST_MD5) {
+    mech = "DIGEST-MD5";
+    state1 = SMTP_AUTH_DIGESTMD5;
+    smtpc->authused = SASL_MECH_DIGEST_MD5;
+  }
+  else if(smtpc->authmechs & SASL_MECH_CRAM_MD5) {
+    mech = "CRAM-MD5";
+    state1 = SMTP_AUTH_CRAMMD5;
+    smtpc->authused = SASL_MECH_CRAM_MD5;
+  }
+  else
+#endif
+#ifdef USE_NTLM
+  if(smtpc->authmechs & SASL_MECH_NTLM) {
+    mech = "NTLM";
+    state1 = SMTP_AUTH_NTLM;
+    state2 = SMTP_AUTH_NTLM_TYPE2MSG;
+    smtpc->authused = SASL_MECH_NTLM;
+    result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
+                                                 &conn->ntlm,
+                                                 &initresp, &len);
+  }
+  else
+#endif
+  if(smtpc->authmechs & SASL_MECH_LOGIN) {
+    mech = "LOGIN";
+    state1 = SMTP_AUTH_LOGIN;
+    state2 = SMTP_AUTH_PASSWD;
+    smtpc->authused = SASL_MECH_LOGIN;
+    result = Curl_sasl_create_login_message(conn->data, conn->user,
+                                            &initresp, &len);
+  }
+  else if(smtpc->authmechs & SASL_MECH_PLAIN) {
+    mech = "PLAIN";
+    state1 = SMTP_AUTH_PLAIN;
+    state2 = SMTP_AUTH;
+    smtpc->authused = SASL_MECH_PLAIN;
+    result = Curl_sasl_create_plain_message(conn->data, conn->user,
+                                            conn->passwd, &initresp, &len);
+  }
+  else {
+    infof(conn->data, "No known authentication mechanisms supported!\n");
+    result = CURLE_LOGIN_DENIED; /* Other mechanisms not supported */
+  }
+
+  if(!result) {
+    if(initresp &&
+       strlen(mech) + len <= 512 - 8) { /* AUTH <mech> ...<crlf> */
+       result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
+
+      if(!result)
+        state(conn, state2);
+    }
+    else {
+      result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
+
+      if(!result)
+        state(conn, state1);
+    }
+    Curl_safefree(initresp);
+  }
+
+  return result;
+}
+
+/* For the SMTP "protocol connect" and "doing" phases only */
+static int smtp_getsock(struct connectdata *conn,
+                        curl_socket_t *socks,
+                        int numsocks)
+{
+  return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
+}
+
+#ifdef USE_SSL
+static void smtp_to_smtps(struct connectdata *conn)
+{
+  conn->handler = &Curl_handler_smtps;
+}
+#else
+#define smtp_to_smtps(x) Curl_nop_stmt
+#endif
+
+/* For the initial server greeting */
+static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
+                                            int smtpcode,
+                                            smtpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+
+  (void)instate; /* no use for this yet */
+
+  if(smtpcode/100 != 2) {
+    failf(data, "Got unexpected smtp-server response: %d", smtpcode);
+    return CURLE_FTP_WEIRD_SERVER_REPLY;
+  }
+
+  result = smtp_state_ehlo(conn);
+
+  return result;
+}
+
+/* For STARTTLS responses */
+static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
+                                         int smtpcode,
+                                         smtpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+
+  (void)instate; /* no use for this yet */
+
+  if(smtpcode != 220) {
+    if(data->set.use_ssl != CURLUSESSL_TRY) {
+      failf(data, "STARTTLS denied. %c", smtpcode);
+      result = CURLE_USE_SSL_FAILED;
+    }
+    else
+      result = smtp_authenticate(conn);
+  }
+  else {
+    if(data->state.used_interface == Curl_if_multi) {
+      state(conn, SMTP_UPGRADETLS);
+      result = smtp_state_upgrade_tls(conn);
+    }
+    else {
+      result = Curl_ssl_connect(conn, FIRSTSOCKET);
+      if(CURLE_OK == result) {
+        smtp_to_smtps(conn);
+        result = smtp_state_ehlo(conn);
+      }
+    }
+  }
+
+  return result;
+}
+
+static CURLcode smtp_state_upgrade_tls(struct connectdata *conn)
+{
+  struct smtp_conn *smtpc = &conn->proto.smtpc;
+  CURLcode result;
+
+  result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
+
+  if(smtpc->ssldone) {
+    smtp_to_smtps(conn);
+    result = smtp_state_ehlo(conn);
+  }
+
+  return result;
+}
+
+/* For EHLO responses */
+static CURLcode smtp_state_ehlo_resp(struct connectdata *conn,
+                                     int smtpcode,
+                                     smtpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+
+  (void)instate; /* no use for this yet */
+
+  if(smtpcode/100 != 2) {
+    if((data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) &&
+     !conn->bits.user_passwd)
+      result = smtp_state_helo(conn);
+    else {
+      failf(data, "Remote access denied: %d", smtpcode);
+      result = CURLE_REMOTE_ACCESS_DENIED;
+    }
+  }
+  else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
+    /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
+       to TLS connection now */
+    result = Curl_pp_sendf(&conn->proto.smtpc.pp, "STARTTLS");
+    state(conn, SMTP_STARTTLS);
+  }
+  else
+    result = smtp_authenticate(conn);
+
+  return result;
+}
+
+/* For HELO responses */
+static CURLcode smtp_state_helo_resp(struct connectdata *conn,
+                                     int smtpcode,
+                                     smtpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+
+  (void)instate; /* no use for this yet */
+
+  if(smtpcode/100 != 2) {
+    failf(data, "Remote access denied: %d", smtpcode);
+    result = CURLE_REMOTE_ACCESS_DENIED;
+  }
+  else
+    /* End of connect phase */
+    state(conn, SMTP_STOP);
+
+  return result;
+}
+
+/* For AUTH PLAIN (without initial response) responses */
+static CURLcode smtp_state_auth_plain_resp(struct connectdata *conn,
+                                           int smtpcode,
+                                           smtpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  size_t len = 0;
+  char *plainauth = NULL;
+
+  (void)instate; /* no use for this yet */
+
+  if(smtpcode != 334) {
+    failf(data, "Access denied: %d", smtpcode);
+    result = CURLE_LOGIN_DENIED;
+  }
+  else {
+    /* Create the authorisation message */
+    result = Curl_sasl_create_plain_message(conn->data, conn->user,
+                                            conn->passwd, &plainauth, &len);
+
+    /* Send the message */
+    if(!result) {
+      if(plainauth) {
+        result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth);
+
+        if(!result)
+          state(conn, SMTP_AUTH);
+      }
+
+      Curl_safefree(plainauth);
+    }
+  }
+
+  return result;
+}
+
+/* For AUTH LOGIN (without initial response) responses */
+static CURLcode smtp_state_auth_login_resp(struct connectdata *conn,
+                                           int smtpcode,
+                                           smtpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  size_t len = 0;
+  char *authuser = NULL;
+
+  (void)instate; /* no use for this yet */
+
+  if(smtpcode != 334) {
+    failf(data, "Access denied: %d", smtpcode);
+    result = CURLE_LOGIN_DENIED;
+  }
+  else {
+    /* Create the user message */
+    result = Curl_sasl_create_login_message(conn->data, conn->user,
+                                            &authuser, &len);
+
+    /* Send the user */
+    if(!result) {
+      if(authuser) {
+        result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser);
+
+        if(!result)
+          state(conn, SMTP_AUTH_PASSWD);
+      }
+
+      Curl_safefree(authuser);
+    }
+  }
+
+  return result;
+}
+
+/* For responses to user entry of AUTH LOGIN */
+static CURLcode smtp_state_auth_passwd_resp(struct connectdata *conn,
+                                            int smtpcode,
+                                            smtpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  size_t len = 0;
+  char *authpasswd = NULL;
+
+  (void)instate; /* no use for this yet */
+
+  if(smtpcode != 334) {
+    failf(data, "Access denied: %d", smtpcode);
+    result = CURLE_LOGIN_DENIED;
+  }
+  else {
+    /* Create the password message */
+    result = Curl_sasl_create_login_message(conn->data, conn->passwd,
+                                            &authpasswd, &len);
+
+    /* Send the password */
+    if(!result) {
+      if(authpasswd) {
+        result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd);
+
+        if(!result)
+          state(conn, SMTP_AUTH);
+      }
+
+      Curl_safefree(authpasswd);
+    }
+  }
+
+  return result;
+}
+
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+/* For AUTH CRAM-MD5 responses */
+static CURLcode smtp_state_auth_cram_resp(struct connectdata *conn,
+                                          int smtpcode,
+                                          smtpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  char *chlg64 = data->state.buffer;
+  size_t len = 0;
+  char *rplyb64 = NULL;
+
+  (void)instate; /* no use for this yet */
+
+  if(smtpcode != 334) {
+    failf(data, "Access denied: %d", smtpcode);
+    return CURLE_LOGIN_DENIED;
+  }
+
+  /* Get the challenge */
+  for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
+    ;
+
+  /* Terminate the challenge */
+  if(*chlg64 != '=') {
+    for(len = strlen(chlg64); len--;)
+      if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
+         chlg64[len] != '\t')
+        break;
+
+    if(++len) {
+      chlg64[len] = '\0';
+    }
+  }
+
+  /* Create the response message */
+  result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
+                                             conn->passwd, &rplyb64, &len);
+
+  /* Send the response */
+  if(!result) {
+    if(rplyb64) {
+      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
+
+      if(!result)
+        state(conn, SMTP_AUTH);
+    }
+
+    Curl_safefree(rplyb64);
+  }
+
+  return result;
+}
+
+/* For AUTH DIGEST-MD5 challenge responses */
+static CURLcode smtp_state_auth_digest_resp(struct connectdata *conn,
+                                            int smtpcode,
+                                            smtpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  char *chlg64 = data->state.buffer;
+  size_t len = 0;
+  char *rplyb64 = NULL;
+
+  (void)instate; /* no use for this yet */
+
+  if(smtpcode != 334) {
+    failf(data, "Access denied: %d", smtpcode);
+    return CURLE_LOGIN_DENIED;
+  }
+
+  /* Get the challenge */
+  for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
+    ;
+
+  /* Create the response message */
+  result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
+                                               conn->passwd, "smtp",
+                                               &rplyb64, &len);
+
+  /* Send the response */
+  if(!result) {
+    if(rplyb64) {
+      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
+
+      if(!result)
+        state(conn, SMTP_AUTH_DIGESTMD5_RESP);
+    }
+
+    Curl_safefree(rplyb64);
+  }
+
+  return result;
+}
+
+/* For AUTH DIGEST-MD5 challenge-response responses */
+static CURLcode smtp_state_auth_digest_resp_resp(struct connectdata *conn,
+                                                 int smtpcode,
+                                                 smtpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+
+  (void)instate; /* no use for this yet */
+
+  if(smtpcode != 334) {
+    failf(data, "Authentication failed: %d", smtpcode);
+    result = CURLE_LOGIN_DENIED;
+  }
+  else {
+    /* Send an empty response */
+    result = Curl_pp_sendf(&conn->proto.smtpc.pp, "");
+
+    if(!result)
+      state(conn, SMTP_AUTH);
+  }
+
+  return result;
+}
+
+#endif
+
+#ifdef USE_NTLM
+/* For AUTH NTLM (without initial response) responses */
+static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn,
+                                          int smtpcode,
+                                          smtpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  char *type1msg = NULL;
+  size_t len = 0;
+
+  (void)instate; /* no use for this yet */
+
+  if(smtpcode != 334) {
+    failf(data, "Access denied: %d", smtpcode);
+    result = CURLE_LOGIN_DENIED;
+  }
+  else {
+    /* Create the type-1 message */
+    result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
+                                                 &conn->ntlm,
+                                                 &type1msg, &len);
+
+    /* Send the message */
+    if(!result) {
+      if(type1msg) {
+        result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1msg);
+
+        if(!result)
+          state(conn, SMTP_AUTH_NTLM_TYPE2MSG);
+      }
+
+      Curl_safefree(type1msg);
+    }
+  }
+
+  return result;
+}
+
+/* For NTLM type-2 responses (sent in reponse to our type-1 message) */
+static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
+                                                   int smtpcode,
+                                                   smtpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  char *type3msg = NULL;
+  size_t len = 0;
+
+  (void)instate; /* no use for this yet */
+
+  if(smtpcode != 334) {
+    failf(data, "Access denied: %d", smtpcode);
+    result = CURLE_LOGIN_DENIED;
+  }
+  else {
+    /* Create the type-3 message */
+    result = Curl_sasl_create_ntlm_type3_message(data,
+                                                 data->state.buffer + 4,
+                                                 conn->user, conn->passwd,
+                                                 &conn->ntlm,
+                                                 &type3msg, &len);
+
+    /* Send the message */
+    if(!result) {
+      if(type3msg) {
+        result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg);
+
+        if(!result)
+          state(conn, SMTP_AUTH);
+      }
+
+      Curl_safefree(type3msg);
+    }
+  }
+
+  return result;
+}
+#endif
+
+/* For the final responses to the AUTH sequence */
+static CURLcode smtp_state_auth_resp(struct connectdata *conn,
+                                     int smtpcode,
+                                     smtpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+
+  (void)instate; /* no use for this yet */
+
+  if(smtpcode != 235) {
+    failf(data, "Authentication failed: %d", smtpcode);
+    result = CURLE_LOGIN_DENIED;
+  }
+  else
+    /* End of connect phase */
+    state(conn, SMTP_STOP);
+
+  return result;
+}
+
+/* Start the DO phase */
+static CURLcode smtp_mail(struct connectdata *conn)
+{
+  char *from = NULL;
+  char *auth = NULL;
+  char *size = NULL;
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+
+  /* Calculate the FROM parameter */
+  if(!data->set.str[STRING_MAIL_FROM])
+    /* Null reverse-path, RFC-2821, sect. 3.7 */
+    from = strdup("<>");
+  else if(data->set.str[STRING_MAIL_FROM][0] == '<')
+    from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
+  else
+    from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
+
+  if(!from)
+    return CURLE_OUT_OF_MEMORY;
+
+  /* Calculate the optional AUTH parameter */
+  if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.authused) {
+    if(data->set.str[STRING_MAIL_AUTH][0] != '\0')
+      auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]);
+    else
+      /* Empty AUTH, RFC-2554, sect. 5 */
+      auth = strdup("<>");
+
+    if(!auth) {
+      Curl_safefree(from);
+
+      return CURLE_OUT_OF_MEMORY;
+    }
+  }
+
+  /* calculate the optional SIZE parameter */
+  if(conn->proto.smtpc.size_supported && conn->data->set.infilesize > 0) {
+    size = aprintf("%" FORMAT_OFF_T, data->set.infilesize);
+
+    if(!size) {
+      Curl_safefree(from);
+      Curl_safefree(auth);
+
+      return CURLE_OUT_OF_MEMORY;
+    }
+  }
+
+  /* Send the MAIL command */
+  if(!auth && !size)
+    result = Curl_pp_sendf(&conn->proto.smtpc.pp,
+                           "MAIL FROM:%s", from);
+  else if(auth && !size)
+    result = Curl_pp_sendf(&conn->proto.smtpc.pp,
+                           "MAIL FROM:%s AUTH=%s", from, auth);
+  else if(auth && size)
+    result = Curl_pp_sendf(&conn->proto.smtpc.pp,
+                           "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size);
+  else
+    result = Curl_pp_sendf(&conn->proto.smtpc.pp,
+                           "MAIL FROM:%s SIZE=%s", from, size);
+
+  Curl_safefree(from);
+  Curl_safefree(auth);
+  Curl_safefree(size);
+
+  if(result)
+    return result;
+
+  state(conn, SMTP_MAIL);
+
+  return result;
+}
+
+static CURLcode smtp_rcpt_to(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+  struct smtp_conn *smtpc = &conn->proto.smtpc;
+
+  /* Send the RCPT TO command */
+  if(smtpc->rcpt) {
+    if(smtpc->rcpt->data[0] == '<')
+      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
+                             smtpc->rcpt->data);
+    else
+      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
+                             smtpc->rcpt->data);
+    if(!result)
+      state(conn, SMTP_RCPT);
+  }
+
+  return result;
+}
+
+/* For MAIL responses */
+static CURLcode smtp_state_mail_resp(struct connectdata *conn,
+                                     int smtpcode,
+                                     smtpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+
+  (void)instate; /* no use for this yet */
+
+  if(smtpcode/100 != 2) {
+    failf(data, "MAIL failed: %d", smtpcode);
+    result = CURLE_SEND_ERROR;
+    state(conn, SMTP_STOP);
+  }
+  else {
+    struct smtp_conn *smtpc = &conn->proto.smtpc;
+    smtpc->rcpt = data->set.mail_rcpt;
+
+    result = smtp_rcpt_to(conn);
+  }
+
+  return result;
+}
+
+/* For RCPT responses */
+static CURLcode smtp_state_rcpt_resp(struct connectdata *conn,
+                                     int smtpcode,
+                                     smtpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+
+  (void)instate; /* no use for this yet */
+
+  if(smtpcode/100 != 2) {
+    failf(data, "RCPT failed: %d", smtpcode);
+    result = CURLE_SEND_ERROR;
+    state(conn, SMTP_STOP);
+  }
+  else {
+    struct smtp_conn *smtpc = &conn->proto.smtpc;
+
+    if(smtpc->rcpt) {
+      smtpc->rcpt = smtpc->rcpt->next;
+      result = smtp_rcpt_to(conn);
+
+      /* If we failed or still are sending RCPT data then return */
+      if(result || smtpc->rcpt)
+        return result;
+    }
+
+    /* Send the DATA command */
+    result = Curl_pp_sendf(&conn->proto.smtpc.pp, "DATA");
+
+    if(result)
+      return result;
+
+    state(conn, SMTP_DATA);
+  }
+
+  return result;
+}
+
+/* For DATA response */
+static CURLcode smtp_state_data_resp(struct connectdata *conn,
+                                     int smtpcode,
+                                     smtpstate instate)
+{
+  struct SessionHandle *data = conn->data;
+  struct FTP *smtp = data->state.proto.smtp;
+
+  (void)instate; /* no use for this yet */
+
+  if(smtpcode != 354) {
+    state(conn, SMTP_STOP);
+    return CURLE_SEND_ERROR;
+  }
+
+  /* SMTP upload */
+  Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
+                      FIRSTSOCKET, smtp->bytecountp);
+
+  /* End of do phase */
+  state(conn, SMTP_STOP);
+
+  return CURLE_OK;
+}
+
+/* For POSTDATA responses, which are received after the entire DATA
+   part has been sent to the server */
+static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
+                                         int smtpcode,
+                                         smtpstate instate)
+{
+  CURLcode result = CURLE_OK;
+
+  (void)instate; /* no use for this yet */
+
+  if(smtpcode != 250)
+    result = CURLE_RECV_ERROR;
+
+  /* End of done phase */
+  state(conn, SMTP_STOP);
+
+  return result;
+}
+
+static CURLcode smtp_statemach_act(struct connectdata *conn)
+{
+  CURLcode result;
+  curl_socket_t sock = conn->sock[FIRSTSOCKET];
+  struct SessionHandle *data = conn->data;
+  int smtpcode;
+  struct smtp_conn *smtpc = &conn->proto.smtpc;
+  struct pingpong *pp = &smtpc->pp;
+  size_t nread = 0;
+
+  /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
+  if(smtpc->state == SMTP_UPGRADETLS)
+    return smtp_state_upgrade_tls(conn);
+
+  /* Flush any data that needs to be sent */
+  if(pp->sendleft)
+    return Curl_pp_flushsend(pp);
+
+  /* Read the response from the server */
+  result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
+  if(result)
+    return result;
+
+  /* Store the latest response for later retrieval */
+  if(smtpc->state != SMTP_QUIT)
+    data->info.httpcode = smtpcode;
+
+  if(smtpcode) {
+    /* We have now received a full SMTP server response */
+    switch(smtpc->state) {
+    case SMTP_SERVERGREET:
+      result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
+      break;
+
+    case SMTP_EHLO:
+      result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
+      break;
+
+    case SMTP_HELO:
+      result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
+      break;
+
+    case SMTP_STARTTLS:
+      result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
+      break;
+
+    case SMTP_AUTH_PLAIN:
+      result = smtp_state_auth_plain_resp(conn, smtpcode, smtpc->state);
+      break;
+
+    case SMTP_AUTH_LOGIN:
+      result = smtp_state_auth_login_resp(conn, smtpcode, smtpc->state);
+      break;
+
+    case SMTP_AUTH_PASSWD:
+      result = smtp_state_auth_passwd_resp(conn, smtpcode, smtpc->state);
+      break;
+
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+    case SMTP_AUTH_CRAMMD5:
+      result = smtp_state_auth_cram_resp(conn, smtpcode, smtpc->state);
+      break;
+
+    case SMTP_AUTH_DIGESTMD5:
+      result = smtp_state_auth_digest_resp(conn, smtpcode, smtpc->state);
+      break;
+
+    case SMTP_AUTH_DIGESTMD5_RESP:
+      result = smtp_state_auth_digest_resp_resp(conn, smtpcode, smtpc->state);
+      break;
+#endif
+
+#ifdef USE_NTLM
+    case SMTP_AUTH_NTLM:
+      result = smtp_state_auth_ntlm_resp(conn, smtpcode, smtpc->state);
+      break;
+
+    case SMTP_AUTH_NTLM_TYPE2MSG:
+      result = smtp_state_auth_ntlm_type2msg_resp(conn, smtpcode,
+                                                  smtpc->state);
+      break;
+#endif
+
+    case SMTP_AUTH:
+      result = smtp_state_auth_resp(conn, smtpcode, smtpc->state);
+      break;
+
+    case SMTP_MAIL:
+      result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
+      break;
+
+    case SMTP_RCPT:
+      result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
+      break;
+
+    case SMTP_DATA:
+      result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
+      break;
+
+    case SMTP_POSTDATA:
+      result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
+      break;
+
+    case SMTP_QUIT:
+      /* fallthrough, just stop! */
+    default:
+      /* internal error */
+      state(conn, SMTP_STOP);
+      break;
+    }
+  }
+
+  return result;
+}
+
+/* Called repeatedly until done from curl_multi.c */
+static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
+{
+  struct smtp_conn *smtpc = &conn->proto.smtpc;
+  CURLcode result;
+
+  if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone)
+    result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
+  else
+    result = Curl_pp_multi_statemach(&smtpc->pp);
+
+  *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
+
+  return result;
+}
+
+static CURLcode smtp_easy_statemach(struct connectdata *conn)
+{
+  struct smtp_conn *smtpc = &conn->proto.smtpc;
+  struct pingpong *pp = &smtpc->pp;
+  CURLcode result = CURLE_OK;
+
+  while(smtpc->state != SMTP_STOP) {
+    result = Curl_pp_easy_statemach(pp);
+    if(result)
+      break;
+  }
+
+  return result;
+}
+
+/* Allocate and initialize the SMTP struct for the current SessionHandle if
+   required */
+static CURLcode smtp_init(struct connectdata *conn)
+{
+  struct SessionHandle *data = conn->data;
+  struct FTP *smtp = data->state.proto.smtp;
+
+  if(!smtp) {
+    smtp = data->state.proto.smtp = calloc(sizeof(struct FTP), 1);
+    if(!smtp)
+      return CURLE_OUT_OF_MEMORY;
+  }
+
+  /* Get some initial data into the smtp struct */
+  smtp->bytecountp = &data->req.bytecount;
+
+  /* No need to duplicate user+password, the connectdata struct won't change
+     during a session, but we re-init them here since on subsequent inits
+     since the conn struct may have changed or been replaced.
+  */
+  smtp->user = conn->user;
+  smtp->passwd = conn->passwd;
+
+  return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * smtp_connect()
+ *
+ * This function should do everything that is to be considered a part of
+ * the connection phase.
+ *
+ * The variable pointed to by 'done' will be TRUE if the protocol-layer
+ * connect phase is done when this function returns, or FALSE if not. When
+ * called as a part of the easy interface, it will always be TRUE.
+ */
+static CURLcode smtp_connect(struct connectdata *conn, bool *done)
+{
+  CURLcode result;
+  struct smtp_conn *smtpc = &conn->proto.smtpc;
+  struct SessionHandle *data = conn->data;
+  struct pingpong *pp = &smtpc->pp;
+  const char *path = conn->data->state.path;
+  char localhost[HOSTNAME_MAX + 1];
+
+  *done = FALSE; /* default to not done yet */
+
+  /* If there already is a protocol-specific struct allocated for this
+     sessionhandle, deal with it */
+  Curl_reset_reqproto(conn);
+
+  result = smtp_init(conn);
+  if(CURLE_OK != result)
+    return result;
+
+  /* We always support persistent connections on smtp */
+  conn->bits.close = FALSE;
+
+  pp->response_time = RESP_TIMEOUT; /* set default response time-out */
+  pp->statemach_act = smtp_statemach_act;
+  pp->endofresp = smtp_endofresp;
+  pp->conn = conn;
+
+  if((conn->handler->protocol & CURLPROTO_SMTPS) &&
+      data->state.used_interface != Curl_if_multi) {
+    /* SMTPS is simply smtp with SSL for the control channel */
+    /* so perform the SSL initialization for this socket */
+    result = Curl_ssl_connect(conn, FIRSTSOCKET);
+    if(result)
+      return result;
+  }
+
+  /* Initialise the response reader stuff */
+  Curl_pp_init(pp);
+
+  /* Set the default response time-out */
+  pp->response_time = RESP_TIMEOUT;
+  pp->statemach_act = smtp_statemach_act;
+  pp->endofresp = smtp_endofresp;
+  pp->conn = conn;
+
+  /* Calculate the path if necessary */
+  if(!*path) {
+    if(!Curl_gethostname(localhost, sizeof(localhost)))
+      path = localhost;
+    else
+      path = "localhost";
+  }
+
+  /* URL decode the path and use it as the domain in our EHLO */
+  result = Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
+  if(result)
+    return result;
+
+  /* Start off waiting for the server greeting response */
+  state(conn, SMTP_SERVERGREET);
+
+  if(data->state.used_interface == Curl_if_multi)
+    result = smtp_multi_statemach(conn, done);
+  else {
+    result = smtp_easy_statemach(conn);
+    if(!result)
+      *done = TRUE;
+  }
+
+  return result;
+}
+
+/***********************************************************************
+ *
+ * smtp_done()
+ *
+ * The DONE function. This does what needs to be done after a single DO has
+ * performed.
+ *
+ * Input argument is already checked for validity.
+ */
+static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
+                          bool premature)
+{
+  struct SessionHandle *data = conn->data;
+  struct FTP *smtp = data->state.proto.smtp;
+  CURLcode result = CURLE_OK;
+  ssize_t bytes_written;
+
+  (void)premature;
+
+  if(!smtp)
+    /* When the easy handle is removed from the multi while libcurl is still
+     * trying to resolve the host name, it seems that the smtp struct is not
+     * yet initialized, but the removal action calls Curl_done() which calls
+     * this function. So we simply return success if no smtp pointer is set.
+     */
+    return CURLE_OK;
+
+  if(status) {
+    conn->bits.close = TRUE; /* marked for closure */
+    result = status;         /* use the already set error code */
+  }
+  else if(!data->set.connect_only) {
+    struct smtp_conn *smtpc = &conn->proto.smtpc;
+    struct pingpong *pp = &smtpc->pp;
+
+    /* Send the end of block data */
+    result = Curl_write(conn,
+                        conn->writesockfd,  /* socket to send to */
+                        SMTP_EOB,           /* buffer pointer */
+                        SMTP_EOB_LEN,       /* buffer size */
+                        &bytes_written);    /* actually sent away */
+
+    if(result)
+      return result;
+
+    if(bytes_written != SMTP_EOB_LEN) {
+      /* The whole chunk was not sent so keep it around and adjust the
+         pingpong structure accordingly */
+      pp->sendthis = strdup(SMTP_EOB);
+      pp->sendsize = SMTP_EOB_LEN;
+      pp->sendleft = SMTP_EOB_LEN - bytes_written;
+    }
+    else
+      /* Successfully sent so adjust the response timeout relative to now */
+      pp->response = Curl_tvnow();
+
+    state(conn, SMTP_POSTDATA);
+
+    /* Run the state-machine
+
+       TODO: when the multi interface is used, this _really_ should be using
+       the smtp_multi_statemach function but we have no general support for
+       non-blocking DONE operations, not in the multi state machine and with
+       Curl_done() invokes on several places in the code!
+    */
+    result = smtp_easy_statemach(conn);
+  }
+
+  /* Clear the transfer mode for the next connection */
+  smtp->transfer = FTPTRANSFER_BODY;
+
+  return result;
+}
+
+/***********************************************************************
+ *
+ * smtp_perform()
+ *
+ * This is the actual DO function for SMTP. Get a file/directory according to
+ * the options previously setup.
+ */
+static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
+                             bool *dophase_done)
+{
+  /* This is SMTP and no proxy */
+  CURLcode result = CURLE_OK;
+
+  DEBUGF(infof(conn->data, "DO phase starts\n"));
+
+  if(conn->data->set.opt_no_body) {
+    /* Requested no body means no transfer */
+    struct FTP *smtp = conn->data->state.proto.smtp;
+    smtp->transfer = FTPTRANSFER_INFO;
+  }
+
+  *dophase_done = FALSE; /* not done yet */
+
+  /* Start the first command in the DO phase */
+  result = smtp_mail(conn);
+  if(result)
+    return result;
+
+  /* Run the state-machine */
+  if(conn->data->state.used_interface == Curl_if_multi)
+    result = smtp_multi_statemach(conn, dophase_done);
+  else {
+    result = smtp_easy_statemach(conn);
+    *dophase_done = TRUE; /* with the easy interface we are done here */
+  }
+  *connected = conn->bits.tcpconnect[FIRSTSOCKET];
+
+  if(*dophase_done)
+    DEBUGF(infof(conn->data, "DO phase is complete\n"));
+
+  return result;
+}
+
+/***********************************************************************
+ *
+ * smtp_do()
+ *
+ * This function is registered as 'curl_do' function. It decodes the path
+ * parts etc as a wrapper to the actual DO function (smtp_perform).
+ *
+ * The input argument is already checked for validity.
+ */
+static CURLcode smtp_do(struct connectdata *conn, bool *done)
+{
+  CURLcode retcode = CURLE_OK;
+
+  *done = FALSE; /* default to false */
+
+  /*
+    Since connections can be re-used between SessionHandles, this might be a
+    connection already existing but on a fresh SessionHandle struct so we must
+    make sure we have a good 'struct SMTP' to play with. For new connections,
+    the struct SMTP is allocated and setup in the smtp_connect() function.
+  */
+  Curl_reset_reqproto(conn);
+  retcode = smtp_init(conn);
+  if(retcode)
+    return retcode;
+
+  retcode = smtp_regular_transfer(conn, done);
+
+  return retcode;
+}
+
+/***********************************************************************
+ *
+ * smtp_quit()
+ *
+ * This should be called before calling sclose().  We should then wait for the
+ * response from the server before returning. The calling code should then try
+ * to close the connection.
+ */
+static CURLcode smtp_quit(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+
+  result = Curl_pp_sendf(&conn->proto.smtpc.pp, "QUIT");
+  if(result)
+    return result;
+
+  state(conn, SMTP_QUIT);
+
+  result = smtp_easy_statemach(conn);
+
+  return result;
+}
+
+/***********************************************************************
+ *
+ * smtp_disconnect()
+ *
+ * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
+ * resources. BLOCKING.
+ */
+static CURLcode smtp_disconnect(struct connectdata *conn,
+                                bool dead_connection)
+{
+  struct smtp_conn *smtpc = &conn->proto.smtpc;
+
+  /* We cannot send quit unconditionally. If this connection is stale or
+     bad in any way, sending quit and waiting around here will make the
+     disconnect wait in vain and cause more problems than we need to */
+
+  /* The SMTP session may or may not have been allocated/setup at this
+     point! */
+  if(!dead_connection && smtpc->pp.conn)
+    (void)smtp_quit(conn); /* ignore errors on the LOGOUT */
+
+  /* Disconnect from the server */
+  Curl_pp_disconnect(&smtpc->pp);
+
+  /* Cleanup the SASL module */
+  Curl_sasl_cleanup(conn, smtpc->authused);
+
+  /* Cleanup our connection based variables */
+  Curl_safefree(smtpc->domain);
+
+  return CURLE_OK;
+}
+
+/* Call this when the DO phase has completed */
+static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
+{
+  struct FTP *smtp = conn->data->state.proto.smtp;
+
+  (void)connected;
+
+  if(smtp->transfer != FTPTRANSFER_BODY)
+    /* no data to transfer */
+    Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+
+  return CURLE_OK;
+}
+
+/* Called from curl_multi.c while DOing */
+static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
+{
+  CURLcode result = smtp_multi_statemach(conn, dophase_done);
+
+  if(result)
+    DEBUGF(infof(conn->data, "DO phase failed\n"));
+  else {
+    if(*dophase_done) {
+      result = smtp_dophase_done(conn, FALSE /* not connected */);
+
+      DEBUGF(infof(conn->data, "DO phase is complete\n"));
+    }
+  }
+
+  return result;
+}
+
+/***********************************************************************
+ *
+ * smtp_regular_transfer()
+ *
+ * The input argument is already checked for validity.
+ *
+ * Performs all commands done before a regular transfer between a local and a
+ * remote host.
+ */
+static CURLcode smtp_regular_transfer(struct connectdata *conn,
+                                      bool *dophase_done)
+{
+  CURLcode result = CURLE_OK;
+  bool connected = FALSE;
+  struct SessionHandle *data = conn->data;
+
+  /* Make sure size is unknown at this point */
+  data->req.size = -1;
+
+  Curl_pgrsSetUploadCounter(data, 0);
+  Curl_pgrsSetDownloadCounter(data, 0);
+  Curl_pgrsSetUploadSize(data, 0);
+  Curl_pgrsSetDownloadSize(data, 0);
+
+  result = smtp_perform(conn, &connected, dophase_done);
+
+  if(CURLE_OK == result) {
+    if(!*dophase_done)
+      /* The DO phase has not completed yet */
+      return CURLE_OK;
+
+    result = smtp_dophase_done(conn, connected);
+    if(result)
+      return result;
+  }
+
+  return result;
+}
+
+static CURLcode smtp_setup_connection(struct connectdata *conn)
+{
+  struct SessionHandle *data = conn->data;
+
+  if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
+    /* Unless we have asked to tunnel smtp operations through the proxy, we
+       switch and use HTTP operations only */
+#ifndef CURL_DISABLE_HTTP
+    if(conn->handler == &Curl_handler_smtp)
+      conn->handler = &Curl_handler_smtp_proxy;
+    else {
+#ifdef USE_SSL
+      conn->handler = &Curl_handler_smtps_proxy;
+#else
+      failf(data, "SMTPS not supported!");
+      return CURLE_UNSUPPORTED_PROTOCOL;
+#endif
+    }
+
+    /* We explicitly mark this connection as persistent here as we're doing
+       SMTP over HTTP and thus we accidentally avoid setting this value
+       otherwise */
+    conn->bits.close = FALSE;
+#else
+    failf(data, "SMTP over http proxy requires HTTP support built-in!");
+    return CURLE_UNSUPPORTED_PROTOCOL;
+#endif
+  }
+
+  data->state.path++;   /* don't include the initial slash */
+
+  return CURLE_OK;
+}
+
+CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread)
+{
+  /* When sending a SMTP payload we must detect CRLF. sequences making sure
+     they are sent as CRLF.. instead, as a . on the beginning of a line will
+     be deleted by the server when not part of an EOB terminator and a
+     genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
+     data by the server.
+  */
+  ssize_t i;
+  ssize_t si;
+  struct smtp_conn *smtpc = &conn->proto.smtpc;
+  struct SessionHandle *data = conn->data;
+
+  /* Do we need to allocate the scatch buffer? */
+  if(!data->state.scratch) {
+    data->state.scratch = malloc(2 * BUFSIZE);
+
+    if(!data->state.scratch) {
+      failf (data, "Failed to alloc scratch buffer!");
+      return CURLE_OUT_OF_MEMORY;
+    }
+  }
+
+  /* This loop can be improved by some kind of Boyer-Moore style of
+     approach but that is saved for later... */
+  for(i = 0, si = 0; i < nread; i++) {
+    if(SMTP_EOB[smtpc->eob] == data->req.upload_fromhere[i])
+      smtpc->eob++;
+    else if(smtpc->eob) {
+      /* A previous substring matched so output that first */
+      memcpy(&data->state.scratch[si], SMTP_EOB, smtpc->eob);
+      si += smtpc->eob;
+
+      /* Then compare the first byte */
+      if(SMTP_EOB[0] == data->req.upload_fromhere[i])
+        smtpc->eob = 1;
+      else
+        smtpc->eob = 0;
+    }
+
+    /* Do we have a match for CRLF. as per RFC-2821, sect. 4.5.2 */
+    if(SMTP_EOB_FIND_LEN == smtpc->eob) {
+      /* Copy the replacement data to the target buffer */
+      memcpy(&data->state.scratch[si], SMTP_EOB_REPL, SMTP_EOB_REPL_LEN);
+      si += SMTP_EOB_REPL_LEN;
+      smtpc->eob = 0;
+    }
+    else if(!smtpc->eob)
+      data->state.scratch[si++] = data->req.upload_fromhere[i];
+  }
+
+  if(smtpc->eob) {
+    /* A substring matched before processing ended so output that now */
+    memcpy(&data->state.scratch[si], SMTP_EOB, smtpc->eob);
+    si += smtpc->eob;
+    smtpc->eob = 0;
+  }
+
+  if(si != nread) {
+    /* Only use the new buffer if we replaced something */
+    nread = si;
+
+    /* Upload from the new (replaced) buffer instead */
+    data->req.upload_fromhere = data->state.scratch;
+
+    /* Set the new amount too */
+    data->req.upload_present = nread;
+  }
+
+  return CURLE_OK;
+}
+
+#endif /* CURL_DISABLE_SMTP */
diff --git a/lib/curl_socks.c b/lib/curl_socks.c
new file mode 100644 (file)
index 0000000..1b70dd6
--- /dev/null
@@ -0,0 +1,744 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_PROXY)
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_strequal.h"
+#include "curl_select.h"
+#include "curl_connect.h"
+#include "curl_timeval.h"
+#include "curl_socks.h"
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/*
+ * Helper read-from-socket functions. Does the same as Curl_read() but it
+ * blocks until all bytes amount of buffersize will be read. No more, no less.
+ *
+ * This is STUPID BLOCKING behaviour which we frown upon, but right now this
+ * is what we have...
+ */
+int Curl_blockread_all(struct connectdata *conn, /* connection data */
+                       curl_socket_t sockfd,     /* read from this socket */
+                       char *buf,                /* store read data here */
+                       ssize_t buffersize,       /* max amount to read */
+                       ssize_t *n)               /* amount bytes read */
+{
+  ssize_t nread;
+  ssize_t allread = 0;
+  int result;
+  long timeleft;
+  *n = 0;
+  for(;;) {
+    timeleft = Curl_timeleft(conn->data, NULL, TRUE);
+    if(timeleft < 0) {
+      /* we already got the timeout */
+      result = CURLE_OPERATION_TIMEDOUT;
+      break;
+    }
+    if(Curl_socket_ready(sockfd, CURL_SOCKET_BAD, timeleft) <= 0) {
+      result = ~CURLE_OK;
+      break;
+    }
+    result = Curl_read_plain(sockfd, buf, buffersize, &nread);
+    if(CURLE_AGAIN == result)
+      continue;
+    else if(result)
+      break;
+
+    if(buffersize == nread) {
+      allread += nread;
+      *n = allread;
+      result = CURLE_OK;
+      break;
+    }
+    if(!nread) {
+      result = ~CURLE_OK;
+      break;
+    }
+
+    buffersize -= nread;
+    buf += nread;
+    allread += nread;
+  }
+  return result;
+}
+
+/*
+* This function logs in to a SOCKS4 proxy and sends the specifics to the final
+* destination server.
+*
+* Reference :
+*   http://socks.permeo.com/protocol/socks4.protocol
+*
+* Note :
+*   Set protocol4a=true for  "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
+*   Nonsupport "Identification Protocol (RFC1413)"
+*/
+CURLcode Curl_SOCKS4(const char *proxy_name,
+                     const char *hostname,
+                     int remote_port,
+                     int sockindex,
+                     struct connectdata *conn,
+                     bool protocol4a)
+{
+#define SOCKS4REQLEN 262
+  unsigned char socksreq[SOCKS4REQLEN]; /* room for SOCKS4 request incl. user
+                                           id */
+  int result;
+  CURLcode code;
+  curl_socket_t sock = conn->sock[sockindex];
+  struct SessionHandle *data = conn->data;
+
+  if(Curl_timeleft(data, NULL, TRUE) < 0) {
+    /* time-out, bail out, go home */
+    failf(data, "Connection time-out");
+    return CURLE_OPERATION_TIMEDOUT;
+  }
+
+  curlx_nonblock(sock, FALSE);
+
+  /*
+   * Compose socks4 request
+   *
+   * Request format
+   *
+   *     +----+----+----+----+----+----+----+----+----+----+....+----+
+   *     | VN | CD | DSTPORT |      DSTIP        | USERID       |NULL|
+   *     +----+----+----+----+----+----+----+----+----+----+....+----+
+   * # of bytes:  1    1      2              4           variable       1
+   */
+
+  socksreq[0] = 4; /* version (SOCKS4) */
+  socksreq[1] = 1; /* connect */
+  socksreq[2] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */
+  socksreq[3] = (unsigned char)(remote_port & 0xff);        /* PORT LSB */
+
+  /* DNS resolve only for SOCKS4, not SOCKS4a */
+  if(!protocol4a) {
+    struct Curl_dns_entry *dns;
+    Curl_addrinfo *hp=NULL;
+    int rc;
+
+    rc = Curl_resolv(conn, hostname, remote_port, &dns);
+
+    if(rc == CURLRESOLV_ERROR)
+      return CURLE_COULDNT_RESOLVE_PROXY;
+
+    if(rc == CURLRESOLV_PENDING)
+      /* ignores the return code, but 'dns' remains NULL on failure */
+      (void)Curl_resolver_wait_resolv(conn, &dns);
+
+    /*
+     * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
+     * returns a Curl_addrinfo pointer that may not always look the same.
+     */
+    if(dns)
+      hp=dns->addr;
+    if(hp) {
+      char buf[64];
+      unsigned short ip[4];
+      Curl_printable_address(hp, buf, sizeof(buf));
+
+      if(4 == sscanf( buf, "%hu.%hu.%hu.%hu",
+                      &ip[0], &ip[1], &ip[2], &ip[3])) {
+        /* Set DSTIP */
+        socksreq[4] = (unsigned char)ip[0];
+        socksreq[5] = (unsigned char)ip[1];
+        socksreq[6] = (unsigned char)ip[2];
+        socksreq[7] = (unsigned char)ip[3];
+      }
+      else
+        hp = NULL; /* fail! */
+
+      Curl_resolv_unlock(data, dns); /* not used anymore from now on */
+
+    }
+    if(!hp) {
+      failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
+            hostname);
+      return CURLE_COULDNT_RESOLVE_HOST;
+    }
+  }
+
+  /*
+   * This is currently not supporting "Identification Protocol (RFC1413)".
+   */
+  socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
+  if(proxy_name)
+    strlcat((char*)socksreq + 8, proxy_name, sizeof(socksreq) - 8);
+
+  /*
+   * Make connection
+   */
+  {
+    ssize_t actualread;
+    ssize_t written;
+    ssize_t hostnamelen = 0;
+    int packetsize = 9 +
+      (int)strlen((char*)socksreq + 8); /* size including NUL */
+
+    /* If SOCKS4a, set special invalid IP address 0.0.0.x */
+    if(protocol4a) {
+      socksreq[4] = 0;
+      socksreq[5] = 0;
+      socksreq[6] = 0;
+      socksreq[7] = 1;
+      /* If still enough room in buffer, also append hostname */
+      hostnamelen = (ssize_t)strlen(hostname) + 1; /* length including NUL */
+      if(packetsize + hostnamelen <= SOCKS4REQLEN)
+        strcpy((char*)socksreq + packetsize, hostname);
+      else
+        hostnamelen = 0; /* Flag: hostname did not fit in buffer */
+    }
+
+    /* Send request */
+    code = Curl_write_plain(conn, sock, (char *)socksreq,
+                            packetsize + hostnamelen,
+                            &written);
+    if((code != CURLE_OK) || (written != packetsize + hostnamelen)) {
+      failf(data, "Failed to send SOCKS4 connect request.");
+      return CURLE_COULDNT_CONNECT;
+    }
+    if(protocol4a && hostnamelen == 0) {
+      /* SOCKS4a with very long hostname - send that name separately */
+      hostnamelen = (ssize_t)strlen(hostname) + 1;
+      code = Curl_write_plain(conn, sock, (char *)hostname, hostnamelen,
+                              &written);
+      if((code != CURLE_OK) || (written != hostnamelen)) {
+        failf(data, "Failed to send SOCKS4 connect request.");
+        return CURLE_COULDNT_CONNECT;
+      }
+    }
+
+    packetsize = 8; /* receive data size */
+
+    /* Receive response */
+    result = Curl_blockread_all(conn, sock, (char *)socksreq, packetsize,
+                                &actualread);
+    if((result != CURLE_OK) || (actualread != packetsize)) {
+      failf(data, "Failed to receive SOCKS4 connect request ack.");
+      return CURLE_COULDNT_CONNECT;
+    }
+
+    /*
+     * Response format
+     *
+     *     +----+----+----+----+----+----+----+----+
+     *     | VN | CD | DSTPORT |      DSTIP        |
+     *     +----+----+----+----+----+----+----+----+
+     * # of bytes:  1    1      2              4
+     *
+     * VN is the version of the reply code and should be 0. CD is the result
+     * code with one of the following values:
+     *
+     * 90: request granted
+     * 91: request rejected or failed
+     * 92: request rejected because SOCKS server cannot connect to
+     *     identd on the client
+     * 93: request rejected because the client program and identd
+     *     report different user-ids
+     */
+
+    /* wrong version ? */
+    if(socksreq[0] != 0) {
+      failf(data,
+            "SOCKS4 reply has wrong version, version should be 4.");
+      return CURLE_COULDNT_CONNECT;
+    }
+
+    /* Result */
+    switch(socksreq[1]) {
+    case 90:
+      infof(data, "SOCKS4%s request granted.\n", protocol4a?"a":"");
+      break;
+    case 91:
+      failf(data,
+            "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
+            ", request rejected or failed.",
+            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
+            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
+            ((socksreq[8] << 8) | socksreq[9]),
+            socksreq[1]);
+      return CURLE_COULDNT_CONNECT;
+    case 92:
+      failf(data,
+            "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
+            ", request rejected because SOCKS server cannot connect to "
+            "identd on the client.",
+            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
+            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
+            ((socksreq[8] << 8) | socksreq[9]),
+            socksreq[1]);
+      return CURLE_COULDNT_CONNECT;
+    case 93:
+      failf(data,
+            "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
+            ", request rejected because the client program and identd "
+            "report different user-ids.",
+            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
+            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
+            ((socksreq[8] << 8) | socksreq[9]),
+            socksreq[1]);
+      return CURLE_COULDNT_CONNECT;
+    default:
+      failf(data,
+            "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
+            ", Unknown.",
+            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
+            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
+            ((socksreq[8] << 8) | socksreq[9]),
+            socksreq[1]);
+      return CURLE_COULDNT_CONNECT;
+    }
+  }
+
+  curlx_nonblock(sock, TRUE);
+
+  return CURLE_OK; /* Proxy was successful! */
+}
+
+/*
+ * This function logs in to a SOCKS5 proxy and sends the specifics to the final
+ * destination server.
+ */
+CURLcode Curl_SOCKS5(const char *proxy_name,
+                     const char *proxy_password,
+                     const char *hostname,
+                     int remote_port,
+                     int sockindex,
+                     struct connectdata *conn)
+{
+  /*
+    According to the RFC1928, section "6.  Replies". This is what a SOCK5
+    replies:
+
+        +----+-----+-------+------+----------+----------+
+        |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
+        +----+-----+-------+------+----------+----------+
+        | 1  |  1  | X'00' |  1   | Variable |    2     |
+        +----+-----+-------+------+----------+----------+
+
+    Where:
+
+    o  VER    protocol version: X'05'
+    o  REP    Reply field:
+    o  X'00' succeeded
+  */
+
+  unsigned char socksreq[600]; /* room for large user/pw (255 max each) */
+  ssize_t actualread;
+  ssize_t written;
+  int result;
+  CURLcode code;
+  curl_socket_t sock = conn->sock[sockindex];
+  struct SessionHandle *data = conn->data;
+  long timeout;
+  bool socks5_resolve_local = (conn->proxytype == CURLPROXY_SOCKS5)?TRUE:FALSE;
+  const size_t hostname_len = strlen(hostname);
+  ssize_t len = 0;
+
+  /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
+  if(!socks5_resolve_local && hostname_len > 255) {
+    infof(conn->data,"SOCKS5: server resolving disabled for hostnames of "
+          "length > 255 [actual len=%zu]\n", hostname_len);
+    socks5_resolve_local = TRUE;
+  }
+
+  /* get timeout */
+  timeout = Curl_timeleft(data, NULL, TRUE);
+
+  if(timeout < 0) {
+    /* time-out, bail out, go home */
+    failf(data, "Connection time-out");
+    return CURLE_OPERATION_TIMEDOUT;
+  }
+
+  curlx_nonblock(sock, TRUE);
+
+  /* wait until socket gets connected */
+  result = Curl_socket_ready(CURL_SOCKET_BAD, sock, timeout);
+
+  if(-1 == result) {
+    failf(conn->data, "SOCKS5: no connection here");
+    return CURLE_COULDNT_CONNECT;
+  }
+  else if(0 == result) {
+    failf(conn->data, "SOCKS5: connection timeout");
+    return CURLE_OPERATION_TIMEDOUT;
+  }
+
+  if(result & CURL_CSELECT_ERR) {
+    failf(conn->data, "SOCKS5: error occurred during connection");
+    return CURLE_COULDNT_CONNECT;
+  }
+
+  socksreq[0] = 5; /* version */
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+  socksreq[1] = (char)(proxy_name ? 3 : 2); /* number of methods (below) */
+  socksreq[2] = 0; /* no authentication */
+  socksreq[3] = 1; /* gssapi */
+  socksreq[4] = 2; /* username/password */
+#else
+  socksreq[1] = (char)(proxy_name ? 2 : 1); /* number of methods (below) */
+  socksreq[2] = 0; /* no authentication */
+  socksreq[3] = 2; /* username/password */
+#endif
+
+  curlx_nonblock(sock, FALSE);
+
+  code = Curl_write_plain(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]),
+                          &written);
+  if((code != CURLE_OK) || (written != (2 + (int)socksreq[1]))) {
+    failf(data, "Unable to send initial SOCKS5 request.");
+    return CURLE_COULDNT_CONNECT;
+  }
+
+  curlx_nonblock(sock, TRUE);
+
+  result = Curl_socket_ready(sock, CURL_SOCKET_BAD, timeout);
+
+  if(-1 == result) {
+    failf(conn->data, "SOCKS5 nothing to read");
+    return CURLE_COULDNT_CONNECT;
+  }
+  else if(0 == result) {
+    failf(conn->data, "SOCKS5 read timeout");
+    return CURLE_OPERATION_TIMEDOUT;
+  }
+
+  if(result & CURL_CSELECT_ERR) {
+    failf(conn->data, "SOCKS5 read error occurred");
+    return CURLE_RECV_ERROR;
+  }
+
+  curlx_nonblock(sock, FALSE);
+
+  result=Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread);
+  if((result != CURLE_OK) || (actualread != 2)) {
+    failf(data, "Unable to receive initial SOCKS5 response.");
+    return CURLE_COULDNT_CONNECT;
+  }
+
+  if(socksreq[0] != 5) {
+    failf(data, "Received invalid version in initial SOCKS5 response.");
+    return CURLE_COULDNT_CONNECT;
+  }
+  if(socksreq[1] == 0) {
+    /* Nothing to do, no authentication needed */
+    ;
+  }
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+  else if(socksreq[1] == 1) {
+    code = Curl_SOCKS5_gssapi_negotiate(sockindex, conn);
+    if(code != CURLE_OK) {
+      failf(data, "Unable to negotiate SOCKS5 gssapi context.");
+      return CURLE_COULDNT_CONNECT;
+    }
+  }
+#endif
+  else if(socksreq[1] == 2) {
+    /* Needs user name and password */
+    size_t proxy_name_len, proxy_password_len;
+    if(proxy_name && proxy_password) {
+      proxy_name_len = strlen(proxy_name);
+      proxy_password_len = strlen(proxy_password);
+    }
+    else {
+      proxy_name_len = 0;
+      proxy_password_len = 0;
+    }
+
+    /*   username/password request looks like
+     * +----+------+----------+------+----------+
+     * |VER | ULEN |  UNAME   | PLEN |  PASSWD  |
+     * +----+------+----------+------+----------+
+     * | 1  |  1   | 1 to 255 |  1   | 1 to 255 |
+     * +----+------+----------+------+----------+
+     */
+    len = 0;
+    socksreq[len++] = 1;    /* username/pw subnegotiation version */
+    socksreq[len++] = (unsigned char) proxy_name_len;
+    if(proxy_name && proxy_name_len)
+      memcpy(socksreq + len, proxy_name, proxy_name_len);
+    len += proxy_name_len;
+    socksreq[len++] = (unsigned char) proxy_password_len;
+    if(proxy_password && proxy_password_len)
+      memcpy(socksreq + len, proxy_password, proxy_password_len);
+    len += proxy_password_len;
+
+    code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written);
+    if((code != CURLE_OK) || (len != written)) {
+      failf(data, "Failed to send SOCKS5 sub-negotiation request.");
+      return CURLE_COULDNT_CONNECT;
+    }
+
+    result=Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread);
+    if((result != CURLE_OK) || (actualread != 2)) {
+      failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
+      return CURLE_COULDNT_CONNECT;
+    }
+
+    /* ignore the first (VER) byte */
+    if(socksreq[1] != 0) { /* status */
+      failf(data, "User was rejected by the SOCKS5 server (%d %d).",
+            socksreq[0], socksreq[1]);
+      return CURLE_COULDNT_CONNECT;
+    }
+
+    /* Everything is good so far, user was authenticated! */
+  }
+  else {
+    /* error */
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+    if(socksreq[1] == 255) {
+#else
+    if(socksreq[1] == 1) {
+      failf(data,
+            "SOCKS5 GSSAPI per-message authentication is not supported.");
+      return CURLE_COULDNT_CONNECT;
+    }
+    else if(socksreq[1] == 255) {
+#endif
+      if(!proxy_name || !*proxy_name) {
+        failf(data,
+              "No authentication method was acceptable. (It is quite likely"
+              " that the SOCKS5 server wanted a username/password, since none"
+              " was supplied to the server on this connection.)");
+      }
+      else {
+        failf(data, "No authentication method was acceptable.");
+      }
+      return CURLE_COULDNT_CONNECT;
+    }
+    else {
+      failf(data,
+            "Undocumented SOCKS5 mode attempted to be used by server.");
+      return CURLE_COULDNT_CONNECT;
+    }
+  }
+
+  /* Authentication is complete, now specify destination to the proxy */
+  len = 0;
+  socksreq[len++] = 5; /* version (SOCKS5) */
+  socksreq[len++] = 1; /* connect */
+  socksreq[len++] = 0; /* must be zero */
+
+  if(!socks5_resolve_local) {
+    socksreq[len++] = 3; /* ATYP: domain name = 3 */
+    socksreq[len++] = (char) hostname_len; /* address length */
+    memcpy(&socksreq[len], hostname, hostname_len); /* address str w/o NULL */
+    len += hostname_len;
+  }
+  else {
+    struct Curl_dns_entry *dns;
+    Curl_addrinfo *hp = NULL;
+    int rc = Curl_resolv(conn, hostname, remote_port, &dns);
+
+    if(rc == CURLRESOLV_ERROR)
+      return CURLE_COULDNT_RESOLVE_HOST;
+
+    if(rc == CURLRESOLV_PENDING) {
+      /* this requires that we're in "wait for resolve" state */
+      code = Curl_resolver_wait_resolv(conn, &dns);
+      if(code != CURLE_OK)
+        return code;
+    }
+
+    /*
+     * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
+     * returns a Curl_addrinfo pointer that may not always look the same.
+     */
+    if(dns)
+      hp=dns->addr;
+    if(hp) {
+      struct sockaddr_in *saddr_in;
+#ifdef ENABLE_IPV6
+      struct sockaddr_in6 *saddr_in6;
+#endif
+      int i;
+
+      if(hp->ai_family == AF_INET) {
+        socksreq[len++] = 1; /* ATYP: IPv4 = 1 */
+
+        saddr_in = (struct sockaddr_in*)hp->ai_addr;
+        for(i = 0; i < 4; i++) {
+          socksreq[len++] = ((unsigned char*)&saddr_in->sin_addr.s_addr)[i];
+          infof(data, "%d\n", socksreq[len-1]);
+        }
+      }
+#ifdef ENABLE_IPV6
+      else if(hp->ai_family == AF_INET6) {
+        socksreq[len++] = 4; /* ATYP: IPv6 = 4 */
+
+        saddr_in6 = (struct sockaddr_in6*)hp->ai_addr;
+        for(i = 0; i < 16; i++) {
+          socksreq[len++] = ((unsigned char*)&saddr_in6->sin6_addr.s6_addr)[i];
+        }
+      }
+#endif
+      else
+        hp = NULL; /* fail! */
+
+      Curl_resolv_unlock(data, dns); /* not used anymore from now on */
+    }
+    if(!hp) {
+      failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
+            hostname);
+      return CURLE_COULDNT_RESOLVE_HOST;
+    }
+  }
+
+  socksreq[len++] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */
+  socksreq[len++] = (unsigned char)(remote_port & 0xff);        /* PORT LSB */
+
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+  if(conn->socks5_gssapi_enctype) {
+    failf(data, "SOCKS5 gssapi protection not yet implemented.");
+  }
+  else
+#endif
+    code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written);
+
+  if((code != CURLE_OK) || (len != written)) {
+    failf(data, "Failed to send SOCKS5 connect request.");
+    return CURLE_COULDNT_CONNECT;
+  }
+
+  len = 10; /* minimum packet size is 10 */
+
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+  if(conn->socks5_gssapi_enctype) {
+    failf(data, "SOCKS5 gssapi protection not yet implemented.");
+  }
+  else
+#endif
+    result = Curl_blockread_all(conn, sock, (char *)socksreq,
+                                len, &actualread);
+
+  if((result != CURLE_OK) || (len != actualread)) {
+    failf(data, "Failed to receive SOCKS5 connect request ack.");
+    return CURLE_COULDNT_CONNECT;
+  }
+
+  if(socksreq[0] != 5) { /* version */
+    failf(data,
+          "SOCKS5 reply has wrong version, version should be 5.");
+    return CURLE_COULDNT_CONNECT;
+  }
+  if(socksreq[1] != 0) { /* Anything besides 0 is an error */
+    if(socksreq[3] == 1) {
+      failf(data,
+            "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)",
+            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
+            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
+            ((socksreq[8] << 8) | socksreq[9]),
+            socksreq[1]);
+    }
+    else if(socksreq[3] == 3) {
+      failf(data,
+            "Can't complete SOCKS5 connection to %s:%d. (%d)",
+            hostname,
+            ((socksreq[8] << 8) | socksreq[9]),
+            socksreq[1]);
+    }
+    else if(socksreq[3] == 4) {
+      failf(data,
+            "Can't complete SOCKS5 connection to %02x%02x:%02x%02x:"
+            "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%d. (%d)",
+            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
+            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
+            (unsigned char)socksreq[8], (unsigned char)socksreq[9],
+            (unsigned char)socksreq[10], (unsigned char)socksreq[11],
+            (unsigned char)socksreq[12], (unsigned char)socksreq[13],
+            (unsigned char)socksreq[14], (unsigned char)socksreq[15],
+            (unsigned char)socksreq[16], (unsigned char)socksreq[17],
+            (unsigned char)socksreq[18], (unsigned char)socksreq[19],
+            ((socksreq[8] << 8) | socksreq[9]),
+            socksreq[1]);
+    }
+    return CURLE_COULDNT_CONNECT;
+  }
+
+  /* Fix: in general, returned BND.ADDR is variable length parameter by RFC
+     1928, so the reply packet should be read until the end to avoid errors at
+     subsequent protocol level.
+
+    +----+-----+-------+------+----------+----------+
+    |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
+    +----+-----+-------+------+----------+----------+
+    | 1  |  1  | X'00' |  1   | Variable |    2     |
+    +----+-----+-------+------+----------+----------+
+
+     ATYP:
+     o  IP v4 address: X'01', BND.ADDR = 4 byte
+     o  domain name:  X'03', BND.ADDR = [ 1 byte length, string ]
+     o  IP v6 address: X'04', BND.ADDR = 16 byte
+     */
+
+  /* Calculate real packet size */
+  if(socksreq[3] == 3) {
+    /* domain name */
+    int addrlen = (int) socksreq[4];
+    len = 5 + addrlen + 2;
+  }
+  else if(socksreq[3] == 4) {
+    /* IPv6 */
+    len = 4 + 16 + 2;
+  }
+
+  /* At this point we already read first 10 bytes */
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+  if(!conn->socks5_gssapi_enctype) {
+    /* decrypt_gssapi_blockread already read the whole packet */
+#endif
+    if(len > 10) {
+      len -= 10;
+      result = Curl_blockread_all(conn, sock, (char *)&socksreq[10],
+                                  len, &actualread);
+      if((result != CURLE_OK) || (len != actualread)) {
+        failf(data, "Failed to receive SOCKS5 connect request ack.");
+        return CURLE_COULDNT_CONNECT;
+      }
+    }
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+  }
+#endif
+
+  curlx_nonblock(sock, TRUE);
+  return CURLE_OK; /* Proxy was successful! */
+}
+
+#endif /* CURL_DISABLE_PROXY */
+
diff --git a/lib/curl_socks_gssapi.c b/lib/curl_socks_gssapi.c
new file mode 100644 (file)
index 0000000..2bd3d45
--- /dev/null
@@ -0,0 +1,533 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2009, 2011, Markus Moeller, <markus_moeller@compuserve.com>
+ * Copyright (C) 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_PROXY
+
+#ifdef HAVE_GSSAPI
+#ifdef HAVE_OLD_GSSMIT
+#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
+#define NCOMPAT 1
+#endif
+#ifndef gss_nt_service_name
+#define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
+#endif
+
+#include "curl_gssapi.h"
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_connect.h"
+#include "curl_timeval.h"
+#include "curl_socks.h"
+#include "curl_warnless.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+static gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
+
+/*
+ * Helper gssapi error functions.
+ */
+static int check_gss_err(struct SessionHandle *data,
+                         OM_uint32 major_status,
+                         OM_uint32 minor_status,
+                         const char* function)
+{
+  if(GSS_ERROR(major_status)) {
+    OM_uint32 maj_stat,min_stat;
+    OM_uint32 msg_ctx = 0;
+    gss_buffer_desc status_string;
+    char buf[1024];
+    size_t len;
+
+    len = 0;
+    msg_ctx = 0;
+    while(!msg_ctx) {
+      /* convert major status code (GSS-API error) to text */
+      maj_stat = gss_display_status(&min_stat, major_status,
+                                    GSS_C_GSS_CODE,
+                                    GSS_C_NULL_OID,
+                                    &msg_ctx, &status_string);
+      if(maj_stat == GSS_S_COMPLETE) {
+        if(sizeof(buf) > len + status_string.length + 1) {
+          strcpy(buf+len, (char*) status_string.value);
+          len += status_string.length;
+        }
+        gss_release_buffer(&min_stat, &status_string);
+        break;
+      }
+      gss_release_buffer(&min_stat, &status_string);
+    }
+    if(sizeof(buf) > len + 3) {
+      strcpy(buf+len, ".\n");
+      len += 2;
+    }
+    msg_ctx = 0;
+    while(!msg_ctx) {
+      /* convert minor status code (underlying routine error) to text */
+      maj_stat = gss_display_status(&min_stat, minor_status,
+                                    GSS_C_MECH_CODE,
+                                    GSS_C_NULL_OID,
+                                    &msg_ctx, &status_string);
+      if(maj_stat == GSS_S_COMPLETE) {
+        if(sizeof(buf) > len + status_string.length)
+          strcpy(buf+len, (char*) status_string.value);
+        gss_release_buffer(&min_stat, &status_string);
+        break;
+      }
+      gss_release_buffer(&min_stat, &status_string);
+    }
+    failf(data, "GSSAPI error: %s failed:\n%s", function, buf);
+    return(1);
+  }
+
+  return(0);
+}
+
+CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
+                                      struct connectdata *conn)
+{
+  struct SessionHandle *data = conn->data;
+  curl_socket_t sock = conn->sock[sockindex];
+  CURLcode code;
+  ssize_t actualread;
+  ssize_t written;
+  int result;
+  OM_uint32 gss_major_status, gss_minor_status, gss_status;
+  OM_uint32 gss_ret_flags;
+  int gss_conf_state, gss_enc;
+  gss_buffer_desc  service = GSS_C_EMPTY_BUFFER;
+  gss_buffer_desc  gss_send_token = GSS_C_EMPTY_BUFFER;
+  gss_buffer_desc  gss_recv_token = GSS_C_EMPTY_BUFFER;
+  gss_buffer_desc  gss_w_token = GSS_C_EMPTY_BUFFER;
+  gss_buffer_desc* gss_token = GSS_C_NO_BUFFER;
+  gss_name_t       server = GSS_C_NO_NAME;
+  gss_name_t       gss_client_name = GSS_C_NO_NAME;
+  unsigned short   us_length;
+  char             *user=NULL;
+  unsigned char socksreq[4]; /* room for gssapi exchange header only */
+  char *serviceptr = data->set.str[STRING_SOCKS5_GSSAPI_SERVICE];
+
+  /*   GSSAPI request looks like
+   * +----+------+-----+----------------+
+   * |VER | MTYP | LEN |     TOKEN      |
+   * +----+------+----------------------+
+   * | 1  |  1   |  2  | up to 2^16 - 1 |
+   * +----+------+-----+----------------+
+   */
+
+  /* prepare service name */
+  if(strchr(serviceptr,'/')) {
+    service.value = malloc(strlen(serviceptr));
+    if(!service.value)
+      return CURLE_OUT_OF_MEMORY;
+    service.length = strlen(serviceptr);
+    memcpy(service.value, serviceptr, service.length);
+
+    gss_major_status = gss_import_name(&gss_minor_status, &service,
+                                       (gss_OID) GSS_C_NULL_OID, &server);
+  }
+  else {
+    service.value = malloc(strlen(serviceptr) +strlen(conn->proxy.name)+2);
+    if(!service.value)
+      return CURLE_OUT_OF_MEMORY;
+    service.length = strlen(serviceptr) +strlen(conn->proxy.name)+1;
+    snprintf(service.value, service.length+1, "%s@%s",
+             serviceptr, conn->proxy.name);
+
+    gss_major_status = gss_import_name(&gss_minor_status, &service,
+                                       gss_nt_service_name, &server);
+  }
+
+  gss_release_buffer(&gss_status, &service); /* clear allocated memory */
+
+  if(check_gss_err(data,gss_major_status,
+                   gss_minor_status,"gss_import_name()")) {
+    failf(data, "Failed to create service name.");
+    gss_release_name(&gss_status, &server);
+    return CURLE_COULDNT_CONNECT;
+  }
+
+  /* As long as we need to keep sending some context info, and there's no  */
+  /* errors, keep sending it...                                            */
+  for(;;) {
+    gss_major_status = Curl_gss_init_sec_context(data,
+                                                 &gss_minor_status,
+                                                 &gss_context,
+                                                 server,
+                                                 NULL,
+                                                 gss_token,
+                                                 &gss_send_token,
+                                                 &gss_ret_flags);
+
+    if(gss_token != GSS_C_NO_BUFFER)
+      gss_release_buffer(&gss_status, &gss_recv_token);
+    if(check_gss_err(data,gss_major_status,
+                     gss_minor_status,"gss_init_sec_context")) {
+      gss_release_name(&gss_status, &server);
+      gss_release_buffer(&gss_status, &gss_recv_token);
+      gss_release_buffer(&gss_status, &gss_send_token);
+      gss_delete_sec_context(&gss_status, &gss_context, NULL);
+      failf(data, "Failed to initial GSSAPI token.");
+      return CURLE_COULDNT_CONNECT;
+    }
+
+    if(gss_send_token.length != 0) {
+      socksreq[0] = 1;    /* gssapi subnegotiation version */
+      socksreq[1] = 1;    /* authentication message type */
+      us_length = htons((short)gss_send_token.length);
+      memcpy(socksreq+2,&us_length,sizeof(short));
+
+      code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
+      if((code != CURLE_OK) || (4 != written)) {
+        failf(data, "Failed to send GSSAPI authentication request.");
+        gss_release_name(&gss_status, &server);
+        gss_release_buffer(&gss_status, &gss_recv_token);
+        gss_release_buffer(&gss_status, &gss_send_token);
+        gss_delete_sec_context(&gss_status, &gss_context, NULL);
+        return CURLE_COULDNT_CONNECT;
+      }
+
+      code = Curl_write_plain(conn, sock, (char *)gss_send_token.value,
+                              gss_send_token.length, &written);
+
+      if((code != CURLE_OK) || ((ssize_t)gss_send_token.length != written)) {
+        failf(data, "Failed to send GSSAPI authentication token.");
+        gss_release_name(&gss_status, &server);
+        gss_release_buffer(&gss_status, &gss_recv_token);
+        gss_release_buffer(&gss_status, &gss_send_token);
+        gss_delete_sec_context(&gss_status, &gss_context, NULL);
+        return CURLE_COULDNT_CONNECT;
+      }
+
+    }
+
+    gss_release_buffer(&gss_status, &gss_send_token);
+    gss_release_buffer(&gss_status, &gss_recv_token);
+    if(gss_major_status != GSS_S_CONTINUE_NEEDED) break;
+
+    /* analyse response */
+
+    /*   GSSAPI response looks like
+     * +----+------+-----+----------------+
+     * |VER | MTYP | LEN |     TOKEN      |
+     * +----+------+----------------------+
+     * | 1  |  1   |  2  | up to 2^16 - 1 |
+     * +----+------+-----+----------------+
+     */
+
+    result=Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread);
+    if(result != CURLE_OK || actualread != 4) {
+      failf(data, "Failed to receive GSSAPI authentication response.");
+      gss_release_name(&gss_status, &server);
+      gss_delete_sec_context(&gss_status, &gss_context, NULL);
+      return CURLE_COULDNT_CONNECT;
+    }
+
+    /* ignore the first (VER) byte */
+    if(socksreq[1] == 255) { /* status / message type */
+      failf(data, "User was rejected by the SOCKS5 server (%d %d).",
+            socksreq[0], socksreq[1]);
+      gss_release_name(&gss_status, &server);
+      gss_delete_sec_context(&gss_status, &gss_context, NULL);
+      return CURLE_COULDNT_CONNECT;
+    }
+
+    if(socksreq[1] != 1) { /* status / messgae type */
+      failf(data, "Invalid GSSAPI authentication response type (%d %d).",
+            socksreq[0], socksreq[1]);
+      gss_release_name(&gss_status, &server);
+      gss_delete_sec_context(&gss_status, &gss_context, NULL);
+      return CURLE_COULDNT_CONNECT;
+    }
+
+    memcpy(&us_length, socksreq+2, sizeof(short));
+    us_length = ntohs(us_length);
+
+    gss_recv_token.length=us_length;
+    gss_recv_token.value=malloc(us_length);
+    if(!gss_recv_token.value) {
+      failf(data,
+            "Could not allocate memory for GSSAPI authentication "
+            "response token.");
+      gss_release_name(&gss_status, &server);
+      gss_delete_sec_context(&gss_status, &gss_context, NULL);
+      return CURLE_OUT_OF_MEMORY;
+    }
+
+    result=Curl_blockread_all(conn, sock, (char *)gss_recv_token.value,
+                              gss_recv_token.length, &actualread);
+
+    if(result != CURLE_OK || actualread != us_length) {
+      failf(data, "Failed to receive GSSAPI authentication token.");
+      gss_release_name(&gss_status, &server);
+      gss_release_buffer(&gss_status, &gss_recv_token);
+      gss_delete_sec_context(&gss_status, &gss_context, NULL);
+      return CURLE_COULDNT_CONNECT;
+    }
+
+    gss_token = &gss_recv_token;
+  }
+
+  gss_release_name(&gss_status, &server);
+
+  /* Everything is good so far, user was authenticated! */
+  gss_major_status = gss_inquire_context (&gss_minor_status, gss_context,
+                                          &gss_client_name, NULL, NULL, NULL,
+                                          NULL, NULL, NULL);
+  if(check_gss_err(data,gss_major_status,
+                   gss_minor_status,"gss_inquire_context")) {
+    gss_delete_sec_context(&gss_status, &gss_context, NULL);
+    gss_release_name(&gss_status, &gss_client_name);
+    failf(data, "Failed to determine user name.");
+    return CURLE_COULDNT_CONNECT;
+  }
+  gss_major_status = gss_display_name(&gss_minor_status, gss_client_name,
+                                      &gss_send_token, NULL);
+  if(check_gss_err(data,gss_major_status,
+                   gss_minor_status,"gss_display_name")) {
+    gss_delete_sec_context(&gss_status, &gss_context, NULL);
+    gss_release_name(&gss_status, &gss_client_name);
+    gss_release_buffer(&gss_status, &gss_send_token);
+    failf(data, "Failed to determine user name.");
+    return CURLE_COULDNT_CONNECT;
+  }
+  user=malloc(gss_send_token.length+1);
+  if(!user) {
+    gss_delete_sec_context(&gss_status, &gss_context, NULL);
+    gss_release_name(&gss_status, &gss_client_name);
+    gss_release_buffer(&gss_status, &gss_send_token);
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  memcpy(user, gss_send_token.value, gss_send_token.length);
+  user[gss_send_token.length] = '\0';
+  gss_release_name(&gss_status, &gss_client_name);
+  gss_release_buffer(&gss_status, &gss_send_token);
+  infof(data, "SOCKS5 server authencticated user %s with gssapi.\n",user);
+  free(user);
+  user=NULL;
+
+  /* Do encryption */
+  socksreq[0] = 1;    /* gssapi subnegotiation version */
+  socksreq[1] = 2;    /* encryption message type */
+
+  gss_enc = 0; /* no data protection */
+  /* do confidentiality protection if supported */
+  if(gss_ret_flags & GSS_C_CONF_FLAG)
+    gss_enc = 2;
+  /* else do integrity protection */
+  else if(gss_ret_flags & GSS_C_INTEG_FLAG)
+    gss_enc = 1;
+
+  infof(data, "SOCKS5 server supports gssapi %s data protection.\n",
+        (gss_enc==0)?"no":((gss_enc==1)?"integrity":"confidentiality"));
+  /* force for the moment to no data protection */
+  gss_enc = 0;
+  /*
+   * Sending the encryption type in clear seems wrong. It should be
+   * protected with gss_seal()/gss_wrap(). See RFC1961 extract below
+   * The NEC reference implementations on which this is based is
+   * therefore at fault
+   *
+   *  +------+------+------+.......................+
+   *  + ver  | mtyp | len  |   token               |
+   *  +------+------+------+.......................+
+   *  + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets |
+   *  +------+------+------+.......................+
+   *
+   *   Where:
+   *
+   *  - "ver" is the protocol version number, here 1 to represent the
+   *    first version of the SOCKS/GSS-API protocol
+   *
+   *  - "mtyp" is the message type, here 2 to represent a protection
+   *    -level negotiation message
+   *
+   *  - "len" is the length of the "token" field in octets
+   *
+   *  - "token" is the GSS-API encapsulated protection level
+   *
+   * The token is produced by encapsulating an octet containing the
+   * required protection level using gss_seal()/gss_wrap() with conf_req
+   * set to FALSE.  The token is verified using gss_unseal()/
+   * gss_unwrap().
+   *
+   */
+  if(data->set.socks5_gssapi_nec) {
+    us_length = htons((short)1);
+    memcpy(socksreq+2,&us_length,sizeof(short));
+  }
+  else {
+    gss_send_token.length = 1;
+    gss_send_token.value = malloc(1);
+    if(!gss_send_token.value) {
+      gss_delete_sec_context(&gss_status, &gss_context, NULL);
+      return CURLE_OUT_OF_MEMORY;
+    }
+    memcpy(gss_send_token.value, &gss_enc, 1);
+
+    gss_major_status = gss_wrap(&gss_minor_status, gss_context, 0,
+                                GSS_C_QOP_DEFAULT, &gss_send_token,
+                                &gss_conf_state, &gss_w_token);
+
+    if(check_gss_err(data,gss_major_status,gss_minor_status,"gss_wrap")) {
+      gss_release_buffer(&gss_status, &gss_send_token);
+      gss_release_buffer(&gss_status, &gss_w_token);
+      gss_delete_sec_context(&gss_status, &gss_context, NULL);
+      failf(data, "Failed to wrap GSSAPI encryption value into token.");
+      return CURLE_COULDNT_CONNECT;
+    }
+    gss_release_buffer(&gss_status, &gss_send_token);
+
+    us_length = htons((short)gss_w_token.length);
+    memcpy(socksreq+2,&us_length,sizeof(short));
+  }
+
+  code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
+  if((code != CURLE_OK) || (4 != written)) {
+    failf(data, "Failed to send GSSAPI encryption request.");
+    gss_release_buffer(&gss_status, &gss_w_token);
+    gss_delete_sec_context(&gss_status, &gss_context, NULL);
+    return CURLE_COULDNT_CONNECT;
+  }
+
+  if(data->set.socks5_gssapi_nec) {
+    memcpy(socksreq, &gss_enc, 1);
+    code = Curl_write_plain(conn, sock, socksreq, 1, &written);
+    if((code != CURLE_OK) || ( 1 != written)) {
+      failf(data, "Failed to send GSSAPI encryption type.");
+      gss_delete_sec_context(&gss_status, &gss_context, NULL);
+      return CURLE_COULDNT_CONNECT;
+    }
+  }
+  else {
+    code = Curl_write_plain(conn, sock, (char *)gss_w_token.value,
+                            gss_w_token.length, &written);
+    if((code != CURLE_OK) || ((ssize_t)gss_w_token.length != written)) {
+      failf(data, "Failed to send GSSAPI encryption type.");
+      gss_release_buffer(&gss_status, &gss_w_token);
+      gss_delete_sec_context(&gss_status, &gss_context, NULL);
+      return CURLE_COULDNT_CONNECT;
+    }
+    gss_release_buffer(&gss_status, &gss_w_token);
+  }
+
+  result=Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread);
+  if(result != CURLE_OK || actualread != 4) {
+    failf(data, "Failed to receive GSSAPI encryption response.");
+    gss_delete_sec_context(&gss_status, &gss_context, NULL);
+    return CURLE_COULDNT_CONNECT;
+  }
+
+  /* ignore the first (VER) byte */
+  if(socksreq[1] == 255) { /* status / message type */
+    failf(data, "User was rejected by the SOCKS5 server (%d %d).",
+          socksreq[0], socksreq[1]);
+    gss_delete_sec_context(&gss_status, &gss_context, NULL);
+    return CURLE_COULDNT_CONNECT;
+  }
+
+  if(socksreq[1] != 2) { /* status / messgae type */
+    failf(data, "Invalid GSSAPI encryption response type (%d %d).",
+          socksreq[0], socksreq[1]);
+    gss_delete_sec_context(&gss_status, &gss_context, NULL);
+    return CURLE_COULDNT_CONNECT;
+  }
+
+  memcpy(&us_length, socksreq+2, sizeof(short));
+  us_length = ntohs(us_length);
+
+  gss_recv_token.length= us_length;
+  gss_recv_token.value=malloc(gss_recv_token.length);
+  if(!gss_recv_token.value) {
+    gss_delete_sec_context(&gss_status, &gss_context, NULL);
+    return CURLE_OUT_OF_MEMORY;
+  }
+  result=Curl_blockread_all(conn, sock, (char *)gss_recv_token.value,
+                            gss_recv_token.length, &actualread);
+
+  if(result != CURLE_OK || actualread != us_length) {
+    failf(data, "Failed to receive GSSAPI encryptrion type.");
+    gss_release_buffer(&gss_status, &gss_recv_token);
+    gss_delete_sec_context(&gss_status, &gss_context, NULL);
+    return CURLE_COULDNT_CONNECT;
+  }
+
+  if(!data->set.socks5_gssapi_nec) {
+    gss_major_status = gss_unwrap(&gss_minor_status, gss_context,
+                                  &gss_recv_token, &gss_w_token,
+                                  0, GSS_C_QOP_DEFAULT);
+
+    if(check_gss_err(data,gss_major_status,gss_minor_status,"gss_unwrap")) {
+      gss_release_buffer(&gss_status, &gss_recv_token);
+      gss_release_buffer(&gss_status, &gss_w_token);
+      gss_delete_sec_context(&gss_status, &gss_context, NULL);
+      failf(data, "Failed to unwrap GSSAPI encryption value into token.");
+      return CURLE_COULDNT_CONNECT;
+    }
+    gss_release_buffer(&gss_status, &gss_recv_token);
+
+    if(gss_w_token.length != 1) {
+      failf(data, "Invalid GSSAPI encryption response length (%d).",
+            gss_w_token.length);
+      gss_release_buffer(&gss_status, &gss_w_token);
+      gss_delete_sec_context(&gss_status, &gss_context, NULL);
+      return CURLE_COULDNT_CONNECT;
+    }
+
+    memcpy(socksreq,gss_w_token.value,gss_w_token.length);
+    gss_release_buffer(&gss_status, &gss_w_token);
+  }
+  else {
+    if(gss_recv_token.length != 1) {
+      failf(data, "Invalid GSSAPI encryption response length (%d).",
+            gss_recv_token.length);
+      gss_release_buffer(&gss_status, &gss_recv_token);
+      gss_delete_sec_context(&gss_status, &gss_context, NULL);
+      return CURLE_COULDNT_CONNECT;
+    }
+
+    memcpy(socksreq,gss_recv_token.value,gss_recv_token.length);
+    gss_release_buffer(&gss_status, &gss_recv_token);
+  }
+
+  infof(data, "SOCKS5 access with%s protection granted.\n",
+        (socksreq[0]==0)?"out gssapi data":
+        ((socksreq[0]==1)?" gssapi integrity":" gssapi confidentiality"));
+
+  conn->socks5_gssapi_enctype = socksreq[0];
+  if(socksreq[0] == 0)
+    gss_delete_sec_context(&gss_status, &gss_context, NULL);
+
+  return CURLE_OK;
+}
+#endif
+
+#endif /* CURL_DISABLE_PROXY */
diff --git a/lib/curl_socks_sspi.c b/lib/curl_socks_sspi.c
new file mode 100644 (file)
index 0000000..c576107
--- /dev/null
@@ -0,0 +1,591 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2009, 2011, Markus Moeller, <markus_moeller@compuserve.com>
+ * Copyright (C) 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_PROXY)
+
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_connect.h"
+#include "curl_strerror.h"
+#include "curl_timeval.h"
+#include "curl_socks.h"
+#include "curl_sspi.h"
+#include "curl_multibyte.h"
+#include "curl_warnless.h"
+
+#define _MPRINTF_REPLACE /* use the internal *printf() functions */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/*
+ * Definitions required from ntsecapi.h are directly provided below this point
+ * to avoid including ntsecapi.h due to a conflict with OpenSSL's safestack.h
+ */
+#define KERB_WRAP_NO_ENCRYPT 0x80000001
+
+/*
+ * Helper sspi error functions.
+ */
+static int check_sspi_err(struct connectdata *conn,
+                          SECURITY_STATUS status,
+                          const char* function)
+{
+  if(status != SEC_E_OK &&
+     status != SEC_I_COMPLETE_AND_CONTINUE &&
+     status != SEC_I_COMPLETE_NEEDED &&
+     status != SEC_I_CONTINUE_NEEDED) {
+    failf(conn->data, "SSPI error: %s failed: %s", function,
+          Curl_sspi_strerror(conn, status));
+    return 1;
+  }
+  return 0;
+}
+
+/* This is the SSPI-using version of this function */
+CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
+                                      struct connectdata *conn)
+{
+  struct SessionHandle *data = conn->data;
+  curl_socket_t sock = conn->sock[sockindex];
+  CURLcode code;
+  ssize_t actualread;
+  ssize_t written;
+  int result;
+  /* Needs GSSAPI authentication */
+  SECURITY_STATUS status;
+  unsigned long sspi_ret_flags = 0;
+  int gss_enc;
+  SecBuffer sspi_send_token, sspi_recv_token, sspi_w_token[3];
+  SecBufferDesc input_desc, output_desc, wrap_desc;
+  SecPkgContext_Sizes sspi_sizes;
+  CredHandle cred_handle;
+  CtxtHandle sspi_context;
+  PCtxtHandle context_handle = NULL;
+  SecPkgCredentials_Names names;
+  TimeStamp expiry;
+  char *service_name = NULL;
+  unsigned short us_length;
+  unsigned long qop;
+  unsigned char socksreq[4]; /* room for gssapi exchange header only */
+  char *service = data->set.str[STRING_SOCKS5_GSSAPI_SERVICE];
+
+  /*   GSSAPI request looks like
+   * +----+------+-----+----------------+
+   * |VER | MTYP | LEN |     TOKEN      |
+   * +----+------+----------------------+
+   * | 1  |  1   |  2  | up to 2^16 - 1 |
+   * +----+------+-----+----------------+
+   */
+
+  /* prepare service name */
+  if(strchr(service, '/')) {
+    service_name = malloc(strlen(service));
+    if(!service_name)
+      return CURLE_OUT_OF_MEMORY;
+    memcpy(service_name, service, strlen(service));
+  }
+  else {
+    service_name = malloc(strlen(service) + strlen(conn->proxy.name) + 2);
+    if(!service_name)
+      return CURLE_OUT_OF_MEMORY;
+    snprintf(service_name,strlen(service) +strlen(conn->proxy.name)+2,"%s/%s",
+             service,conn->proxy.name);
+  }
+
+  input_desc.cBuffers = 1;
+  input_desc.pBuffers = &sspi_recv_token;
+  input_desc.ulVersion = SECBUFFER_VERSION;
+
+  sspi_recv_token.BufferType = SECBUFFER_TOKEN;
+  sspi_recv_token.cbBuffer = 0;
+  sspi_recv_token.pvBuffer = NULL;
+
+  output_desc.cBuffers = 1;
+  output_desc.pBuffers = &sspi_send_token;
+  output_desc.ulVersion = SECBUFFER_VERSION;
+
+  sspi_send_token.BufferType = SECBUFFER_TOKEN;
+  sspi_send_token.cbBuffer = 0;
+  sspi_send_token.pvBuffer = NULL;
+
+  wrap_desc.cBuffers = 3;
+  wrap_desc.pBuffers = sspi_w_token;
+  wrap_desc.ulVersion = SECBUFFER_VERSION;
+
+  cred_handle.dwLower = 0;
+  cred_handle.dwUpper = 0;
+
+  status = s_pSecFn->AcquireCredentialsHandle(NULL,
+                                              (TCHAR *) TEXT("Kerberos"),
+                                              SECPKG_CRED_OUTBOUND,
+                                              NULL,
+                                              NULL,
+                                              NULL,
+                                              NULL,
+                                              &cred_handle,
+                                              &expiry);
+
+  if(check_sspi_err(conn, status, "AcquireCredentialsHandle")) {
+    failf(data, "Failed to acquire credentials.");
+    Curl_safefree(service_name);
+    s_pSecFn->FreeCredentialsHandle(&cred_handle);
+    return CURLE_COULDNT_CONNECT;
+  }
+
+  /* As long as we need to keep sending some context info, and there's no  */
+  /* errors, keep sending it...                                            */
+  for(;;) {
+    TCHAR *sname;
+
+    sname = Curl_convert_UTF8_to_tchar(service_name);
+    if(!sname)
+      return CURLE_OUT_OF_MEMORY;
+
+    status = s_pSecFn->InitializeSecurityContext(&cred_handle,
+                                                 context_handle,
+                                                 sname,
+                                                 ISC_REQ_MUTUAL_AUTH |
+                                                 ISC_REQ_ALLOCATE_MEMORY |
+                                                 ISC_REQ_CONFIDENTIALITY |
+                                                 ISC_REQ_REPLAY_DETECT,
+                                                 0,
+                                                 SECURITY_NATIVE_DREP,
+                                                 &input_desc,
+                                                 0,
+                                                 &sspi_context,
+                                                 &output_desc,
+                                                 &sspi_ret_flags,
+                                                 &expiry);
+
+    Curl_unicodefree(sname);
+
+    if(sspi_recv_token.pvBuffer) {
+      s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
+      sspi_recv_token.pvBuffer = NULL;
+      sspi_recv_token.cbBuffer = 0;
+    }
+
+    if(check_sspi_err(conn, status, "InitializeSecurityContext")) {
+      Curl_safefree(service_name);
+      s_pSecFn->FreeCredentialsHandle(&cred_handle);
+      s_pSecFn->DeleteSecurityContext(&sspi_context);
+      s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
+      failf(data, "Failed to initialise security context.");
+      return CURLE_COULDNT_CONNECT;
+    }
+
+    if(sspi_send_token.cbBuffer != 0) {
+      socksreq[0] = 1;    /* gssapi subnegotiation version */
+      socksreq[1] = 1;    /* authentication message type */
+      us_length = htons((short)sspi_send_token.cbBuffer);
+      memcpy(socksreq+2, &us_length, sizeof(short));
+
+      code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
+      if((code != CURLE_OK) || (4 != written)) {
+        failf(data, "Failed to send SSPI authentication request.");
+        Curl_safefree(service_name);
+        s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
+        s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
+        s_pSecFn->FreeCredentialsHandle(&cred_handle);
+        s_pSecFn->DeleteSecurityContext(&sspi_context);
+        return CURLE_COULDNT_CONNECT;
+      }
+
+      code = Curl_write_plain(conn, sock, (char *)sspi_send_token.pvBuffer,
+                              sspi_send_token.cbBuffer, &written);
+      if((code != CURLE_OK) || (sspi_send_token.cbBuffer != (size_t)written)) {
+        failf(data, "Failed to send SSPI authentication token.");
+        Curl_safefree(service_name);
+        s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
+        s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
+        s_pSecFn->FreeCredentialsHandle(&cred_handle);
+        s_pSecFn->DeleteSecurityContext(&sspi_context);
+        return CURLE_COULDNT_CONNECT;
+      }
+
+    }
+
+    s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
+    sspi_send_token.pvBuffer = NULL;
+    sspi_send_token.cbBuffer = 0;
+    s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
+    sspi_recv_token.pvBuffer = NULL;
+    sspi_recv_token.cbBuffer = 0;
+    if(status != SEC_I_CONTINUE_NEEDED)
+      break;
+
+    /* analyse response */
+
+    /*   GSSAPI response looks like
+     * +----+------+-----+----------------+
+     * |VER | MTYP | LEN |     TOKEN      |
+     * +----+------+----------------------+
+     * | 1  |  1   |  2  | up to 2^16 - 1 |
+     * +----+------+-----+----------------+
+     */
+
+    result = Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread);
+    if(result != CURLE_OK || actualread != 4) {
+      failf(data, "Failed to receive SSPI authentication response.");
+      Curl_safefree(service_name);
+      s_pSecFn->FreeCredentialsHandle(&cred_handle);
+      s_pSecFn->DeleteSecurityContext(&sspi_context);
+      return CURLE_COULDNT_CONNECT;
+    }
+
+    /* ignore the first (VER) byte */
+    if(socksreq[1] == 255) { /* status / message type */
+      failf(data, "User was rejected by the SOCKS5 server (%d %d).",
+            socksreq[0], socksreq[1]);
+      Curl_safefree(service_name);
+      s_pSecFn->FreeCredentialsHandle(&cred_handle);
+      s_pSecFn->DeleteSecurityContext(&sspi_context);
+      return CURLE_COULDNT_CONNECT;
+    }
+
+    if(socksreq[1] != 1) { /* status / messgae type */
+      failf(data, "Invalid SSPI authentication response type (%d %d).",
+            socksreq[0], socksreq[1]);
+      Curl_safefree(service_name);
+      s_pSecFn->FreeCredentialsHandle(&cred_handle);
+      s_pSecFn->DeleteSecurityContext(&sspi_context);
+      return CURLE_COULDNT_CONNECT;
+    }
+
+    memcpy(&us_length, socksreq+2, sizeof(short));
+    us_length = ntohs(us_length);
+
+    sspi_recv_token.cbBuffer = us_length;
+    sspi_recv_token.pvBuffer = malloc(us_length);
+
+    if(!sspi_recv_token.pvBuffer) {
+      Curl_safefree(service_name);
+      s_pSecFn->FreeCredentialsHandle(&cred_handle);
+      s_pSecFn->DeleteSecurityContext(&sspi_context);
+      return CURLE_OUT_OF_MEMORY;
+    }
+    result = Curl_blockread_all(conn, sock, (char *)sspi_recv_token.pvBuffer,
+                                sspi_recv_token.cbBuffer, &actualread);
+
+    if(result != CURLE_OK || actualread != us_length) {
+      failf(data, "Failed to receive SSPI authentication token.");
+      Curl_safefree(service_name);
+      s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
+      s_pSecFn->FreeCredentialsHandle(&cred_handle);
+      s_pSecFn->DeleteSecurityContext(&sspi_context);
+      return CURLE_COULDNT_CONNECT;
+    }
+
+    context_handle = &sspi_context;
+  }
+
+  Curl_safefree(service_name);
+
+  /* Everything is good so far, user was authenticated! */
+  status = s_pSecFn->QueryCredentialsAttributes(&cred_handle,
+                                                SECPKG_CRED_ATTR_NAMES,
+                                                &names);
+  s_pSecFn->FreeCredentialsHandle(&cred_handle);
+  if(check_sspi_err(conn, status, "QueryCredentialAttributes")) {
+    s_pSecFn->DeleteSecurityContext(&sspi_context);
+    s_pSecFn->FreeContextBuffer(names.sUserName);
+    failf(data, "Failed to determine user name.");
+    return CURLE_COULDNT_CONNECT;
+  }
+  infof(data, "SOCKS5 server authencticated user %s with gssapi.\n",
+        names.sUserName);
+  s_pSecFn->FreeContextBuffer(names.sUserName);
+
+  /* Do encryption */
+  socksreq[0] = 1;    /* gssapi subnegotiation version */
+  socksreq[1] = 2;    /* encryption message type */
+
+  gss_enc = 0; /* no data protection */
+  /* do confidentiality protection if supported */
+  if(sspi_ret_flags & ISC_REQ_CONFIDENTIALITY)
+    gss_enc = 2;
+  /* else do integrity protection */
+  else if(sspi_ret_flags & ISC_REQ_INTEGRITY)
+    gss_enc = 1;
+
+  infof(data, "SOCKS5 server supports gssapi %s data protection.\n",
+        (gss_enc==0)?"no":((gss_enc==1)?"integrity":"confidentiality") );
+  /* force to no data protection, avoid encryption/decryption for now */
+  gss_enc = 0;
+  /*
+   * Sending the encryption type in clear seems wrong. It should be
+   * protected with gss_seal()/gss_wrap(). See RFC1961 extract below
+   * The NEC reference implementations on which this is based is
+   * therefore at fault
+   *
+   *  +------+------+------+.......................+
+   *  + ver  | mtyp | len  |   token               |
+   *  +------+------+------+.......................+
+   *  + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets |
+   *  +------+------+------+.......................+
+   *
+   *   Where:
+   *
+   *  - "ver" is the protocol version number, here 1 to represent the
+   *    first version of the SOCKS/GSS-API protocol
+   *
+   *  - "mtyp" is the message type, here 2 to represent a protection
+   *    -level negotiation message
+   *
+   *  - "len" is the length of the "token" field in octets
+   *
+   *  - "token" is the GSS-API encapsulated protection level
+   *
+   * The token is produced by encapsulating an octet containing the
+   * required protection level using gss_seal()/gss_wrap() with conf_req
+   * set to FALSE.  The token is verified using gss_unseal()/
+   * gss_unwrap().
+   *
+   */
+
+  if(data->set.socks5_gssapi_nec) {
+    us_length = htons((short)1);
+    memcpy(socksreq+2, &us_length, sizeof(short));
+  }
+  else {
+    status = s_pSecFn->QueryContextAttributes(&sspi_context,
+                                              SECPKG_ATTR_SIZES,
+                                              &sspi_sizes);
+    if(check_sspi_err(conn, status, "QueryContextAttributes")) {
+      s_pSecFn->DeleteSecurityContext(&sspi_context);
+      failf(data, "Failed to query security context attributes.");
+      return CURLE_COULDNT_CONNECT;
+    }
+
+    sspi_w_token[0].cbBuffer = sspi_sizes.cbSecurityTrailer;
+    sspi_w_token[0].BufferType = SECBUFFER_TOKEN;
+    sspi_w_token[0].pvBuffer = malloc(sspi_sizes.cbSecurityTrailer);
+
+    if(!sspi_w_token[0].pvBuffer) {
+      s_pSecFn->DeleteSecurityContext(&sspi_context);
+      return CURLE_OUT_OF_MEMORY;
+    }
+
+    sspi_w_token[1].cbBuffer = 1;
+    sspi_w_token[1].pvBuffer = malloc(1);
+    if(!sspi_w_token[1].pvBuffer) {
+      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+      s_pSecFn->DeleteSecurityContext(&sspi_context);
+      return CURLE_OUT_OF_MEMORY;
+    }
+
+    memcpy(sspi_w_token[1].pvBuffer,&gss_enc,1);
+    sspi_w_token[2].BufferType = SECBUFFER_PADDING;
+    sspi_w_token[2].cbBuffer = sspi_sizes.cbBlockSize;
+    sspi_w_token[2].pvBuffer = malloc(sspi_sizes.cbBlockSize);
+    if(!sspi_w_token[2].pvBuffer) {
+      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+      s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
+      s_pSecFn->DeleteSecurityContext(&sspi_context);
+      return CURLE_OUT_OF_MEMORY;
+    }
+    status = s_pSecFn->EncryptMessage(&sspi_context,
+                                      KERB_WRAP_NO_ENCRYPT,
+                                      &wrap_desc,
+                                      0);
+    if(check_sspi_err(conn, status, "EncryptMessage")) {
+      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+      s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
+      s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
+      s_pSecFn->DeleteSecurityContext(&sspi_context);
+      failf(data, "Failed to query security context attributes.");
+      return CURLE_COULDNT_CONNECT;
+    }
+    sspi_send_token.cbBuffer = sspi_w_token[0].cbBuffer
+      + sspi_w_token[1].cbBuffer
+      + sspi_w_token[2].cbBuffer;
+    sspi_send_token.pvBuffer = malloc(sspi_send_token.cbBuffer);
+    if(!sspi_send_token.pvBuffer) {
+      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+      s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
+      s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
+      s_pSecFn->DeleteSecurityContext(&sspi_context);
+      return CURLE_OUT_OF_MEMORY;
+    }
+
+    memcpy(sspi_send_token.pvBuffer, sspi_w_token[0].pvBuffer,
+           sspi_w_token[0].cbBuffer);
+    memcpy((PUCHAR) sspi_send_token.pvBuffer +(int)sspi_w_token[0].cbBuffer,
+           sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer);
+    memcpy((PUCHAR) sspi_send_token.pvBuffer
+           +sspi_w_token[0].cbBuffer
+           +sspi_w_token[1].cbBuffer,
+           sspi_w_token[2].pvBuffer, sspi_w_token[2].cbBuffer);
+
+    s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+    sspi_w_token[0].pvBuffer = NULL;
+    sspi_w_token[0].cbBuffer = 0;
+    s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
+    sspi_w_token[1].pvBuffer = NULL;
+    sspi_w_token[1].cbBuffer = 0;
+    s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
+    sspi_w_token[2].pvBuffer = NULL;
+    sspi_w_token[2].cbBuffer = 0;
+
+    us_length = htons((short)sspi_send_token.cbBuffer);
+    memcpy(socksreq+2,&us_length,sizeof(short));
+  }
+
+  code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
+  if((code != CURLE_OK) || (4 != written)) {
+    failf(data, "Failed to send SSPI encryption request.");
+    s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
+    s_pSecFn->DeleteSecurityContext(&sspi_context);
+    return CURLE_COULDNT_CONNECT;
+  }
+
+  if(data->set.socks5_gssapi_nec) {
+    memcpy(socksreq,&gss_enc,1);
+    code = Curl_write_plain(conn, sock, (char *)socksreq, 1, &written);
+    if((code != CURLE_OK) || (1 != written)) {
+      failf(data, "Failed to send SSPI encryption type.");
+      s_pSecFn->DeleteSecurityContext(&sspi_context);
+      return CURLE_COULDNT_CONNECT;
+    }
+  }
+  else {
+    code = Curl_write_plain(conn, sock, (char *)sspi_send_token.pvBuffer,
+                            sspi_send_token.cbBuffer, &written);
+    if((code != CURLE_OK) || (sspi_send_token.cbBuffer != (size_t)written)) {
+      failf(data, "Failed to send SSPI encryption type.");
+      s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
+      s_pSecFn->DeleteSecurityContext(&sspi_context);
+      return CURLE_COULDNT_CONNECT;
+    }
+    s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
+  }
+
+  result = Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread);
+  if(result != CURLE_OK || actualread != 4) {
+    failf(data, "Failed to receive SSPI encryption response.");
+    s_pSecFn->DeleteSecurityContext(&sspi_context);
+    return CURLE_COULDNT_CONNECT;
+  }
+
+  /* ignore the first (VER) byte */
+  if(socksreq[1] == 255) { /* status / message type */
+    failf(data, "User was rejected by the SOCKS5 server (%d %d).",
+          socksreq[0], socksreq[1]);
+    s_pSecFn->DeleteSecurityContext(&sspi_context);
+    return CURLE_COULDNT_CONNECT;
+  }
+
+  if(socksreq[1] != 2) { /* status / message type */
+    failf(data, "Invalid SSPI encryption response type (%d %d).",
+          socksreq[0], socksreq[1]);
+    s_pSecFn->DeleteSecurityContext(&sspi_context);
+    return CURLE_COULDNT_CONNECT;
+  }
+
+  memcpy(&us_length, socksreq+2, sizeof(short));
+  us_length = ntohs(us_length);
+
+  sspi_w_token[0].cbBuffer = us_length;
+  sspi_w_token[0].pvBuffer = malloc(us_length);
+  if(!sspi_w_token[0].pvBuffer) {
+    s_pSecFn->DeleteSecurityContext(&sspi_context);
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  result = Curl_blockread_all(conn, sock, (char *)sspi_w_token[0].pvBuffer,
+                              sspi_w_token[0].cbBuffer, &actualread);
+
+  if(result != CURLE_OK || actualread != us_length) {
+    failf(data, "Failed to receive SSPI encryption type.");
+    s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+    s_pSecFn->DeleteSecurityContext(&sspi_context);
+    return CURLE_COULDNT_CONNECT;
+  }
+
+
+  if(!data->set.socks5_gssapi_nec) {
+    wrap_desc.cBuffers = 2;
+    sspi_w_token[0].BufferType = SECBUFFER_STREAM;
+    sspi_w_token[1].BufferType = SECBUFFER_DATA;
+    sspi_w_token[1].cbBuffer = 0;
+    sspi_w_token[1].pvBuffer = NULL;
+
+    status = s_pSecFn->DecryptMessage(&sspi_context,
+                                      &wrap_desc,
+                                      0,
+                                      &qop);
+
+    if(check_sspi_err(conn, status, "DecryptMessage")) {
+      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+      s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
+      s_pSecFn->DeleteSecurityContext(&sspi_context);
+      failf(data, "Failed to query security context attributes.");
+      return CURLE_COULDNT_CONNECT;
+    }
+
+    if(sspi_w_token[1].cbBuffer != 1) {
+      failf(data, "Invalid SSPI encryption response length (%d).",
+            sspi_w_token[1].cbBuffer);
+      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+      s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
+      s_pSecFn->DeleteSecurityContext(&sspi_context);
+      return CURLE_COULDNT_CONNECT;
+    }
+
+    memcpy(socksreq,sspi_w_token[1].pvBuffer,sspi_w_token[1].cbBuffer);
+    s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+    s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
+  }
+  else {
+    if(sspi_w_token[0].cbBuffer != 1) {
+      failf(data, "Invalid SSPI encryption response length (%d).",
+            sspi_w_token[0].cbBuffer);
+      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+      s_pSecFn->DeleteSecurityContext(&sspi_context);
+      return CURLE_COULDNT_CONNECT;
+    }
+    memcpy(socksreq,sspi_w_token[0].pvBuffer,sspi_w_token[0].cbBuffer);
+    s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+  }
+
+  infof(data, "SOCKS5 access with%s protection granted.\n",
+        (socksreq[0]==0)?"out gssapi data":
+        ((socksreq[0]==1)?" gssapi integrity":" gssapi confidentiality"));
+
+  /* For later use if encryption is required
+     conn->socks5_gssapi_enctype = socksreq[0];
+     if(socksreq[0] != 0)
+       conn->socks5_sspi_context = sspi_context;
+     else {
+       s_pSecFn->DeleteSecurityContext(&sspi_context);
+       conn->socks5_sspi_context = sspi_context;
+     }
+  */
+  return CURLE_OK;
+}
+#endif
diff --git a/lib/curl_speedcheck.c b/lib/curl_speedcheck.c
new file mode 100644 (file)
index 0000000..b9ce77d
--- /dev/null
@@ -0,0 +1,74 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_multiif.h"
+#include "curl_speedcheck.h"
+
+void Curl_speedinit(struct SessionHandle *data)
+{
+  memset(&data->state.keeps_speed, 0, sizeof(struct timeval));
+}
+
+CURLcode Curl_speedcheck(struct SessionHandle *data,
+                         struct timeval now)
+{
+  if((data->progress.current_speed >= 0) &&
+     data->set.low_speed_time &&
+     (Curl_tvlong(data->state.keeps_speed) != 0) &&
+     (data->progress.current_speed < data->set.low_speed_limit)) {
+    long howlong = Curl_tvdiff(now, data->state.keeps_speed);
+    long nextcheck = (data->set.low_speed_time * 1000) - howlong;
+
+    /* We are now below the "low speed limit". If we are below it
+       for "low speed time" seconds we consider that enough reason
+       to abort the download. */
+    if(nextcheck <= 0) {
+      /* we have been this slow for long enough, now die */
+      failf(data,
+            "Operation too slow. "
+            "Less than %ld bytes/sec transferred the last %ld seconds",
+            data->set.low_speed_limit,
+            data->set.low_speed_time);
+      return CURLE_OPERATION_TIMEDOUT;
+    }
+    else {
+      /* wait complete low_speed_time */
+      Curl_expire(data, nextcheck);
+    }
+  }
+  else {
+    /* we keep up the required speed all right */
+    data->state.keeps_speed = now;
+
+    if(data->set.low_speed_limit)
+      /* if there is a low speed limit enabled, we set the expire timer to
+         make this connection's speed get checked again no later than when
+         this time is up */
+      Curl_expire(data, data->set.low_speed_time*1000);
+  }
+  return CURLE_OK;
+}
diff --git a/lib/curl_splay.c b/lib/curl_splay.c
new file mode 100644 (file)
index 0000000..21f1d22
--- /dev/null
@@ -0,0 +1,288 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1997 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include "curl_splay.h"
+
+/*
+ * This macro compares two node keys i and j and returns:
+ *
+ *  negative value: when i is smaller than j
+ *  zero          : when i is equal   to   j
+ *  positive when : when i is larger  than j
+ */
+#define compare(i,j) Curl_splaycomparekeys((i),(j))
+
+/*
+ * Splay using the key i (which may or may not be in the tree.) The starting
+ * root is t.
+ */
+struct Curl_tree *Curl_splay(struct timeval i,
+                             struct Curl_tree *t)
+{
+  struct Curl_tree N, *l, *r, *y;
+  long comp;
+
+  if(t == NULL)
+    return t;
+  N.smaller = N.larger = NULL;
+  l = r = &N;
+
+  for(;;) {
+    comp = compare(i, t->key);
+    if(comp < 0) {
+      if(t->smaller == NULL)
+        break;
+      if(compare(i, t->smaller->key) < 0) {
+        y = t->smaller;                           /* rotate smaller */
+        t->smaller = y->larger;
+        y->larger = t;
+        t = y;
+        if(t->smaller == NULL)
+          break;
+      }
+      r->smaller = t;                               /* link smaller */
+      r = t;
+      t = t->smaller;
+    }
+    else if(comp > 0) {
+      if(t->larger == NULL)
+        break;
+      if(compare(i, t->larger->key) > 0) {
+        y = t->larger;                          /* rotate larger */
+        t->larger = y->smaller;
+        y->smaller = t;
+        t = y;
+        if(t->larger == NULL)
+          break;
+      }
+      l->larger = t;                              /* link larger */
+      l = t;
+      t = t->larger;
+    }
+    else
+      break;
+  }
+
+  l->larger = t->smaller;                                /* assemble */
+  r->smaller = t->larger;
+  t->smaller = N.larger;
+  t->larger = N.smaller;
+
+  return t;
+}
+
+/* Insert key i into the tree t.  Return a pointer to the resulting tree or
+ * NULL if something went wrong.
+ *
+ * @unittest: 1309
+ */
+struct Curl_tree *Curl_splayinsert(struct timeval i,
+                                   struct Curl_tree *t,
+                                   struct Curl_tree *node)
+{
+  static struct timeval KEY_NOTUSED = {-1,-1}; /* will *NEVER* appear */
+
+  if(node == NULL)
+    return t;
+
+  if(t != NULL) {
+    t = Curl_splay(i,t);
+    if(compare(i, t->key)==0) {
+      /* There already exists a node in the tree with the very same key. Build
+         a linked list of nodes. We make the new 'node' struct the new master
+         node and make the previous node the first one in the 'same' list. */
+
+      node->same = t;
+      node->key = i;
+      node->smaller = t->smaller;
+      node->larger = t->larger;
+
+      t->smaller = node; /* in the sub node for this same key, we use the
+                            smaller pointer to point back to the master
+                            node */
+
+      t->key = KEY_NOTUSED; /* and we set the key in the sub node to NOTUSED
+                               to quickly identify this node as a subnode */
+
+      return node; /* new root node */
+    }
+  }
+
+  if(t == NULL) {
+    node->smaller = node->larger = NULL;
+  }
+  else if(compare(i, t->key) < 0) {
+    node->smaller = t->smaller;
+    node->larger = t;
+    t->smaller = NULL;
+
+  }
+  else {
+    node->larger = t->larger;
+    node->smaller = t;
+    t->larger = NULL;
+  }
+  node->key = i;
+
+  node->same = NULL; /* no identical node (yet) */
+  return node;
+}
+
+/* Finds and deletes the best-fit node from the tree. Return a pointer to the
+   resulting tree.  best-fit means the node with the given or lower key */
+struct Curl_tree *Curl_splaygetbest(struct timeval i,
+                                    struct Curl_tree *t,
+                                    struct Curl_tree **removed)
+{
+  struct Curl_tree *x;
+
+  if(!t) {
+    *removed = NULL; /* none removed since there was no root */
+    return NULL;
+  }
+
+  t = Curl_splay(i,t);
+  if(compare(i, t->key) < 0) {
+    /* too big node, try the smaller chain */
+    if(t->smaller)
+      t=Curl_splay(t->smaller->key, t);
+    else {
+      /* fail */
+      *removed = NULL;
+      return t;
+    }
+  }
+
+  if(compare(i, t->key) >= 0) {               /* found it */
+    /* FIRST! Check if there is a list with identical keys */
+    x = t->same;
+    if(x) {
+      /* there is, pick one from the list */
+
+      /* 'x' is the new root node */
+
+      x->key = t->key;
+      x->larger = t->larger;
+      x->smaller = t->smaller;
+
+      *removed = t;
+      return x; /* new root */
+    }
+
+    if(t->smaller == NULL) {
+      x = t->larger;
+    }
+    else {
+      x = Curl_splay(i, t->smaller);
+      x->larger = t->larger;
+    }
+    *removed = t;
+
+    return x;
+  }
+  else {
+    *removed = NULL; /* no match */
+    return t;        /* It wasn't there */
+  }
+}
+
+
+/* Deletes the very node we point out from the tree if it's there. Stores a
+ * pointer to the new resulting tree in 'newroot'.
+ *
+ * Returns zero on success and non-zero on errors! TODO: document error codes.
+ * When returning error, it does not touch the 'newroot' pointer.
+ *
+ * NOTE: when the last node of the tree is removed, there's no tree left so
+ * 'newroot' will be made to point to NULL.
+ *
+ * @unittest: 1309
+ */
+int Curl_splayremovebyaddr(struct Curl_tree *t,
+                           struct Curl_tree *removenode,
+                           struct Curl_tree **newroot)
+{
+  static struct timeval KEY_NOTUSED = {-1,-1}; /* will *NEVER* appear */
+  struct Curl_tree *x;
+
+  if(!t || !removenode)
+    return 1;
+
+  if(compare(KEY_NOTUSED, removenode->key) == 0) {
+    /* Key set to NOTUSED means it is a subnode within a 'same' linked list
+       and thus we can unlink it easily. The 'smaller' link of a subnode
+       links to the parent node. */
+    if(removenode->smaller == NULL)
+      return 3;
+
+    removenode->smaller->same = removenode->same;
+    if(removenode->same)
+      removenode->same->smaller = removenode->smaller;
+
+    /* Ensures that double-remove gets caught. */
+    removenode->smaller = NULL;
+
+    /* voila, we're done! */
+    *newroot = t; /* return the same root */
+    return 0;
+  }
+
+  t = Curl_splay(removenode->key, t);
+
+  /* First make sure that we got the same root node as the one we want
+     to remove, as otherwise we might be trying to remove a node that
+     isn't actually in the tree.
+
+     We cannot just compare the keys here as a double remove in quick
+     succession of a node with key != KEY_NOTUSED && same != NULL
+     could return the same key but a different node. */
+  if(t != removenode)
+    return 2;
+
+  /* Check if there is a list with identical sizes, as then we're trying to
+     remove the root node of a list of nodes with identical keys. */
+  x = t->same;
+  if(x) {
+    /* 'x' is the new root node, we just make it use the root node's
+       smaller/larger links */
+
+    x->key = t->key;
+    x->larger = t->larger;
+    x->smaller = t->smaller;
+  }
+  else {
+    /* Remove the root node */
+    if(t->smaller == NULL)
+      x = t->larger;
+    else {
+      x = Curl_splay(removenode->key, t->smaller);
+      x->larger = t->larger;
+    }
+  }
+
+  *newroot = x; /* store new root pointer */
+
+  return 0;
+}
+
diff --git a/lib/curl_ssh.c b/lib/curl_ssh.c
new file mode 100644 (file)
index 0000000..d769a04
--- /dev/null
@@ -0,0 +1,3310 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/* #define CURL_LIBSSH2_DEBUG */
+
+#include "curl_setup.h"
+
+#ifdef USE_LIBSSH2
+
+#ifdef HAVE_LIMITS_H
+#  include <limits.h>
+#endif
+
+#include <libssh2.h>
+#include <libssh2_sftp.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_UTSNAME_H
+#include <sys/utsname.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
+#undef in_addr_t
+#define in_addr_t unsigned long
+#endif
+
+#include <curl/curl.h>
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_hostip.h"
+#include "curl_progress.h"
+#include "curl_transfer.h"
+#include "curl_escape.h"
+#include "curl_http.h" /* for HTTP proxy tunnel stuff */
+#include "curl_ssh.h"
+#include "curl_url.h"
+#include "curl_speedcheck.h"
+#include "curl_getinfo.h"
+
+#include "curl_strequal.h"
+#include "curl_sslgen.h"
+#include "curl_connect.h"
+#include "curl_strerror.h"
+#include "curl_inet_ntop.h"
+#include "curl_parsedate.h" /* for the week day and month names */
+#include "curl_sockaddr.h" /* required for Curl_sockaddr_storage */
+#include "curl_strtoofft.h"
+#include "curl_multiif.h"
+#include "curl_select.h"
+#include "curl_warnless.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+#ifdef WIN32
+#  undef  PATH_MAX
+#  define PATH_MAX MAX_PATH
+#endif
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024 /* just an extra precaution since there are systems that
+                         have their definition hidden well */
+#endif
+
+#define sftp_libssh2_last_error(s) curlx_ultosi(libssh2_sftp_last_error(s))
+
+#define sftp_libssh2_realpath(s,p,t,m) \
+        libssh2_sftp_symlink_ex((s), (p), curlx_uztoui(strlen(p)), \
+                                (t), (m), LIBSSH2_SFTP_REALPATH)
+
+/* Local functions: */
+static const char *sftp_libssh2_strerror(int err);
+static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc);
+static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc);
+static LIBSSH2_FREE_FUNC(my_libssh2_free);
+
+static CURLcode get_pathname(const char **cpp, char **path);
+
+static CURLcode ssh_connect(struct connectdata *conn, bool *done);
+static CURLcode ssh_multi_statemach(struct connectdata *conn, bool *done);
+static CURLcode ssh_do(struct connectdata *conn, bool *done);
+
+static CURLcode ssh_getworkingpath(struct connectdata *conn,
+                                   char *homedir, /* when SFTP is used */
+                                   char **path);
+
+static CURLcode scp_done(struct connectdata *conn,
+                         CURLcode, bool premature);
+static CURLcode scp_doing(struct connectdata *conn,
+                          bool *dophase_done);
+static CURLcode scp_disconnect(struct connectdata *conn, bool dead_connection);
+
+static CURLcode sftp_done(struct connectdata *conn,
+                          CURLcode, bool premature);
+static CURLcode sftp_doing(struct connectdata *conn,
+                           bool *dophase_done);
+static CURLcode sftp_disconnect(struct connectdata *conn, bool dead);
+static
+CURLcode sftp_perform(struct connectdata *conn,
+                      bool *connected,
+                      bool *dophase_done);
+
+static int ssh_getsock(struct connectdata *conn,
+                       curl_socket_t *sock, /* points to numsocks number
+                                               of sockets */
+                       int numsocks);
+
+static int ssh_perform_getsock(const struct connectdata *conn,
+                               curl_socket_t *sock, /* points to numsocks
+                                                       number of sockets */
+                               int numsocks);
+
+/*
+ * SCP protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_scp = {
+  "SCP",                                /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  ssh_do,                               /* do_it */
+  scp_done,                             /* done */
+  ZERO_NULL,                            /* do_more */
+  ssh_connect,                          /* connect_it */
+  ssh_multi_statemach,                  /* connecting */
+  scp_doing,                            /* doing */
+  ssh_getsock,                          /* proto_getsock */
+  ssh_getsock,                          /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ssh_perform_getsock,                  /* perform_getsock */
+  scp_disconnect,                       /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  PORT_SSH,                             /* defport */
+  CURLPROTO_SCP,                        /* protocol */
+  PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
+  | PROTOPT_NOURLQUERY                  /* flags */
+};
+
+
+/*
+ * SFTP protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_sftp = {
+  "SFTP",                               /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  ssh_do,                               /* do_it */
+  sftp_done,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  ssh_connect,                          /* connect_it */
+  ssh_multi_statemach,                  /* connecting */
+  sftp_doing,                           /* doing */
+  ssh_getsock,                          /* proto_getsock */
+  ssh_getsock,                          /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ssh_perform_getsock,                  /* perform_getsock */
+  sftp_disconnect,                      /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  PORT_SSH,                             /* defport */
+  CURLPROTO_SFTP,                       /* protocol */
+  PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
+  | PROTOPT_NOURLQUERY                  /* flags */
+};
+
+
+static void
+kbd_callback(const char *name, int name_len, const char *instruction,
+             int instruction_len, int num_prompts,
+             const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts,
+             LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses,
+             void **abstract)
+{
+  struct connectdata *conn = (struct connectdata *)*abstract;
+
+#ifdef CURL_LIBSSH2_DEBUG
+  fprintf(stderr, "name=%s\n", name);
+  fprintf(stderr, "name_len=%d\n", name_len);
+  fprintf(stderr, "instruction=%s\n", instruction);
+  fprintf(stderr, "instruction_len=%d\n", instruction_len);
+  fprintf(stderr, "num_prompts=%d\n", num_prompts);
+#else
+  (void)name;
+  (void)name_len;
+  (void)instruction;
+  (void)instruction_len;
+#endif  /* CURL_LIBSSH2_DEBUG */
+  if(num_prompts == 1) {
+    responses[0].text = strdup(conn->passwd);
+    responses[0].length = curlx_uztoui(strlen(conn->passwd));
+  }
+  (void)prompts;
+  (void)abstract;
+} /* kbd_callback */
+
+static CURLcode sftp_libssh2_error_to_CURLE(int err)
+{
+  switch (err) {
+    case LIBSSH2_FX_OK:
+      return CURLE_OK;
+
+    case LIBSSH2_FX_NO_SUCH_FILE:
+    case LIBSSH2_FX_NO_SUCH_PATH:
+      return CURLE_REMOTE_FILE_NOT_FOUND;
+
+    case LIBSSH2_FX_PERMISSION_DENIED:
+    case LIBSSH2_FX_WRITE_PROTECT:
+    case LIBSSH2_FX_LOCK_CONFlICT:
+      return CURLE_REMOTE_ACCESS_DENIED;
+
+    case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM:
+    case LIBSSH2_FX_QUOTA_EXCEEDED:
+      return CURLE_REMOTE_DISK_FULL;
+
+    case LIBSSH2_FX_FILE_ALREADY_EXISTS:
+      return CURLE_REMOTE_FILE_EXISTS;
+
+    case LIBSSH2_FX_DIR_NOT_EMPTY:
+      return CURLE_QUOTE_ERROR;
+
+    default:
+      break;
+  }
+
+  return CURLE_SSH;
+}
+
+static CURLcode libssh2_session_error_to_CURLE(int err)
+{
+  switch (err) {
+    /* Ordered by order of appearance in libssh2.h */
+    case LIBSSH2_ERROR_NONE:
+      return CURLE_OK;
+
+    case LIBSSH2_ERROR_SOCKET_NONE:
+      return CURLE_COULDNT_CONNECT;
+
+    case LIBSSH2_ERROR_ALLOC:
+      return CURLE_OUT_OF_MEMORY;
+
+    case LIBSSH2_ERROR_SOCKET_SEND:
+      return CURLE_SEND_ERROR;
+
+    case LIBSSH2_ERROR_HOSTKEY_INIT:
+    case LIBSSH2_ERROR_HOSTKEY_SIGN:
+    case LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED:
+    case LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED:
+      return CURLE_PEER_FAILED_VERIFICATION;
+
+    case LIBSSH2_ERROR_PASSWORD_EXPIRED:
+      return CURLE_LOGIN_DENIED;
+
+    case LIBSSH2_ERROR_SOCKET_TIMEOUT:
+    case LIBSSH2_ERROR_TIMEOUT:
+      return CURLE_OPERATION_TIMEDOUT;
+
+    case LIBSSH2_ERROR_EAGAIN:
+      return CURLE_AGAIN;
+  }
+
+  /* TODO: map some more of the libssh2 errors to the more appropriate CURLcode
+     error code, and possibly add a few new SSH-related one. We must however
+     not return or even depend on libssh2 errors in the public libcurl API */
+
+  return CURLE_SSH;
+}
+
+static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc)
+{
+  (void)abstract; /* arg not used */
+  return malloc(count);
+}
+
+static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc)
+{
+  (void)abstract; /* arg not used */
+  return realloc(ptr, count);
+}
+
+static LIBSSH2_FREE_FUNC(my_libssh2_free)
+{
+  (void)abstract; /* arg not used */
+  if(ptr) /* ssh2 agent sometimes call free with null ptr */
+    free(ptr);
+}
+
+/*
+ * SSH State machine related code
+ */
+/* This is the ONLY way to change SSH state! */
+static void state(struct connectdata *conn, sshstate nowstate)
+{
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+  /* for debug purposes */
+  static const char * const names[] = {
+    "SSH_STOP",
+    "SSH_INIT",
+    "SSH_S_STARTUP",
+    "SSH_HOSTKEY",
+    "SSH_AUTHLIST",
+    "SSH_AUTH_PKEY_INIT",
+    "SSH_AUTH_PKEY",
+    "SSH_AUTH_PASS_INIT",
+    "SSH_AUTH_PASS",
+    "SSH_AUTH_AGENT_INIT",
+    "SSH_AUTH_AGENT_LIST",
+    "SSH_AUTH_AGENT",
+    "SSH_AUTH_HOST_INIT",
+    "SSH_AUTH_HOST",
+    "SSH_AUTH_KEY_INIT",
+    "SSH_AUTH_KEY",
+    "SSH_AUTH_DONE",
+    "SSH_SFTP_INIT",
+    "SSH_SFTP_REALPATH",
+    "SSH_SFTP_QUOTE_INIT",
+    "SSH_SFTP_POSTQUOTE_INIT",
+    "SSH_SFTP_QUOTE",
+    "SSH_SFTP_NEXT_QUOTE",
+    "SSH_SFTP_QUOTE_STAT",
+    "SSH_SFTP_QUOTE_SETSTAT",
+    "SSH_SFTP_QUOTE_SYMLINK",
+    "SSH_SFTP_QUOTE_MKDIR",
+    "SSH_SFTP_QUOTE_RENAME",
+    "SSH_SFTP_QUOTE_RMDIR",
+    "SSH_SFTP_QUOTE_UNLINK",
+    "SSH_SFTP_TRANS_INIT",
+    "SSH_SFTP_UPLOAD_INIT",
+    "SSH_SFTP_CREATE_DIRS_INIT",
+    "SSH_SFTP_CREATE_DIRS",
+    "SSH_SFTP_CREATE_DIRS_MKDIR",
+    "SSH_SFTP_READDIR_INIT",
+    "SSH_SFTP_READDIR",
+    "SSH_SFTP_READDIR_LINK",
+    "SSH_SFTP_READDIR_BOTTOM",
+    "SSH_SFTP_READDIR_DONE",
+    "SSH_SFTP_DOWNLOAD_INIT",
+    "SSH_SFTP_DOWNLOAD_STAT",
+    "SSH_SFTP_CLOSE",
+    "SSH_SFTP_SHUTDOWN",
+    "SSH_SCP_TRANS_INIT",
+    "SSH_SCP_UPLOAD_INIT",
+    "SSH_SCP_DOWNLOAD_INIT",
+    "SSH_SCP_DONE",
+    "SSH_SCP_SEND_EOF",
+    "SSH_SCP_WAIT_EOF",
+    "SSH_SCP_WAIT_CLOSE",
+    "SSH_SCP_CHANNEL_FREE",
+    "SSH_SESSION_DISCONNECT",
+    "SSH_SESSION_FREE",
+    "QUIT"
+  };
+#endif
+  struct ssh_conn *sshc = &conn->proto.sshc;
+
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+  if(sshc->state != nowstate) {
+    infof(conn->data, "SFTP %p state change from %s to %s\n",
+          sshc, names[sshc->state], names[nowstate]);
+  }
+#endif
+
+  sshc->state = nowstate;
+}
+
+/* figure out the path to work with in this particular request */
+static CURLcode ssh_getworkingpath(struct connectdata *conn,
+                                   char *homedir,  /* when SFTP is used */
+                                   char **path) /* returns the  allocated
+                                                   real path to work with */
+{
+  struct SessionHandle *data = conn->data;
+  char *real_path = NULL;
+  char *working_path;
+  int working_path_len;
+
+  working_path = curl_easy_unescape(data, data->state.path, 0,
+                                    &working_path_len);
+  if(!working_path)
+    return CURLE_OUT_OF_MEMORY;
+
+  /* Check for /~/ , indicating relative to the user's home directory */
+  if(conn->handler->protocol & CURLPROTO_SCP) {
+    real_path = malloc(working_path_len+1);
+    if(real_path == NULL) {
+      free(working_path);
+      return CURLE_OUT_OF_MEMORY;
+    }
+    if((working_path_len > 3) && (!memcmp(working_path, "/~/", 3)))
+      /* It is referenced to the home directory, so strip the leading '/~/' */
+      memcpy(real_path, working_path+3, 4 + working_path_len-3);
+    else
+      memcpy(real_path, working_path, 1 + working_path_len);
+  }
+  else if(conn->handler->protocol & CURLPROTO_SFTP) {
+    if((working_path_len > 1) && (working_path[1] == '~')) {
+      size_t homelen = strlen(homedir);
+      real_path = malloc(homelen + working_path_len + 1);
+      if(real_path == NULL) {
+        free(working_path);
+        return CURLE_OUT_OF_MEMORY;
+      }
+      /* It is referenced to the home directory, so strip the
+         leading '/' */
+      memcpy(real_path, homedir, homelen);
+      real_path[homelen] = '/';
+      real_path[homelen+1] = '\0';
+      if(working_path_len > 3) {
+        memcpy(real_path+homelen+1, working_path + 3,
+               1 + working_path_len -3);
+      }
+    }
+    else {
+      real_path = malloc(working_path_len+1);
+      if(real_path == NULL) {
+        free(working_path);
+        return CURLE_OUT_OF_MEMORY;
+      }
+      memcpy(real_path, working_path, 1+working_path_len);
+    }
+  }
+
+  free(working_path);
+
+  /* store the pointer for the caller to receive */
+  *path = real_path;
+
+  return CURLE_OK;
+}
+
+#ifdef HAVE_LIBSSH2_KNOWNHOST_API
+static int sshkeycallback(CURL *easy,
+                          const struct curl_khkey *knownkey, /* known */
+                          const struct curl_khkey *foundkey, /* found */
+                          enum curl_khmatch match,
+                          void *clientp)
+{
+  (void)easy;
+  (void)knownkey;
+  (void)foundkey;
+  (void)clientp;
+
+  /* we only allow perfect matches, and we reject everything else */
+  return (match != CURLKHMATCH_OK)?CURLKHSTAT_REJECT:CURLKHSTAT_FINE;
+}
+#endif
+
+/*
+ * Earlier libssh2 versions didn't have the ability to seek to 64bit positions
+ * with 32bit size_t.
+ */
+#ifdef HAVE_LIBSSH2_SFTP_SEEK64
+#define SFTP_SEEK(x,y) libssh2_sftp_seek64(x, (libssh2_uint64_t)y)
+#else
+#define SFTP_SEEK(x,y) libssh2_sftp_seek(x, (size_t)y)
+#endif
+
+/*
+ * Earlier libssh2 versions didn't do SCP properly beyond 32bit sizes on 32bit
+ * architectures so we check of the necessary function is present.
+ */
+#ifndef HAVE_LIBSSH2_SCP_SEND64
+#define SCP_SEND(a,b,c,d) libssh2_scp_send_ex(a, b, (int)(c), (size_t)d, 0, 0)
+#else
+#define SCP_SEND(a,b,c,d) libssh2_scp_send64(a, b, (int)(c),            \
+                                             (libssh2_uint64_t)d, 0, 0)
+#endif
+
+/*
+ * libssh2 1.2.8 fixed the problem with 32bit ints used for sockets on win64.
+ */
+#ifdef HAVE_LIBSSH2_SESSION_HANDSHAKE
+#define libssh2_session_startup(x,y) libssh2_session_handshake(x,y)
+#endif
+
+static CURLcode ssh_knownhost(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+
+#ifdef HAVE_LIBSSH2_KNOWNHOST_API
+  struct SessionHandle *data = conn->data;
+
+  if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
+    /* we're asked to verify the host against a file */
+    struct ssh_conn *sshc = &conn->proto.sshc;
+    int rc;
+    int keytype;
+    size_t keylen;
+    const char *remotekey = libssh2_session_hostkey(sshc->ssh_session,
+                                                    &keylen, &keytype);
+    int keycheck = LIBSSH2_KNOWNHOST_CHECK_FAILURE;
+    int keybit = 0;
+
+    if(remotekey) {
+      /*
+       * A subject to figure out is what host name we need to pass in here.
+       * What host name does OpenSSH store in its file if an IDN name is
+       * used?
+       */
+      struct libssh2_knownhost *host;
+      enum curl_khmatch keymatch;
+      curl_sshkeycallback func =
+        data->set.ssh_keyfunc?data->set.ssh_keyfunc:sshkeycallback;
+      struct curl_khkey knownkey;
+      struct curl_khkey *knownkeyp = NULL;
+      struct curl_khkey foundkey;
+
+      keybit = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)?
+        LIBSSH2_KNOWNHOST_KEY_SSHRSA:LIBSSH2_KNOWNHOST_KEY_SSHDSS;
+
+      keycheck = libssh2_knownhost_check(sshc->kh,
+                                         conn->host.name,
+                                         remotekey, keylen,
+                                         LIBSSH2_KNOWNHOST_TYPE_PLAIN|
+                                         LIBSSH2_KNOWNHOST_KEYENC_RAW|
+                                         keybit,
+                                         &host);
+
+      infof(data, "SSH host check: %d, key: %s\n", keycheck,
+            (keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH)?
+            host->key:"<none>");
+
+      /* setup 'knownkey' */
+      if(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) {
+        knownkey.key = host->key;
+        knownkey.len = 0;
+        knownkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)?
+          CURLKHTYPE_RSA : CURLKHTYPE_DSS;
+        knownkeyp = &knownkey;
+      }
+
+      /* setup 'foundkey' */
+      foundkey.key = remotekey;
+      foundkey.len = keylen;
+      foundkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)?
+        CURLKHTYPE_RSA : CURLKHTYPE_DSS;
+
+      /*
+       * if any of the LIBSSH2_KNOWNHOST_CHECK_* defines and the
+       * curl_khmatch enum are ever modified, we need to introduce a
+       * translation table here!
+       */
+      keymatch = (enum curl_khmatch)keycheck;
+
+      /* Ask the callback how to behave */
+      rc = func(data, knownkeyp, /* from the knownhosts file */
+                &foundkey, /* from the remote host */
+                keymatch, data->set.ssh_keyfunc_userp);
+    }
+    else
+      /* no remotekey means failure! */
+      rc = CURLKHSTAT_REJECT;
+
+    switch(rc) {
+    default: /* unknown return codes will equal reject */
+    case CURLKHSTAT_REJECT:
+      state(conn, SSH_SESSION_FREE);
+    case CURLKHSTAT_DEFER:
+      /* DEFER means bail out but keep the SSH_HOSTKEY state */
+      result = sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
+      break;
+    case CURLKHSTAT_FINE:
+    case CURLKHSTAT_FINE_ADD_TO_FILE:
+      /* proceed */
+      if(keycheck != LIBSSH2_KNOWNHOST_CHECK_MATCH) {
+        /* the found host+key didn't match but has been told to be fine
+           anyway so we add it in memory */
+        int addrc = libssh2_knownhost_add(sshc->kh,
+                                          conn->host.name, NULL,
+                                          remotekey, keylen,
+                                          LIBSSH2_KNOWNHOST_TYPE_PLAIN|
+                                          LIBSSH2_KNOWNHOST_KEYENC_RAW|
+                                          keybit, NULL);
+        if(addrc)
+          infof(data, "Warning adding the known host %s failed!\n",
+                conn->host.name);
+        else if(rc == CURLKHSTAT_FINE_ADD_TO_FILE) {
+          /* now we write the entire in-memory list of known hosts to the
+             known_hosts file */
+          int wrc =
+            libssh2_knownhost_writefile(sshc->kh,
+                                        data->set.str[STRING_SSH_KNOWNHOSTS],
+                                        LIBSSH2_KNOWNHOST_FILE_OPENSSH);
+          if(wrc) {
+            infof(data, "Warning, writing %s failed!\n",
+                  data->set.str[STRING_SSH_KNOWNHOSTS]);
+          }
+        }
+      }
+      break;
+    }
+  }
+#else /* HAVE_LIBSSH2_KNOWNHOST_API */
+  (void)conn;
+#endif
+  return result;
+}
+
+static CURLcode ssh_check_fingerprint(struct connectdata *conn)
+{
+  struct ssh_conn *sshc = &conn->proto.sshc;
+  struct SessionHandle *data = conn->data;
+  const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5];
+  char md5buffer[33];
+  int i;
+
+  const char *fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
+      LIBSSH2_HOSTKEY_HASH_MD5);
+
+  if(fingerprint) {
+    /* The fingerprint points to static storage (!), don't free() it. */
+    for(i = 0; i < 16; i++)
+      snprintf(&md5buffer[i*2], 3, "%02x", (unsigned char) fingerprint[i]);
+    infof(data, "SSH MD5 fingerprint: %s\n", md5buffer);
+  }
+
+  /* Before we authenticate we check the hostkey's MD5 fingerprint
+   * against a known fingerprint, if available.
+   */
+  if(pubkey_md5 && strlen(pubkey_md5) == 32) {
+    if(!fingerprint || !strequal(md5buffer, pubkey_md5)) {
+      if(fingerprint)
+        failf(data,
+            "Denied establishing ssh session: mismatch md5 fingerprint. "
+            "Remote %s is not equal to %s", md5buffer, pubkey_md5);
+      else
+        failf(data,
+            "Denied establishing ssh session: md5 fingerprint not available");
+      state(conn, SSH_SESSION_FREE);
+      sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
+      return sshc->actualcode;
+    }
+    else {
+      infof(data, "MD5 checksum match!\n");
+      /* as we already matched, we skip the check for known hosts */
+      return CURLE_OK;
+    }
+  }
+  else
+    return ssh_knownhost(conn);
+}
+
+/*
+ * ssh_statemach_act() runs the SSH state machine as far as it can without
+ * blocking and without reaching the end.  The data the pointer 'block' points
+ * to will be set to TRUE if the libssh2 function returns LIBSSH2_ERROR_EAGAIN
+ * meaning it wants to be called again when the socket is ready
+ */
+
+static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  struct SSHPROTO *sftp_scp = data->state.proto.ssh;
+  struct ssh_conn *sshc = &conn->proto.sshc;
+  curl_socket_t sock = conn->sock[FIRSTSOCKET];
+  char *new_readdir_line;
+  int rc = LIBSSH2_ERROR_NONE;
+  int err;
+  int seekerr = CURL_SEEKFUNC_OK;
+  *block = 0; /* we're not blocking by default */
+
+  do {
+
+    switch(sshc->state) {
+    case SSH_INIT:
+      sshc->secondCreateDirs = 0;
+      sshc->nextstate = SSH_NO_STATE;
+      sshc->actualcode = CURLE_OK;
+
+      /* Set libssh2 to non-blocking, since everything internally is
+         non-blocking */
+      libssh2_session_set_blocking(sshc->ssh_session, 0);
+
+      state(conn, SSH_S_STARTUP);
+      /* fall-through */
+
+    case SSH_S_STARTUP:
+      rc = libssh2_session_startup(sshc->ssh_session, (int)sock);
+      if(rc == LIBSSH2_ERROR_EAGAIN) {
+        break;
+      }
+      else if(rc) {
+        failf(data, "Failure establishing ssh session");
+        state(conn, SSH_SESSION_FREE);
+        sshc->actualcode = CURLE_FAILED_INIT;
+        break;
+      }
+
+      state(conn, SSH_HOSTKEY);
+
+      /* fall-through */
+    case SSH_HOSTKEY:
+      /*
+       * Before we authenticate we should check the hostkey's fingerprint
+       * against our known hosts. How that is handled (reading from file,
+       * whatever) is up to us.
+       */
+      result = ssh_check_fingerprint(conn);
+      if(result == CURLE_OK)
+        state(conn, SSH_AUTHLIST);
+      break;
+
+    case SSH_AUTHLIST:
+      /*
+       * Figure out authentication methods
+       * NB: As soon as we have provided a username to an openssh server we
+       * must never change it later. Thus, always specify the correct username
+       * here, even though the libssh2 docs kind of indicate that it should be
+       * possible to get a 'generic' list (not user-specific) of authentication
+       * methods, presumably with a blank username. That won't work in my
+       * experience.
+       * So always specify it here.
+       */
+      sshc->authlist = libssh2_userauth_list(sshc->ssh_session,
+                                             conn->user,
+                                             curlx_uztoui(strlen(conn->user)));
+
+      if(!sshc->authlist) {
+        if((err = libssh2_session_last_errno(sshc->ssh_session)) ==
+           LIBSSH2_ERROR_EAGAIN) {
+          rc = LIBSSH2_ERROR_EAGAIN;
+          break;
+        }
+        else {
+          state(conn, SSH_SESSION_FREE);
+          sshc->actualcode = libssh2_session_error_to_CURLE(err);
+          break;
+        }
+      }
+      infof(data, "SSH authentication methods available: %s\n",
+            sshc->authlist);
+
+      state(conn, SSH_AUTH_PKEY_INIT);
+      break;
+
+    case SSH_AUTH_PKEY_INIT:
+      /*
+       * Check the supported auth types in the order I feel is most secure
+       * with the requested type of authentication
+       */
+      sshc->authed = FALSE;
+
+      if((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) &&
+         (strstr(sshc->authlist, "publickey") != NULL)) {
+        char *home = NULL;
+        bool rsa_pub_empty_but_ok = FALSE;
+
+        sshc->rsa_pub = sshc->rsa = NULL;
+
+        /* To ponder about: should really the lib be messing about with the
+           HOME environment variable etc? */
+        home = curl_getenv("HOME");
+
+        if(data->set.str[STRING_SSH_PUBLIC_KEY] &&
+           !*data->set.str[STRING_SSH_PUBLIC_KEY])
+           rsa_pub_empty_but_ok = true;
+        else if(data->set.str[STRING_SSH_PUBLIC_KEY])
+          sshc->rsa_pub = aprintf("%s", data->set.str[STRING_SSH_PUBLIC_KEY]);
+        else if(home)
+          sshc->rsa_pub = aprintf("%s/.ssh/id_dsa.pub", home);
+        else
+          /* as a final resort, try current dir! */
+          sshc->rsa_pub = strdup("id_dsa.pub");
+
+        if(!rsa_pub_empty_but_ok && (sshc->rsa_pub == NULL)) {
+          Curl_safefree(home);
+          state(conn, SSH_SESSION_FREE);
+          sshc->actualcode = CURLE_OUT_OF_MEMORY;
+          break;
+        }
+
+        if(data->set.str[STRING_SSH_PRIVATE_KEY])
+          sshc->rsa = aprintf("%s", data->set.str[STRING_SSH_PRIVATE_KEY]);
+        else if(home)
+          sshc->rsa = aprintf("%s/.ssh/id_dsa", home);
+        else
+          /* as a final resort, try current dir! */
+          sshc->rsa = strdup("id_dsa");
+
+        if(sshc->rsa == NULL) {
+          Curl_safefree(home);
+          Curl_safefree(sshc->rsa_pub);
+          state(conn, SSH_SESSION_FREE);
+          sshc->actualcode = CURLE_OUT_OF_MEMORY;
+          break;
+        }
+
+        sshc->passphrase = data->set.str[STRING_KEY_PASSWD];
+        if(!sshc->passphrase)
+          sshc->passphrase = "";
+
+        Curl_safefree(home);
+
+        infof(data, "Using ssh public key file %s\n", sshc->rsa_pub);
+        infof(data, "Using ssh private key file %s\n", sshc->rsa);
+
+        state(conn, SSH_AUTH_PKEY);
+      }
+      else {
+        state(conn, SSH_AUTH_PASS_INIT);
+      }
+      break;
+
+    case SSH_AUTH_PKEY:
+      /* The function below checks if the files exists, no need to stat() here.
+       */
+      rc = libssh2_userauth_publickey_fromfile_ex(sshc->ssh_session,
+                                                  conn->user,
+                                                  curlx_uztoui(
+                                                    strlen(conn->user)),
+                                                  sshc->rsa_pub,
+                                                  sshc->rsa, sshc->passphrase);
+      if(rc == LIBSSH2_ERROR_EAGAIN) {
+        break;
+      }
+
+      Curl_safefree(sshc->rsa_pub);
+      Curl_safefree(sshc->rsa);
+
+      if(rc == 0) {
+        sshc->authed = TRUE;
+        infof(data, "Initialized SSH public key authentication\n");
+        state(conn, SSH_AUTH_DONE);
+      }
+      else {
+        char *err_msg;
+        (void)libssh2_session_last_error(sshc->ssh_session,
+                                         &err_msg, NULL, 0);
+        infof(data, "SSH public key authentication failed: %s\n", err_msg);
+        state(conn, SSH_AUTH_PASS_INIT);
+      }
+      break;
+
+    case SSH_AUTH_PASS_INIT:
+      if((data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) &&
+         (strstr(sshc->authlist, "password") != NULL)) {
+        state(conn, SSH_AUTH_PASS);
+      }
+      else {
+        state(conn, SSH_AUTH_HOST_INIT);
+      }
+      break;
+
+    case SSH_AUTH_PASS:
+      rc = libssh2_userauth_password_ex(sshc->ssh_session, conn->user,
+                                        curlx_uztoui(strlen(conn->user)),
+                                        conn->passwd,
+                                        curlx_uztoui(strlen(conn->passwd)),
+                                        NULL);
+      if(rc == LIBSSH2_ERROR_EAGAIN) {
+        break;
+      }
+      else if(rc == 0) {
+        sshc->authed = TRUE;
+        infof(data, "Initialized password authentication\n");
+        state(conn, SSH_AUTH_DONE);
+      }
+      else {
+        state(conn, SSH_AUTH_HOST_INIT);
+      }
+      break;
+
+    case SSH_AUTH_HOST_INIT:
+      if((data->set.ssh_auth_types & CURLSSH_AUTH_HOST) &&
+         (strstr(sshc->authlist, "hostbased") != NULL)) {
+        state(conn, SSH_AUTH_HOST);
+      }
+      else {
+        state(conn, SSH_AUTH_AGENT_INIT);
+      }
+      break;
+
+    case SSH_AUTH_HOST:
+      state(conn, SSH_AUTH_AGENT_INIT);
+      break;
+
+    case SSH_AUTH_AGENT_INIT:
+#ifdef HAVE_LIBSSH2_AGENT_API
+      if((data->set.ssh_auth_types & CURLSSH_AUTH_AGENT)
+         && (strstr(sshc->authlist, "publickey") != NULL)) {
+
+        /* Connect to the ssh-agent */
+        /* The agent could be shared by a curl thread i believe
+           but nothing obvious as keys can be added/removed at any time */
+        if(!sshc->ssh_agent) {
+          sshc->ssh_agent = libssh2_agent_init(sshc->ssh_session);
+          if(!sshc->ssh_agent) {
+            infof(data, "Could not create agent object\n");
+
+            state(conn, SSH_AUTH_KEY_INIT);
+          }
+        }
+
+        rc = libssh2_agent_connect(sshc->ssh_agent);
+        if(rc == LIBSSH2_ERROR_EAGAIN)
+          break;
+        if(rc < 0) {
+          infof(data, "Failure connecting to agent\n");
+          state(conn, SSH_AUTH_KEY_INIT);
+        }
+        else {
+          state(conn, SSH_AUTH_AGENT_LIST);
+        }
+      }
+      else
+#endif /* HAVE_LIBSSH2_AGENT_API */
+        state(conn, SSH_AUTH_KEY_INIT);
+      break;
+
+    case SSH_AUTH_AGENT_LIST:
+#ifdef HAVE_LIBSSH2_AGENT_API
+      rc = libssh2_agent_list_identities(sshc->ssh_agent);
+
+      if(rc == LIBSSH2_ERROR_EAGAIN)
+        break;
+      if(rc < 0) {
+        infof(data, "Failure requesting identities to agent\n");
+        state(conn, SSH_AUTH_KEY_INIT);
+      }
+      else {
+        state(conn, SSH_AUTH_AGENT);
+        sshc->sshagent_prev_identity = NULL;
+      }
+#endif
+      break;
+
+    case SSH_AUTH_AGENT:
+#ifdef HAVE_LIBSSH2_AGENT_API
+      /* as prev_identity evolves only after an identity user auth finished we
+         can safely request it again as long as EAGAIN is returned here or by
+         libssh2_agent_userauth */
+      rc = libssh2_agent_get_identity(sshc->ssh_agent,
+                                      &sshc->sshagent_identity,
+                                      sshc->sshagent_prev_identity);
+      if(rc == LIBSSH2_ERROR_EAGAIN)
+        break;
+
+      if(rc == 0) {
+        rc = libssh2_agent_userauth(sshc->ssh_agent, conn->user,
+                                    sshc->sshagent_identity);
+
+        if(rc < 0) {
+          if(rc != LIBSSH2_ERROR_EAGAIN) {
+            /* tried and failed? go to next identity */
+            sshc->sshagent_prev_identity = sshc->sshagent_identity;
+          }
+          break;
+        }
+      }
+
+      if(rc < 0)
+        infof(data, "Failure requesting identities to agent\n");
+      else if(rc == 1)
+        infof(data, "No identity would match\n");
+
+      if(rc == LIBSSH2_ERROR_NONE) {
+        sshc->authed = TRUE;
+        infof(data, "Agent based authentication successful\n");
+        state(conn, SSH_AUTH_DONE);
+      }
+      else
+        state(conn, SSH_AUTH_KEY_INIT);
+#endif
+      break;
+
+    case SSH_AUTH_KEY_INIT:
+      if((data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD)
+         && (strstr(sshc->authlist, "keyboard-interactive") != NULL)) {
+        state(conn, SSH_AUTH_KEY);
+      }
+      else {
+        state(conn, SSH_AUTH_DONE);
+      }
+      break;
+
+    case SSH_AUTH_KEY:
+      /* Authentication failed. Continue with keyboard-interactive now. */
+      rc = libssh2_userauth_keyboard_interactive_ex(sshc->ssh_session,
+                                                    conn->user,
+                                                    curlx_uztoui(
+                                                      strlen(conn->user)),
+                                                    &kbd_callback);
+      if(rc == LIBSSH2_ERROR_EAGAIN) {
+        break;
+      }
+      else if(rc == 0) {
+        sshc->authed = TRUE;
+        infof(data, "Initialized keyboard interactive authentication\n");
+      }
+      state(conn, SSH_AUTH_DONE);
+      break;
+
+    case SSH_AUTH_DONE:
+      if(!sshc->authed) {
+        failf(data, "Authentication failure");
+        state(conn, SSH_SESSION_FREE);
+        sshc->actualcode = CURLE_LOGIN_DENIED;
+        break;
+      }
+
+      /*
+       * At this point we have an authenticated ssh session.
+       */
+      infof(data, "Authentication complete\n");
+
+      Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSH is connected */
+
+      conn->sockfd = sock;
+      conn->writesockfd = CURL_SOCKET_BAD;
+
+      if(conn->handler->protocol == CURLPROTO_SFTP) {
+        state(conn, SSH_SFTP_INIT);
+        break;
+      }
+      infof(data, "SSH CONNECT phase done\n");
+      state(conn, SSH_STOP);
+      break;
+
+    case SSH_SFTP_INIT:
+      /*
+       * Start the libssh2 sftp session
+       */
+      sshc->sftp_session = libssh2_sftp_init(sshc->ssh_session);
+      if(!sshc->sftp_session) {
+        if(libssh2_session_last_errno(sshc->ssh_session) ==
+           LIBSSH2_ERROR_EAGAIN) {
+          rc = LIBSSH2_ERROR_EAGAIN;
+          break;
+        }
+        else {
+          char *err_msg;
+
+          (void)libssh2_session_last_error(sshc->ssh_session,
+                                           &err_msg, NULL, 0);
+          failf(data, "Failure initializing sftp session: %s", err_msg);
+          state(conn, SSH_SESSION_FREE);
+          sshc->actualcode = CURLE_FAILED_INIT;
+          break;
+        }
+      }
+      state(conn, SSH_SFTP_REALPATH);
+      break;
+
+    case SSH_SFTP_REALPATH:
+    {
+      char tempHome[PATH_MAX];
+
+      /*
+       * Get the "home" directory
+       */
+      rc = sftp_libssh2_realpath(sshc->sftp_session, ".",
+                                 tempHome, PATH_MAX-1);
+      if(rc == LIBSSH2_ERROR_EAGAIN) {
+        break;
+      }
+      else if(rc > 0) {
+        /* It seems that this string is not always NULL terminated */
+        tempHome[rc] = '\0';
+        sshc->homedir = strdup(tempHome);
+        if(!sshc->homedir) {
+          state(conn, SSH_SFTP_CLOSE);
+          sshc->actualcode = CURLE_OUT_OF_MEMORY;
+          break;
+        }
+        conn->data->state.most_recent_ftp_entrypath = sshc->homedir;
+      }
+      else {
+        /* Return the error type */
+        err = sftp_libssh2_last_error(sshc->sftp_session);
+        result = sftp_libssh2_error_to_CURLE(err);
+        sshc->actualcode = result?result:CURLE_SSH;
+        DEBUGF(infof(data, "error = %d makes libcurl = %d\n",
+                     err, (int)result));
+        state(conn, SSH_STOP);
+        break;
+      }
+    }
+    /* This is the last step in the SFTP connect phase. Do note that while
+       we get the homedir here, we get the "workingpath" in the DO action
+       since the homedir will remain the same between request but the
+       working path will not. */
+    DEBUGF(infof(data, "SSH CONNECT phase done\n"));
+    state(conn, SSH_STOP);
+    break;
+
+    case SSH_SFTP_QUOTE_INIT:
+
+      result = ssh_getworkingpath(conn, sshc->homedir, &sftp_scp->path);
+      if(result) {
+        sshc->actualcode = result;
+        state(conn, SSH_STOP);
+        break;
+      }
+
+      if(data->set.quote) {
+        infof(data, "Sending quote commands\n");
+        sshc->quote_item = data->set.quote;
+        state(conn, SSH_SFTP_QUOTE);
+      }
+      else {
+        state(conn, SSH_SFTP_TRANS_INIT);
+      }
+      break;
+
+    case SSH_SFTP_POSTQUOTE_INIT:
+      if(data->set.postquote) {
+        infof(data, "Sending quote commands\n");
+        sshc->quote_item = data->set.postquote;
+        state(conn, SSH_SFTP_QUOTE);
+      }
+      else {
+        state(conn, SSH_STOP);
+      }
+      break;
+
+    case SSH_SFTP_QUOTE:
+      /* Send any quote commands */
+    {
+      const char *cp;
+
+      /*
+       * Support some of the "FTP" commands
+       */
+      char *cmd = sshc->quote_item->data;
+      sshc->acceptfail = FALSE;
+
+      /* if a command starts with an asterisk, which a legal SFTP command never
+         can, the command will be allowed to fail without it causing any
+         aborts or cancels etc. It will cause libcurl to act as if the command
+         is successful, whatever the server reponds. */
+
+      if(cmd[0] == '*') {
+        cmd++;
+        sshc->acceptfail = TRUE;
+      }
+
+      if(curl_strequal("pwd", cmd)) {
+        /* output debug output if that is requested */
+        char *tmp = aprintf("257 \"%s\" is current directory.\n",
+                            sftp_scp->path);
+        if(!tmp) {
+          result = CURLE_OUT_OF_MEMORY;
+          state(conn, SSH_SFTP_CLOSE);
+          sshc->nextstate = SSH_NO_STATE;
+          break;
+        }
+        if(data->set.verbose) {
+          Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"PWD\n", 4, conn);
+          Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp), conn);
+        }
+        /* this sends an FTP-like "header" to the header callback so that the
+           current directory can be read very similar to how it is read when
+           using ordinary FTP. */
+        result = Curl_client_write(conn, CLIENTWRITE_HEADER, tmp, strlen(tmp));
+        free(tmp);
+        state(conn, SSH_SFTP_NEXT_QUOTE);
+        break;
+      }
+      else if(cmd) {
+        /*
+         * the arguments following the command must be separated from the
+         * command with a space so we can check for it unconditionally
+         */
+        cp = strchr(cmd, ' ');
+        if(cp == NULL) {
+          failf(data, "Syntax error in SFTP command. Supply parameter(s)!");
+          state(conn, SSH_SFTP_CLOSE);
+          sshc->nextstate = SSH_NO_STATE;
+          sshc->actualcode = CURLE_QUOTE_ERROR;
+          break;
+        }
+
+        /*
+         * also, every command takes at least one argument so we get that
+         * first argument right now
+         */
+        result = get_pathname(&cp, &sshc->quote_path1);
+        if(result) {
+          if(result == CURLE_OUT_OF_MEMORY)
+            failf(data, "Out of memory");
+          else
+            failf(data, "Syntax error: Bad first parameter");
+          state(conn, SSH_SFTP_CLOSE);
+          sshc->nextstate = SSH_NO_STATE;
+          sshc->actualcode = result;
+          break;
+        }
+
+        /*
+         * SFTP is a binary protocol, so we don't send text commands to
+         * the server. Instead, we scan for commands for commands used by
+         * OpenSSH's sftp program and call the appropriate libssh2
+         * functions.
+         */
+        if(curl_strnequal(cmd, "chgrp ", 6) ||
+           curl_strnequal(cmd, "chmod ", 6) ||
+           curl_strnequal(cmd, "chown ", 6) ) {
+          /* attribute change */
+
+          /* sshc->quote_path1 contains the mode to set */
+          /* get the destination */
+          result = get_pathname(&cp, &sshc->quote_path2);
+          if(result) {
+            if(result == CURLE_OUT_OF_MEMORY)
+              failf(data, "Out of memory");
+            else
+              failf(data, "Syntax error in chgrp/chmod/chown: "
+                    "Bad second parameter");
+            Curl_safefree(sshc->quote_path1);
+            state(conn, SSH_SFTP_CLOSE);
+            sshc->nextstate = SSH_NO_STATE;
+            sshc->actualcode = result;
+            break;
+          }
+          memset(&sshc->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
+          state(conn, SSH_SFTP_QUOTE_STAT);
+          break;
+        }
+        else if(curl_strnequal(cmd, "ln ", 3) ||
+                curl_strnequal(cmd, "symlink ", 8)) {
+          /* symbolic linking */
+          /* sshc->quote_path1 is the source */
+          /* get the destination */
+          result = get_pathname(&cp, &sshc->quote_path2);
+          if(result) {
+            if(result == CURLE_OUT_OF_MEMORY)
+              failf(data, "Out of memory");
+            else
+              failf(data,
+                    "Syntax error in ln/symlink: Bad second parameter");
+            Curl_safefree(sshc->quote_path1);
+            state(conn, SSH_SFTP_CLOSE);
+            sshc->nextstate = SSH_NO_STATE;
+            sshc->actualcode = result;
+            break;
+          }
+          state(conn, SSH_SFTP_QUOTE_SYMLINK);
+          break;
+        }
+        else if(curl_strnequal(cmd, "mkdir ", 6)) {
+          /* create dir */
+          state(conn, SSH_SFTP_QUOTE_MKDIR);
+          break;
+        }
+        else if(curl_strnequal(cmd, "rename ", 7)) {
+          /* rename file */
+          /* first param is the source path */
+          /* second param is the dest. path */
+          result = get_pathname(&cp, &sshc->quote_path2);
+          if(result) {
+            if(result == CURLE_OUT_OF_MEMORY)
+              failf(data, "Out of memory");
+            else
+              failf(data, "Syntax error in rename: Bad second parameter");
+            Curl_safefree(sshc->quote_path1);
+            state(conn, SSH_SFTP_CLOSE);
+            sshc->nextstate = SSH_NO_STATE;
+            sshc->actualcode = result;
+            break;
+          }
+          state(conn, SSH_SFTP_QUOTE_RENAME);
+          break;
+        }
+        else if(curl_strnequal(cmd, "rmdir ", 6)) {
+          /* delete dir */
+          state(conn, SSH_SFTP_QUOTE_RMDIR);
+          break;
+        }
+        else if(curl_strnequal(cmd, "rm ", 3)) {
+          state(conn, SSH_SFTP_QUOTE_UNLINK);
+          break;
+        }
+
+        failf(data, "Unknown SFTP command");
+        Curl_safefree(sshc->quote_path1);
+        Curl_safefree(sshc->quote_path2);
+        state(conn, SSH_SFTP_CLOSE);
+        sshc->nextstate = SSH_NO_STATE;
+        sshc->actualcode = CURLE_QUOTE_ERROR;
+        break;
+      }
+    }
+    if(!sshc->quote_item) {
+      state(conn, SSH_SFTP_TRANS_INIT);
+    }
+    break;
+
+    case SSH_SFTP_NEXT_QUOTE:
+      Curl_safefree(sshc->quote_path1);
+      Curl_safefree(sshc->quote_path2);
+
+      sshc->quote_item = sshc->quote_item->next;
+
+      if(sshc->quote_item) {
+        state(conn, SSH_SFTP_QUOTE);
+      }
+      else {
+        if(sshc->nextstate != SSH_NO_STATE) {
+          state(conn, sshc->nextstate);
+          sshc->nextstate = SSH_NO_STATE;
+        }
+        else {
+          state(conn, SSH_SFTP_TRANS_INIT);
+        }
+      }
+      break;
+
+    case SSH_SFTP_QUOTE_STAT:
+    {
+      char *cmd = sshc->quote_item->data;
+      sshc->acceptfail = FALSE;
+
+      /* if a command starts with an asterisk, which a legal SFTP command never
+         can, the command will be allowed to fail without it causing any
+         aborts or cancels etc. It will cause libcurl to act as if the command
+         is successful, whatever the server reponds. */
+
+      if(cmd[0] == '*') {
+        cmd++;
+        sshc->acceptfail = TRUE;
+      }
+
+      if(!curl_strnequal(cmd, "chmod", 5)) {
+        /* Since chown and chgrp only set owner OR group but libssh2 wants to
+         * set them both at once, we need to obtain the current ownership
+         * first.  This takes an extra protocol round trip.
+         */
+        rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2,
+                                  curlx_uztoui(strlen(sshc->quote_path2)),
+                                  LIBSSH2_SFTP_STAT,
+                                  &sshc->quote_attrs);
+        if(rc == LIBSSH2_ERROR_EAGAIN) {
+          break;
+        }
+        else if(rc != 0 && !sshc->acceptfail) { /* get those attributes */
+          err = sftp_libssh2_last_error(sshc->sftp_session);
+          Curl_safefree(sshc->quote_path1);
+          Curl_safefree(sshc->quote_path2);
+          failf(data, "Attempt to get SFTP stats failed: %s",
+                sftp_libssh2_strerror(err));
+          state(conn, SSH_SFTP_CLOSE);
+          sshc->nextstate = SSH_NO_STATE;
+          sshc->actualcode = CURLE_QUOTE_ERROR;
+          break;
+        }
+      }
+
+      /* Now set the new attributes... */
+      if(curl_strnequal(cmd, "chgrp", 5)) {
+        sshc->quote_attrs.gid = strtoul(sshc->quote_path1, NULL, 10);
+        sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID;
+        if(sshc->quote_attrs.gid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
+           !sshc->acceptfail) {
+          Curl_safefree(sshc->quote_path1);
+          Curl_safefree(sshc->quote_path2);
+          failf(data, "Syntax error: chgrp gid not a number");
+          state(conn, SSH_SFTP_CLOSE);
+          sshc->nextstate = SSH_NO_STATE;
+          sshc->actualcode = CURLE_QUOTE_ERROR;
+          break;
+        }
+      }
+      else if(curl_strnequal(cmd, "chmod", 5)) {
+        sshc->quote_attrs.permissions = strtoul(sshc->quote_path1, NULL, 8);
+        sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;
+        /* permissions are octal */
+        if(sshc->quote_attrs.permissions == 0 &&
+           !ISDIGIT(sshc->quote_path1[0])) {
+          Curl_safefree(sshc->quote_path1);
+          Curl_safefree(sshc->quote_path2);
+          failf(data, "Syntax error: chmod permissions not a number");
+          state(conn, SSH_SFTP_CLOSE);
+          sshc->nextstate = SSH_NO_STATE;
+          sshc->actualcode = CURLE_QUOTE_ERROR;
+          break;
+        }
+      }
+      else if(curl_strnequal(cmd, "chown", 5)) {
+        sshc->quote_attrs.uid = strtoul(sshc->quote_path1, NULL, 10);
+        sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID;
+        if(sshc->quote_attrs.uid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
+           !sshc->acceptfail) {
+          Curl_safefree(sshc->quote_path1);
+          Curl_safefree(sshc->quote_path2);
+          failf(data, "Syntax error: chown uid not a number");
+          state(conn, SSH_SFTP_CLOSE);
+          sshc->nextstate = SSH_NO_STATE;
+          sshc->actualcode = CURLE_QUOTE_ERROR;
+          break;
+        }
+      }
+
+      /* Now send the completed structure... */
+      state(conn, SSH_SFTP_QUOTE_SETSTAT);
+      break;
+    }
+
+    case SSH_SFTP_QUOTE_SETSTAT:
+      rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2,
+                                curlx_uztoui(strlen(sshc->quote_path2)),
+                                LIBSSH2_SFTP_SETSTAT,
+                                &sshc->quote_attrs);
+      if(rc == LIBSSH2_ERROR_EAGAIN) {
+        break;
+      }
+      else if(rc != 0 && !sshc->acceptfail) {
+        err = sftp_libssh2_last_error(sshc->sftp_session);
+        Curl_safefree(sshc->quote_path1);
+        Curl_safefree(sshc->quote_path2);
+        failf(data, "Attempt to set SFTP stats failed: %s",
+              sftp_libssh2_strerror(err));
+        state(conn, SSH_SFTP_CLOSE);
+        sshc->nextstate = SSH_NO_STATE;
+        sshc->actualcode = CURLE_QUOTE_ERROR;
+        break;
+      }
+      state(conn, SSH_SFTP_NEXT_QUOTE);
+      break;
+
+    case SSH_SFTP_QUOTE_SYMLINK:
+      rc = libssh2_sftp_symlink_ex(sshc->sftp_session, sshc->quote_path1,
+                                   curlx_uztoui(strlen(sshc->quote_path1)),
+                                   sshc->quote_path2,
+                                   curlx_uztoui(strlen(sshc->quote_path2)),
+                                   LIBSSH2_SFTP_SYMLINK);
+      if(rc == LIBSSH2_ERROR_EAGAIN) {
+        break;
+      }
+      else if(rc != 0 && !sshc->acceptfail) {
+        err = sftp_libssh2_last_error(sshc->sftp_session);
+        Curl_safefree(sshc->quote_path1);
+        Curl_safefree(sshc->quote_path2);
+        failf(data, "symlink command failed: %s",
+              sftp_libssh2_strerror(err));
+        state(conn, SSH_SFTP_CLOSE);
+        sshc->nextstate = SSH_NO_STATE;
+        sshc->actualcode = CURLE_QUOTE_ERROR;
+        break;
+      }
+      state(conn, SSH_SFTP_NEXT_QUOTE);
+      break;
+
+    case SSH_SFTP_QUOTE_MKDIR:
+      rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshc->quote_path1,
+                                 curlx_uztoui(strlen(sshc->quote_path1)),
+                                 data->set.new_directory_perms);
+      if(rc == LIBSSH2_ERROR_EAGAIN) {
+        break;
+      }
+      else if(rc != 0 && !sshc->acceptfail) {
+        err = sftp_libssh2_last_error(sshc->sftp_session);
+        Curl_safefree(sshc->quote_path1);
+        failf(data, "mkdir command failed: %s", sftp_libssh2_strerror(err));
+        state(conn, SSH_SFTP_CLOSE);
+        sshc->nextstate = SSH_NO_STATE;
+        sshc->actualcode = CURLE_QUOTE_ERROR;
+        break;
+      }
+      state(conn, SSH_SFTP_NEXT_QUOTE);
+      break;
+
+    case SSH_SFTP_QUOTE_RENAME:
+      rc = libssh2_sftp_rename_ex(sshc->sftp_session, sshc->quote_path1,
+                                  curlx_uztoui(strlen(sshc->quote_path1)),
+                                  sshc->quote_path2,
+                                  curlx_uztoui(strlen(sshc->quote_path2)),
+                                  LIBSSH2_SFTP_RENAME_OVERWRITE |
+                                  LIBSSH2_SFTP_RENAME_ATOMIC |
+                                  LIBSSH2_SFTP_RENAME_NATIVE);
+
+      if(rc == LIBSSH2_ERROR_EAGAIN) {
+        break;
+      }
+      else if(rc != 0 && !sshc->acceptfail) {
+        err = sftp_libssh2_last_error(sshc->sftp_session);
+        Curl_safefree(sshc->quote_path1);
+        Curl_safefree(sshc->quote_path2);
+        failf(data, "rename command failed: %s", sftp_libssh2_strerror(err));
+        state(conn, SSH_SFTP_CLOSE);
+        sshc->nextstate = SSH_NO_STATE;
+        sshc->actualcode = CURLE_QUOTE_ERROR;
+        break;
+      }
+      state(conn, SSH_SFTP_NEXT_QUOTE);
+      break;
+
+    case SSH_SFTP_QUOTE_RMDIR:
+      rc = libssh2_sftp_rmdir_ex(sshc->sftp_session, sshc->quote_path1,
+                                 curlx_uztoui(strlen(sshc->quote_path1)));
+      if(rc == LIBSSH2_ERROR_EAGAIN) {
+        break;
+      }
+      else if(rc != 0 && !sshc->acceptfail) {
+        err = sftp_libssh2_last_error(sshc->sftp_session);
+        Curl_safefree(sshc->quote_path1);
+        failf(data, "rmdir command failed: %s", sftp_libssh2_strerror(err));
+        state(conn, SSH_SFTP_CLOSE);
+        sshc->nextstate = SSH_NO_STATE;
+        sshc->actualcode = CURLE_QUOTE_ERROR;
+        break;
+      }
+      state(conn, SSH_SFTP_NEXT_QUOTE);
+      break;
+
+    case SSH_SFTP_QUOTE_UNLINK:
+      rc = libssh2_sftp_unlink_ex(sshc->sftp_session, sshc->quote_path1,
+                                  curlx_uztoui(strlen(sshc->quote_path1)));
+      if(rc == LIBSSH2_ERROR_EAGAIN) {
+        break;
+      }
+      else if(rc != 0 && !sshc->acceptfail) {
+        err = sftp_libssh2_last_error(sshc->sftp_session);
+        Curl_safefree(sshc->quote_path1);
+        failf(data, "rm command failed: %s", sftp_libssh2_strerror(err));
+        state(conn, SSH_SFTP_CLOSE);
+        sshc->nextstate = SSH_NO_STATE;
+        sshc->actualcode = CURLE_QUOTE_ERROR;
+        break;
+      }
+      state(conn, SSH_SFTP_NEXT_QUOTE);
+      break;
+
+    case SSH_SFTP_TRANS_INIT:
+      if(data->set.upload)
+        state(conn, SSH_SFTP_UPLOAD_INIT);
+      else {
+        if(data->set.opt_no_body)
+          state(conn, SSH_STOP);
+        else if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/')
+          state(conn, SSH_SFTP_READDIR_INIT);
+        else
+          state(conn, SSH_SFTP_DOWNLOAD_INIT);
+      }
+      break;
+
+    case SSH_SFTP_UPLOAD_INIT:
+    {
+      unsigned long flags;
+      /*
+       * NOTE!!!  libssh2 requires that the destination path is a full path
+       *          that includes the destination file and name OR ends in a "/"
+       *          If this is not done the destination file will be named the
+       *          same name as the last directory in the path.
+       */
+
+      if(data->state.resume_from != 0) {
+        LIBSSH2_SFTP_ATTRIBUTES attrs;
+        if(data->state.resume_from < 0) {
+          rc = libssh2_sftp_stat_ex(sshc->sftp_session, sftp_scp->path,
+                                    curlx_uztoui(strlen(sftp_scp->path)),
+                                    LIBSSH2_SFTP_STAT, &attrs);
+          if(rc == LIBSSH2_ERROR_EAGAIN) {
+            break;
+          }
+          else if(rc) {
+            data->state.resume_from = 0;
+          }
+          else {
+            curl_off_t size = attrs.filesize;
+            if(size < 0) {
+              failf(data, "Bad file size (%" FORMAT_OFF_T ")", size);
+              return CURLE_BAD_DOWNLOAD_RESUME;
+            }
+            data->state.resume_from = attrs.filesize;
+          }
+        }
+      }
+
+      if(data->set.ftp_append)
+        /* Try to open for append, but create if nonexisting */
+        flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_APPEND;
+      else if(data->state.resume_from > 0)
+        /* If we have restart position then open for append */
+        flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_APPEND;
+      else
+        /* Clear file before writing (normal behaviour) */
+        flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC;
+
+      sshc->sftp_handle =
+        libssh2_sftp_open_ex(sshc->sftp_session, sftp_scp->path,
+                             curlx_uztoui(strlen(sftp_scp->path)),
+                             flags, data->set.new_file_perms,
+                             LIBSSH2_SFTP_OPENFILE);
+
+      if(!sshc->sftp_handle) {
+        rc = libssh2_session_last_errno(sshc->ssh_session);
+
+        if(LIBSSH2_ERROR_EAGAIN == rc)
+          break;
+        else {
+          if(LIBSSH2_ERROR_SFTP_PROTOCOL == rc)
+            /* only when there was an SFTP protocol error can we extract
+               the sftp error! */
+            err = sftp_libssh2_last_error(sshc->sftp_session);
+          else
+            err = -1; /* not an sftp error at all */
+
+          if(sshc->secondCreateDirs) {
+            state(conn, SSH_SFTP_CLOSE);
+            sshc->actualcode = err>= LIBSSH2_FX_OK?
+              sftp_libssh2_error_to_CURLE(err):CURLE_SSH;
+            failf(data, "Creating the dir/file failed: %s",
+                  sftp_libssh2_strerror(err));
+            break;
+          }
+          else if(((err == LIBSSH2_FX_NO_SUCH_FILE) ||
+                   (err == LIBSSH2_FX_FAILURE) ||
+                   (err == LIBSSH2_FX_NO_SUCH_PATH)) &&
+                  (data->set.ftp_create_missing_dirs &&
+                   (strlen(sftp_scp->path) > 1))) {
+            /* try to create the path remotely */
+            sshc->secondCreateDirs = 1;
+            state(conn, SSH_SFTP_CREATE_DIRS_INIT);
+            break;
+          }
+          state(conn, SSH_SFTP_CLOSE);
+          sshc->actualcode = err>= LIBSSH2_FX_OK?
+            sftp_libssh2_error_to_CURLE(err):CURLE_SSH;
+          if(!sshc->actualcode) {
+            /* Sometimes, for some reason libssh2_sftp_last_error() returns
+               zero even though libssh2_sftp_open() failed previously! We need
+               to work around that! */
+            sshc->actualcode = CURLE_SSH;
+            err=-1;
+          }
+          failf(data, "Upload failed: %s (%d/%d)",
+                err>= LIBSSH2_FX_OK?sftp_libssh2_strerror(err):"ssh error",
+                err, rc);
+          break;
+        }
+      }
+
+      /* If we have restart point then we need to seek to the correct
+         position. */
+      if(data->state.resume_from > 0) {
+        /* Let's read off the proper amount of bytes from the input. */
+        if(conn->seek_func) {
+          seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
+                                    SEEK_SET);
+        }
+
+        if(seekerr != CURL_SEEKFUNC_OK) {
+
+          if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
+            failf(data, "Could not seek stream");
+            return CURLE_FTP_COULDNT_USE_REST;
+          }
+          /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
+          else {
+            curl_off_t passed=0;
+            do {
+              size_t readthisamountnow =
+                (data->state.resume_from - passed > CURL_OFF_T_C(BUFSIZE)) ?
+                BUFSIZE : curlx_sotouz(data->state.resume_from - passed);
+
+              size_t actuallyread =
+                conn->fread_func(data->state.buffer, 1, readthisamountnow,
+                                 conn->fread_in);
+
+              passed += actuallyread;
+              if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
+                /* this checks for greater-than only to make sure that the
+                   CURL_READFUNC_ABORT return code still aborts */
+                failf(data, "Failed to read data");
+                return CURLE_FTP_COULDNT_USE_REST;
+              }
+            } while(passed < data->state.resume_from);
+          }
+        }
+
+        /* now, decrease the size of the read */
+        if(data->set.infilesize > 0) {
+          data->set.infilesize -= data->state.resume_from;
+          data->req.size = data->set.infilesize;
+          Curl_pgrsSetUploadSize(data, data->set.infilesize);
+        }
+
+        SFTP_SEEK(sshc->sftp_handle, data->state.resume_from);
+      }
+      if(data->set.infilesize > 0) {
+        data->req.size = data->set.infilesize;
+        Curl_pgrsSetUploadSize(data, data->set.infilesize);
+      }
+      /* upload data */
+      Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
+
+      /* not set by Curl_setup_transfer to preserve keepon bits */
+      conn->sockfd = conn->writesockfd;
+
+      if(result) {
+        state(conn, SSH_SFTP_CLOSE);
+        sshc->actualcode = result;
+      }
+      else {
+        /* store this original bitmask setup to use later on if we can't
+           figure out a "real" bitmask */
+        sshc->orig_waitfor = data->req.keepon;
+
+        /* we want to use the _sending_ function even when the socket turns
+           out readable as the underlying libssh2 sftp send function will deal
+           with both accordingly */
+        conn->cselect_bits = CURL_CSELECT_OUT;
+
+        /* since we don't really wait for anything at this point, we want the
+           state machine to move on as soon as possible so we set a very short
+           timeout here */
+        Curl_expire(data, 1);
+
+        state(conn, SSH_STOP);
+      }
+      break;
+    }
+
+    case SSH_SFTP_CREATE_DIRS_INIT:
+      if(strlen(sftp_scp->path) > 1) {
+        sshc->slash_pos = sftp_scp->path + 1; /* ignore the leading '/' */
+        state(conn, SSH_SFTP_CREATE_DIRS);
+      }
+      else {
+        state(conn, SSH_SFTP_UPLOAD_INIT);
+      }
+      break;
+
+    case SSH_SFTP_CREATE_DIRS:
+      if((sshc->slash_pos = strchr(sshc->slash_pos, '/')) != NULL) {
+        *sshc->slash_pos = 0;
+
+        infof(data, "Creating directory '%s'\n", sftp_scp->path);
+        state(conn, SSH_SFTP_CREATE_DIRS_MKDIR);
+        break;
+      }
+      else {
+        state(conn, SSH_SFTP_UPLOAD_INIT);
+      }
+      break;
+
+    case SSH_SFTP_CREATE_DIRS_MKDIR:
+      /* 'mode' - parameter is preliminary - default to 0644 */
+      rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sftp_scp->path,
+                                 curlx_uztoui(strlen(sftp_scp->path)),
+                                 data->set.new_directory_perms);
+      if(rc == LIBSSH2_ERROR_EAGAIN) {
+        break;
+      }
+      *sshc->slash_pos = '/';
+      ++sshc->slash_pos;
+      if(rc == -1) {
+        /*
+         * Abort if failure wasn't that the dir already exists or the
+         * permission was denied (creation might succeed further down the
+         * path) - retry on unspecific FAILURE also
+         */
+        err = sftp_libssh2_last_error(sshc->sftp_session);
+        if((err != LIBSSH2_FX_FILE_ALREADY_EXISTS) &&
+           (err != LIBSSH2_FX_FAILURE) &&
+           (err != LIBSSH2_FX_PERMISSION_DENIED)) {
+          result = sftp_libssh2_error_to_CURLE(err);
+          state(conn, SSH_SFTP_CLOSE);
+          sshc->actualcode = result?result:CURLE_SSH;
+          break;
+        }
+      }
+      state(conn, SSH_SFTP_CREATE_DIRS);
+      break;
+
+    case SSH_SFTP_READDIR_INIT:
+      /*
+       * This is a directory that we are trying to get, so produce a directory
+       * listing
+       */
+      sshc->sftp_handle = libssh2_sftp_open_ex(sshc->sftp_session,
+                                               sftp_scp->path,
+                                               curlx_uztoui(
+                                                 strlen(sftp_scp->path)),
+                                               0, 0, LIBSSH2_SFTP_OPENDIR);
+      if(!sshc->sftp_handle) {
+        if(libssh2_session_last_errno(sshc->ssh_session) ==
+           LIBSSH2_ERROR_EAGAIN) {
+          rc = LIBSSH2_ERROR_EAGAIN;
+          break;
+        }
+        else {
+          err = sftp_libssh2_last_error(sshc->sftp_session);
+          failf(data, "Could not open directory for reading: %s",
+                sftp_libssh2_strerror(err));
+          state(conn, SSH_SFTP_CLOSE);
+          result = sftp_libssh2_error_to_CURLE(err);
+          sshc->actualcode = result?result:CURLE_SSH;
+          break;
+        }
+      }
+      if((sshc->readdir_filename = malloc(PATH_MAX+1)) == NULL) {
+        state(conn, SSH_SFTP_CLOSE);
+        sshc->actualcode = CURLE_OUT_OF_MEMORY;
+        break;
+      }
+      if((sshc->readdir_longentry = malloc(PATH_MAX+1)) == NULL) {
+        Curl_safefree(sshc->readdir_filename);
+        state(conn, SSH_SFTP_CLOSE);
+        sshc->actualcode = CURLE_OUT_OF_MEMORY;
+        break;
+      }
+      state(conn, SSH_SFTP_READDIR);
+      break;
+
+    case SSH_SFTP_READDIR:
+      sshc->readdir_len = libssh2_sftp_readdir_ex(sshc->sftp_handle,
+                                                  sshc->readdir_filename,
+                                                  PATH_MAX,
+                                                  sshc->readdir_longentry,
+                                                  PATH_MAX,
+                                                  &sshc->readdir_attrs);
+      if(sshc->readdir_len == LIBSSH2_ERROR_EAGAIN) {
+        rc = LIBSSH2_ERROR_EAGAIN;
+        break;
+      }
+      if(sshc->readdir_len > 0) {
+        sshc->readdir_filename[sshc->readdir_len] = '\0';
+
+        if(data->set.ftp_list_only) {
+          char *tmpLine;
+
+          tmpLine = aprintf("%s\n", sshc->readdir_filename);
+          if(tmpLine == NULL) {
+            state(conn, SSH_SFTP_CLOSE);
+            sshc->actualcode = CURLE_OUT_OF_MEMORY;
+            break;
+          }
+          result = Curl_client_write(conn, CLIENTWRITE_BODY,
+                                     tmpLine, sshc->readdir_len+1);
+          Curl_safefree(tmpLine);
+
+          if(result) {
+            state(conn, SSH_STOP);
+            break;
+          }
+          /* since this counts what we send to the client, we include the
+             newline in this counter */
+          data->req.bytecount += sshc->readdir_len+1;
+
+          /* output debug output if that is requested */
+          if(data->set.verbose) {
+            Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_filename,
+                       sshc->readdir_len, conn);
+          }
+        }
+        else {
+          sshc->readdir_currLen = (int)strlen(sshc->readdir_longentry);
+          sshc->readdir_totalLen = 80 + sshc->readdir_currLen;
+          sshc->readdir_line = calloc(sshc->readdir_totalLen, 1);
+          if(!sshc->readdir_line) {
+            Curl_safefree(sshc->readdir_filename);
+            Curl_safefree(sshc->readdir_longentry);
+            state(conn, SSH_SFTP_CLOSE);
+            sshc->actualcode = CURLE_OUT_OF_MEMORY;
+            break;
+          }
+
+          memcpy(sshc->readdir_line, sshc->readdir_longentry,
+                 sshc->readdir_currLen);
+          if((sshc->readdir_attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) &&
+             ((sshc->readdir_attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
+              LIBSSH2_SFTP_S_IFLNK)) {
+            sshc->readdir_linkPath = malloc(PATH_MAX + 1);
+            if(sshc->readdir_linkPath == NULL) {
+              Curl_safefree(sshc->readdir_filename);
+              Curl_safefree(sshc->readdir_longentry);
+              state(conn, SSH_SFTP_CLOSE);
+              sshc->actualcode = CURLE_OUT_OF_MEMORY;
+              break;
+            }
+
+            snprintf(sshc->readdir_linkPath, PATH_MAX, "%s%s", sftp_scp->path,
+                     sshc->readdir_filename);
+            state(conn, SSH_SFTP_READDIR_LINK);
+            break;
+          }
+          state(conn, SSH_SFTP_READDIR_BOTTOM);
+          break;
+        }
+      }
+      else if(sshc->readdir_len == 0) {
+        Curl_safefree(sshc->readdir_filename);
+        Curl_safefree(sshc->readdir_longentry);
+        state(conn, SSH_SFTP_READDIR_DONE);
+        break;
+      }
+      else if(sshc->readdir_len <= 0) {
+        err = sftp_libssh2_last_error(sshc->sftp_session);
+        result = sftp_libssh2_error_to_CURLE(err);
+        sshc->actualcode = result?result:CURLE_SSH;
+        failf(data, "Could not open remote file for reading: %s :: %d",
+              sftp_libssh2_strerror(err),
+              libssh2_session_last_errno(sshc->ssh_session));
+        Curl_safefree(sshc->readdir_filename);
+        Curl_safefree(sshc->readdir_longentry);
+        state(conn, SSH_SFTP_CLOSE);
+        break;
+      }
+      break;
+
+    case SSH_SFTP_READDIR_LINK:
+      sshc->readdir_len =
+        libssh2_sftp_symlink_ex(sshc->sftp_session,
+                                sshc->readdir_linkPath,
+                                curlx_uztoui(strlen(sshc->readdir_linkPath)),
+                                sshc->readdir_filename,
+                                PATH_MAX, LIBSSH2_SFTP_READLINK);
+      if(sshc->readdir_len == LIBSSH2_ERROR_EAGAIN) {
+        rc = LIBSSH2_ERROR_EAGAIN;
+        break;
+      }
+      Curl_safefree(sshc->readdir_linkPath);
+
+      /* get room for the filename and extra output */
+      sshc->readdir_totalLen += 4 + sshc->readdir_len;
+      new_readdir_line = realloc(sshc->readdir_line, sshc->readdir_totalLen);
+      if(!new_readdir_line) {
+        Curl_safefree(sshc->readdir_line);
+        Curl_safefree(sshc->readdir_filename);
+        Curl_safefree(sshc->readdir_longentry);
+        state(conn, SSH_SFTP_CLOSE);
+        sshc->actualcode = CURLE_OUT_OF_MEMORY;
+        break;
+      }
+      sshc->readdir_line = new_readdir_line;
+
+      sshc->readdir_currLen += snprintf(sshc->readdir_line +
+                                        sshc->readdir_currLen,
+                                        sshc->readdir_totalLen -
+                                        sshc->readdir_currLen,
+                                        " -> %s",
+                                        sshc->readdir_filename);
+
+      state(conn, SSH_SFTP_READDIR_BOTTOM);
+      break;
+
+    case SSH_SFTP_READDIR_BOTTOM:
+      sshc->readdir_currLen += snprintf(sshc->readdir_line +
+                                        sshc->readdir_currLen,
+                                        sshc->readdir_totalLen -
+                                        sshc->readdir_currLen, "\n");
+      result = Curl_client_write(conn, CLIENTWRITE_BODY,
+                                 sshc->readdir_line,
+                                 sshc->readdir_currLen);
+
+      if(result == CURLE_OK) {
+
+        /* output debug output if that is requested */
+        if(data->set.verbose) {
+          Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_line,
+                     sshc->readdir_currLen, conn);
+        }
+        data->req.bytecount += sshc->readdir_currLen;
+      }
+      Curl_safefree(sshc->readdir_line);
+      if(result) {
+        state(conn, SSH_STOP);
+      }
+      else
+        state(conn, SSH_SFTP_READDIR);
+      break;
+
+    case SSH_SFTP_READDIR_DONE:
+      if(libssh2_sftp_closedir(sshc->sftp_handle) ==
+         LIBSSH2_ERROR_EAGAIN) {
+        rc = LIBSSH2_ERROR_EAGAIN;
+        break;
+      }
+      sshc->sftp_handle = NULL;
+      Curl_safefree(sshc->readdir_filename);
+      Curl_safefree(sshc->readdir_longentry);
+
+      /* no data to transfer */
+      Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+      state(conn, SSH_STOP);
+      break;
+
+    case SSH_SFTP_DOWNLOAD_INIT:
+      /*
+       * Work on getting the specified file
+       */
+      sshc->sftp_handle =
+        libssh2_sftp_open_ex(sshc->sftp_session, sftp_scp->path,
+                             curlx_uztoui(strlen(sftp_scp->path)),
+                             LIBSSH2_FXF_READ, data->set.new_file_perms,
+                             LIBSSH2_SFTP_OPENFILE);
+      if(!sshc->sftp_handle) {
+        if(libssh2_session_last_errno(sshc->ssh_session) ==
+           LIBSSH2_ERROR_EAGAIN) {
+          rc = LIBSSH2_ERROR_EAGAIN;
+          break;
+        }
+        else {
+          err = sftp_libssh2_last_error(sshc->sftp_session);
+          failf(data, "Could not open remote file for reading: %s",
+                sftp_libssh2_strerror(err));
+          state(conn, SSH_SFTP_CLOSE);
+          result = sftp_libssh2_error_to_CURLE(err);
+          sshc->actualcode = result?result:CURLE_SSH;
+          break;
+        }
+      }
+      state(conn, SSH_SFTP_DOWNLOAD_STAT);
+      break;
+
+    case SSH_SFTP_DOWNLOAD_STAT:
+    {
+      LIBSSH2_SFTP_ATTRIBUTES attrs;
+
+      rc = libssh2_sftp_stat_ex(sshc->sftp_session, sftp_scp->path,
+                                curlx_uztoui(strlen(sftp_scp->path)),
+                                LIBSSH2_SFTP_STAT, &attrs);
+      if(rc == LIBSSH2_ERROR_EAGAIN) {
+        break;
+      }
+      else if(rc) {
+        /*
+         * libssh2_sftp_open() didn't return an error, so maybe the server
+         * just doesn't support stat()
+         */
+        data->req.size = -1;
+        data->req.maxdownload = -1;
+      }
+      else {
+        curl_off_t size = attrs.filesize;
+
+        if(size < 0) {
+          failf(data, "Bad file size (%" FORMAT_OFF_T ")", size);
+          return CURLE_BAD_DOWNLOAD_RESUME;
+        }
+        if(conn->data->state.use_range) {
+          curl_off_t from, to;
+          char *ptr;
+          char *ptr2;
+
+          from=curlx_strtoofft(conn->data->state.range, &ptr, 0);
+          while(*ptr && (ISSPACE(*ptr) || (*ptr=='-')))
+            ptr++;
+          to=curlx_strtoofft(ptr, &ptr2, 0);
+          if((ptr == ptr2) /* no "to" value given */
+             || (to >= size)) {
+            to = size - 1;
+          }
+          if(from < 0) {
+            /* from is relative to end of file */
+            from += size;
+          }
+          if(from >= size) {
+            failf(data, "Offset (%"
+                  FORMAT_OFF_T ") was beyond file size (%" FORMAT_OFF_T ")",
+                  from, attrs.filesize);
+            return CURLE_BAD_DOWNLOAD_RESUME;
+          }
+          if(from > to) {
+            from = to;
+            size = 0;
+          }
+          else {
+            size = to - from + 1;
+          }
+
+          SFTP_SEEK(conn->proto.sshc.sftp_handle, from);
+        }
+        data->req.size = size;
+        data->req.maxdownload = size;
+        Curl_pgrsSetDownloadSize(data, size);
+      }
+
+      /* We can resume if we can seek to the resume position */
+      if(data->state.resume_from) {
+        if(data->state.resume_from < 0) {
+          /* We're supposed to download the last abs(from) bytes */
+          if((curl_off_t)attrs.filesize < -data->state.resume_from) {
+            failf(data, "Offset (%"
+                  FORMAT_OFF_T ") was beyond file size (%" FORMAT_OFF_T ")",
+                  data->state.resume_from, attrs.filesize);
+            return CURLE_BAD_DOWNLOAD_RESUME;
+          }
+          /* download from where? */
+          data->state.resume_from += attrs.filesize;
+        }
+        else {
+          if((curl_off_t)attrs.filesize < data->state.resume_from) {
+            failf(data, "Offset (%" FORMAT_OFF_T
+                  ") was beyond file size (%" FORMAT_OFF_T ")",
+                  data->state.resume_from, attrs.filesize);
+            return CURLE_BAD_DOWNLOAD_RESUME;
+          }
+        }
+        /* Does a completed file need to be seeked and started or closed ? */
+        /* Now store the number of bytes we are expected to download */
+        data->req.size = attrs.filesize - data->state.resume_from;
+        data->req.maxdownload = attrs.filesize - data->state.resume_from;
+        Curl_pgrsSetDownloadSize(data,
+                                 attrs.filesize - data->state.resume_from);
+        SFTP_SEEK(sshc->sftp_handle, data->state.resume_from);
+      }
+    }
+    /* Setup the actual download */
+    if(data->req.size == 0) {
+      /* no data to transfer */
+      Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+      infof(data, "File already completely downloaded\n");
+      state(conn, SSH_STOP);
+      break;
+    }
+    else {
+      Curl_setup_transfer(conn, FIRSTSOCKET, data->req.size,
+                          FALSE, NULL, -1, NULL);
+
+      /* not set by Curl_setup_transfer to preserve keepon bits */
+      conn->writesockfd = conn->sockfd;
+
+      /* we want to use the _receiving_ function even when the socket turns
+         out writableable as the underlying libssh2 recv function will deal
+         with both accordingly */
+      conn->cselect_bits = CURL_CSELECT_IN;
+    }
+    if(result) {
+      state(conn, SSH_SFTP_CLOSE);
+      sshc->actualcode = result;
+    }
+    else {
+      state(conn, SSH_STOP);
+    }
+    break;
+
+    case SSH_SFTP_CLOSE:
+      if(sshc->sftp_handle) {
+        rc = libssh2_sftp_close(sshc->sftp_handle);
+        if(rc == LIBSSH2_ERROR_EAGAIN) {
+          break;
+        }
+        else if(rc < 0) {
+          infof(data, "Failed to close libssh2 file\n");
+        }
+        sshc->sftp_handle = NULL;
+      }
+      if(sftp_scp)
+        Curl_safefree(sftp_scp->path);
+
+      DEBUGF(infof(data, "SFTP DONE done\n"));
+
+      /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT
+         After nextstate is executed,the control should come back to
+         SSH_SFTP_CLOSE to pass the correct result back  */
+      if(sshc->nextstate != SSH_NO_STATE) {
+        state(conn, sshc->nextstate);
+        sshc->nextstate = SSH_SFTP_CLOSE;
+      }
+      else {
+        state(conn, SSH_STOP);
+        result = sshc->actualcode;
+      }
+      break;
+
+    case SSH_SFTP_SHUTDOWN:
+      /* during times we get here due to a broken transfer and then the
+         sftp_handle might not have been taken down so make sure that is done
+         before we proceed */
+
+      if(sshc->sftp_handle) {
+        rc = libssh2_sftp_close(sshc->sftp_handle);
+        if(rc == LIBSSH2_ERROR_EAGAIN) {
+          break;
+        }
+        else if(rc < 0) {
+          infof(data, "Failed to close libssh2 file\n");
+        }
+        sshc->sftp_handle = NULL;
+      }
+      if(sshc->sftp_session) {
+        rc = libssh2_sftp_shutdown(sshc->sftp_session);
+        if(rc == LIBSSH2_ERROR_EAGAIN) {
+          break;
+        }
+        else if(rc < 0) {
+          infof(data, "Failed to stop libssh2 sftp subsystem\n");
+        }
+        sshc->sftp_session = NULL;
+      }
+
+      Curl_safefree(sshc->homedir);
+      conn->data->state.most_recent_ftp_entrypath = NULL;
+
+      state(conn, SSH_SESSION_DISCONNECT);
+      break;
+
+    case SSH_SCP_TRANS_INIT:
+      result = ssh_getworkingpath(conn, sshc->homedir, &sftp_scp->path);
+      if(result) {
+        sshc->actualcode = result;
+        state(conn, SSH_STOP);
+        break;
+      }
+
+      if(data->set.upload) {
+        if(data->set.infilesize < 0) {
+          failf(data, "SCP requires a known file size for upload");
+          sshc->actualcode = CURLE_UPLOAD_FAILED;
+          state(conn, SSH_SCP_CHANNEL_FREE);
+          break;
+        }
+        state(conn, SSH_SCP_UPLOAD_INIT);
+      }
+      else {
+        state(conn, SSH_SCP_DOWNLOAD_INIT);
+      }
+      break;
+
+    case SSH_SCP_UPLOAD_INIT:
+      /*
+       * libssh2 requires that the destination path is a full path that
+       * includes the destination file and name OR ends in a "/" .  If this is
+       * not done the destination file will be named the same name as the last
+       * directory in the path.
+       */
+      sshc->ssh_channel =
+        SCP_SEND(sshc->ssh_session, sftp_scp->path, data->set.new_file_perms,
+                 data->set.infilesize);
+      if(!sshc->ssh_channel) {
+        if(libssh2_session_last_errno(sshc->ssh_session) ==
+           LIBSSH2_ERROR_EAGAIN) {
+          rc = LIBSSH2_ERROR_EAGAIN;
+          break;
+        }
+        else {
+          int ssh_err;
+          char *err_msg;
+
+          ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session,
+                                                     &err_msg, NULL, 0));
+          failf(conn->data, "%s", err_msg);
+          state(conn, SSH_SCP_CHANNEL_FREE);
+          sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err);
+          break;
+        }
+      }
+
+      /* upload data */
+      Curl_setup_transfer(conn, -1, data->req.size, FALSE, NULL,
+                          FIRSTSOCKET, NULL);
+
+      /* not set by Curl_setup_transfer to preserve keepon bits */
+      conn->sockfd = conn->writesockfd;
+
+      if(result) {
+        state(conn, SSH_SCP_CHANNEL_FREE);
+        sshc->actualcode = result;
+      }
+      else {
+        /* we want to use the _sending_ function even when the socket turns
+           out readable as the underlying libssh2 scp send function will deal
+           with both accordingly */
+        conn->cselect_bits = CURL_CSELECT_OUT;
+
+        state(conn, SSH_STOP);
+      }
+      break;
+
+    case SSH_SCP_DOWNLOAD_INIT:
+    {
+      /*
+       * We must check the remote file; if it is a directory no values will
+       * be set in sb
+       */
+      struct stat sb;
+      curl_off_t bytecount;
+
+      /* clear the struct scp recv will fill in */
+      memset(&sb, 0, sizeof(struct stat));
+
+      /* get a fresh new channel from the ssh layer */
+      sshc->ssh_channel = libssh2_scp_recv(sshc->ssh_session,
+                                           sftp_scp->path, &sb);
+      if(!sshc->ssh_channel) {
+        if(libssh2_session_last_errno(sshc->ssh_session) ==
+           LIBSSH2_ERROR_EAGAIN) {
+          rc = LIBSSH2_ERROR_EAGAIN;
+          break;
+        }
+        else {
+          int ssh_err;
+          char *err_msg;
+
+          ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session,
+                                                     &err_msg, NULL, 0));
+          failf(conn->data, "%s", err_msg);
+          state(conn, SSH_SCP_CHANNEL_FREE);
+          sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err);
+          break;
+        }
+      }
+
+      /* download data */
+      bytecount = (curl_off_t)sb.st_size;
+      data->req.maxdownload =  (curl_off_t)sb.st_size;
+      Curl_setup_transfer(conn, FIRSTSOCKET, bytecount, FALSE, NULL, -1, NULL);
+
+      /* not set by Curl_setup_transfer to preserve keepon bits */
+      conn->writesockfd = conn->sockfd;
+
+      /* we want to use the _receiving_ function even when the socket turns
+         out writableable as the underlying libssh2 recv function will deal
+         with both accordingly */
+      conn->cselect_bits = CURL_CSELECT_IN;
+
+      if(result) {
+        state(conn, SSH_SCP_CHANNEL_FREE);
+        sshc->actualcode = result;
+      }
+      else
+        state(conn, SSH_STOP);
+    }
+    break;
+
+    case SSH_SCP_DONE:
+      if(data->set.upload)
+        state(conn, SSH_SCP_SEND_EOF);
+      else
+        state(conn, SSH_SCP_CHANNEL_FREE);
+      break;
+
+    case SSH_SCP_SEND_EOF:
+      if(sshc->ssh_channel) {
+        rc = libssh2_channel_send_eof(sshc->ssh_channel);
+        if(rc == LIBSSH2_ERROR_EAGAIN) {
+          break;
+        }
+        else if(rc) {
+          infof(data, "Failed to send libssh2 channel EOF\n");
+        }
+      }
+      state(conn, SSH_SCP_WAIT_EOF);
+      break;
+
+    case SSH_SCP_WAIT_EOF:
+      if(sshc->ssh_channel) {
+        rc = libssh2_channel_wait_eof(sshc->ssh_channel);
+        if(rc == LIBSSH2_ERROR_EAGAIN) {
+          break;
+        }
+        else if(rc) {
+          infof(data, "Failed to get channel EOF: %d\n", rc);
+        }
+      }
+      state(conn, SSH_SCP_WAIT_CLOSE);
+      break;
+
+    case SSH_SCP_WAIT_CLOSE:
+      if(sshc->ssh_channel) {
+        rc = libssh2_channel_wait_closed(sshc->ssh_channel);
+        if(rc == LIBSSH2_ERROR_EAGAIN) {
+          break;
+        }
+        else if(rc) {
+          infof(data, "Channel failed to close: %d\n", rc);
+        }
+      }
+      state(conn, SSH_SCP_CHANNEL_FREE);
+      break;
+
+    case SSH_SCP_CHANNEL_FREE:
+      if(sshc->ssh_channel) {
+        rc = libssh2_channel_free(sshc->ssh_channel);
+        if(rc == LIBSSH2_ERROR_EAGAIN) {
+          break;
+        }
+        else if(rc < 0) {
+          infof(data, "Failed to free libssh2 scp subsystem\n");
+        }
+        sshc->ssh_channel = NULL;
+      }
+      DEBUGF(infof(data, "SCP DONE phase complete\n"));
+#if 0 /* PREV */
+      state(conn, SSH_SESSION_DISCONNECT);
+#endif
+      state(conn, SSH_STOP);
+      result = sshc->actualcode;
+      break;
+
+    case SSH_SESSION_DISCONNECT:
+      /* during weird times when we've been prematurely aborted, the channel
+         is still alive when we reach this state and we MUST kill the channel
+         properly first */
+      if(sshc->ssh_channel) {
+        rc = libssh2_channel_free(sshc->ssh_channel);
+        if(rc == LIBSSH2_ERROR_EAGAIN) {
+          break;
+        }
+        else if(rc < 0) {
+          infof(data, "Failed to free libssh2 scp subsystem\n");
+        }
+        sshc->ssh_channel = NULL;
+      }
+
+      if(sshc->ssh_session) {
+        rc = libssh2_session_disconnect(sshc->ssh_session, "Shutdown");
+        if(rc == LIBSSH2_ERROR_EAGAIN) {
+          break;
+        }
+        else if(rc < 0) {
+          infof(data, "Failed to disconnect libssh2 session\n");
+        }
+      }
+
+      Curl_safefree(sshc->homedir);
+      conn->data->state.most_recent_ftp_entrypath = NULL;
+
+      state(conn, SSH_SESSION_FREE);
+      break;
+
+    case SSH_SESSION_FREE:
+#ifdef HAVE_LIBSSH2_KNOWNHOST_API
+      if(sshc->kh) {
+        libssh2_knownhost_free(sshc->kh);
+        sshc->kh = NULL;
+      }
+#endif
+
+#ifdef HAVE_LIBSSH2_AGENT_API
+      if(sshc->ssh_agent) {
+        rc = libssh2_agent_disconnect(sshc->ssh_agent);
+        if(rc == LIBSSH2_ERROR_EAGAIN) {
+          break;
+        }
+        else if(rc < 0) {
+          infof(data, "Failed to disconnect from libssh2 agent\n");
+        }
+        libssh2_agent_free (sshc->ssh_agent);
+        sshc->ssh_agent = NULL;
+
+        /* NB: there is no need to free identities, they are part of internal
+           agent stuff */
+        sshc->sshagent_identity = NULL;
+        sshc->sshagent_prev_identity = NULL;
+      }
+#endif
+
+      if(sshc->ssh_session) {
+        rc = libssh2_session_free(sshc->ssh_session);
+        if(rc == LIBSSH2_ERROR_EAGAIN) {
+          break;
+        }
+        else if(rc < 0) {
+          infof(data, "Failed to free libssh2 session\n");
+        }
+        sshc->ssh_session = NULL;
+      }
+
+      /* worst-case scenario cleanup */
+
+      DEBUGASSERT(sshc->ssh_session == NULL);
+      DEBUGASSERT(sshc->ssh_channel == NULL);
+      DEBUGASSERT(sshc->sftp_session == NULL);
+      DEBUGASSERT(sshc->sftp_handle == NULL);
+#ifdef HAVE_LIBSSH2_KNOWNHOST_API
+      DEBUGASSERT(sshc->kh == NULL);
+#endif
+#ifdef HAVE_LIBSSH2_AGENT_API
+      DEBUGASSERT(sshc->ssh_agent == NULL);
+#endif
+
+      Curl_safefree(sshc->rsa_pub);
+      Curl_safefree(sshc->rsa);
+
+      Curl_safefree(sshc->quote_path1);
+      Curl_safefree(sshc->quote_path2);
+
+      Curl_safefree(sshc->homedir);
+
+      Curl_safefree(sshc->readdir_filename);
+      Curl_safefree(sshc->readdir_longentry);
+      Curl_safefree(sshc->readdir_line);
+      Curl_safefree(sshc->readdir_linkPath);
+
+      /* the code we are about to return */
+      result = sshc->actualcode;
+
+      memset(sshc, 0, sizeof(struct ssh_conn));
+
+      conn->bits.close = TRUE;
+      sshc->state = SSH_SESSION_FREE; /* current */
+      sshc->nextstate = SSH_NO_STATE;
+      state(conn, SSH_STOP);
+      break;
+
+    case SSH_QUIT:
+      /* fallthrough, just stop! */
+    default:
+      /* internal error */
+      sshc->nextstate = SSH_NO_STATE;
+      state(conn, SSH_STOP);
+      break;
+    }
+
+  } while(!rc && (sshc->state != SSH_STOP));
+
+  if(rc == LIBSSH2_ERROR_EAGAIN) {
+    /* we would block, we need to wait for the socket to be ready (in the
+       right direction too)! */
+    *block = TRUE;
+  }
+
+  return result;
+}
+
+/* called by the multi interface to figure out what socket(s) to wait for and
+   for what actions in the DO_DONE, PERFORM and WAITPERFORM states */
+static int ssh_perform_getsock(const struct connectdata *conn,
+                               curl_socket_t *sock, /* points to numsocks
+                                                       number of sockets */
+                               int numsocks)
+{
+#ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
+  int bitmap = GETSOCK_BLANK;
+  (void)numsocks;
+
+  sock[0] = conn->sock[FIRSTSOCKET];
+
+  if(conn->waitfor & KEEP_RECV)
+    bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
+
+  if(conn->waitfor & KEEP_SEND)
+    bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
+
+  return bitmap;
+#else
+  /* if we don't know the direction we can use the generic *_getsock()
+     function even for the protocol_connect and doing states */
+  return Curl_single_getsock(conn, sock, numsocks);
+#endif
+}
+
+/* Generic function called by the multi interface to figure out what socket(s)
+   to wait for and for what actions during the DOING and PROTOCONNECT states*/
+static int ssh_getsock(struct connectdata *conn,
+                       curl_socket_t *sock, /* points to numsocks number
+                                               of sockets */
+                       int numsocks)
+{
+#ifndef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
+  (void)conn;
+  (void)sock;
+  (void)numsocks;
+  /* if we don't know any direction we can just play along as we used to and
+     not provide any sensible info */
+  return GETSOCK_BLANK;
+#else
+  /* if we know the direction we can use the generic *_getsock() function even
+     for the protocol_connect and doing states */
+  return ssh_perform_getsock(conn, sock, numsocks);
+#endif
+}
+
+#ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
+/*
+ * When one of the libssh2 functions has returned LIBSSH2_ERROR_EAGAIN this
+ * function is used to figure out in what direction and stores this info so
+ * that the multi interface can take advantage of it. Make sure to call this
+ * function in all cases so that when it _doesn't_ return EAGAIN we can
+ * restore the default wait bits.
+ */
+static void ssh_block2waitfor(struct connectdata *conn, bool block)
+{
+  struct ssh_conn *sshc = &conn->proto.sshc;
+  int dir;
+  if(!block)
+    conn->waitfor = 0;
+  else if((dir = libssh2_session_block_directions(sshc->ssh_session))) {
+    /* translate the libssh2 define bits into our own bit defines */
+    conn->waitfor = ((dir&LIBSSH2_SESSION_BLOCK_INBOUND)?KEEP_RECV:0) |
+      ((dir&LIBSSH2_SESSION_BLOCK_OUTBOUND)?KEEP_SEND:0);
+  }
+  else
+    /* It didn't block or libssh2 didn't reveal in which direction, put back
+       the original set */
+    conn->waitfor = sshc->orig_waitfor;
+}
+#else
+  /* no libssh2 directional support so we simply don't know */
+#define ssh_block2waitfor(x,y) Curl_nop_stmt
+#endif
+
+/* called repeatedly until done from curl_multi.c */
+static CURLcode ssh_multi_statemach(struct connectdata *conn, bool *done)
+{
+  struct ssh_conn *sshc = &conn->proto.sshc;
+  CURLcode result = CURLE_OK;
+  bool block; /* we store the status and use that to provide a ssh_getsock()
+                 implementation */
+
+  result = ssh_statemach_act(conn, &block);
+  *done = (sshc->state == SSH_STOP) ? TRUE : FALSE;
+  ssh_block2waitfor(conn, block);
+
+  return result;
+}
+
+static CURLcode ssh_easy_statemach(struct connectdata *conn,
+                                   bool duringconnect)
+{
+  struct ssh_conn *sshc = &conn->proto.sshc;
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+
+  while((sshc->state != SSH_STOP) && !result) {
+    bool block;
+    long left;
+
+    result = ssh_statemach_act(conn, &block);
+    if(result)
+      break;
+
+    if(Curl_pgrsUpdate(conn))
+      return CURLE_ABORTED_BY_CALLBACK;
+    else {
+      struct timeval now = Curl_tvnow();
+      result = Curl_speedcheck(data, now);
+      if(result)
+        break;
+    }
+
+    left = Curl_timeleft(data, NULL, duringconnect);
+    if(left < 0) {
+      failf(data, "Operation timed out");
+      return CURLE_OPERATION_TIMEDOUT;
+    }
+
+#ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
+    if((CURLE_OK == result) && block) {
+      int dir = libssh2_session_block_directions(sshc->ssh_session);
+      curl_socket_t sock = conn->sock[FIRSTSOCKET];
+      curl_socket_t fd_read = CURL_SOCKET_BAD;
+      curl_socket_t fd_write = CURL_SOCKET_BAD;
+      if(LIBSSH2_SESSION_BLOCK_INBOUND & dir)
+        fd_read = sock;
+      if(LIBSSH2_SESSION_BLOCK_OUTBOUND & dir)
+        fd_write = sock;
+      /* wait for the socket to become ready */
+      Curl_socket_ready(fd_read, fd_write,
+                        left>1000?1000:left); /* ignore result */
+    }
+#endif
+
+  }
+
+  return result;
+}
+
+/*
+ * SSH setup and connection
+ */
+static CURLcode ssh_init(struct connectdata *conn)
+{
+  struct SessionHandle *data = conn->data;
+  struct SSHPROTO *ssh;
+  struct ssh_conn *sshc = &conn->proto.sshc;
+
+  sshc->actualcode = CURLE_OK; /* reset error code */
+  sshc->secondCreateDirs =0;   /* reset the create dir attempt state
+                                  variable */
+
+  if(data->state.proto.ssh)
+    return CURLE_OK;
+
+  ssh = calloc(1, sizeof(struct SSHPROTO));
+  if(!ssh)
+    return CURLE_OUT_OF_MEMORY;
+
+  data->state.proto.ssh = ssh;
+
+  return CURLE_OK;
+}
+
+static Curl_recv scp_recv, sftp_recv;
+static Curl_send scp_send, sftp_send;
+
+/*
+ * Curl_ssh_connect() gets called from Curl_protocol_connect() to allow us to
+ * do protocol-specific actions at connect-time.
+ */
+static CURLcode ssh_connect(struct connectdata *conn, bool *done)
+{
+#ifdef CURL_LIBSSH2_DEBUG
+  curl_socket_t sock;
+#endif
+  struct ssh_conn *ssh;
+  CURLcode result;
+  struct SessionHandle *data = conn->data;
+
+  /* We default to persistent connections. We set this already in this connect
+     function to make the re-use checks properly be able to check this bit. */
+  conn->bits.close = FALSE;
+
+  /* If there already is a protocol-specific struct allocated for this
+     sessionhandle, deal with it */
+  Curl_reset_reqproto(conn);
+
+  result = ssh_init(conn);
+  if(result)
+    return result;
+
+  if(conn->handler->protocol & CURLPROTO_SCP) {
+    conn->recv[FIRSTSOCKET] = scp_recv;
+    conn->send[FIRSTSOCKET] = scp_send;
+  }
+  else {
+    conn->recv[FIRSTSOCKET] = sftp_recv;
+    conn->send[FIRSTSOCKET] = sftp_send;
+  }
+  ssh = &conn->proto.sshc;
+
+#ifdef CURL_LIBSSH2_DEBUG
+  if(conn->user) {
+    infof(data, "User: %s\n", conn->user);
+  }
+  if(conn->passwd) {
+    infof(data, "Password: %s\n", conn->passwd);
+  }
+  sock = conn->sock[FIRSTSOCKET];
+#endif /* CURL_LIBSSH2_DEBUG */
+
+  ssh->ssh_session = libssh2_session_init_ex(my_libssh2_malloc,
+                                             my_libssh2_free,
+                                             my_libssh2_realloc, conn);
+  if(ssh->ssh_session == NULL) {
+    failf(data, "Failure initialising ssh session");
+    return CURLE_FAILED_INIT;
+  }
+
+#ifdef HAVE_LIBSSH2_KNOWNHOST_API
+  if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
+    int rc;
+    ssh->kh = libssh2_knownhost_init(ssh->ssh_session);
+    if(!ssh->kh) {
+      /* eeek. TODO: free the ssh_session! */
+      return CURLE_FAILED_INIT;
+    }
+
+    /* read all known hosts from there */
+    rc = libssh2_knownhost_readfile(ssh->kh,
+                                    data->set.str[STRING_SSH_KNOWNHOSTS],
+                                    LIBSSH2_KNOWNHOST_FILE_OPENSSH);
+    if(rc < 0)
+      infof(data, "Failed to read known hosts from %s\n",
+            data->set.str[STRING_SSH_KNOWNHOSTS]);
+  }
+#endif /* HAVE_LIBSSH2_KNOWNHOST_API */
+
+#ifdef CURL_LIBSSH2_DEBUG
+  libssh2_trace(ssh->ssh_session, ~0);
+  infof(data, "SSH socket: %d\n", (int)sock);
+#endif /* CURL_LIBSSH2_DEBUG */
+
+  state(conn, SSH_INIT);
+
+  if(data->state.used_interface == Curl_if_multi)
+    result = ssh_multi_statemach(conn, done);
+  else {
+    result = ssh_easy_statemach(conn, TRUE);
+    if(!result)
+      *done = TRUE;
+  }
+
+  return result;
+}
+
+/*
+ ***********************************************************************
+ *
+ * scp_perform()
+ *
+ * This is the actual DO function for SCP. Get a file according to
+ * the options previously setup.
+ */
+
+static
+CURLcode scp_perform(struct connectdata *conn,
+                      bool *connected,
+                      bool *dophase_done)
+{
+  CURLcode result = CURLE_OK;
+
+  DEBUGF(infof(conn->data, "DO phase starts\n"));
+
+  *dophase_done = FALSE; /* not done yet */
+
+  /* start the first command in the DO phase */
+  state(conn, SSH_SCP_TRANS_INIT);
+
+  /* run the state-machine */
+  if(conn->data->state.used_interface == Curl_if_multi) {
+    result = ssh_multi_statemach(conn, dophase_done);
+  }
+  else {
+    result = ssh_easy_statemach(conn, FALSE);
+    *dophase_done = TRUE; /* with the easy interface we are done here */
+  }
+  *connected = conn->bits.tcpconnect[FIRSTSOCKET];
+
+  if(*dophase_done) {
+    DEBUGF(infof(conn->data, "DO phase is complete\n"));
+  }
+
+  return result;
+}
+
+/* called from curl_multi.c while DOing */
+static CURLcode scp_doing(struct connectdata *conn,
+                               bool *dophase_done)
+{
+  CURLcode result;
+  result = ssh_multi_statemach(conn, dophase_done);
+
+  if(*dophase_done) {
+    DEBUGF(infof(conn->data, "DO phase is complete\n"));
+  }
+  return result;
+}
+
+/*
+ * The DO function is generic for both protocols. There was previously two
+ * separate ones but this way means less duplicated code.
+ */
+
+static CURLcode ssh_do(struct connectdata *conn, bool *done)
+{
+  CURLcode res;
+  bool connected = 0;
+  struct SessionHandle *data = conn->data;
+
+  *done = FALSE; /* default to false */
+
+  /*
+    Since connections can be re-used between SessionHandles, this might be a
+    connection already existing but on a fresh SessionHandle struct so we must
+    make sure we have a good 'struct SSHPROTO' to play with. For new
+    connections, the struct SSHPROTO is allocated and setup in the
+    ssh_connect() function.
+  */
+  Curl_reset_reqproto(conn);
+  res = ssh_init(conn);
+  if(res)
+    return res;
+
+  data->req.size = -1; /* make sure this is unknown at this point */
+
+  Curl_pgrsSetUploadCounter(data, 0);
+  Curl_pgrsSetDownloadCounter(data, 0);
+  Curl_pgrsSetUploadSize(data, 0);
+  Curl_pgrsSetDownloadSize(data, 0);
+
+  if(conn->handler->protocol & CURLPROTO_SCP)
+    res = scp_perform(conn, &connected,  done);
+  else
+    res = sftp_perform(conn, &connected,  done);
+
+  return res;
+}
+
+/* BLOCKING, but the function is using the state machine so the only reason
+   this is still blocking is that the multi interface code has no support for
+   disconnecting operations that takes a while */
+static CURLcode scp_disconnect(struct connectdata *conn, bool dead_connection)
+{
+  CURLcode result = CURLE_OK;
+  struct ssh_conn *ssh = &conn->proto.sshc;
+  (void) dead_connection;
+
+  Curl_safefree(conn->data->state.proto.ssh);
+
+  if(ssh->ssh_session) {
+    /* only if there's a session still around to use! */
+
+    state(conn, SSH_SESSION_DISCONNECT);
+
+    result = ssh_easy_statemach(conn, FALSE);
+  }
+
+  return result;
+}
+
+/* generic done function for both SCP and SFTP called from their specific
+   done functions */
+static CURLcode ssh_done(struct connectdata *conn, CURLcode status)
+{
+  CURLcode result = CURLE_OK;
+  struct SSHPROTO *sftp_scp = conn->data->state.proto.ssh;
+
+  if(status == CURLE_OK) {
+    /* run the state-machine
+
+       TODO: when the multi interface is used, this _really_ should be using
+       the ssh_multi_statemach function but we have no general support for
+       non-blocking DONE operations, not in the multi state machine and with
+       Curl_done() invokes on several places in the code!
+    */
+    result = ssh_easy_statemach(conn, FALSE);
+  }
+  else
+    result = status;
+
+  if(sftp_scp)
+    Curl_safefree(sftp_scp->path);
+  if(Curl_pgrsDone(conn))
+    return CURLE_ABORTED_BY_CALLBACK;
+
+  conn->data->req.keepon = 0; /* clear all bits */
+  return result;
+}
+
+
+static CURLcode scp_done(struct connectdata *conn, CURLcode status,
+                         bool premature)
+{
+  (void)premature; /* not used */
+
+  if(status == CURLE_OK)
+    state(conn, SSH_SCP_DONE);
+
+  return ssh_done(conn, status);
+
+}
+
+/* return number of received (decrypted) bytes */
+static ssize_t scp_send(struct connectdata *conn, int sockindex,
+                        const void *mem, size_t len, CURLcode *err)
+{
+  ssize_t nwrite;
+  (void)sockindex; /* we only support SCP on the fixed known primary socket */
+
+  /* libssh2_channel_write() returns int! */
+  nwrite = (ssize_t)
+    libssh2_channel_write(conn->proto.sshc.ssh_channel, mem, len);
+
+  ssh_block2waitfor(conn, (nwrite == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE);
+
+  if(nwrite == LIBSSH2_ERROR_EAGAIN) {
+    *err = CURLE_AGAIN;
+    nwrite = 0;
+  }
+  else if(nwrite < LIBSSH2_ERROR_NONE) {
+    *err = libssh2_session_error_to_CURLE((int)nwrite);
+    nwrite = -1;
+  }
+
+  return nwrite;
+}
+
+/*
+ * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
+ * a regular CURLcode value.
+ */
+static ssize_t scp_recv(struct connectdata *conn, int sockindex,
+                        char *mem, size_t len, CURLcode *err)
+{
+  ssize_t nread;
+  (void)sockindex; /* we only support SCP on the fixed known primary socket */
+
+  /* libssh2_channel_read() returns int */
+  nread = (ssize_t)
+    libssh2_channel_read(conn->proto.sshc.ssh_channel, mem, len);
+
+  ssh_block2waitfor(conn, (nread == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE);
+  if(nread == LIBSSH2_ERROR_EAGAIN) {
+    *err = CURLE_AGAIN;
+    nread = -1;
+  }
+
+  return nread;
+}
+
+/*
+ * =============== SFTP ===============
+ */
+
+/*
+ ***********************************************************************
+ *
+ * sftp_perform()
+ *
+ * This is the actual DO function for SFTP. Get a file/directory according to
+ * the options previously setup.
+ */
+
+static
+CURLcode sftp_perform(struct connectdata *conn,
+                      bool *connected,
+                      bool *dophase_done)
+{
+  CURLcode result = CURLE_OK;
+
+  DEBUGF(infof(conn->data, "DO phase starts\n"));
+
+  *dophase_done = FALSE; /* not done yet */
+
+  /* start the first command in the DO phase */
+  state(conn, SSH_SFTP_QUOTE_INIT);
+
+  /* run the state-machine */
+  if(conn->data->state.used_interface == Curl_if_multi) {
+    result = ssh_multi_statemach(conn, dophase_done);
+  }
+  else {
+    result = ssh_easy_statemach(conn, FALSE);
+    *dophase_done = TRUE; /* with the easy interface we are done here */
+  }
+  *connected = conn->bits.tcpconnect[FIRSTSOCKET];
+
+  if(*dophase_done) {
+    DEBUGF(infof(conn->data, "DO phase is complete\n"));
+  }
+
+  return result;
+}
+
+/* called from curl_multi.c while DOing */
+static CURLcode sftp_doing(struct connectdata *conn,
+                           bool *dophase_done)
+{
+  CURLcode result;
+  result = ssh_multi_statemach(conn, dophase_done);
+
+  if(*dophase_done) {
+    DEBUGF(infof(conn->data, "DO phase is complete\n"));
+  }
+  return result;
+}
+
+/* BLOCKING, but the function is using the state machine so the only reason
+   this is still blocking is that the multi interface code has no support for
+   disconnecting operations that takes a while */
+static CURLcode sftp_disconnect(struct connectdata *conn, bool dead_connection)
+{
+  CURLcode result = CURLE_OK;
+  (void) dead_connection;
+
+  DEBUGF(infof(conn->data, "SSH DISCONNECT starts now\n"));
+
+  Curl_safefree(conn->data->state.proto.ssh);
+
+  if(conn->proto.sshc.ssh_session) {
+    /* only if there's a session still around to use! */
+    state(conn, SSH_SFTP_SHUTDOWN);
+    result = ssh_easy_statemach(conn, FALSE);
+  }
+
+  DEBUGF(infof(conn->data, "SSH DISCONNECT is done\n"));
+
+  return result;
+
+}
+
+static CURLcode sftp_done(struct connectdata *conn, CURLcode status,
+                               bool premature)
+{
+  struct ssh_conn *sshc = &conn->proto.sshc;
+
+  if(status == CURLE_OK) {
+    /* Post quote commands are executed after the SFTP_CLOSE state to avoid
+       errors that could happen due to open file handles during POSTQUOTE
+       operation */
+    if(!status && !premature && conn->data->set.postquote) {
+      sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT;
+      state(conn, SSH_SFTP_CLOSE);
+    }
+    else
+      state(conn, SSH_SFTP_CLOSE);
+  }
+  return ssh_done(conn, status);
+}
+
+/* return number of sent bytes */
+static ssize_t sftp_send(struct connectdata *conn, int sockindex,
+                         const void *mem, size_t len, CURLcode *err)
+{
+  ssize_t nwrite;   /* libssh2_sftp_write() used to return size_t in 0.14
+                       but is changed to ssize_t in 0.15. These days we don't
+                       support libssh2 0.15*/
+  (void)sockindex;
+
+  nwrite = libssh2_sftp_write(conn->proto.sshc.sftp_handle, mem, len);
+
+  ssh_block2waitfor(conn, (nwrite == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE);
+
+  if(nwrite == LIBSSH2_ERROR_EAGAIN) {
+    *err = CURLE_AGAIN;
+    nwrite = 0;
+  }
+  else if(nwrite < LIBSSH2_ERROR_NONE) {
+    *err = libssh2_session_error_to_CURLE((int)nwrite);
+    nwrite = -1;
+  }
+
+  return nwrite;
+}
+
+/*
+ * Return number of received (decrypted) bytes
+ */
+static ssize_t sftp_recv(struct connectdata *conn, int sockindex,
+                         char *mem, size_t len, CURLcode *err)
+{
+  ssize_t nread;
+  (void)sockindex;
+
+  nread = libssh2_sftp_read(conn->proto.sshc.sftp_handle, mem, len);
+
+  ssh_block2waitfor(conn, (nread == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE);
+
+  if(nread == LIBSSH2_ERROR_EAGAIN) {
+    *err = CURLE_AGAIN;
+    nread = -1;
+  }
+  return nread;
+}
+
+/* The get_pathname() function is being borrowed from OpenSSH-sftp.c
+   version 4.6p1. */
+/*
+ * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+static CURLcode
+get_pathname(const char **cpp, char **path)
+{
+  const char *cp = *cpp, *end;
+  char quot;
+  unsigned int i, j;
+  static const char WHITESPACE[] = " \t\r\n";
+
+  cp += strspn(cp, WHITESPACE);
+  if(!*cp) {
+    *cpp = cp;
+    *path = NULL;
+    return CURLE_QUOTE_ERROR;
+  }
+
+  *path = malloc(strlen(cp) + 1);
+  if(*path == NULL)
+    return CURLE_OUT_OF_MEMORY;
+
+  /* Check for quoted filenames */
+  if(*cp == '\"' || *cp == '\'') {
+    quot = *cp++;
+
+    /* Search for terminating quote, unescape some chars */
+    for(i = j = 0; i <= strlen(cp); i++) {
+      if(cp[i] == quot) {  /* Found quote */
+        i++;
+        (*path)[j] = '\0';
+        break;
+      }
+      if(cp[i] == '\0') {  /* End of string */
+        /*error("Unterminated quote");*/
+        goto fail;
+      }
+      if(cp[i] == '\\') {  /* Escaped characters */
+        i++;
+        if(cp[i] != '\'' && cp[i] != '\"' &&
+            cp[i] != '\\') {
+          /*error("Bad escaped character '\\%c'",
+              cp[i]);*/
+          goto fail;
+        }
+      }
+      (*path)[j++] = cp[i];
+    }
+
+    if(j == 0) {
+      /*error("Empty quotes");*/
+      goto fail;
+    }
+    *cpp = cp + i + strspn(cp + i, WHITESPACE);
+  }
+  else {
+    /* Read to end of filename */
+    end = strpbrk(cp, WHITESPACE);
+    if(end == NULL)
+      end = strchr(cp, '\0');
+    *cpp = end + strspn(end, WHITESPACE);
+
+    memcpy(*path, cp, end - cp);
+    (*path)[end - cp] = '\0';
+  }
+  return CURLE_OK;
+
+  fail:
+    Curl_safefree(*path);
+    return CURLE_QUOTE_ERROR;
+}
+
+
+static const char *sftp_libssh2_strerror(int err)
+{
+  switch (err) {
+    case LIBSSH2_FX_NO_SUCH_FILE:
+      return "No such file or directory";
+
+    case LIBSSH2_FX_PERMISSION_DENIED:
+      return "Permission denied";
+
+    case LIBSSH2_FX_FAILURE:
+      return "Operation failed";
+
+    case LIBSSH2_FX_BAD_MESSAGE:
+      return "Bad message from SFTP server";
+
+    case LIBSSH2_FX_NO_CONNECTION:
+      return "Not connected to SFTP server";
+
+    case LIBSSH2_FX_CONNECTION_LOST:
+      return "Connection to SFTP server lost";
+
+    case LIBSSH2_FX_OP_UNSUPPORTED:
+      return "Operation not supported by SFTP server";
+
+    case LIBSSH2_FX_INVALID_HANDLE:
+      return "Invalid handle";
+
+    case LIBSSH2_FX_NO_SUCH_PATH:
+      return "No such file or directory";
+
+    case LIBSSH2_FX_FILE_ALREADY_EXISTS:
+      return "File already exists";
+
+    case LIBSSH2_FX_WRITE_PROTECT:
+      return "File is write protected";
+
+    case LIBSSH2_FX_NO_MEDIA:
+      return "No media";
+
+    case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM:
+      return "Disk full";
+
+    case LIBSSH2_FX_QUOTA_EXCEEDED:
+      return "User quota exceeded";
+
+    case LIBSSH2_FX_UNKNOWN_PRINCIPLE:
+      return "Unknown principle";
+
+    case LIBSSH2_FX_LOCK_CONFlICT:
+      return "File lock conflict";
+
+    case LIBSSH2_FX_DIR_NOT_EMPTY:
+      return "Directory not empty";
+
+    case LIBSSH2_FX_NOT_A_DIRECTORY:
+      return "Not a directory";
+
+    case LIBSSH2_FX_INVALID_FILENAME:
+      return "Invalid filename";
+
+    case LIBSSH2_FX_LINK_LOOP:
+      return "Link points to itself";
+  }
+  return "Unknown error in libssh2";
+}
+
+#endif /* USE_LIBSSH2 */
diff --git a/lib/curl_sslgen.c b/lib/curl_sslgen.c
new file mode 100644 (file)
index 0000000..d85ba8a
--- /dev/null
@@ -0,0 +1,541 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/* This file is for implementing all "generic" SSL functions that all libcurl
+   internals should use. It is then responsible for calling the proper
+   "backend" function.
+
+   SSL-functions in libcurl should call functions in this source file, and not
+   to any specific SSL-layer.
+
+   Curl_ssl_ - prefix for generic ones
+   Curl_ossl_ - prefix for OpenSSL ones
+   Curl_gtls_ - prefix for GnuTLS ones
+   Curl_nss_ - prefix for NSS ones
+   Curl_polarssl_ - prefix for PolarSSL ones
+   Curl_cyassl_ - prefix for CyaSSL ones
+   Curl_schannel_ - prefix for Schannel SSPI ones
+   Curl_darwinssl_ - prefix for SecureTransport (Darwin) ones
+
+   Note that this source code uses curlssl_* functions, and they are all
+   defines/macros #defined by the lib-specific header files.
+
+   "SSL/TLS Strong Encryption: An Introduction"
+   http://httpd.apache.org/docs-2.0/ssl/ssl_intro.html
+*/
+
+#include "curl_setup.h"
+
+#include "curl_urldata.h"
+#define SSLGEN_C
+#include "curl_sslgen.h" /* generic SSL protos etc */
+#include "curl_ssluse.h" /* OpenSSL versions */
+#include "curl_gtls.h"   /* GnuTLS versions */
+#include "curl_nssg.h"   /* NSS versions */
+#include "curl_qssl.h"   /* QSOSSL versions */
+#include "curl_polarssl.h" /* PolarSSL versions */
+#include "curl_axtls.h"  /* axTLS versions */
+#include "curl_cyassl.h"  /* CyaSSL versions */
+#include "curl_schannel.h" /* Schannel SSPI version */
+#include "curl_darwinssl.h" /* SecureTransport (Darwin) version */
+#include "curl_sendf.h"
+#include "curl_rawstr.h"
+#include "curl_url.h"
+#include "curl_memory.h"
+#include "curl_progress.h"
+#include "curl_share.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/* convenience macro to check if this handle is using a shared SSL session */
+#define SSLSESSION_SHARED(data) (data->share &&                        \
+                                 (data->share->specifier &             \
+                                  (1<<CURL_LOCK_DATA_SSL_SESSION)))
+
+static bool safe_strequal(char* str1, char* str2)
+{
+  if(str1 && str2)
+    /* both pointers point to something then compare them */
+    return (0 != Curl_raw_equal(str1, str2)) ? TRUE : FALSE;
+  else
+    /* if both pointers are NULL then treat them as equal */
+    return (!str1 && !str2) ? TRUE : FALSE;
+}
+
+bool
+Curl_ssl_config_matches(struct ssl_config_data* data,
+                        struct ssl_config_data* needle)
+{
+  if((data->version == needle->version) &&
+     (data->verifypeer == needle->verifypeer) &&
+     (data->verifyhost == needle->verifyhost) &&
+     safe_strequal(data->CApath, needle->CApath) &&
+     safe_strequal(data->CAfile, needle->CAfile) &&
+     safe_strequal(data->random_file, needle->random_file) &&
+     safe_strequal(data->egdsocket, needle->egdsocket) &&
+     safe_strequal(data->cipher_list, needle->cipher_list))
+    return TRUE;
+
+  return FALSE;
+}
+
+bool
+Curl_clone_ssl_config(struct ssl_config_data *source,
+                      struct ssl_config_data *dest)
+{
+  dest->sessionid = source->sessionid;
+  dest->verifyhost = source->verifyhost;
+  dest->verifypeer = source->verifypeer;
+  dest->version = source->version;
+
+  if(source->CAfile) {
+    dest->CAfile = strdup(source->CAfile);
+    if(!dest->CAfile)
+      return FALSE;
+  }
+  else
+    dest->CAfile = NULL;
+
+  if(source->CApath) {
+    dest->CApath = strdup(source->CApath);
+    if(!dest->CApath)
+      return FALSE;
+  }
+  else
+    dest->CApath = NULL;
+
+  if(source->cipher_list) {
+    dest->cipher_list = strdup(source->cipher_list);
+    if(!dest->cipher_list)
+      return FALSE;
+  }
+  else
+    dest->cipher_list = NULL;
+
+  if(source->egdsocket) {
+    dest->egdsocket = strdup(source->egdsocket);
+    if(!dest->egdsocket)
+      return FALSE;
+  }
+  else
+    dest->egdsocket = NULL;
+
+  if(source->random_file) {
+    dest->random_file = strdup(source->random_file);
+    if(!dest->random_file)
+      return FALSE;
+  }
+  else
+    dest->random_file = NULL;
+
+  return TRUE;
+}
+
+void Curl_free_ssl_config(struct ssl_config_data* sslc)
+{
+  Curl_safefree(sslc->CAfile);
+  Curl_safefree(sslc->CApath);
+  Curl_safefree(sslc->cipher_list);
+  Curl_safefree(sslc->egdsocket);
+  Curl_safefree(sslc->random_file);
+}
+
+#ifdef USE_SSL
+
+/* "global" init done? */
+static bool init_ssl=FALSE;
+
+/**
+ * Global SSL init
+ *
+ * @retval 0 error initializing SSL
+ * @retval 1 SSL initialized successfully
+ */
+int Curl_ssl_init(void)
+{
+  /* make sure this is only done once */
+  if(init_ssl)
+    return 1;
+  init_ssl = TRUE; /* never again */
+
+  return curlssl_init();
+}
+
+
+/* Global cleanup */
+void Curl_ssl_cleanup(void)
+{
+  if(init_ssl) {
+    /* only cleanup if we did a previous init */
+    curlssl_cleanup();
+    init_ssl = FALSE;
+  }
+}
+
+CURLcode
+Curl_ssl_connect(struct connectdata *conn, int sockindex)
+{
+  CURLcode res;
+  /* mark this is being ssl-enabled from here on. */
+  conn->ssl[sockindex].use = TRUE;
+  conn->ssl[sockindex].state = ssl_connection_negotiating;
+
+  res = curlssl_connect(conn, sockindex);
+
+  if(!res)
+    Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSL is connected */
+
+  return res;
+}
+
+CURLcode
+Curl_ssl_connect_nonblocking(struct connectdata *conn, int sockindex,
+                             bool *done)
+{
+  CURLcode res;
+  /* mark this is being ssl requested from here on. */
+  conn->ssl[sockindex].use = TRUE;
+#ifdef curlssl_connect_nonblocking
+  res = curlssl_connect_nonblocking(conn, sockindex, done);
+#else
+  *done = TRUE; /* fallback to BLOCKING */
+  res = curlssl_connect(conn, sockindex);
+#endif /* non-blocking connect support */
+  if(!res && *done)
+    Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSL is connected */
+  return res;
+}
+
+/*
+ * Check if there's a session ID for the given connection in the cache, and if
+ * there's one suitable, it is provided. Returns TRUE when no entry matched.
+ */
+int Curl_ssl_getsessionid(struct connectdata *conn,
+                          void **ssl_sessionid,
+                          size_t *idsize) /* set 0 if unknown */
+{
+  struct curl_ssl_session *check;
+  struct SessionHandle *data = conn->data;
+  size_t i;
+  long *general_age;
+  bool no_match = TRUE;
+
+  *ssl_sessionid = NULL;
+
+  if(!conn->ssl_config.sessionid)
+    /* session ID re-use is disabled */
+    return TRUE;
+
+  /* Lock if shared */
+  if(SSLSESSION_SHARED(data)) {
+    Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
+    general_age = &data->share->sessionage;
+  }
+  else
+    general_age = &data->state.sessionage;
+
+  for(i = 0; i < data->set.ssl.max_ssl_sessions; i++) {
+    check = &data->state.session[i];
+    if(!check->sessionid)
+      /* not session ID means blank entry */
+      continue;
+    if(Curl_raw_equal(conn->host.name, check->name) &&
+       (conn->remote_port == check->remote_port) &&
+       Curl_ssl_config_matches(&conn->ssl_config, &check->ssl_config)) {
+      /* yes, we have a session ID! */
+      (*general_age)++;          /* increase general age */
+      check->age = *general_age; /* set this as used in this age */
+      *ssl_sessionid = check->sessionid;
+      if(idsize)
+        *idsize = check->idsize;
+      no_match = FALSE;
+      break;
+    }
+  }
+
+  /* Unlock */
+  if(SSLSESSION_SHARED(data))
+    Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
+
+  return no_match;
+}
+
+/*
+ * Kill a single session ID entry in the cache.
+ */
+void Curl_ssl_kill_session(struct curl_ssl_session *session)
+{
+  if(session->sessionid) {
+    /* defensive check */
+
+    /* free the ID the SSL-layer specific way */
+    curlssl_session_free(session->sessionid);
+
+    session->sessionid = NULL;
+    session->age = 0; /* fresh */
+
+    Curl_free_ssl_config(&session->ssl_config);
+
+    Curl_safefree(session->name);
+  }
+}
+
+/*
+ * Delete the given session ID from the cache.
+ */
+void Curl_ssl_delsessionid(struct connectdata *conn, void *ssl_sessionid)
+{
+  size_t i;
+  struct SessionHandle *data=conn->data;
+
+  if(SSLSESSION_SHARED(data))
+    Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
+
+  for(i = 0; i < data->set.ssl.max_ssl_sessions; i++) {
+    struct curl_ssl_session *check = &data->state.session[i];
+
+    if(check->sessionid == ssl_sessionid) {
+      Curl_ssl_kill_session(check);
+      break;
+    }
+  }
+
+  if(SSLSESSION_SHARED(data))
+    Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
+}
+
+/*
+ * Store session id in the session cache. The ID passed on to this function
+ * must already have been extracted and allocated the proper way for the SSL
+ * layer. Curl_XXXX_session_free() will be called to free/kill the session ID
+ * later on.
+ */
+CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
+                               void *ssl_sessionid,
+                               size_t idsize)
+{
+  size_t i;
+  struct SessionHandle *data=conn->data; /* the mother of all structs */
+  struct curl_ssl_session *store = &data->state.session[0];
+  long oldest_age=data->state.session[0].age; /* zero if unused */
+  char *clone_host;
+  long *general_age;
+
+  /* Even though session ID re-use might be disabled, that only disables USING
+     IT. We still store it here in case the re-using is again enabled for an
+     upcoming transfer */
+
+  clone_host = strdup(conn->host.name);
+  if(!clone_host)
+    return CURLE_OUT_OF_MEMORY; /* bail out */
+
+  /* Now we should add the session ID and the host name to the cache, (remove
+     the oldest if necessary) */
+
+  /* If using shared SSL session, lock! */
+  if(SSLSESSION_SHARED(data)) {
+    Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
+    general_age = &data->share->sessionage;
+  }
+  else {
+    general_age = &data->state.sessionage;
+  }
+
+  /* find an empty slot for us, or find the oldest */
+  for(i = 1; (i < data->set.ssl.max_ssl_sessions) &&
+        data->state.session[i].sessionid; i++) {
+    if(data->state.session[i].age < oldest_age) {
+      oldest_age = data->state.session[i].age;
+      store = &data->state.session[i];
+    }
+  }
+  if(i == data->set.ssl.max_ssl_sessions)
+    /* cache is full, we must "kill" the oldest entry! */
+    Curl_ssl_kill_session(store);
+  else
+    store = &data->state.session[i]; /* use this slot */
+
+  /* now init the session struct wisely */
+  store->sessionid = ssl_sessionid;
+  store->idsize = idsize;
+  store->age = *general_age;    /* set current age */
+  if(store->name)
+    /* free it if there's one already present */
+    free(store->name);
+  store->name = clone_host;               /* clone host name */
+  store->remote_port = conn->remote_port; /* port number */
+
+
+  /* Unlock */
+  if(SSLSESSION_SHARED(data))
+    Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
+
+  if(!Curl_clone_ssl_config(&conn->ssl_config, &store->ssl_config)) {
+    store->sessionid = NULL; /* let caller free sessionid */
+    free(clone_host);
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  return CURLE_OK;
+}
+
+
+void Curl_ssl_close_all(struct SessionHandle *data)
+{
+  size_t i;
+  /* kill the session ID cache if not shared */
+  if(data->state.session && !SSLSESSION_SHARED(data)) {
+    for(i = 0; i < data->set.ssl.max_ssl_sessions; i++)
+      /* the single-killer function handles empty table slots */
+      Curl_ssl_kill_session(&data->state.session[i]);
+
+    /* free the cache data */
+    Curl_safefree(data->state.session);
+  }
+
+  curlssl_close_all(data);
+}
+
+void Curl_ssl_close(struct connectdata *conn, int sockindex)
+{
+  DEBUGASSERT((sockindex <= 1) && (sockindex >= -1));
+  curlssl_close(conn, sockindex);
+}
+
+CURLcode Curl_ssl_shutdown(struct connectdata *conn, int sockindex)
+{
+  if(curlssl_shutdown(conn, sockindex))
+    return CURLE_SSL_SHUTDOWN_FAILED;
+
+  conn->ssl[sockindex].use = FALSE; /* get back to ordinary socket usage */
+  conn->ssl[sockindex].state = ssl_connection_none;
+
+  conn->recv[sockindex] = Curl_recv_plain;
+  conn->send[sockindex] = Curl_send_plain;
+
+  return CURLE_OK;
+}
+
+/* Selects an SSL crypto engine
+ */
+CURLcode Curl_ssl_set_engine(struct SessionHandle *data, const char *engine)
+{
+  return curlssl_set_engine(data, engine);
+}
+
+/* Selects the default SSL crypto engine
+ */
+CURLcode Curl_ssl_set_engine_default(struct SessionHandle *data)
+{
+  return curlssl_set_engine_default(data);
+}
+
+/* Return list of OpenSSL crypto engine names. */
+struct curl_slist *Curl_ssl_engines_list(struct SessionHandle *data)
+{
+  return curlssl_engines_list(data);
+}
+
+/*
+ * This sets up a session ID cache to the specified size. Make sure this code
+ * is agnostic to what underlying SSL technology we use.
+ */
+CURLcode Curl_ssl_initsessions(struct SessionHandle *data, size_t amount)
+{
+  struct curl_ssl_session *session;
+
+  if(data->state.session)
+    /* this is just a precaution to prevent multiple inits */
+    return CURLE_OK;
+
+  session = calloc(amount, sizeof(struct curl_ssl_session));
+  if(!session)
+    return CURLE_OUT_OF_MEMORY;
+
+  /* store the info in the SSL section */
+  data->set.ssl.max_ssl_sessions = amount;
+  data->state.session = session;
+  data->state.sessionage = 1; /* this is brand new */
+  return CURLE_OK;
+}
+
+size_t Curl_ssl_version(char *buffer, size_t size)
+{
+  return curlssl_version(buffer, size);
+}
+
+/*
+ * This function tries to determine connection status.
+ *
+ * Return codes:
+ *     1 means the connection is still in place
+ *     0 means the connection has been closed
+ *    -1 means the connection status is unknown
+ */
+int Curl_ssl_check_cxn(struct connectdata *conn)
+{
+  return curlssl_check_cxn(conn);
+}
+
+bool Curl_ssl_data_pending(const struct connectdata *conn,
+                           int connindex)
+{
+  return curlssl_data_pending(conn, connindex);
+}
+
+void Curl_ssl_free_certinfo(struct SessionHandle *data)
+{
+  int i;
+  struct curl_certinfo *ci = &data->info.certs;
+  if(ci->num_of_certs) {
+    /* free all individual lists used */
+    for(i=0; i<ci->num_of_certs; i++) {
+      curl_slist_free_all(ci->certinfo[i]);
+      ci->certinfo[i] = NULL;
+    }
+    free(ci->certinfo); /* free the actual array too */
+    ci->certinfo = NULL;
+    ci->num_of_certs = 0;
+  }
+}
+
+#if defined(USE_SSLEAY) || defined(USE_GNUTLS) || defined(USE_NSS) || \
+    defined(USE_DARWINSSL)
+/* these functions are only used by some SSL backends */
+
+void Curl_ssl_random(struct SessionHandle *data,
+                     unsigned char *entropy,
+                     size_t length)
+{
+  curlssl_random(data, entropy, length);
+}
+
+void Curl_ssl_md5sum(unsigned char *tmp, /* input */
+                     size_t tmplen,
+                     unsigned char *md5sum, /* output */
+                     size_t md5len)
+{
+  curlssl_md5sum(tmp, tmplen, md5sum, md5len);
+}
+#endif /* USE_SSLEAY || USE_GNUTLS || USE_NSS || USE_DARWINSSL */
+
+#endif /* USE_SSL */
diff --git a/lib/curl_ssluse.c b/lib/curl_ssluse.c
new file mode 100644 (file)
index 0000000..0809d46
--- /dev/null
@@ -0,0 +1,2736 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/*
+ * Source file for all OpenSSL-specific code for the TLS/SSL layer. No code
+ * but curl_sslgen.c should ever call or use these functions.
+ */
+
+/*
+ * The original SSLeay-using code for curl was written by Linas Vepstas and
+ * Sampo Kellomaki 1998.
+ */
+
+#include "curl_setup.h"
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include "curl_urldata.h"
+#include "curl_sendf.h"
+#include "curl_formdata.h" /* for the boundary function */
+#include "curl_url.h" /* for the ssl config check function */
+#include "curl_inet_pton.h"
+#include "curl_ssluse.h"
+#include "curl_connect.h"
+#include "curl_strequal.h"
+#include "curl_select.h"
+#include "curl_sslgen.h"
+#include "curl_rawstr.h"
+#include "curl_hostcheck.h"
+
+#define _MPRINTF_REPLACE /* use the internal *printf() functions */
+#include <curl/mprintf.h>
+
+#ifdef USE_SSLEAY
+
+#ifdef USE_OPENSSL
+#include <openssl/rand.h>
+#include <openssl/x509v3.h>
+#include <openssl/dsa.h>
+#include <openssl/dh.h>
+#include <openssl/err.h>
+#include <openssl/md5.h>
+#else
+#include <rand.h>
+#include <x509v3.h>
+#include <md5.h>
+#endif
+
+#include "curl_warnless.h"
+#include "curl_memory.h"
+#include "curl_non_ascii.h" /* for Curl_convert_from_utf8 prototype */
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+#ifndef OPENSSL_VERSION_NUMBER
+#error "OPENSSL_VERSION_NUMBER not defined"
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x0090581fL
+#define HAVE_SSL_GET1_SESSION 1
+#else
+#undef HAVE_SSL_GET1_SESSION
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x00904100L
+#define HAVE_USERDATA_IN_PWD_CALLBACK 1
+#else
+#undef HAVE_USERDATA_IN_PWD_CALLBACK
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x00907001L
+/* ENGINE_load_private_key() takes four arguments */
+#define HAVE_ENGINE_LOAD_FOUR_ARGS
+#include <openssl/ui.h>
+#else
+/* ENGINE_load_private_key() takes three arguments */
+#undef HAVE_ENGINE_LOAD_FOUR_ARGS
+#endif
+
+#if (OPENSSL_VERSION_NUMBER >= 0x00903001L) && defined(HAVE_OPENSSL_PKCS12_H)
+/* OpenSSL has PKCS 12 support */
+#define HAVE_PKCS12_SUPPORT
+#else
+/* OpenSSL/SSLEay does not have PKCS12 support */
+#undef HAVE_PKCS12_SUPPORT
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x00906001L
+#define HAVE_ERR_ERROR_STRING_N 1
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x00909000L
+#define SSL_METHOD_QUAL const
+#else
+#define SSL_METHOD_QUAL
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+/* 0.9.6 didn't have X509_STORE_set_flags() */
+#define HAVE_X509_STORE_SET_FLAGS 1
+#else
+#define X509_STORE_set_flags(x,y) Curl_nop_stmt
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+#define HAVE_ERR_REMOVE_THREAD_STATE 1
+#endif
+
+#ifndef HAVE_SSLV2_CLIENT_METHOD
+#undef OPENSSL_NO_SSL2 /* undef first to avoid compiler warnings */
+#define OPENSSL_NO_SSL2
+#endif
+
+/*
+ * Number of bytes to read from the random number seed file. This must be
+ * a finite value (because some entropy "files" like /dev/urandom have
+ * an infinite length), but must be large enough to provide enough
+ * entopy to properly seed OpenSSL's PRNG.
+ */
+#define RAND_LOAD_LENGTH 1024
+
+#ifndef HAVE_USERDATA_IN_PWD_CALLBACK
+static char global_passwd[64];
+#endif
+
+static int passwd_callback(char *buf, int num, int encrypting
+#ifdef HAVE_USERDATA_IN_PWD_CALLBACK
+                           /* This was introduced in 0.9.4, we can set this
+                              using SSL_CTX_set_default_passwd_cb_userdata()
+                              */
+                           , void *global_passwd
+#endif
+                           )
+{
+  DEBUGASSERT(0 == encrypting);
+
+  if(!encrypting) {
+    int klen = curlx_uztosi(strlen((char *)global_passwd));
+    if(num > klen) {
+      memcpy(buf, global_passwd, klen+1);
+      return klen;
+    }
+  }
+  return 0;
+}
+
+/*
+ * rand_enough() is a function that returns TRUE if we have seeded the random
+ * engine properly. We use some preprocessor magic to provide a seed_enough()
+ * macro to use, just to prevent a compiler warning on this function if we
+ * pass in an argument that is never used.
+ */
+
+#ifdef HAVE_RAND_STATUS
+#define seed_enough(x) rand_enough()
+static bool rand_enough(void)
+{
+  return (0 != RAND_status()) ? TRUE : FALSE;
+}
+#else
+#define seed_enough(x) rand_enough(x)
+static bool rand_enough(int nread)
+{
+  /* this is a very silly decision to make */
+  return (nread > 500) ? TRUE : FALSE;
+}
+#endif
+
+static int ossl_seed(struct SessionHandle *data)
+{
+  char *buf = data->state.buffer; /* point to the big buffer */
+  int nread=0;
+
+  /* Q: should we add support for a random file name as a libcurl option?
+     A: Yes, it is here */
+
+#ifndef RANDOM_FILE
+  /* if RANDOM_FILE isn't defined, we only perform this if an option tells
+     us to! */
+  if(data->set.ssl.random_file)
+#define RANDOM_FILE "" /* doesn't matter won't be used */
+#endif
+  {
+    /* let the option override the define */
+    nread += RAND_load_file((data->set.str[STRING_SSL_RANDOM_FILE]?
+                             data->set.str[STRING_SSL_RANDOM_FILE]:
+                             RANDOM_FILE),
+                            RAND_LOAD_LENGTH);
+    if(seed_enough(nread))
+      return nread;
+  }
+
+#if defined(HAVE_RAND_EGD)
+  /* only available in OpenSSL 0.9.5 and later */
+  /* EGD_SOCKET is set at configure time or not at all */
+#ifndef EGD_SOCKET
+  /* If we don't have the define set, we only do this if the egd-option
+     is set */
+  if(data->set.str[STRING_SSL_EGDSOCKET])
+#define EGD_SOCKET "" /* doesn't matter won't be used */
+#endif
+  {
+    /* If there's an option and a define, the option overrides the
+       define */
+    int ret = RAND_egd(data->set.str[STRING_SSL_EGDSOCKET]?
+                       data->set.str[STRING_SSL_EGDSOCKET]:EGD_SOCKET);
+    if(-1 != ret) {
+      nread += ret;
+      if(seed_enough(nread))
+        return nread;
+    }
+  }
+#endif
+
+  /* If we get here, it means we need to seed the PRNG using a "silly"
+     approach! */
+  {
+    int len;
+    char *area;
+
+    /* Changed call to RAND_seed to use the underlying RAND_add implementation
+     * directly.  Do this in a loop, with the amount of additional entropy
+     * being dependent upon the algorithm used by Curl_FormBoundary(): N bytes
+     * of a 7-bit ascii set. -- Richard Gorton, March 11 2003.
+     */
+
+    do {
+      area = Curl_FormBoundary();
+      if(!area)
+        return 3; /* out of memory */
+
+      len = curlx_uztosi(strlen(area));
+      RAND_add(area, len, (len >> 1));
+
+      free(area); /* now remove the random junk */
+    } while(!RAND_status());
+  }
+
+  /* generates a default path for the random seed file */
+  buf[0]=0; /* blank it first */
+  RAND_file_name(buf, BUFSIZE);
+  if(buf[0]) {
+    /* we got a file name to try */
+    nread += RAND_load_file(buf, RAND_LOAD_LENGTH);
+    if(seed_enough(nread))
+      return nread;
+  }
+
+  infof(data, "libcurl is now using a weak random seed!\n");
+  return nread;
+}
+
+int Curl_ossl_seed(struct SessionHandle *data)
+{
+  /* we have the "SSL is seeded" boolean static to prevent multiple
+     time-consuming seedings in vain */
+  static bool ssl_seeded = FALSE;
+
+  if(!ssl_seeded || data->set.str[STRING_SSL_RANDOM_FILE] ||
+     data->set.str[STRING_SSL_EGDSOCKET]) {
+    ossl_seed(data);
+    ssl_seeded = TRUE;
+  }
+  return 0;
+}
+
+
+#ifndef SSL_FILETYPE_ENGINE
+#define SSL_FILETYPE_ENGINE 42
+#endif
+#ifndef SSL_FILETYPE_PKCS12
+#define SSL_FILETYPE_PKCS12 43
+#endif
+static int do_file_type(const char *type)
+{
+  if(!type || !type[0])
+    return SSL_FILETYPE_PEM;
+  if(Curl_raw_equal(type, "PEM"))
+    return SSL_FILETYPE_PEM;
+  if(Curl_raw_equal(type, "DER"))
+    return SSL_FILETYPE_ASN1;
+  if(Curl_raw_equal(type, "ENG"))
+    return SSL_FILETYPE_ENGINE;
+  if(Curl_raw_equal(type, "P12"))
+    return SSL_FILETYPE_PKCS12;
+  return -1;
+}
+
+static
+int cert_stuff(struct connectdata *conn,
+               SSL_CTX* ctx,
+               char *cert_file,
+               const char *cert_type,
+               char *key_file,
+               const char *key_type)
+{
+  struct SessionHandle *data = conn->data;
+
+  int file_type = do_file_type(cert_type);
+
+  if(cert_file != NULL || file_type == SSL_FILETYPE_ENGINE) {
+    SSL *ssl;
+    X509 *x509;
+    int cert_done = 0;
+
+    if(data->set.str[STRING_KEY_PASSWD]) {
+#ifndef HAVE_USERDATA_IN_PWD_CALLBACK
+      /*
+       * If password has been given, we store that in the global
+       * area (*shudder*) for a while:
+       */
+      size_t len = strlen(data->set.str[STRING_KEY_PASSWD]);
+      if(len < sizeof(global_passwd))
+        memcpy(global_passwd, data->set.str[STRING_KEY_PASSWD], len+1);
+      else
+        global_passwd[0] = '\0';
+#else
+      /*
+       * We set the password in the callback userdata
+       */
+      SSL_CTX_set_default_passwd_cb_userdata(ctx,
+                                             data->set.str[STRING_KEY_PASSWD]);
+#endif
+      /* Set passwd callback: */
+      SSL_CTX_set_default_passwd_cb(ctx, passwd_callback);
+    }
+
+
+#define SSL_CLIENT_CERT_ERR \
+    "unable to use client certificate (no key found or wrong pass phrase?)"
+
+    switch(file_type) {
+    case SSL_FILETYPE_PEM:
+      /* SSL_CTX_use_certificate_chain_file() only works on PEM files */
+      if(SSL_CTX_use_certificate_chain_file(ctx,
+                                            cert_file) != 1) {
+        failf(data, SSL_CLIENT_CERT_ERR);
+        return 0;
+      }
+      break;
+
+    case SSL_FILETYPE_ASN1:
+      /* SSL_CTX_use_certificate_file() works with either PEM or ASN1, but
+         we use the case above for PEM so this can only be performed with
+         ASN1 files. */
+      if(SSL_CTX_use_certificate_file(ctx,
+                                      cert_file,
+                                      file_type) != 1) {
+        failf(data, SSL_CLIENT_CERT_ERR);
+        return 0;
+      }
+      break;
+    case SSL_FILETYPE_ENGINE:
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(ENGINE_CTRL_GET_CMD_FROM_NAME)
+      {
+        if(data->state.engine) {
+          const char *cmd_name = "LOAD_CERT_CTRL";
+          struct {
+            const char *cert_id;
+            X509 *cert;
+          } params;
+
+          params.cert_id = cert_file;
+          params.cert = NULL;
+
+          /* Does the engine supports LOAD_CERT_CTRL ? */
+          if(!ENGINE_ctrl(data->state.engine, ENGINE_CTRL_GET_CMD_FROM_NAME,
+                          0, (void *)cmd_name, NULL)) {
+            failf(data, "ssl engine does not support loading certificates");
+            return 0;
+          }
+
+          /* Load the certificate from the engine */
+          if(!ENGINE_ctrl_cmd(data->state.engine, cmd_name,
+                              0, &params, NULL, 1)) {
+            failf(data, "ssl engine cannot load client cert with id"
+                  " '%s' [%s]", cert_file,
+                  ERR_error_string(ERR_get_error(), NULL));
+            return 0;
+          }
+
+          if(!params.cert) {
+            failf(data, "ssl engine didn't initialized the certificate "
+                  "properly.");
+            return 0;
+          }
+
+          if(SSL_CTX_use_certificate(ctx, params.cert) != 1) {
+            failf(data, "unable to set client certificate");
+            X509_free(params.cert);
+            return 0;
+          }
+          X509_free(params.cert); /* we don't need the handle any more... */
+        }
+        else {
+          failf(data, "crypto engine not set, can't load certificate");
+          return 0;
+        }
+      }
+      break;
+#else
+      failf(data, "file type ENG for certificate not implemented");
+      return 0;
+#endif
+
+    case SSL_FILETYPE_PKCS12:
+    {
+#ifdef HAVE_PKCS12_SUPPORT
+      FILE *f;
+      PKCS12 *p12;
+      EVP_PKEY *pri;
+      STACK_OF(X509) *ca = NULL;
+      int i;
+
+      f = fopen(cert_file,"rb");
+      if(!f) {
+        failf(data, "could not open PKCS12 file '%s'", cert_file);
+        return 0;
+      }
+      p12 = d2i_PKCS12_fp(f, NULL);
+      fclose(f);
+
+      if(!p12) {
+        failf(data, "error reading PKCS12 file '%s'", cert_file );
+        return 0;
+      }
+
+      PKCS12_PBE_add();
+
+      if(!PKCS12_parse(p12, data->set.str[STRING_KEY_PASSWD], &pri, &x509,
+                        &ca)) {
+        failf(data,
+              "could not parse PKCS12 file, check password, OpenSSL error %s",
+              ERR_error_string(ERR_get_error(), NULL) );
+        PKCS12_free(p12);
+        return 0;
+      }
+
+      PKCS12_free(p12);
+
+      if(SSL_CTX_use_certificate(ctx, x509) != 1) {
+        failf(data, SSL_CLIENT_CERT_ERR);
+        EVP_PKEY_free(pri);
+        X509_free(x509);
+        sk_X509_pop_free(ca, X509_free);
+        return 0;
+      }
+
+      if(SSL_CTX_use_PrivateKey(ctx, pri) != 1) {
+        failf(data, "unable to use private key from PKCS12 file '%s'",
+              cert_file);
+        EVP_PKEY_free(pri);
+        X509_free(x509);
+        sk_X509_pop_free(ca, X509_free);
+        return 0;
+      }
+
+      if(!SSL_CTX_check_private_key (ctx)) {
+        failf(data, "private key from PKCS12 file '%s' "
+              "does not match certificate in same file", cert_file);
+        EVP_PKEY_free(pri);
+        X509_free(x509);
+        sk_X509_pop_free(ca, X509_free);
+        return 0;
+      }
+      /* Set Certificate Verification chain */
+      if(ca && sk_X509_num(ca)) {
+        for(i = 0; i < sk_X509_num(ca); i++) {
+          if(!SSL_CTX_add_extra_chain_cert(ctx,sk_X509_value(ca, i))) {
+            failf(data, "cannot add certificate to certificate chain");
+            EVP_PKEY_free(pri);
+            X509_free(x509);
+            sk_X509_pop_free(ca, X509_free);
+            return 0;
+          }
+          if(!SSL_CTX_add_client_CA(ctx, sk_X509_value(ca, i))) {
+            failf(data, "cannot add certificate to client CA list");
+            EVP_PKEY_free(pri);
+            X509_free(x509);
+            sk_X509_pop_free(ca, X509_free);
+            return 0;
+          }
+        }
+      }
+
+      EVP_PKEY_free(pri);
+      X509_free(x509);
+      sk_X509_pop_free(ca, X509_free);
+      cert_done = 1;
+      break;
+#else
+      failf(data, "file type P12 for certificate not supported");
+      return 0;
+#endif
+    }
+    default:
+      failf(data, "not supported file type '%s' for certificate", cert_type);
+      return 0;
+    }
+
+    file_type = do_file_type(key_type);
+
+    switch(file_type) {
+    case SSL_FILETYPE_PEM:
+      if(cert_done)
+        break;
+      if(key_file == NULL)
+        /* cert & key can only be in PEM case in the same file */
+        key_file=cert_file;
+    case SSL_FILETYPE_ASN1:
+      if(SSL_CTX_use_PrivateKey_file(ctx, key_file, file_type) != 1) {
+        failf(data, "unable to set private key file: '%s' type %s",
+              key_file, key_type?key_type:"PEM");
+        return 0;
+      }
+      break;
+    case SSL_FILETYPE_ENGINE:
+#ifdef HAVE_OPENSSL_ENGINE_H
+      {                         /* XXXX still needs some work */
+        EVP_PKEY *priv_key = NULL;
+        if(data->state.engine) {
+#ifdef HAVE_ENGINE_LOAD_FOUR_ARGS
+          UI_METHOD *ui_method = UI_OpenSSL();
+#endif
+          /* the typecast below was added to please mingw32 */
+          priv_key = (EVP_PKEY *)
+            ENGINE_load_private_key(data->state.engine,key_file,
+#ifdef HAVE_ENGINE_LOAD_FOUR_ARGS
+                                    ui_method,
+#endif
+                                    data->set.str[STRING_KEY_PASSWD]);
+          if(!priv_key) {
+            failf(data, "failed to load private key from crypto engine");
+            return 0;
+          }
+          if(SSL_CTX_use_PrivateKey(ctx, priv_key) != 1) {
+            failf(data, "unable to set private key");
+            EVP_PKEY_free(priv_key);
+            return 0;
+          }
+          EVP_PKEY_free(priv_key);  /* we don't need the handle any more... */
+        }
+        else {
+          failf(data, "crypto engine not set, can't load private key");
+          return 0;
+        }
+      }
+      break;
+#else
+      failf(data, "file type ENG for private key not supported");
+      return 0;
+#endif
+    case SSL_FILETYPE_PKCS12:
+      if(!cert_done) {
+        failf(data, "file type P12 for private key not supported");
+        return 0;
+      }
+      break;
+    default:
+      failf(data, "not supported file type for private key");
+      return 0;
+    }
+
+    ssl=SSL_new(ctx);
+    if(NULL == ssl) {
+      failf(data,"unable to create an SSL structure");
+      return 0;
+    }
+
+    x509=SSL_get_certificate(ssl);
+
+    /* This version was provided by Evan Jordan and is supposed to not
+       leak memory as the previous version: */
+    if(x509 != NULL) {
+      EVP_PKEY *pktmp = X509_get_pubkey(x509);
+      EVP_PKEY_copy_parameters(pktmp,SSL_get_privatekey(ssl));
+      EVP_PKEY_free(pktmp);
+    }
+
+    SSL_free(ssl);
+
+    /* If we are using DSA, we can copy the parameters from
+     * the private key */
+
+
+    /* Now we know that a key and cert have been set against
+     * the SSL context */
+    if(!SSL_CTX_check_private_key(ctx)) {
+      failf(data, "Private key does not match the certificate public key");
+      return 0;
+    }
+#ifndef HAVE_USERDATA_IN_PWD_CALLBACK
+    /* erase it now */
+    memset(global_passwd, 0, sizeof(global_passwd));
+#endif
+  }
+  return 1;
+}
+
+/* returns non-zero on failure */
+static int x509_name_oneline(X509_NAME *a, char *buf, size_t size)
+{
+#if 0
+  return X509_NAME_oneline(a, buf, size);
+#else
+  BIO *bio_out = BIO_new(BIO_s_mem());
+  BUF_MEM *biomem;
+  int rc;
+
+  if(!bio_out)
+    return 1; /* alloc failed! */
+
+  rc = X509_NAME_print_ex(bio_out, a, 0, XN_FLAG_SEP_SPLUS_SPC);
+  BIO_get_mem_ptr(bio_out, &biomem);
+
+  if((size_t)biomem->length < size)
+    size = biomem->length;
+  else
+    size--; /* don't overwrite the buffer end */
+
+  memcpy(buf, biomem->data, size);
+  buf[size]=0;
+
+  BIO_free(bio_out);
+
+  return !rc;
+#endif
+}
+
+static
+int cert_verify_callback(int ok, X509_STORE_CTX *ctx)
+{
+  X509 *err_cert;
+  char buf[256];
+
+  err_cert=X509_STORE_CTX_get_current_cert(ctx);
+  (void)x509_name_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
+  return ok;
+}
+
+/* Return error string for last OpenSSL error
+ */
+static char *SSL_strerror(unsigned long error, char *buf, size_t size)
+{
+#ifdef HAVE_ERR_ERROR_STRING_N
+  /* OpenSSL 0.9.6 and later has a function named
+     ERRO_error_string_n() that takes the size of the buffer as a
+     third argument */
+  ERR_error_string_n(error, buf, size);
+#else
+  (void) size;
+  ERR_error_string(error, buf);
+#endif
+  return buf;
+}
+
+#endif /* USE_SSLEAY */
+
+#ifdef USE_SSLEAY
+/**
+ * Global SSL init
+ *
+ * @retval 0 error initializing SSL
+ * @retval 1 SSL initialized successfully
+ */
+int Curl_ossl_init(void)
+{
+#ifdef HAVE_ENGINE_LOAD_BUILTIN_ENGINES
+  ENGINE_load_builtin_engines();
+#endif
+
+  /* Lets get nice error messages */
+  SSL_load_error_strings();
+
+  /* Init the global ciphers and digests */
+  if(!SSLeay_add_ssl_algorithms())
+    return 0;
+
+  OpenSSL_add_all_algorithms();
+
+  return 1;
+}
+
+#endif /* USE_SSLEAY */
+
+#ifdef USE_SSLEAY
+
+/* Global cleanup */
+void Curl_ossl_cleanup(void)
+{
+  /* Free ciphers and digests lists */
+  EVP_cleanup();
+
+#ifdef HAVE_ENGINE_CLEANUP
+  /* Free engine list */
+  ENGINE_cleanup();
+#endif
+
+#ifdef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA
+  /* Free OpenSSL ex_data table */
+  CRYPTO_cleanup_all_ex_data();
+#endif
+
+  /* Free OpenSSL error strings */
+  ERR_free_strings();
+
+  /* Free thread local error state, destroying hash upon zero refcount */
+#ifdef HAVE_ERR_REMOVE_THREAD_STATE
+  ERR_remove_thread_state(NULL);
+#else
+  ERR_remove_state(0);
+#endif
+}
+
+/*
+ * This function uses SSL_peek to determine connection status.
+ *
+ * Return codes:
+ *     1 means the connection is still in place
+ *     0 means the connection has been closed
+ *    -1 means the connection status is unknown
+ */
+int Curl_ossl_check_cxn(struct connectdata *conn)
+{
+  int rc;
+  char buf;
+
+  rc = SSL_peek(conn->ssl[FIRSTSOCKET].handle, (void*)&buf, 1);
+  if(rc > 0)
+    return 1; /* connection still in place */
+
+  if(rc == 0)
+    return 0; /* connection has been closed */
+
+  return -1; /* connection status unknown */
+}
+
+/* Selects an OpenSSL crypto engine
+ */
+CURLcode Curl_ossl_set_engine(struct SessionHandle *data, const char *engine)
+{
+#if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H)
+  ENGINE *e;
+
+#if OPENSSL_VERSION_NUMBER >= 0x00909000L
+  e = ENGINE_by_id(engine);
+#else
+  /* avoid memory leak */
+  for(e = ENGINE_get_first(); e; e = ENGINE_get_next(e)) {
+    const char *e_id = ENGINE_get_id(e);
+    if(!strcmp(engine, e_id))
+      break;
+  }
+#endif
+
+  if(!e) {
+    failf(data, "SSL Engine '%s' not found", engine);
+    return CURLE_SSL_ENGINE_NOTFOUND;
+  }
+
+  if(data->state.engine) {
+    ENGINE_finish(data->state.engine);
+    ENGINE_free(data->state.engine);
+    data->state.engine = NULL;
+  }
+  if(!ENGINE_init(e)) {
+    char buf[256];
+
+    ENGINE_free(e);
+    failf(data, "Failed to initialise SSL Engine '%s':\n%s",
+          engine, SSL_strerror(ERR_get_error(), buf, sizeof(buf)));
+    return CURLE_SSL_ENGINE_INITFAILED;
+  }
+  data->state.engine = e;
+  return CURLE_OK;
+#else
+  (void)engine;
+  failf(data, "SSL Engine not supported");
+  return CURLE_SSL_ENGINE_NOTFOUND;
+#endif
+}
+
+/* Sets engine as default for all SSL operations
+ */
+CURLcode Curl_ossl_set_engine_default(struct SessionHandle *data)
+{
+#ifdef HAVE_OPENSSL_ENGINE_H
+  if(data->state.engine) {
+    if(ENGINE_set_default(data->state.engine, ENGINE_METHOD_ALL) > 0) {
+      infof(data,"set default crypto engine '%s'\n",
+            ENGINE_get_id(data->state.engine));
+    }
+    else {
+      failf(data, "set default crypto engine '%s' failed",
+            ENGINE_get_id(data->state.engine));
+      return CURLE_SSL_ENGINE_SETFAILED;
+    }
+  }
+#else
+  (void) data;
+#endif
+  return CURLE_OK;
+}
+
+/* Return list of OpenSSL crypto engine names.
+ */
+struct curl_slist *Curl_ossl_engines_list(struct SessionHandle *data)
+{
+  struct curl_slist *list = NULL;
+#if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H)
+  struct curl_slist *beg;
+  ENGINE *e;
+
+  for(e = ENGINE_get_first(); e; e = ENGINE_get_next(e)) {
+    beg = curl_slist_append(list, ENGINE_get_id(e));
+    if(!beg) {
+      curl_slist_free_all(list);
+      return NULL;
+    }
+    list = beg;
+  }
+#endif
+  (void) data;
+  return list;
+}
+
+
+/*
+ * This function is called when an SSL connection is closed.
+ */
+void Curl_ossl_close(struct connectdata *conn, int sockindex)
+{
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+
+  if(connssl->handle) {
+    (void)SSL_shutdown(connssl->handle);
+    SSL_set_connect_state(connssl->handle);
+
+    SSL_free (connssl->handle);
+    connssl->handle = NULL;
+  }
+  if(connssl->ctx) {
+    SSL_CTX_free (connssl->ctx);
+    connssl->ctx = NULL;
+  }
+}
+
+/*
+ * This function is called to shut down the SSL layer but keep the
+ * socket open (CCC - Clear Command Channel)
+ */
+int Curl_ossl_shutdown(struct connectdata *conn, int sockindex)
+{
+  int retval = 0;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  struct SessionHandle *data = conn->data;
+  char buf[120]; /* We will use this for the OpenSSL error buffer, so it has
+                    to be at least 120 bytes long. */
+  unsigned long sslerror;
+  ssize_t nread;
+  int buffsize;
+  int err;
+  int done = 0;
+
+  /* This has only been tested on the proftpd server, and the mod_tls code
+     sends a close notify alert without waiting for a close notify alert in
+     response. Thus we wait for a close notify alert from the server, but
+     we do not send one. Let's hope other servers do the same... */
+
+  if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE)
+      (void)SSL_shutdown(connssl->handle);
+
+  if(connssl->handle) {
+    buffsize = (int)sizeof(buf);
+    while(!done) {
+      int what = Curl_socket_ready(conn->sock[sockindex],
+                                   CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT);
+      if(what > 0) {
+        ERR_clear_error();
+
+        /* Something to read, let's do it and hope that it is the close
+           notify alert from the server */
+        nread = (ssize_t)SSL_read(conn->ssl[sockindex].handle, buf,
+                                  buffsize);
+        err = SSL_get_error(conn->ssl[sockindex].handle, (int)nread);
+
+        switch(err) {
+        case SSL_ERROR_NONE: /* this is not an error */
+        case SSL_ERROR_ZERO_RETURN: /* no more data */
+          /* This is the expected response. There was no data but only
+             the close notify alert */
+          done = 1;
+          break;
+        case SSL_ERROR_WANT_READ:
+          /* there's data pending, re-invoke SSL_read() */
+          infof(data, "SSL_ERROR_WANT_READ\n");
+          break;
+        case SSL_ERROR_WANT_WRITE:
+          /* SSL wants a write. Really odd. Let's bail out. */
+          infof(data, "SSL_ERROR_WANT_WRITE\n");
+          done = 1;
+          break;
+        default:
+          /* openssl/ssl.h says "look at error stack/return value/errno" */
+          sslerror = ERR_get_error();
+          failf(conn->data, "SSL read: %s, errno %d",
+                ERR_error_string(sslerror, buf),
+                SOCKERRNO);
+          done = 1;
+          break;
+        }
+      }
+      else if(0 == what) {
+        /* timeout */
+        failf(data, "SSL shutdown timeout");
+        done = 1;
+      }
+      else {
+        /* anything that gets here is fatally bad */
+        failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+        retval = -1;
+        done = 1;
+      }
+    } /* while()-loop for the select() */
+
+    if(data->set.verbose) {
+#ifdef HAVE_SSL_GET_SHUTDOWN
+      switch(SSL_get_shutdown(connssl->handle)) {
+      case SSL_SENT_SHUTDOWN:
+        infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN\n");
+        break;
+      case SSL_RECEIVED_SHUTDOWN:
+        infof(data, "SSL_get_shutdown() returned SSL_RECEIVED_SHUTDOWN\n");
+        break;
+      case SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN:
+        infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN|"
+              "SSL_RECEIVED__SHUTDOWN\n");
+        break;
+      }
+#endif
+    }
+
+    SSL_free (connssl->handle);
+    connssl->handle = NULL;
+  }
+  return retval;
+}
+
+void Curl_ossl_session_free(void *ptr)
+{
+  /* free the ID */
+  SSL_SESSION_free(ptr);
+}
+
+/*
+ * This function is called when the 'data' struct is going away. Close
+ * down everything and free all resources!
+ */
+int Curl_ossl_close_all(struct SessionHandle *data)
+{
+#ifdef HAVE_OPENSSL_ENGINE_H
+  if(data->state.engine) {
+    ENGINE_finish(data->state.engine);
+    ENGINE_free(data->state.engine);
+    data->state.engine = NULL;
+  }
+#else
+  (void)data;
+#endif
+  return 0;
+}
+
+static int asn1_output(const ASN1_UTCTIME *tm,
+                       char *buf,
+                       size_t sizeofbuf)
+{
+  const char *asn1_string;
+  int gmt=FALSE;
+  int i;
+  int year=0,month=0,day=0,hour=0,minute=0,second=0;
+
+  i=tm->length;
+  asn1_string=(const char *)tm->data;
+
+  if(i < 10)
+    return 1;
+  if(asn1_string[i-1] == 'Z')
+    gmt=TRUE;
+  for(i=0; i<10; i++)
+    if((asn1_string[i] > '9') || (asn1_string[i] < '0'))
+      return 2;
+
+  year= (asn1_string[0]-'0')*10+(asn1_string[1]-'0');
+  if(year < 50)
+    year+=100;
+
+  month= (asn1_string[2]-'0')*10+(asn1_string[3]-'0');
+  if((month > 12) || (month < 1))
+    return 3;
+
+  day= (asn1_string[4]-'0')*10+(asn1_string[5]-'0');
+  hour= (asn1_string[6]-'0')*10+(asn1_string[7]-'0');
+  minute=  (asn1_string[8]-'0')*10+(asn1_string[9]-'0');
+
+  if((asn1_string[10] >= '0') && (asn1_string[10] <= '9') &&
+     (asn1_string[11] >= '0') && (asn1_string[11] <= '9'))
+    second= (asn1_string[10]-'0')*10+(asn1_string[11]-'0');
+
+  snprintf(buf, sizeofbuf,
+           "%04d-%02d-%02d %02d:%02d:%02d %s",
+           year+1900, month, day, hour, minute, second, (gmt?"GMT":""));
+
+  return 0;
+}
+
+/* ====================================================== */
+
+
+/* Quote from RFC2818 section 3.1 "Server Identity"
+
+   If a subjectAltName extension of type dNSName is present, that MUST
+   be used as the identity. Otherwise, the (most specific) Common Name
+   field in the Subject field of the certificate MUST be used. Although
+   the use of the Common Name is existing practice, it is deprecated and
+   Certification Authorities are encouraged to use the dNSName instead.
+
+   Matching is performed using the matching rules specified by
+   [RFC2459].  If more than one identity of a given type is present in
+   the certificate (e.g., more than one dNSName name, a match in any one
+   of the set is considered acceptable.) Names may contain the wildcard
+   character * which is considered to match any single domain name
+   component or component fragment. E.g., *.a.com matches foo.a.com but
+   not bar.foo.a.com. f*.com matches foo.com but not bar.com.
+
+   In some cases, the URI is specified as an IP address rather than a
+   hostname. In this case, the iPAddress subjectAltName must be present
+   in the certificate and must exactly match the IP in the URI.
+
+*/
+static CURLcode verifyhost(struct connectdata *conn,
+                           X509 *server_cert)
+{
+  int matched = -1; /* -1 is no alternative match yet, 1 means match and 0
+                       means mismatch */
+  int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */
+  size_t addrlen = 0;
+  struct SessionHandle *data = conn->data;
+  STACK_OF(GENERAL_NAME) *altnames;
+#ifdef ENABLE_IPV6
+  struct in6_addr addr;
+#else
+  struct in_addr addr;
+#endif
+  CURLcode res = CURLE_OK;
+
+#ifdef ENABLE_IPV6
+  if(conn->bits.ipv6_ip &&
+     Curl_inet_pton(AF_INET6, conn->host.name, &addr)) {
+    target = GEN_IPADD;
+    addrlen = sizeof(struct in6_addr);
+  }
+  else
+#endif
+    if(Curl_inet_pton(AF_INET, conn->host.name, &addr)) {
+      target = GEN_IPADD;
+      addrlen = sizeof(struct in_addr);
+    }
+
+  /* get a "list" of alternative names */
+  altnames = X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL);
+
+  if(altnames) {
+    int numalts;
+    int i;
+
+    /* get amount of alternatives, RFC2459 claims there MUST be at least
+       one, but we don't depend on it... */
+    numalts = sk_GENERAL_NAME_num(altnames);
+
+    /* loop through all alternatives while none has matched */
+    for(i=0; (i<numalts) && (matched != 1); i++) {
+      /* get a handle to alternative name number i */
+      const GENERAL_NAME *check = sk_GENERAL_NAME_value(altnames, i);
+
+      /* only check alternatives of the same type the target is */
+      if(check->type == target) {
+        /* get data and length */
+        const char *altptr = (char *)ASN1_STRING_data(check->d.ia5);
+        size_t altlen = (size_t) ASN1_STRING_length(check->d.ia5);
+
+        switch(target) {
+        case GEN_DNS: /* name/pattern comparison */
+          /* The OpenSSL man page explicitly says: "In general it cannot be
+             assumed that the data returned by ASN1_STRING_data() is null
+             terminated or does not contain embedded nulls." But also that
+             "The actual format of the data will depend on the actual string
+             type itself: for example for and IA5String the data will be ASCII"
+
+             Gisle researched the OpenSSL sources:
+             "I checked the 0.9.6 and 0.9.8 sources before my patch and
+             it always 0-terminates an IA5String."
+          */
+          if((altlen == strlen(altptr)) &&
+             /* if this isn't true, there was an embedded zero in the name
+                string and we cannot match it. */
+             Curl_cert_hostcheck(altptr, conn->host.name))
+            matched = 1;
+          else
+            matched = 0;
+          break;
+
+        case GEN_IPADD: /* IP address comparison */
+          /* compare alternative IP address if the data chunk is the same size
+             our server IP address is */
+          if((altlen == addrlen) && !memcmp(altptr, &addr, altlen))
+            matched = 1;
+          else
+            matched = 0;
+          break;
+        }
+      }
+    }
+    GENERAL_NAMES_free(altnames);
+  }
+
+  if(matched == 1)
+    /* an alternative name matched the server hostname */
+    infof(data, "\t subjectAltName: %s matched\n", conn->host.dispname);
+  else if(matched == 0) {
+    /* an alternative name field existed, but didn't match and then
+       we MUST fail */
+    infof(data, "\t subjectAltName does not match %s\n", conn->host.dispname);
+    res = CURLE_PEER_FAILED_VERIFICATION;
+  }
+  else {
+    /* we have to look to the last occurrence of a commonName in the
+       distinguished one to get the most significant one. */
+    int j,i=-1 ;
+
+/* The following is done because of a bug in 0.9.6b */
+
+    unsigned char *nulstr = (unsigned char *)"";
+    unsigned char *peer_CN = nulstr;
+
+    X509_NAME *name = X509_get_subject_name(server_cert) ;
+    if(name)
+      while((j = X509_NAME_get_index_by_NID(name, NID_commonName, i))>=0)
+        i=j;
+
+    /* we have the name entry and we will now convert this to a string
+       that we can use for comparison. Doing this we support BMPstring,
+       UTF8 etc. */
+
+    if(i>=0) {
+      ASN1_STRING *tmp = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name,i));
+
+      /* In OpenSSL 0.9.7d and earlier, ASN1_STRING_to_UTF8 fails if the input
+         is already UTF-8 encoded. We check for this case and copy the raw
+         string manually to avoid the problem. This code can be made
+         conditional in the future when OpenSSL has been fixed. Work-around
+         brought by Alexis S. L. Carvalho. */
+      if(tmp) {
+        if(ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) {
+          j = ASN1_STRING_length(tmp);
+          if(j >= 0) {
+            peer_CN = OPENSSL_malloc(j+1);
+            if(peer_CN) {
+              memcpy(peer_CN, ASN1_STRING_data(tmp), j);
+              peer_CN[j] = '\0';
+            }
+          }
+        }
+        else /* not a UTF8 name */
+          j = ASN1_STRING_to_UTF8(&peer_CN, tmp);
+
+        if(peer_CN && (curlx_uztosi(strlen((char *)peer_CN)) != j)) {
+          /* there was a terminating zero before the end of string, this
+             cannot match and we return failure! */
+          failf(data, "SSL: illegal cert name field");
+          res = CURLE_PEER_FAILED_VERIFICATION;
+        }
+      }
+    }
+
+    if(peer_CN == nulstr)
+       peer_CN = NULL;
+    else {
+      /* convert peer_CN from UTF8 */
+      CURLcode rc = Curl_convert_from_utf8(data, peer_CN, strlen(peer_CN));
+      /* Curl_convert_from_utf8 calls failf if unsuccessful */
+      if(rc) {
+        OPENSSL_free(peer_CN);
+        return rc;
+      }
+    }
+
+    if(res)
+      /* error already detected, pass through */
+      ;
+    else if(!peer_CN) {
+      failf(data,
+            "SSL: unable to obtain common name from peer certificate");
+      res = CURLE_PEER_FAILED_VERIFICATION;
+    }
+    else if(!Curl_cert_hostcheck((const char *)peer_CN, conn->host.name)) {
+      failf(data, "SSL: certificate subject name '%s' does not match "
+            "target host name '%s'", peer_CN, conn->host.dispname);
+      res = CURLE_PEER_FAILED_VERIFICATION;
+    }
+    else {
+      infof(data, "\t common name: %s (matched)\n", peer_CN);
+    }
+    if(peer_CN)
+      OPENSSL_free(peer_CN);
+  }
+  return res;
+}
+#endif /* USE_SSLEAY */
+
+/* The SSL_CTRL_SET_MSG_CALLBACK doesn't exist in ancient OpenSSL versions
+   and thus this cannot be done there. */
+#ifdef SSL_CTRL_SET_MSG_CALLBACK
+
+static const char *ssl_msg_type(int ssl_ver, int msg)
+{
+  if(ssl_ver == SSL2_VERSION_MAJOR) {
+    switch (msg) {
+      case SSL2_MT_ERROR:
+        return "Error";
+      case SSL2_MT_CLIENT_HELLO:
+        return "Client hello";
+      case SSL2_MT_CLIENT_MASTER_KEY:
+        return "Client key";
+      case SSL2_MT_CLIENT_FINISHED:
+        return "Client finished";
+      case SSL2_MT_SERVER_HELLO:
+        return "Server hello";
+      case SSL2_MT_SERVER_VERIFY:
+        return "Server verify";
+      case SSL2_MT_SERVER_FINISHED:
+        return "Server finished";
+      case SSL2_MT_REQUEST_CERTIFICATE:
+        return "Request CERT";
+      case SSL2_MT_CLIENT_CERTIFICATE:
+        return "Client CERT";
+    }
+  }
+  else if(ssl_ver == SSL3_VERSION_MAJOR) {
+    switch (msg) {
+      case SSL3_MT_HELLO_REQUEST:
+        return "Hello request";
+      case SSL3_MT_CLIENT_HELLO:
+        return "Client hello";
+      case SSL3_MT_SERVER_HELLO:
+        return "Server hello";
+      case SSL3_MT_CERTIFICATE:
+        return "CERT";
+      case SSL3_MT_SERVER_KEY_EXCHANGE:
+        return "Server key exchange";
+      case SSL3_MT_CLIENT_KEY_EXCHANGE:
+        return "Client key exchange";
+      case SSL3_MT_CERTIFICATE_REQUEST:
+        return "Request CERT";
+      case SSL3_MT_SERVER_DONE:
+        return "Server finished";
+      case SSL3_MT_CERTIFICATE_VERIFY:
+        return "CERT verify";
+      case SSL3_MT_FINISHED:
+        return "Finished";
+    }
+  }
+  return "Unknown";
+}
+
+static const char *tls_rt_type(int type)
+{
+  return (
+    type == SSL3_RT_CHANGE_CIPHER_SPEC ? "TLS change cipher, " :
+    type == SSL3_RT_ALERT              ? "TLS alert, "         :
+    type == SSL3_RT_HANDSHAKE          ? "TLS handshake, "     :
+    type == SSL3_RT_APPLICATION_DATA   ? "TLS app data, "      :
+                                         "TLS Unknown, ");
+}
+
+
+/*
+ * Our callback from the SSL/TLS layers.
+ */
+static void ssl_tls_trace(int direction, int ssl_ver, int content_type,
+                          const void *buf, size_t len, const SSL *ssl,
+                          struct connectdata *conn)
+{
+  struct SessionHandle *data;
+  const char *msg_name, *tls_rt_name;
+  char ssl_buf[1024];
+  int  ver, msg_type, txt_len;
+
+  if(!conn || !conn->data || !conn->data->set.fdebug ||
+     (direction != 0 && direction != 1))
+    return;
+
+  data = conn->data;
+  ssl_ver >>= 8;
+  ver = (ssl_ver == SSL2_VERSION_MAJOR ? '2' :
+         ssl_ver == SSL3_VERSION_MAJOR ? '3' : '?');
+
+  /* SSLv2 doesn't seem to have TLS record-type headers, so OpenSSL
+   * always pass-up content-type as 0. But the interesting message-type
+   * is at 'buf[0]'.
+   */
+  if(ssl_ver == SSL3_VERSION_MAJOR && content_type != 0)
+    tls_rt_name = tls_rt_type(content_type);
+  else
+    tls_rt_name = "";
+
+  msg_type = *(char*)buf;
+  msg_name = ssl_msg_type(ssl_ver, msg_type);
+
+  txt_len = snprintf(ssl_buf, sizeof(ssl_buf), "SSLv%c, %s%s (%d):\n",
+                     ver, tls_rt_name, msg_name, msg_type);
+  Curl_debug(data, CURLINFO_TEXT, ssl_buf, (size_t)txt_len, NULL);
+
+  Curl_debug(data, (direction == 1) ? CURLINFO_SSL_DATA_OUT :
+             CURLINFO_SSL_DATA_IN, (char *)buf, len, NULL);
+  (void) ssl;
+}
+#endif
+
+#ifdef USE_SSLEAY
+/* ====================================================== */
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+#  define use_sni(x)  sni = (x)
+#else
+#  define use_sni(x)  Curl_nop_stmt
+#endif
+
+static CURLcode
+ossl_connect_step1(struct connectdata *conn,
+                   int sockindex)
+{
+  CURLcode retcode = CURLE_OK;
+
+  struct SessionHandle *data = conn->data;
+  SSL_METHOD_QUAL SSL_METHOD *req_method=NULL;
+  void *ssl_sessionid=NULL;
+  X509_LOOKUP *lookup=NULL;
+  curl_socket_t sockfd = conn->sock[sockindex];
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  long ctx_options;
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+  bool sni;
+#ifdef ENABLE_IPV6
+  struct in6_addr addr;
+#else
+  struct in_addr addr;
+#endif
+#endif
+
+  DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
+
+  /* Make funny stuff to get random input */
+  Curl_ossl_seed(data);
+
+  /* check to see if we've been told to use an explicit SSL/TLS version */
+
+  switch(data->set.ssl.version) {
+  default:
+  case CURL_SSLVERSION_DEFAULT:
+#ifdef USE_TLS_SRP
+    if(data->set.ssl.authtype == CURL_TLSAUTH_SRP) {
+      infof(data, "Set version TLSv1 for SRP authorisation\n");
+      req_method = TLSv1_client_method() ;
+    }
+    else
+#endif
+    /* we try to figure out version */
+    req_method = SSLv23_client_method();
+    use_sni(TRUE);
+    break;
+  case CURL_SSLVERSION_TLSv1:
+    req_method = TLSv1_client_method();
+    use_sni(TRUE);
+    break;
+  case CURL_SSLVERSION_SSLv2:
+#ifdef OPENSSL_NO_SSL2
+    failf(data, "OpenSSL was built without SSLv2 support");
+    return CURLE_NOT_BUILT_IN;
+#else
+#ifdef USE_TLS_SRP
+    if(data->set.ssl.authtype == CURL_TLSAUTH_SRP)
+      return CURLE_SSL_CONNECT_ERROR;
+#endif
+    req_method = SSLv2_client_method();
+    use_sni(FALSE);
+    break;
+#endif
+  case CURL_SSLVERSION_SSLv3:
+#ifdef USE_TLS_SRP
+    if(data->set.ssl.authtype == CURL_TLSAUTH_SRP)
+      return CURLE_SSL_CONNECT_ERROR;
+#endif
+    req_method = SSLv3_client_method();
+    use_sni(FALSE);
+    break;
+  }
+
+  if(connssl->ctx)
+    SSL_CTX_free(connssl->ctx);
+  connssl->ctx = SSL_CTX_new(req_method);
+
+  if(!connssl->ctx) {
+    failf(data, "SSL: couldn't create a context: %s",
+          ERR_error_string(ERR_peek_error(), NULL));
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+#ifdef SSL_MODE_RELEASE_BUFFERS
+  SSL_CTX_set_mode(connssl->ctx, SSL_MODE_RELEASE_BUFFERS);
+#endif
+
+#ifdef SSL_CTRL_SET_MSG_CALLBACK
+  if(data->set.fdebug && data->set.verbose) {
+    /* the SSL trace callback is only used for verbose logging so we only
+       inform about failures of setting it */
+    if(!SSL_CTX_callback_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK,
+                               (void (*)(void))ssl_tls_trace)) {
+      infof(data, "SSL: couldn't set callback!\n");
+    }
+    else if(!SSL_CTX_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK_ARG, 0,
+                          conn)) {
+      infof(data, "SSL: couldn't set callback argument!\n");
+    }
+  }
+#endif
+
+  /* OpenSSL contains code to work-around lots of bugs and flaws in various
+     SSL-implementations. SSL_CTX_set_options() is used to enabled those
+     work-arounds. The man page for this option states that SSL_OP_ALL enables
+     all the work-arounds and that "It is usually safe to use SSL_OP_ALL to
+     enable the bug workaround options if compatibility with somewhat broken
+     implementations is desired."
+
+     The "-no_ticket" option was introduced in Openssl0.9.8j. It's a flag to
+     disable "rfc4507bis session ticket support".  rfc4507bis was later turned
+     into the proper RFC5077 it seems: http://tools.ietf.org/html/rfc5077
+
+     The enabled extension concerns the session management. I wonder how often
+     libcurl stops a connection and then resumes a TLS session. also, sending
+     the session data is some overhead. .I suggest that you just use your
+     proposed patch (which explicitly disables TICKET).
+
+     If someone writes an application with libcurl and openssl who wants to
+     enable the feature, one can do this in the SSL callback.
+
+     SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG option enabling allowed proper
+     interoperability with web server Netscape Enterprise Server 2.0.1 which
+     was released back in 1996.
+
+     Due to CVE-2010-4180, option SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG has
+     become ineffective as of OpenSSL 0.9.8q and 1.0.0c. In order to mitigate
+     CVE-2010-4180 when using previous OpenSSL versions we no longer enable
+     this option regardless of OpenSSL version and SSL_OP_ALL definition.
+
+     OpenSSL added a work-around for a SSL 3.0/TLS 1.0 CBC vulnerability
+     (http://www.openssl.org/~bodo/tls-cbc.txt). In 0.9.6e they added a bit to
+     SSL_OP_ALL that _disables_ that work-around despite the fact that
+     SSL_OP_ALL is documented to do "rather harmless" workarounds. In order to
+     keep the secure work-around, the SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS bit
+     must not be set.
+  */
+
+  ctx_options = SSL_OP_ALL;
+
+#ifdef SSL_OP_NO_TICKET
+  ctx_options |= SSL_OP_NO_TICKET;
+#endif
+
+#ifdef SSL_OP_NO_COMPRESSION
+  ctx_options |= SSL_OP_NO_COMPRESSION;
+#endif
+
+#ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
+  /* mitigate CVE-2010-4180 */
+  ctx_options &= ~SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG;
+#endif
+
+#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
+  /* unless the user explicitly ask to allow the protocol vulnerability we
+     use the work-around */
+  if(!conn->data->set.ssl_enable_beast)
+    ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
+#endif
+
+  /* disable SSLv2 in the default case (i.e. allow SSLv3 and TLSv1) */
+  if(data->set.ssl.version == CURL_SSLVERSION_DEFAULT)
+    ctx_options |= SSL_OP_NO_SSLv2;
+
+  SSL_CTX_set_options(connssl->ctx, ctx_options);
+
+#if 0
+  /*
+   * Not sure it's needed to tell SSL_connect() that socket is
+   * non-blocking. It doesn't seem to care, but just return with
+   * SSL_ERROR_WANT_x.
+   */
+  if(data->state.used_interface == Curl_if_multi)
+    SSL_CTX_ctrl(connssl->ctx, BIO_C_SET_NBIO, 1, NULL);
+#endif
+
+  if(data->set.str[STRING_CERT] || data->set.str[STRING_CERT_TYPE]) {
+    if(!cert_stuff(conn,
+                   connssl->ctx,
+                   data->set.str[STRING_CERT],
+                   data->set.str[STRING_CERT_TYPE],
+                   data->set.str[STRING_KEY],
+                   data->set.str[STRING_KEY_TYPE])) {
+      /* failf() is already done in cert_stuff() */
+      return CURLE_SSL_CERTPROBLEM;
+    }
+  }
+
+  if(data->set.str[STRING_SSL_CIPHER_LIST]) {
+    if(!SSL_CTX_set_cipher_list(connssl->ctx,
+                                data->set.str[STRING_SSL_CIPHER_LIST])) {
+      failf(data, "failed setting cipher list");
+      return CURLE_SSL_CIPHER;
+    }
+  }
+
+#ifdef USE_TLS_SRP
+  if(data->set.ssl.authtype == CURL_TLSAUTH_SRP) {
+    infof(data, "Using TLS-SRP username: %s\n", data->set.ssl.username);
+
+    if(!SSL_CTX_set_srp_username(connssl->ctx, data->set.ssl.username)) {
+      failf(data, "Unable to set SRP user name");
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    }
+    if(!SSL_CTX_set_srp_password(connssl->ctx,data->set.ssl.password)) {
+      failf(data, "failed setting SRP password");
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    }
+    if(!data->set.str[STRING_SSL_CIPHER_LIST]) {
+      infof(data, "Setting cipher list SRP\n");
+
+      if(!SSL_CTX_set_cipher_list(connssl->ctx, "SRP")) {
+        failf(data, "failed setting SRP cipher list");
+        return CURLE_SSL_CIPHER;
+      }
+    }
+  }
+#endif
+  if(data->set.str[STRING_SSL_CAFILE] || data->set.str[STRING_SSL_CAPATH]) {
+    /* tell SSL where to find CA certificates that are used to verify
+       the servers certificate. */
+    if(!SSL_CTX_load_verify_locations(connssl->ctx,
+                                       data->set.str[STRING_SSL_CAFILE],
+                                       data->set.str[STRING_SSL_CAPATH])) {
+      if(data->set.ssl.verifypeer) {
+        /* Fail if we insist on successfully verifying the server. */
+        failf(data,"error setting certificate verify locations:\n"
+              "  CAfile: %s\n  CApath: %s",
+              data->set.str[STRING_SSL_CAFILE]?
+              data->set.str[STRING_SSL_CAFILE]: "none",
+              data->set.str[STRING_SSL_CAPATH]?
+              data->set.str[STRING_SSL_CAPATH] : "none");
+        return CURLE_SSL_CACERT_BADFILE;
+      }
+      else {
+        /* Just continue with a warning if no strict  certificate verification
+           is required. */
+        infof(data, "error setting certificate verify locations,"
+              " continuing anyway:\n");
+      }
+    }
+    else {
+      /* Everything is fine. */
+      infof(data, "successfully set certificate verify locations:\n");
+    }
+    infof(data,
+          "  CAfile: %s\n"
+          "  CApath: %s\n",
+          data->set.str[STRING_SSL_CAFILE] ? data->set.str[STRING_SSL_CAFILE]:
+          "none",
+          data->set.str[STRING_SSL_CAPATH] ? data->set.str[STRING_SSL_CAPATH]:
+          "none");
+  }
+
+  if(data->set.str[STRING_SSL_CRLFILE]) {
+    /* tell SSL where to find CRL file that is used to check certificate
+     * revocation */
+    lookup=X509_STORE_add_lookup(SSL_CTX_get_cert_store(connssl->ctx),
+                                 X509_LOOKUP_file());
+    if(!lookup ||
+       (!X509_load_crl_file(lookup,data->set.str[STRING_SSL_CRLFILE],
+                            X509_FILETYPE_PEM)) ) {
+      failf(data,"error loading CRL file: %s",
+            data->set.str[STRING_SSL_CRLFILE]);
+      return CURLE_SSL_CRL_BADFILE;
+    }
+    else {
+      /* Everything is fine. */
+      infof(data, "successfully load CRL file:\n");
+      X509_STORE_set_flags(SSL_CTX_get_cert_store(connssl->ctx),
+                           X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
+    }
+    infof(data,
+          "  CRLfile: %s\n", data->set.str[STRING_SSL_CRLFILE] ?
+          data->set.str[STRING_SSL_CRLFILE]: "none");
+  }
+
+  /* SSL always tries to verify the peer, this only says whether it should
+   * fail to connect if the verification fails, or if it should continue
+   * anyway. In the latter case the result of the verification is checked with
+   * SSL_get_verify_result() below. */
+  SSL_CTX_set_verify(connssl->ctx,
+                     data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE,
+                     cert_verify_callback);
+
+  /* give application a chance to interfere with SSL set up. */
+  if(data->set.ssl.fsslctx) {
+    retcode = (*data->set.ssl.fsslctx)(data, connssl->ctx,
+                                       data->set.ssl.fsslctxp);
+    if(retcode) {
+      failf(data,"error signaled by ssl ctx callback");
+      return retcode;
+    }
+  }
+
+  /* Lets make an SSL structure */
+  if(connssl->handle)
+    SSL_free(connssl->handle);
+  connssl->handle = SSL_new(connssl->ctx);
+  if(!connssl->handle) {
+    failf(data, "SSL: couldn't create a context (handle)!");
+    return CURLE_OUT_OF_MEMORY;
+  }
+  SSL_set_connect_state(connssl->handle);
+
+  connssl->server_cert = 0x0;
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+  if((0 == Curl_inet_pton(AF_INET, conn->host.name, &addr)) &&
+#ifdef ENABLE_IPV6
+     (0 == Curl_inet_pton(AF_INET6, conn->host.name, &addr)) &&
+#endif
+     sni &&
+     !SSL_set_tlsext_host_name(connssl->handle, conn->host.name))
+    infof(data, "WARNING: failed to configure server name indication (SNI) "
+          "TLS extension\n");
+#endif
+
+  /* Check if there's a cached ID we can/should use here! */
+  if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) {
+    /* we got a session id, use it! */
+    if(!SSL_set_session(connssl->handle, ssl_sessionid)) {
+      failf(data, "SSL: SSL_set_session failed: %s",
+            ERR_error_string(ERR_get_error(),NULL));
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+    /* Informational message */
+    infof (data, "SSL re-using session ID\n");
+  }
+
+  /* pass the raw socket into the SSL layers */
+  if(!SSL_set_fd(connssl->handle, (int)sockfd)) {
+    failf(data, "SSL: SSL_set_fd failed: %s",
+          ERR_error_string(ERR_get_error(),NULL));
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  connssl->connecting_state = ssl_connect_2;
+  return CURLE_OK;
+}
+
+static CURLcode
+ossl_connect_step2(struct connectdata *conn, int sockindex)
+{
+  struct SessionHandle *data = conn->data;
+  int err;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+
+  DEBUGASSERT(ssl_connect_2 == connssl->connecting_state
+             || ssl_connect_2_reading == connssl->connecting_state
+             || ssl_connect_2_writing == connssl->connecting_state);
+
+  ERR_clear_error();
+
+  err = SSL_connect(connssl->handle);
+
+  /* 1  is fine
+     0  is "not successful but was shut down controlled"
+     <0 is "handshake was not successful, because a fatal error occurred" */
+  if(1 != err) {
+    int detail = SSL_get_error(connssl->handle, err);
+
+    if(SSL_ERROR_WANT_READ == detail) {
+      connssl->connecting_state = ssl_connect_2_reading;
+      return CURLE_OK;
+    }
+    else if(SSL_ERROR_WANT_WRITE == detail) {
+      connssl->connecting_state = ssl_connect_2_writing;
+      return CURLE_OK;
+    }
+    else {
+      /* untreated error */
+      unsigned long errdetail;
+      char error_buffer[256]; /* OpenSSL documents that this must be at least
+                                 256 bytes long. */
+      CURLcode rc;
+      const char *cert_problem = NULL;
+      long lerr;
+
+      connssl->connecting_state = ssl_connect_2; /* the connection failed,
+                                                    we're not waiting for
+                                                    anything else. */
+
+      errdetail = ERR_get_error(); /* Gets the earliest error code from the
+                                      thread's error queue and removes the
+                                      entry. */
+
+      switch(errdetail) {
+      case 0x1407E086:
+        /* 1407E086:
+           SSL routines:
+           SSL2_SET_CERTIFICATE:
+           certificate verify failed */
+        /* fall-through */
+      case 0x14090086:
+        /* 14090086:
+           SSL routines:
+           SSL3_GET_SERVER_CERTIFICATE:
+           certificate verify failed */
+        rc = CURLE_SSL_CACERT;
+
+        lerr = SSL_get_verify_result(connssl->handle);
+        if(lerr != X509_V_OK) {
+          snprintf(error_buffer, sizeof(error_buffer),
+                   "SSL certificate problem: %s",
+                   X509_verify_cert_error_string(lerr));
+        }
+        else
+          cert_problem = "SSL certificate problem, verify that the CA cert is"
+            " OK.";
+
+        break;
+      default:
+        rc = CURLE_SSL_CONNECT_ERROR;
+        SSL_strerror(errdetail, error_buffer, sizeof(error_buffer));
+        break;
+      }
+
+      /* detail is already set to the SSL error above */
+
+      /* If we e.g. use SSLv2 request-method and the server doesn't like us
+       * (RST connection etc.), OpenSSL gives no explanation whatsoever and
+       * the SO_ERROR is also lost.
+       */
+      if(CURLE_SSL_CONNECT_ERROR == rc && errdetail == 0) {
+        failf(data, "Unknown SSL protocol error in connection to %s:%ld ",
+              conn->host.name, conn->port);
+        return rc;
+      }
+      /* Could be a CERT problem */
+
+      failf(data, "%s%s", cert_problem ? cert_problem : "", error_buffer);
+      return rc;
+    }
+  }
+  else {
+    /* we have been connected fine, we're not waiting for anything else. */
+    connssl->connecting_state = ssl_connect_3;
+
+    /* Informational message */
+    infof (data, "SSL connection using %s\n",
+           SSL_get_cipher(connssl->handle));
+
+    return CURLE_OK;
+  }
+}
+
+static int asn1_object_dump(ASN1_OBJECT *a, char *buf, size_t len)
+{
+  int i, ilen;
+
+  if((ilen = (int)len) < 0)
+    return 1; /* buffer too big */
+
+  i = i2t_ASN1_OBJECT(buf, ilen, a);
+
+  if(i >= ilen)
+    return 1; /* buffer too small */
+
+  return 0;
+}
+
+static CURLcode push_certinfo_len(struct SessionHandle *data,
+                                  int certnum,
+                                  const char *label,
+                                  const char *value,
+                                  size_t valuelen)
+{
+  struct curl_certinfo *ci = &data->info.certs;
+  char *output;
+  struct curl_slist *nl;
+  CURLcode res = CURLE_OK;
+  size_t labellen = strlen(label);
+  size_t outlen = labellen + 1 + valuelen + 1; /* label:value\0 */
+
+  output = malloc(outlen);
+  if(!output)
+    return CURLE_OUT_OF_MEMORY;
+
+  /* sprintf the label and colon */
+  snprintf(output, outlen, "%s:", label);
+
+  /* memcpy the value (it might not be zero terminated) */
+  memcpy(&output[labellen+1], value, valuelen);
+
+  /* zero terminate the output */
+  output[labellen + 1 + valuelen] = 0;
+
+  /* TODO: we should rather introduce an internal API that can do the
+     equivalent of curl_slist_append but doesn't strdup() the given data as
+     like in this place the extra malloc/free is totally pointless */
+  nl = curl_slist_append(ci->certinfo[certnum], output);
+  free(output);
+  if(!nl) {
+    curl_slist_free_all(ci->certinfo[certnum]);
+    ci->certinfo[certnum] = NULL;
+    res = CURLE_OUT_OF_MEMORY;
+  }
+  else
+    ci->certinfo[certnum] = nl;
+
+  return res;
+}
+
+/* this is a convenience function for push_certinfo_len that takes a zero
+   terminated value */
+static CURLcode push_certinfo(struct SessionHandle *data,
+                              int certnum,
+                              const char *label,
+                              const char *value)
+{
+  size_t valuelen = strlen(value);
+
+  return push_certinfo_len(data, certnum, label, value, valuelen);
+}
+
+static void pubkey_show(struct SessionHandle *data,
+                        int num,
+                        const char *type,
+                        const char *name,
+                        unsigned char *raw,
+                        int len)
+{
+  size_t left;
+  int i;
+  char namebuf[32];
+  char *buffer;
+
+  left = len*3 + 1;
+  buffer = malloc(left);
+  if(buffer) {
+    char *ptr=buffer;
+    snprintf(namebuf, sizeof(namebuf), "%s(%s)", type, name);
+    for(i=0; i< len; i++) {
+      snprintf(ptr, left, "%02x:", raw[i]);
+      ptr += 3;
+      left -= 3;
+    }
+    infof(data, "   %s: %s\n", namebuf, buffer);
+    push_certinfo(data, num, namebuf, buffer);
+    free(buffer);
+  }
+}
+
+#define print_pubkey_BN(_type, _name, _num)    \
+do {                              \
+  if(pubkey->pkey._type->_name != NULL) { \
+    int len = BN_num_bytes(pubkey->pkey._type->_name);  \
+    if(len < CERTBUFFERSIZE) {                                    \
+      BN_bn2bin(pubkey->pkey._type->_name, (unsigned char*)bufp); \
+      bufp[len] = 0;                                                    \
+      pubkey_show(data, _num, #_type, #_name, (unsigned char*)bufp, len); \
+    } \
+  } \
+} WHILE_FALSE
+
+static int X509V3_ext(struct SessionHandle *data,
+                      int certnum,
+                      STACK_OF(X509_EXTENSION) *exts)
+{
+  int i;
+  size_t j;
+
+  if(sk_X509_EXTENSION_num(exts) <= 0)
+    /* no extensions, bail out */
+    return 1;
+
+  for(i=0; i<sk_X509_EXTENSION_num(exts); i++) {
+    ASN1_OBJECT *obj;
+    X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i);
+    BUF_MEM *biomem;
+    char buf[512];
+    char *ptr=buf;
+    char namebuf[128];
+    BIO *bio_out = BIO_new(BIO_s_mem());
+
+    if(!bio_out)
+      return 1;
+
+    obj = X509_EXTENSION_get_object(ext);
+
+    asn1_object_dump(obj, namebuf, sizeof(namebuf));
+
+    infof(data, "%s: %s\n", namebuf,
+          X509_EXTENSION_get_critical(ext)?"(critical)":"");
+
+    if(!X509V3_EXT_print(bio_out, ext, 0, 0))
+      M_ASN1_OCTET_STRING_print(bio_out, ext->value);
+
+    BIO_get_mem_ptr(bio_out, &biomem);
+
+    /* biomem->length bytes at biomem->data, this little loop here is only
+       done for the infof() call, we send the "raw" data to the certinfo
+       function */
+    for(j=0; j<(size_t)biomem->length; j++) {
+      const char *sep="";
+      if(biomem->data[j] == '\n') {
+        sep=", ";
+        j++; /* skip the newline */
+      };
+      while((biomem->data[j] == ' ') && (j<(size_t)biomem->length))
+        j++;
+      if(j<(size_t)biomem->length)
+        ptr+=snprintf(ptr, sizeof(buf)-(ptr-buf), "%s%c", sep,
+                      biomem->data[j]);
+    }
+    infof(data, "  %s\n", buf);
+
+    push_certinfo(data, certnum, namebuf, buf);
+
+    BIO_free(bio_out);
+
+  }
+  return 0; /* all is fine */
+}
+
+
+static void X509_signature(struct SessionHandle *data,
+                           int numcert,
+                           ASN1_STRING *sig)
+{
+  char buf[1024];
+  char *ptr = buf;
+  int i;
+  for(i=0; i<sig->length; i++)
+    ptr+=snprintf(ptr, sizeof(buf)-(ptr-buf), "%02x:", sig->data[i]);
+
+  infof(data, " Signature: %s\n", buf);
+  push_certinfo(data, numcert, "Signature", buf);
+}
+
+static void dumpcert(struct SessionHandle *data, X509 *x, int numcert)
+{
+  BIO *bio_out = BIO_new(BIO_s_mem());
+  BUF_MEM *biomem;
+
+  /* this outputs the cert in this 64 column wide style with newlines and
+     -----BEGIN CERTIFICATE----- texts and more */
+  PEM_write_bio_X509(bio_out, x);
+
+  BIO_get_mem_ptr(bio_out, &biomem);
+
+  infof(data, "%s\n", biomem->data);
+
+  push_certinfo_len(data, numcert, "Cert", biomem->data, biomem->length);
+
+  BIO_free(bio_out);
+
+}
+
+
+static int init_certinfo(struct SessionHandle *data,
+                         int num)
+{
+  struct curl_certinfo *ci = &data->info.certs;
+  struct curl_slist **table;
+
+  Curl_ssl_free_certinfo(data);
+
+  ci->num_of_certs = num;
+  table = calloc((size_t)num, sizeof(struct curl_slist *));
+  if(!table)
+    return 1;
+
+  ci->certinfo = table;
+  return 0;
+}
+
+/*
+ * This size was previously 512 which has been reported "too small" without
+ * any specifics, so it was enlarged to allow more data to get shown uncut.
+ * The "perfect" size is yet to figure out.
+ */
+#define CERTBUFFERSIZE 8192
+
+static CURLcode get_cert_chain(struct connectdata *conn,
+                               struct ssl_connect_data *connssl)
+
+{
+  STACK_OF(X509) *sk;
+  int i;
+  char *bufp;
+  struct SessionHandle *data = conn->data;
+  int numcerts;
+
+  bufp = malloc(CERTBUFFERSIZE);
+  if(!bufp)
+    return CURLE_OUT_OF_MEMORY;
+
+  sk = SSL_get_peer_cert_chain(connssl->handle);
+  if(!sk) {
+    free(bufp);
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  numcerts = sk_X509_num(sk);
+  if(init_certinfo(data, numcerts)) {
+    free(bufp);
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  infof(data, "--- Certificate chain\n");
+  for(i=0; i<numcerts; i++) {
+    long value;
+    ASN1_INTEGER *num;
+    ASN1_TIME *certdate;
+
+    /* get the certs in "importance order" */
+#if 0
+    X509 *x = sk_X509_value(sk, numcerts - i - 1);
+#else
+    X509 *x = sk_X509_value(sk, i);
+#endif
+
+    X509_CINF *cinf;
+    EVP_PKEY *pubkey=NULL;
+    int j;
+    char *ptr;
+
+    (void)x509_name_oneline(X509_get_subject_name(x), bufp, CERTBUFFERSIZE);
+    infof(data, "%2d Subject: %s\n", i, bufp);
+    push_certinfo(data, i, "Subject", bufp);
+
+    (void)x509_name_oneline(X509_get_issuer_name(x), bufp, CERTBUFFERSIZE);
+    infof(data, "   Issuer: %s\n", bufp);
+    push_certinfo(data, i, "Issuer", bufp);
+
+    value = X509_get_version(x);
+    infof(data, "   Version: %lu (0x%lx)\n", value+1, value);
+    snprintf(bufp, CERTBUFFERSIZE, "%lx", value);
+    push_certinfo(data, i, "Version", bufp); /* hex */
+
+    num=X509_get_serialNumber(x);
+    if(num->length <= 4) {
+      value = ASN1_INTEGER_get(num);
+      infof(data,"   Serial Number: %ld (0x%lx)\n", value, value);
+      snprintf(bufp, CERTBUFFERSIZE, "%lx", value);
+    }
+    else {
+      int left = CERTBUFFERSIZE;
+
+      ptr = bufp;
+      *ptr++ = 0;
+      if(num->type == V_ASN1_NEG_INTEGER)
+        *ptr++='-';
+
+      for(j=0; (j<num->length) && (left>=4); j++) {
+        /* TODO: length restrictions */
+        snprintf(ptr, 3, "%02x%c",num->data[j],
+                 ((j+1 == num->length)?'\n':':'));
+        ptr += 3;
+        left-=4;
+      }
+      if(num->length)
+        infof(data,"   Serial Number: %s\n", bufp);
+      else
+        bufp[0]=0;
+    }
+    if(bufp[0])
+      push_certinfo(data, i, "Serial Number", bufp); /* hex */
+
+    cinf = x->cert_info;
+
+    j = asn1_object_dump(cinf->signature->algorithm, bufp, CERTBUFFERSIZE);
+    if(!j) {
+      infof(data, "   Signature Algorithm: %s\n", bufp);
+      push_certinfo(data, i, "Signature Algorithm", bufp);
+    }
+
+    certdate = X509_get_notBefore(x);
+    asn1_output(certdate, bufp, CERTBUFFERSIZE);
+    infof(data, "   Start date: %s\n", bufp);
+    push_certinfo(data, i, "Start date", bufp);
+
+    certdate = X509_get_notAfter(x);
+    asn1_output(certdate, bufp, CERTBUFFERSIZE);
+    infof(data, "   Expire date: %s\n", bufp);
+    push_certinfo(data, i, "Expire date", bufp);
+
+    j = asn1_object_dump(cinf->key->algor->algorithm, bufp, CERTBUFFERSIZE);
+    if(!j) {
+      infof(data, "   Public Key Algorithm: %s\n", bufp);
+      push_certinfo(data, i, "Public Key Algorithm", bufp);
+    }
+
+    pubkey = X509_get_pubkey(x);
+    if(!pubkey)
+      infof(data, "   Unable to load public key\n");
+    else {
+      switch(pubkey->type) {
+      case EVP_PKEY_RSA:
+        infof(data,  "   RSA Public Key (%d bits)\n",
+              BN_num_bits(pubkey->pkey.rsa->n));
+        snprintf(bufp, CERTBUFFERSIZE, "%d", BN_num_bits(pubkey->pkey.rsa->n));
+        push_certinfo(data, i, "RSA Public Key", bufp);
+
+        print_pubkey_BN(rsa, n, i);
+        print_pubkey_BN(rsa, e, i);
+        print_pubkey_BN(rsa, d, i);
+        print_pubkey_BN(rsa, p, i);
+        print_pubkey_BN(rsa, q, i);
+        print_pubkey_BN(rsa, dmp1, i);
+        print_pubkey_BN(rsa, dmq1, i);
+        print_pubkey_BN(rsa, iqmp, i);
+        break;
+      case EVP_PKEY_DSA:
+        print_pubkey_BN(dsa, p, i);
+        print_pubkey_BN(dsa, q, i);
+        print_pubkey_BN(dsa, g, i);
+        print_pubkey_BN(dsa, priv_key, i);
+        print_pubkey_BN(dsa, pub_key, i);
+        break;
+      case EVP_PKEY_DH:
+        print_pubkey_BN(dh, p, i);
+        print_pubkey_BN(dh, g, i);
+        print_pubkey_BN(dh, priv_key, i);
+        print_pubkey_BN(dh, pub_key, i);
+        break;
+#if 0
+      case EVP_PKEY_EC: /* symbol not present in OpenSSL 0.9.6 */
+        /* left TODO */
+        break;
+#endif
+      }
+      EVP_PKEY_free(pubkey);
+    }
+
+    X509V3_ext(data, i, cinf->extensions);
+
+    X509_signature(data, i, x->signature);
+
+    dumpcert(data, x, i);
+  }
+
+  free(bufp);
+
+  return CURLE_OK;
+}
+
+/*
+ * Get the server cert, verify it and show it etc, only call failf() if the
+ * 'strict' argument is TRUE as otherwise all this is for informational
+ * purposes only!
+ *
+ * We check certificates to authenticate the server; otherwise we risk
+ * man-in-the-middle attack.
+ */
+static CURLcode servercert(struct connectdata *conn,
+                           struct ssl_connect_data *connssl,
+                           bool strict)
+{
+  CURLcode retcode = CURLE_OK;
+  int rc;
+  long lerr;
+  ASN1_TIME *certdate;
+  struct SessionHandle *data = conn->data;
+  X509 *issuer;
+  FILE *fp;
+  char *buffer = data->state.buffer;
+
+  if(data->set.ssl.certinfo)
+    /* we've been asked to gather certificate info! */
+    (void)get_cert_chain(conn, connssl);
+
+  data->set.ssl.certverifyresult = !X509_V_OK;
+
+  connssl->server_cert = SSL_get_peer_certificate(connssl->handle);
+  if(!connssl->server_cert) {
+    if(strict)
+      failf(data, "SSL: couldn't get peer certificate!");
+    return CURLE_PEER_FAILED_VERIFICATION;
+  }
+  infof (data, "Server certificate:\n");
+
+  rc = x509_name_oneline(X509_get_subject_name(connssl->server_cert),
+                         buffer, BUFSIZE);
+  if(rc) {
+    if(strict)
+      failf(data, "SSL: couldn't get X509-subject!");
+    X509_free(connssl->server_cert);
+    connssl->server_cert = NULL;
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+  infof(data, "\t subject: %s\n", buffer);
+
+  certdate = X509_get_notBefore(connssl->server_cert);
+  asn1_output(certdate, buffer, BUFSIZE);
+  infof(data, "\t start date: %s\n", buffer);
+
+  certdate = X509_get_notAfter(connssl->server_cert);
+  asn1_output(certdate, buffer, BUFSIZE);
+  infof(data, "\t expire date: %s\n", buffer);
+
+  if(data->set.ssl.verifyhost) {
+    retcode = verifyhost(conn, connssl->server_cert);
+    if(retcode) {
+      X509_free(connssl->server_cert);
+      connssl->server_cert = NULL;
+      return retcode;
+    }
+  }
+
+  rc = x509_name_oneline(X509_get_issuer_name(connssl->server_cert),
+                         buffer, BUFSIZE);
+  if(rc) {
+    if(strict)
+      failf(data, "SSL: couldn't get X509-issuer name!");
+    retcode = CURLE_SSL_CONNECT_ERROR;
+  }
+  else {
+    infof(data, "\t issuer: %s\n", buffer);
+
+    /* We could do all sorts of certificate verification stuff here before
+       deallocating the certificate. */
+
+    /* e.g. match issuer name with provided issuer certificate */
+    if(data->set.str[STRING_SSL_ISSUERCERT]) {
+      fp=fopen(data->set.str[STRING_SSL_ISSUERCERT],"r");
+      if(!fp) {
+        if(strict)
+          failf(data, "SSL: Unable to open issuer cert (%s)",
+                data->set.str[STRING_SSL_ISSUERCERT]);
+        X509_free(connssl->server_cert);
+        connssl->server_cert = NULL;
+        return CURLE_SSL_ISSUER_ERROR;
+      }
+      issuer = PEM_read_X509(fp,NULL,ZERO_NULL,NULL);
+      if(!issuer) {
+        if(strict)
+          failf(data, "SSL: Unable to read issuer cert (%s)",
+                data->set.str[STRING_SSL_ISSUERCERT]);
+        X509_free(connssl->server_cert);
+        X509_free(issuer);
+        fclose(fp);
+        return CURLE_SSL_ISSUER_ERROR;
+      }
+      fclose(fp);
+      if(X509_check_issued(issuer,connssl->server_cert) != X509_V_OK) {
+        if(strict)
+          failf(data, "SSL: Certificate issuer check failed (%s)",
+                data->set.str[STRING_SSL_ISSUERCERT]);
+        X509_free(connssl->server_cert);
+        X509_free(issuer);
+        connssl->server_cert = NULL;
+        return CURLE_SSL_ISSUER_ERROR;
+      }
+      infof(data, "\t SSL certificate issuer check ok (%s)\n",
+            data->set.str[STRING_SSL_ISSUERCERT]);
+      X509_free(issuer);
+    }
+
+    lerr = data->set.ssl.certverifyresult=
+      SSL_get_verify_result(connssl->handle);
+    if(data->set.ssl.certverifyresult != X509_V_OK) {
+      if(data->set.ssl.verifypeer) {
+        /* We probably never reach this, because SSL_connect() will fail
+           and we return earlier if verifypeer is set? */
+        if(strict)
+          failf(data, "SSL certificate verify result: %s (%ld)",
+                X509_verify_cert_error_string(lerr), lerr);
+        retcode = CURLE_PEER_FAILED_VERIFICATION;
+      }
+      else
+        infof(data, "\t SSL certificate verify result: %s (%ld),"
+              " continuing anyway.\n",
+              X509_verify_cert_error_string(lerr), lerr);
+    }
+    else
+      infof(data, "\t SSL certificate verify ok.\n");
+  }
+
+  X509_free(connssl->server_cert);
+  connssl->server_cert = NULL;
+  connssl->connecting_state = ssl_connect_done;
+
+  return retcode;
+}
+
+
+static CURLcode
+ossl_connect_step3(struct connectdata *conn,
+                   int sockindex)
+{
+  CURLcode retcode = CURLE_OK;
+  void *old_ssl_sessionid=NULL;
+  struct SessionHandle *data = conn->data;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  int incache;
+  SSL_SESSION *our_ssl_sessionid;
+
+  DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
+
+#ifdef HAVE_SSL_GET1_SESSION
+  our_ssl_sessionid = SSL_get1_session(connssl->handle);
+
+  /* SSL_get1_session() will increment the reference
+     count and the session will stay in memory until explicitly freed with
+     SSL_SESSION_free(3), regardless of its state.
+     This function was introduced in openssl 0.9.5a. */
+#else
+  our_ssl_sessionid = SSL_get_session(connssl->handle);
+
+  /* if SSL_get1_session() is unavailable, use SSL_get_session().
+     This is an inferior option because the session can be flushed
+     at any time by openssl. It is included only so curl compiles
+     under versions of openssl < 0.9.5a.
+
+     WARNING: How curl behaves if it's session is flushed is
+     untested.
+  */
+#endif
+
+  incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL));
+  if(incache) {
+    if(old_ssl_sessionid != our_ssl_sessionid) {
+      infof(data, "old SSL session ID is stale, removing\n");
+      Curl_ssl_delsessionid(conn, old_ssl_sessionid);
+      incache = FALSE;
+    }
+  }
+  if(!incache) {
+    retcode = Curl_ssl_addsessionid(conn, our_ssl_sessionid,
+                                    0 /* unknown size */);
+    if(retcode) {
+      failf(data, "failed to store ssl session");
+      return retcode;
+    }
+  }
+#ifdef HAVE_SSL_GET1_SESSION
+  else {
+    /* Session was incache, so refcount already incremented earlier.
+     * Avoid further increments with each SSL_get1_session() call.
+     * This does not free the session as refcount remains > 0
+     */
+    SSL_SESSION_free(our_ssl_sessionid);
+  }
+#endif
+
+  /*
+   * We check certificates to authenticate the server; otherwise we risk
+   * man-in-the-middle attack; NEVERTHELESS, if we're told explicitly not to
+   * verify the peer ignore faults and failures from the server cert
+   * operations.
+   */
+
+  if(!data->set.ssl.verifypeer)
+    (void)servercert(conn, connssl, FALSE);
+  else
+    retcode = servercert(conn, connssl, TRUE);
+
+  if(CURLE_OK == retcode)
+    connssl->connecting_state = ssl_connect_done;
+  return retcode;
+}
+
+static Curl_recv ossl_recv;
+static Curl_send ossl_send;
+
+static CURLcode
+ossl_connect_common(struct connectdata *conn,
+                    int sockindex,
+                    bool nonblocking,
+                    bool *done)
+{
+  CURLcode retcode;
+  struct SessionHandle *data = conn->data;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  curl_socket_t sockfd = conn->sock[sockindex];
+  long timeout_ms;
+  int what;
+
+  /* check if the connection has already been established */
+  if(ssl_connection_complete == connssl->state) {
+    *done = TRUE;
+    return CURLE_OK;
+  }
+
+  if(ssl_connect_1==connssl->connecting_state) {
+    /* Find out how much more time we're allowed */
+    timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+    if(timeout_ms < 0) {
+      /* no need to continue if time already is up */
+      failf(data, "SSL connection timeout");
+      return CURLE_OPERATION_TIMEDOUT;
+    }
+    retcode = ossl_connect_step1(conn, sockindex);
+    if(retcode)
+      return retcode;
+  }
+
+  while(ssl_connect_2 == connssl->connecting_state ||
+        ssl_connect_2_reading == connssl->connecting_state ||
+        ssl_connect_2_writing == connssl->connecting_state) {
+
+    /* check allowed time left */
+    timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+    if(timeout_ms < 0) {
+      /* no need to continue if time already is up */
+      failf(data, "SSL connection timeout");
+      return CURLE_OPERATION_TIMEDOUT;
+    }
+
+    /* if ssl is expecting something, check if it's available. */
+    if(connssl->connecting_state == ssl_connect_2_reading
+        || connssl->connecting_state == ssl_connect_2_writing) {
+
+      curl_socket_t writefd = ssl_connect_2_writing==
+        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+      curl_socket_t readfd = ssl_connect_2_reading==
+        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+
+      what = Curl_socket_ready(readfd, writefd, nonblocking?0:timeout_ms);
+      if(what < 0) {
+        /* fatal error */
+        failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+        return CURLE_SSL_CONNECT_ERROR;
+      }
+      else if(0 == what) {
+        if(nonblocking) {
+          *done = FALSE;
+          return CURLE_OK;
+        }
+        else {
+          /* timeout */
+          failf(data, "SSL connection timeout");
+          return CURLE_OPERATION_TIMEDOUT;
+        }
+      }
+      /* socket is readable or writable */
+    }
+
+    /* Run transaction, and return to the caller if it failed or if this
+     * connection is done nonblocking and this loop would execute again. This
+     * permits the owner of a multi handle to abort a connection attempt
+     * before step2 has completed while ensuring that a client using select()
+     * or epoll() will always have a valid fdset to wait on.
+     */
+    retcode = ossl_connect_step2(conn, sockindex);
+    if(retcode || (nonblocking &&
+                   (ssl_connect_2 == connssl->connecting_state ||
+                    ssl_connect_2_reading == connssl->connecting_state ||
+                    ssl_connect_2_writing == connssl->connecting_state)))
+      return retcode;
+
+  } /* repeat step2 until all transactions are done. */
+
+
+  if(ssl_connect_3==connssl->connecting_state) {
+    retcode = ossl_connect_step3(conn, sockindex);
+    if(retcode)
+      return retcode;
+  }
+
+  if(ssl_connect_done==connssl->connecting_state) {
+    connssl->state = ssl_connection_complete;
+    conn->recv[sockindex] = ossl_recv;
+    conn->send[sockindex] = ossl_send;
+    *done = TRUE;
+  }
+  else
+    *done = FALSE;
+
+  /* Reset our connect state machine */
+  connssl->connecting_state = ssl_connect_1;
+
+  return CURLE_OK;
+}
+
+CURLcode
+Curl_ossl_connect_nonblocking(struct connectdata *conn,
+                              int sockindex,
+                              bool *done)
+{
+  return ossl_connect_common(conn, sockindex, TRUE, done);
+}
+
+CURLcode
+Curl_ossl_connect(struct connectdata *conn,
+                  int sockindex)
+{
+  CURLcode retcode;
+  bool done = FALSE;
+
+  retcode = ossl_connect_common(conn, sockindex, FALSE, &done);
+  if(retcode)
+    return retcode;
+
+  DEBUGASSERT(done);
+
+  return CURLE_OK;
+}
+
+bool Curl_ossl_data_pending(const struct connectdata *conn,
+                            int connindex)
+{
+  if(conn->ssl[connindex].handle)
+    /* SSL is in use */
+    return (0 != SSL_pending(conn->ssl[connindex].handle)) ? TRUE : FALSE;
+  else
+    return FALSE;
+}
+
+static ssize_t ossl_send(struct connectdata *conn,
+                         int sockindex,
+                         const void *mem,
+                         size_t len,
+                         CURLcode *curlcode)
+{
+  /* SSL_write() is said to return 'int' while write() and send() returns
+     'size_t' */
+  int err;
+  char error_buffer[120]; /* OpenSSL documents that this must be at least 120
+                             bytes long. */
+  unsigned long sslerror;
+  int memlen;
+  int rc;
+
+  ERR_clear_error();
+
+  memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
+  rc = SSL_write(conn->ssl[sockindex].handle, mem, memlen);
+
+  if(rc < 0) {
+    err = SSL_get_error(conn->ssl[sockindex].handle, rc);
+
+    switch(err) {
+    case SSL_ERROR_WANT_READ:
+    case SSL_ERROR_WANT_WRITE:
+      /* The operation did not complete; the same TLS/SSL I/O function
+         should be called again later. This is basically an EWOULDBLOCK
+         equivalent. */
+      *curlcode = CURLE_AGAIN;
+      return -1;
+    case SSL_ERROR_SYSCALL:
+      failf(conn->data, "SSL_write() returned SYSCALL, errno = %d",
+            SOCKERRNO);
+      *curlcode = CURLE_SEND_ERROR;
+      return -1;
+    case SSL_ERROR_SSL:
+      /*  A failure in the SSL library occurred, usually a protocol error.
+          The OpenSSL error queue contains more information on the error. */
+      sslerror = ERR_get_error();
+      failf(conn->data, "SSL_write() error: %s",
+            ERR_error_string(sslerror, error_buffer));
+      *curlcode = CURLE_SEND_ERROR;
+      return -1;
+    }
+    /* a true error */
+    failf(conn->data, "SSL_write() return error %d", err);
+    *curlcode = CURLE_SEND_ERROR;
+    return -1;
+  }
+  return (ssize_t)rc; /* number of bytes */
+}
+
+static ssize_t ossl_recv(struct connectdata *conn, /* connection data */
+                         int num,                  /* socketindex */
+                         char *buf,                /* store read data here */
+                         size_t buffersize,        /* max amount to read */
+                         CURLcode *curlcode)
+{
+  char error_buffer[120]; /* OpenSSL documents that this must be at
+                             least 120 bytes long. */
+  unsigned long sslerror;
+  ssize_t nread;
+  int buffsize;
+
+  ERR_clear_error();
+
+  buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
+  nread = (ssize_t)SSL_read(conn->ssl[num].handle, buf, buffsize);
+  if(nread < 0) {
+    /* failed SSL_read */
+    int err = SSL_get_error(conn->ssl[num].handle, (int)nread);
+
+    switch(err) {
+    case SSL_ERROR_NONE: /* this is not an error */
+    case SSL_ERROR_ZERO_RETURN: /* no more data */
+      break;
+    case SSL_ERROR_WANT_READ:
+    case SSL_ERROR_WANT_WRITE:
+      /* there's data pending, re-invoke SSL_read() */
+      *curlcode = CURLE_AGAIN;
+      return -1;
+    default:
+      /* openssl/ssl.h says "look at error stack/return value/errno" */
+      sslerror = ERR_get_error();
+      failf(conn->data, "SSL read: %s, errno %d",
+            ERR_error_string(sslerror, error_buffer),
+            SOCKERRNO);
+      *curlcode = CURLE_RECV_ERROR;
+      return -1;
+    }
+  }
+  return nread;
+}
+
+size_t Curl_ossl_version(char *buffer, size_t size)
+{
+#ifdef YASSL_VERSION
+  /* yassl provides an OpenSSL API compatibility layer so it looks identical
+     to OpenSSL in all other aspects */
+  return snprintf(buffer, size, "yassl/%s", YASSL_VERSION);
+#else /* YASSL_VERSION */
+
+#if(SSLEAY_VERSION_NUMBER >= 0x905000)
+  {
+    char sub[2];
+    unsigned long ssleay_value;
+    sub[1]='\0';
+    ssleay_value=SSLeay();
+    if(ssleay_value < 0x906000) {
+      ssleay_value=SSLEAY_VERSION_NUMBER;
+      sub[0]='\0';
+    }
+    else {
+      if(ssleay_value&0xff0) {
+        sub[0]=(char)(((ssleay_value>>4)&0xff) + 'a' -1);
+      }
+      else
+        sub[0]='\0';
+    }
+
+    return snprintf(buffer, size, "OpenSSL/%lx.%lx.%lx%s",
+                    (ssleay_value>>28)&0xf,
+                    (ssleay_value>>20)&0xff,
+                    (ssleay_value>>12)&0xff,
+                    sub);
+  }
+
+#else /* SSLEAY_VERSION_NUMBER is less than 0.9.5 */
+
+#if(SSLEAY_VERSION_NUMBER >= 0x900000)
+  return snprintf(buffer, size, "OpenSSL/%lx.%lx.%lx",
+                  (SSLEAY_VERSION_NUMBER>>28)&0xff,
+                  (SSLEAY_VERSION_NUMBER>>20)&0xff,
+                  (SSLEAY_VERSION_NUMBER>>12)&0xf);
+
+#else /* (SSLEAY_VERSION_NUMBER >= 0x900000) */
+  {
+    char sub[2];
+    sub[1]='\0';
+    if(SSLEAY_VERSION_NUMBER&0x0f) {
+      sub[0]=(SSLEAY_VERSION_NUMBER&0x0f) + 'a' -1;
+    }
+    else
+      sub[0]='\0';
+
+    return snprintf(buffer, size, "SSL/%x.%x.%x%s",
+                    (SSLEAY_VERSION_NUMBER>>12)&0xff,
+                    (SSLEAY_VERSION_NUMBER>>8)&0xf,
+                    (SSLEAY_VERSION_NUMBER>>4)&0xf, sub);
+  }
+#endif /* (SSLEAY_VERSION_NUMBER >= 0x900000) */
+#endif /* SSLEAY_VERSION_NUMBER is less than 0.9.5 */
+
+#endif /* YASSL_VERSION */
+}
+
+void Curl_ossl_random(struct SessionHandle *data, unsigned char *entropy,
+                      size_t length)
+{
+  Curl_ossl_seed(data); /* Initiate the seed if not already done */
+  RAND_bytes(entropy, curlx_uztosi(length));
+}
+
+void Curl_ossl_md5sum(unsigned char *tmp, /* input */
+                      size_t tmplen,
+                      unsigned char *md5sum /* output */,
+                      size_t unused)
+{
+  MD5_CTX MD5pw;
+  (void)unused;
+  MD5_Init(&MD5pw);
+  MD5_Update(&MD5pw, tmp, tmplen);
+  MD5_Final(md5sum, &MD5pw);
+}
+#endif /* USE_SSLEAY */
diff --git a/lib/curl_strdup.c b/lib/curl_strdup.c
new file mode 100644 (file)
index 0000000..8dcaa67
--- /dev/null
@@ -0,0 +1,52 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+/*
+ * This file is 'mem-include-scan' clean. See test 1132.
+ */
+#include "curl_setup.h"
+
+#include "curl_strdup.h"
+
+#ifndef HAVE_STRDUP
+char *curlx_strdup(const char *str)
+{
+  size_t len;
+  char *newstr;
+
+  if(!str)
+    return (char *)NULL;
+
+  len = strlen(str);
+
+  if(len >= ((size_t)-1) / sizeof(char))
+    return (char *)NULL;
+
+  newstr = malloc((len+1)*sizeof(char));
+  if(!newstr)
+    return (char *)NULL;
+
+  memcpy(newstr,str,(len+1)*sizeof(char));
+
+  return newstr;
+
+}
+#endif
diff --git a/lib/curl_strequal.c b/lib/curl_strequal.c
new file mode 100644 (file)
index 0000000..5d370c8
--- /dev/null
@@ -0,0 +1,124 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#include "curl_strequal.h"
+
+/*
+ * @unittest: 1301
+ */
+int curl_strequal(const char *first, const char *second)
+{
+#if defined(HAVE_STRCASECMP)
+  return !(strcasecmp)(first, second);
+#elif defined(HAVE_STRCMPI)
+  return !(strcmpi)(first, second);
+#elif defined(HAVE_STRICMP)
+  return !(stricmp)(first, second);
+#else
+  while(*first && *second) {
+    if(toupper(*first) != toupper(*second)) {
+      break;
+    }
+    first++;
+    second++;
+  }
+  return toupper(*first) == toupper(*second);
+#endif
+}
+
+/*
+ * @unittest: 1301
+ */
+int curl_strnequal(const char *first, const char *second, size_t max)
+{
+#if defined(HAVE_STRNCASECMP)
+  return !strncasecmp(first, second, max);
+#elif defined(HAVE_STRNCMPI)
+  return !strncmpi(first, second, max);
+#elif defined(HAVE_STRNICMP)
+  return !strnicmp(first, second, max);
+#else
+  while(*first && *second && max) {
+    if(toupper(*first) != toupper(*second)) {
+      break;
+    }
+    max--;
+    first++;
+    second++;
+  }
+  if(0 == max)
+    return 1; /* they are equal this far */
+
+  return toupper(*first) == toupper(*second);
+#endif
+}
+
+#ifndef HAVE_STRLCAT
+/*
+ * The strlcat() function appends the NUL-terminated string src to the end
+ * of dst. It will append at most size - strlen(dst) - 1 bytes, NUL-termi-
+ * nating the result.
+ *
+ * The strlcpy() and strlcat() functions return the total length of the
+ * string they tried to create.  For strlcpy() that means the length of src.
+ * For strlcat() that means the initial length of dst plus the length of
+ * src. While this may seem somewhat confusing it was done to make trunca-
+ * tion detection simple.
+ *
+ *
+ */
+size_t Curl_strlcat(char *dst, const char *src, size_t siz)
+{
+  char *d = dst;
+  const char *s = src;
+  size_t n = siz;
+  union {
+    ssize_t sig;
+     size_t uns;
+  } dlen;
+
+  /* Find the end of dst and adjust bytes left but don't go past end */
+  while(n-- != 0 && *d != '\0')
+    d++;
+  dlen.sig = d - dst;
+  n = siz - dlen.uns;
+
+  if(n == 0)
+    return(dlen.uns + strlen(s));
+  while(*s != '\0') {
+    if(n != 1) {
+      *d++ = *s;
+      n--;
+    }
+    s++;
+  }
+  *d = '\0';
+
+  return(dlen.uns + (s - src));     /* count does not include NUL */
+}
+#endif
diff --git a/lib/curl_strerror.c b/lib/curl_strerror.c
new file mode 100644 (file)
index 0000000..27567a1
--- /dev/null
@@ -0,0 +1,1119 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2004 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_STRERROR_R
+#  if (!defined(HAVE_POSIX_STRERROR_R) && \
+       !defined(HAVE_GLIBC_STRERROR_R) && \
+       !defined(HAVE_VXWORKS_STRERROR_R)) || \
+      (defined(HAVE_POSIX_STRERROR_R) && defined(HAVE_VXWORKS_STRERROR_R)) || \
+      (defined(HAVE_GLIBC_STRERROR_R) && defined(HAVE_VXWORKS_STRERROR_R)) || \
+      (defined(HAVE_POSIX_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R))
+#    error "strerror_r MUST be either POSIX, glibc or vxworks-style"
+#  endif
+#endif
+
+#include <curl/curl.h>
+
+#ifdef USE_LIBIDN
+#include <idna.h>
+#endif
+
+#include "curl_strerror.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+const char *
+curl_easy_strerror(CURLcode error)
+{
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+  switch (error) {
+  case CURLE_OK:
+    return "No error";
+
+  case CURLE_UNSUPPORTED_PROTOCOL:
+    return "Unsupported protocol";
+
+  case CURLE_FAILED_INIT:
+    return "Failed initialization";
+
+  case CURLE_URL_MALFORMAT:
+    return "URL using bad/illegal format or missing URL";
+
+  case CURLE_NOT_BUILT_IN:
+    return "A requested feature, protocol or option was not found built-in in"
+      " this libcurl due to a build-time decision.";
+
+  case CURLE_COULDNT_RESOLVE_PROXY:
+    return "Couldn't resolve proxy name";
+
+  case CURLE_COULDNT_RESOLVE_HOST:
+    return "Couldn't resolve host name";
+
+  case CURLE_COULDNT_CONNECT:
+    return "Couldn't connect to server";
+
+  case CURLE_FTP_WEIRD_SERVER_REPLY:
+    return "FTP: weird server reply";
+
+  case CURLE_REMOTE_ACCESS_DENIED:
+    return "Access denied to remote resource";
+
+  case CURLE_FTP_ACCEPT_FAILED:
+    return "FTP: The server failed to connect to data port";
+
+  case CURLE_FTP_ACCEPT_TIMEOUT:
+    return "FTP: Accepting server connect has timed out";
+
+  case CURLE_FTP_PRET_FAILED:
+    return "FTP: The server did not accept the PRET command.";
+
+  case CURLE_FTP_WEIRD_PASS_REPLY:
+    return "FTP: unknown PASS reply";
+
+  case CURLE_FTP_WEIRD_PASV_REPLY:
+    return "FTP: unknown PASV reply";
+
+  case CURLE_FTP_WEIRD_227_FORMAT:
+    return "FTP: unknown 227 response format";
+
+  case CURLE_FTP_CANT_GET_HOST:
+    return "FTP: can't figure out the host in the PASV response";
+
+  case CURLE_FTP_COULDNT_SET_TYPE:
+    return "FTP: couldn't set file type";
+
+  case CURLE_PARTIAL_FILE:
+    return "Transferred a partial file";
+
+  case CURLE_FTP_COULDNT_RETR_FILE:
+    return "FTP: couldn't retrieve (RETR failed) the specified file";
+
+  case CURLE_QUOTE_ERROR:
+    return "Quote command returned error";
+
+  case CURLE_HTTP_RETURNED_ERROR:
+    return "HTTP response code said error";
+
+  case CURLE_WRITE_ERROR:
+    return "Failed writing received data to disk/application";
+
+  case CURLE_UPLOAD_FAILED:
+    return "Upload failed (at start/before it took off)";
+
+  case CURLE_READ_ERROR:
+    return "Failed to open/read local data from file/application";
+
+  case CURLE_OUT_OF_MEMORY:
+    return "Out of memory";
+
+  case CURLE_OPERATION_TIMEDOUT:
+    return "Timeout was reached";
+
+  case CURLE_FTP_PORT_FAILED:
+    return "FTP: command PORT failed";
+
+  case CURLE_FTP_COULDNT_USE_REST:
+    return "FTP: command REST failed";
+
+  case CURLE_RANGE_ERROR:
+    return "Requested range was not delivered by the server";
+
+  case CURLE_HTTP_POST_ERROR:
+    return "Internal problem setting up the POST";
+
+  case CURLE_SSL_CONNECT_ERROR:
+    return "SSL connect error";
+
+  case CURLE_BAD_DOWNLOAD_RESUME:
+    return "Couldn't resume download";
+
+  case CURLE_FILE_COULDNT_READ_FILE:
+    return "Couldn't read a file:// file";
+
+  case CURLE_LDAP_CANNOT_BIND:
+    return "LDAP: cannot bind";
+
+  case CURLE_LDAP_SEARCH_FAILED:
+    return "LDAP: search failed";
+
+  case CURLE_FUNCTION_NOT_FOUND:
+    return "A required function in the library was not found";
+
+  case CURLE_ABORTED_BY_CALLBACK:
+    return "Operation was aborted by an application callback";
+
+  case CURLE_BAD_FUNCTION_ARGUMENT:
+    return "A libcurl function was given a bad argument";
+
+  case CURLE_INTERFACE_FAILED:
+    return "Failed binding local connection end";
+
+  case CURLE_TOO_MANY_REDIRECTS :
+    return "Number of redirects hit maximum amount";
+
+  case CURLE_UNKNOWN_OPTION:
+    return "An unknown option was passed in to libcurl";
+
+  case CURLE_TELNET_OPTION_SYNTAX :
+    return "Malformed telnet option";
+
+  case CURLE_PEER_FAILED_VERIFICATION:
+    return "SSL peer certificate or SSH remote key was not OK";
+
+  case CURLE_GOT_NOTHING:
+    return "Server returned nothing (no headers, no data)";
+
+  case CURLE_SSL_ENGINE_NOTFOUND:
+    return "SSL crypto engine not found";
+
+  case CURLE_SSL_ENGINE_SETFAILED:
+    return "Can not set SSL crypto engine as default";
+
+  case CURLE_SSL_ENGINE_INITFAILED:
+    return "Failed to initialise SSL crypto engine";
+
+  case CURLE_SEND_ERROR:
+    return "Failed sending data to the peer";
+
+  case CURLE_RECV_ERROR:
+    return "Failure when receiving data from the peer";
+
+  case CURLE_SSL_CERTPROBLEM:
+    return "Problem with the local SSL certificate";
+
+  case CURLE_SSL_CIPHER:
+    return "Couldn't use specified SSL cipher";
+
+  case CURLE_SSL_CACERT:
+    return "Peer certificate cannot be authenticated with given CA "
+      "certificates";
+
+  case CURLE_SSL_CACERT_BADFILE:
+    return "Problem with the SSL CA cert (path? access rights?)";
+
+  case CURLE_BAD_CONTENT_ENCODING:
+    return "Unrecognized or bad HTTP Content or Transfer-Encoding";
+
+  case CURLE_LDAP_INVALID_URL:
+    return "Invalid LDAP URL";
+
+  case CURLE_FILESIZE_EXCEEDED:
+    return "Maximum file size exceeded";
+
+  case CURLE_USE_SSL_FAILED:
+    return "Requested SSL level failed";
+
+  case CURLE_SSL_SHUTDOWN_FAILED:
+    return "Failed to shut down the SSL connection";
+
+  case CURLE_SSL_CRL_BADFILE:
+    return "Failed to load CRL file (path? access rights?, format?)";
+
+  case CURLE_SSL_ISSUER_ERROR:
+    return "Issuer check against peer certificate failed";
+
+  case CURLE_SEND_FAIL_REWIND:
+    return "Send failed since rewinding of the data stream failed";
+
+  case CURLE_LOGIN_DENIED:
+    return "Login denied";
+
+  case CURLE_TFTP_NOTFOUND:
+    return "TFTP: File Not Found";
+
+  case CURLE_TFTP_PERM:
+    return "TFTP: Access Violation";
+
+  case CURLE_REMOTE_DISK_FULL:
+    return "Disk full or allocation exceeded";
+
+  case CURLE_TFTP_ILLEGAL:
+    return "TFTP: Illegal operation";
+
+  case CURLE_TFTP_UNKNOWNID:
+    return "TFTP: Unknown transfer ID";
+
+  case CURLE_REMOTE_FILE_EXISTS:
+    return "Remote file already exists";
+
+  case CURLE_TFTP_NOSUCHUSER:
+    return "TFTP: No such user";
+
+  case CURLE_CONV_FAILED:
+    return "Conversion failed";
+
+  case CURLE_CONV_REQD:
+    return "Caller must register CURLOPT_CONV_ callback options";
+
+  case CURLE_REMOTE_FILE_NOT_FOUND:
+    return "Remote file not found";
+
+  case CURLE_SSH:
+    return "Error in the SSH layer";
+
+  case CURLE_AGAIN:
+    return "Socket not ready for send/recv";
+
+  case CURLE_RTSP_CSEQ_ERROR:
+    return "RTSP CSeq mismatch or invalid CSeq";
+
+  case CURLE_RTSP_SESSION_ERROR:
+    return "RTSP session error";
+
+  case CURLE_FTP_BAD_FILE_LIST:
+    return "Unable to parse FTP file list";
+
+  case CURLE_CHUNK_FAILED:
+    return "Chunk callback failed";
+
+    /* error codes not used by current libcurl */
+  case CURLE_OBSOLETE16:
+  case CURLE_OBSOLETE20:
+  case CURLE_OBSOLETE24:
+  case CURLE_OBSOLETE29:
+  case CURLE_OBSOLETE32:
+  case CURLE_OBSOLETE40:
+  case CURLE_OBSOLETE44:
+  case CURLE_OBSOLETE46:
+  case CURLE_OBSOLETE50:
+  case CURLE_OBSOLETE57:
+  case CURL_LAST:
+    break;
+  }
+  /*
+   * By using a switch, gcc -Wall will complain about enum values
+   * which do not appear, helping keep this function up-to-date.
+   * By using gcc -Wall -Werror, you can't forget.
+   *
+   * A table would not have the same benefit.  Most compilers will
+   * generate code very similar to a table in any case, so there
+   * is little performance gain from a table.  And something is broken
+   * for the user's application, anyways, so does it matter how fast
+   * it _doesn't_ work?
+   *
+   * The line number for the error will be near this comment, which
+   * is why it is here, and not at the start of the switch.
+   */
+  return "Unknown error";
+#else
+  if(error == CURLE_OK)
+    return "No error";
+  else
+    return "Error";
+#endif
+}
+
+const char *
+curl_multi_strerror(CURLMcode error)
+{
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+  switch (error) {
+  case CURLM_CALL_MULTI_PERFORM:
+    return "Please call curl_multi_perform() soon";
+
+  case CURLM_OK:
+    return "No error";
+
+  case CURLM_BAD_HANDLE:
+    return "Invalid multi handle";
+
+  case CURLM_BAD_EASY_HANDLE:
+    return "Invalid easy handle";
+
+  case CURLM_OUT_OF_MEMORY:
+    return "Out of memory";
+
+  case CURLM_INTERNAL_ERROR:
+    return "Internal error";
+
+  case CURLM_BAD_SOCKET:
+    return "Invalid socket argument";
+
+  case CURLM_UNKNOWN_OPTION:
+    return "Unknown option";
+
+  case CURLM_LAST:
+    break;
+  }
+
+  return "Unknown error";
+#else
+  if(error == CURLM_OK)
+    return "No error";
+  else
+    return "Error";
+#endif
+}
+
+const char *
+curl_share_strerror(CURLSHcode error)
+{
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+  switch (error) {
+  case CURLSHE_OK:
+    return "No error";
+
+  case CURLSHE_BAD_OPTION:
+    return "Unknown share option";
+
+  case CURLSHE_IN_USE:
+    return "Share currently in use";
+
+  case CURLSHE_INVALID:
+    return "Invalid share handle";
+
+  case CURLSHE_NOMEM:
+    return "Out of memory";
+
+  case CURLSHE_NOT_BUILT_IN:
+    return "Feature not enabled in this library";
+
+  case CURLSHE_LAST:
+    break;
+  }
+
+  return "CURLSHcode unknown";
+#else
+  if(error == CURLSHE_OK)
+    return "No error";
+  else
+    return "Error";
+#endif
+}
+
+#ifdef USE_WINSOCK
+
+/* This function handles most / all (?) Winsock errors cURL is able to produce.
+ */
+static const char *
+get_winsock_error (int err, char *buf, size_t len)
+{
+  const char *p;
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+  switch (err) {
+  case WSAEINTR:
+    p = "Call interrupted";
+    break;
+  case WSAEBADF:
+    p = "Bad file";
+    break;
+  case WSAEACCES:
+    p = "Bad access";
+    break;
+  case WSAEFAULT:
+    p = "Bad argument";
+    break;
+  case WSAEINVAL:
+    p = "Invalid arguments";
+    break;
+  case WSAEMFILE:
+    p = "Out of file descriptors";
+    break;
+  case WSAEWOULDBLOCK:
+    p = "Call would block";
+    break;
+  case WSAEINPROGRESS:
+  case WSAEALREADY:
+    p = "Blocking call in progress";
+    break;
+  case WSAENOTSOCK:
+    p = "Descriptor is not a socket";
+    break;
+  case WSAEDESTADDRREQ:
+    p = "Need destination address";
+    break;
+  case WSAEMSGSIZE:
+    p = "Bad message size";
+    break;
+  case WSAEPROTOTYPE:
+    p = "Bad protocol";
+    break;
+  case WSAENOPROTOOPT:
+    p = "Protocol option is unsupported";
+    break;
+  case WSAEPROTONOSUPPORT:
+    p = "Protocol is unsupported";
+    break;
+  case WSAESOCKTNOSUPPORT:
+    p = "Socket is unsupported";
+    break;
+  case WSAEOPNOTSUPP:
+    p = "Operation not supported";
+    break;
+  case WSAEAFNOSUPPORT:
+    p = "Address family not supported";
+    break;
+  case WSAEPFNOSUPPORT:
+    p = "Protocol family not supported";
+    break;
+  case WSAEADDRINUSE:
+    p = "Address already in use";
+    break;
+  case WSAEADDRNOTAVAIL:
+    p = "Address not available";
+    break;
+  case WSAENETDOWN:
+    p = "Network down";
+    break;
+  case WSAENETUNREACH:
+    p = "Network unreachable";
+    break;
+  case WSAENETRESET:
+    p = "Network has been reset";
+    break;
+  case WSAECONNABORTED:
+    p = "Connection was aborted";
+    break;
+  case WSAECONNRESET:
+    p = "Connection was reset";
+    break;
+  case WSAENOBUFS:
+    p = "No buffer space";
+    break;
+  case WSAEISCONN:
+    p = "Socket is already connected";
+    break;
+  case WSAENOTCONN:
+    p = "Socket is not connected";
+    break;
+  case WSAESHUTDOWN:
+    p = "Socket has been shut down";
+    break;
+  case WSAETOOMANYREFS:
+    p = "Too many references";
+    break;
+  case WSAETIMEDOUT:
+    p = "Timed out";
+    break;
+  case WSAECONNREFUSED:
+    p = "Connection refused";
+    break;
+  case WSAELOOP:
+    p = "Loop??";
+    break;
+  case WSAENAMETOOLONG:
+    p = "Name too long";
+    break;
+  case WSAEHOSTDOWN:
+    p = "Host down";
+    break;
+  case WSAEHOSTUNREACH:
+    p = "Host unreachable";
+    break;
+  case WSAENOTEMPTY:
+    p = "Not empty";
+    break;
+  case WSAEPROCLIM:
+    p = "Process limit reached";
+    break;
+  case WSAEUSERS:
+    p = "Too many users";
+    break;
+  case WSAEDQUOT:
+    p = "Bad quota";
+    break;
+  case WSAESTALE:
+    p = "Something is stale";
+    break;
+  case WSAEREMOTE:
+    p = "Remote error";
+    break;
+#ifdef WSAEDISCON  /* missing in SalfordC! */
+  case WSAEDISCON:
+    p = "Disconnected";
+    break;
+#endif
+    /* Extended Winsock errors */
+  case WSASYSNOTREADY:
+    p = "Winsock library is not ready";
+    break;
+  case WSANOTINITIALISED:
+    p = "Winsock library not initialised";
+    break;
+  case WSAVERNOTSUPPORTED:
+    p = "Winsock version not supported";
+    break;
+
+    /* getXbyY() errors (already handled in herrmsg):
+     * Authoritative Answer: Host not found */
+  case WSAHOST_NOT_FOUND:
+    p = "Host not found";
+    break;
+
+    /* Non-Authoritative: Host not found, or SERVERFAIL */
+  case WSATRY_AGAIN:
+    p = "Host not found, try again";
+    break;
+
+    /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */
+  case WSANO_RECOVERY:
+    p = "Unrecoverable error in call to nameserver";
+    break;
+
+    /* Valid name, no data record of requested type */
+  case WSANO_DATA:
+    p = "No data record of requested type";
+    break;
+
+  default:
+    return NULL;
+  }
+#else
+  if(err == CURLE_OK)
+    return NULL;
+  else
+    p = "error";
+#endif
+  strncpy (buf, p, len);
+  buf [len-1] = '\0';
+  return buf;
+}
+#endif   /* USE_WINSOCK */
+
+/*
+ * Our thread-safe and smart strerror() replacement.
+ *
+ * The 'err' argument passed in to this function MUST be a true errno number
+ * as reported on this system. We do no range checking on the number before
+ * we pass it to the "number-to-message" conversion function and there might
+ * be systems that don't do proper range checking in there themselves.
+ *
+ * We don't do range checking (on systems other than Windows) since there is
+ * no good reliable and portable way to do it.
+ */
+const char *Curl_strerror(struct connectdata *conn, int err)
+{
+  char *buf, *p;
+  size_t max;
+  int old_errno = ERRNO;
+
+  DEBUGASSERT(conn);
+  DEBUGASSERT(err >= 0);
+
+  buf = conn->syserr_buf;
+  max = sizeof(conn->syserr_buf)-1;
+  *buf = '\0';
+
+#ifdef USE_WINSOCK
+
+#ifdef _WIN32_WCE
+  {
+    wchar_t wbuf[256];
+    wbuf[0] = L'\0';
+
+    FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
+                  LANG_NEUTRAL, wbuf, sizeof(wbuf)/sizeof(wchar_t), NULL);
+    wcstombs(buf,wbuf,max);
+  }
+#else
+  /* 'sys_nerr' is the maximum errno number, it is not widely portable */
+  if(err >= 0 && err < sys_nerr)
+    strncpy(buf, strerror(err), max);
+  else {
+    if(!get_winsock_error(err, buf, max) &&
+       !FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
+                       LANG_NEUTRAL, buf, (DWORD)max, NULL))
+      snprintf(buf, max, "Unknown error %d (%#x)", err, err);
+  }
+#endif
+
+#else /* not USE_WINSOCK coming up */
+
+#if defined(HAVE_STRERROR_R) && defined(HAVE_POSIX_STRERROR_R)
+ /*
+  * The POSIX-style strerror_r() may set errno to ERANGE if insufficient
+  * storage is supplied via 'strerrbuf' and 'buflen' to hold the generated
+  * message string, or EINVAL if 'errnum' is not a valid error number.
+  */
+  if(0 != strerror_r(err, buf, max)) {
+    if('\0' == buf[0])
+      snprintf(buf, max, "Unknown error %d", err);
+  }
+#elif defined(HAVE_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R)
+ /*
+  * The glibc-style strerror_r() only *might* use the buffer we pass to
+  * the function, but it always returns the error message as a pointer,
+  * so we must copy that string unconditionally (if non-NULL).
+  */
+  {
+    char buffer[256];
+    char *msg = strerror_r(err, buffer, sizeof(buffer));
+    if(msg)
+      strncpy(buf, msg, max);
+    else
+      snprintf(buf, max, "Unknown error %d", err);
+  }
+#elif defined(HAVE_STRERROR_R) && defined(HAVE_VXWORKS_STRERROR_R)
+ /*
+  * The vxworks-style strerror_r() does use the buffer we pass to the function.
+  * The buffer size should be at least MAXERRSTR_SIZE (150) defined in rtsold.h
+  */
+  {
+    char buffer[256];
+    if(OK == strerror_r(err, buffer))
+      strncpy(buf, buffer, max);
+    else
+      snprintf(buf, max, "Unknown error %d", err);
+  }
+#else
+  {
+    char *msg = strerror(err);
+    if(msg)
+      strncpy(buf, msg, max);
+    else
+      snprintf(buf, max, "Unknown error %d", err);
+  }
+#endif
+
+#endif /* end of ! USE_WINSOCK */
+
+  buf[max] = '\0'; /* make sure the string is zero terminated */
+
+  /* strip trailing '\r\n' or '\n'. */
+  if((p = strrchr(buf,'\n')) != NULL && (p - buf) >= 2)
+     *p = '\0';
+  if((p = strrchr(buf,'\r')) != NULL && (p - buf) >= 1)
+     *p = '\0';
+
+  if(old_errno != ERRNO)
+    SET_ERRNO(old_errno);
+
+  return buf;
+}
+
+#ifdef USE_LIBIDN
+/*
+ * Return error-string for libidn status as returned from idna_to_ascii_lz().
+ */
+const char *Curl_idn_strerror (struct connectdata *conn, int err)
+{
+#ifdef HAVE_IDNA_STRERROR
+  (void)conn;
+  return idna_strerror((Idna_rc) err);
+#else
+  const char *str;
+  char *buf;
+  size_t max;
+
+  DEBUGASSERT(conn);
+
+  buf = conn->syserr_buf;
+  max = sizeof(conn->syserr_buf)-1;
+  *buf = '\0';
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+  switch ((Idna_rc)err) {
+    case IDNA_SUCCESS:
+      str = "No error";
+      break;
+    case IDNA_STRINGPREP_ERROR:
+      str = "Error in string preparation";
+      break;
+    case IDNA_PUNYCODE_ERROR:
+      str = "Error in Punycode operation";
+      break;
+    case IDNA_CONTAINS_NON_LDH:
+      str = "Illegal ASCII characters";
+      break;
+    case IDNA_CONTAINS_MINUS:
+      str = "Contains minus";
+      break;
+    case IDNA_INVALID_LENGTH:
+      str = "Invalid output length";
+      break;
+    case IDNA_NO_ACE_PREFIX:
+      str = "No ACE prefix (\"xn--\")";
+      break;
+    case IDNA_ROUNDTRIP_VERIFY_ERROR:
+      str = "Round trip verify error";
+      break;
+    case IDNA_CONTAINS_ACE_PREFIX:
+      str = "Already have ACE prefix (\"xn--\")";
+      break;
+    case IDNA_ICONV_ERROR:
+      str = "Locale conversion failed";
+      break;
+    case IDNA_MALLOC_ERROR:
+      str = "Allocation failed";
+      break;
+    case IDNA_DLOPEN_ERROR:
+      str = "dlopen() error";
+      break;
+    default:
+      snprintf(buf, max, "error %d", err);
+      str = NULL;
+      break;
+  }
+#else
+  if((Idna_rc)err == IDNA_SUCCESS)
+    str = "No error";
+  else
+    str = "Error";
+#endif
+  if(str)
+    strncpy(buf, str, max);
+  buf[max] = '\0';
+  return (buf);
+#endif
+}
+#endif  /* USE_LIBIDN */
+
+#ifdef USE_WINDOWS_SSPI
+const char *Curl_sspi_strerror (struct connectdata *conn, int err)
+{
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+  char txtbuf[80];
+  char msgbuf[sizeof(conn->syserr_buf)];
+  char *p, *str, *msg = NULL;
+  bool msg_formatted = FALSE;
+  int old_errno;
+#endif
+  const char *txt;
+  char *outbuf;
+  size_t outmax;
+
+  DEBUGASSERT(conn);
+
+  outbuf = conn->syserr_buf;
+  outmax = sizeof(conn->syserr_buf)-1;
+  *outbuf = '\0';
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+
+  old_errno = ERRNO;
+
+  switch (err) {
+    case SEC_E_OK:
+      txt = "No error";
+      break;
+    case SEC_E_ALGORITHM_MISMATCH:
+      txt = "SEC_E_ALGORITHM_MISMATCH";
+      break;
+    case SEC_E_BAD_BINDINGS:
+      txt = "SEC_E_BAD_BINDINGS";
+      break;
+    case SEC_E_BAD_PKGID:
+      txt = "SEC_E_BAD_PKGID";
+      break;
+    case SEC_E_BUFFER_TOO_SMALL:
+      txt = "SEC_E_BUFFER_TOO_SMALL";
+      break;
+    case SEC_E_CANNOT_INSTALL:
+      txt = "SEC_E_CANNOT_INSTALL";
+      break;
+    case SEC_E_CANNOT_PACK:
+      txt = "SEC_E_CANNOT_PACK";
+      break;
+    case SEC_E_CERT_EXPIRED:
+      txt = "SEC_E_CERT_EXPIRED";
+      break;
+    case SEC_E_CERT_UNKNOWN:
+      txt = "SEC_E_CERT_UNKNOWN";
+      break;
+    case SEC_E_CERT_WRONG_USAGE:
+      txt = "SEC_E_CERT_WRONG_USAGE";
+      break;
+    case SEC_E_CONTEXT_EXPIRED:
+      txt = "SEC_E_CONTEXT_EXPIRED";
+      break;
+    case SEC_E_CROSSREALM_DELEGATION_FAILURE:
+      txt = "SEC_E_CROSSREALM_DELEGATION_FAILURE";
+      break;
+    case SEC_E_CRYPTO_SYSTEM_INVALID:
+      txt = "SEC_E_CRYPTO_SYSTEM_INVALID";
+      break;
+    case SEC_E_DECRYPT_FAILURE:
+      txt = "SEC_E_DECRYPT_FAILURE";
+      break;
+    case SEC_E_DELEGATION_POLICY:
+      txt = "SEC_E_DELEGATION_POLICY";
+      break;
+    case SEC_E_DELEGATION_REQUIRED:
+      txt = "SEC_E_DELEGATION_REQUIRED";
+      break;
+    case SEC_E_DOWNGRADE_DETECTED:
+      txt = "SEC_E_DOWNGRADE_DETECTED";
+      break;
+    case SEC_E_ENCRYPT_FAILURE:
+      txt = "SEC_E_ENCRYPT_FAILURE";
+      break;
+    case SEC_E_ILLEGAL_MESSAGE:
+      txt = "SEC_E_ILLEGAL_MESSAGE";
+      break;
+    case SEC_E_INCOMPLETE_CREDENTIALS:
+      txt = "SEC_E_INCOMPLETE_CREDENTIALS";
+      break;
+    case SEC_E_INCOMPLETE_MESSAGE:
+      txt = "SEC_E_INCOMPLETE_MESSAGE";
+      break;
+    case SEC_E_INSUFFICIENT_MEMORY:
+      txt = "SEC_E_INSUFFICIENT_MEMORY";
+      break;
+    case SEC_E_INTERNAL_ERROR:
+      txt = "SEC_E_INTERNAL_ERROR";
+      break;
+    case SEC_E_INVALID_HANDLE:
+      txt = "SEC_E_INVALID_HANDLE";
+      break;
+    case SEC_E_INVALID_PARAMETER:
+      txt = "SEC_E_INVALID_PARAMETER";
+      break;
+    case SEC_E_INVALID_TOKEN:
+      txt = "SEC_E_INVALID_TOKEN";
+      break;
+    case SEC_E_ISSUING_CA_UNTRUSTED:
+      txt = "SEC_E_ISSUING_CA_UNTRUSTED";
+      break;
+    case SEC_E_ISSUING_CA_UNTRUSTED_KDC:
+      txt = "SEC_E_ISSUING_CA_UNTRUSTED_KDC";
+      break;
+    case SEC_E_KDC_CERT_EXPIRED:
+      txt = "SEC_E_KDC_CERT_EXPIRED";
+      break;
+    case SEC_E_KDC_CERT_REVOKED:
+      txt = "SEC_E_KDC_CERT_REVOKED";
+      break;
+    case SEC_E_KDC_INVALID_REQUEST:
+      txt = "SEC_E_KDC_INVALID_REQUEST";
+      break;
+    case SEC_E_KDC_UNABLE_TO_REFER:
+      txt = "SEC_E_KDC_UNABLE_TO_REFER";
+      break;
+    case SEC_E_KDC_UNKNOWN_ETYPE:
+      txt = "SEC_E_KDC_UNKNOWN_ETYPE";
+      break;
+    case SEC_E_LOGON_DENIED:
+      txt = "SEC_E_LOGON_DENIED";
+      break;
+    case SEC_E_MAX_REFERRALS_EXCEEDED:
+      txt = "SEC_E_MAX_REFERRALS_EXCEEDED";
+      break;
+    case SEC_E_MESSAGE_ALTERED:
+      txt = "SEC_E_MESSAGE_ALTERED";
+      break;
+    case SEC_E_MULTIPLE_ACCOUNTS:
+      txt = "SEC_E_MULTIPLE_ACCOUNTS";
+      break;
+    case SEC_E_MUST_BE_KDC:
+      txt = "SEC_E_MUST_BE_KDC";
+      break;
+    case SEC_E_NOT_OWNER:
+      txt = "SEC_E_NOT_OWNER";
+      break;
+    case SEC_E_NO_AUTHENTICATING_AUTHORITY:
+      txt = "SEC_E_NO_AUTHENTICATING_AUTHORITY";
+      break;
+    case SEC_E_NO_CREDENTIALS:
+      txt = "SEC_E_NO_CREDENTIALS";
+      break;
+    case SEC_E_NO_IMPERSONATION:
+      txt = "SEC_E_NO_IMPERSONATION";
+      break;
+    case SEC_E_NO_IP_ADDRESSES:
+      txt = "SEC_E_NO_IP_ADDRESSES";
+      break;
+    case SEC_E_NO_KERB_KEY:
+      txt = "SEC_E_NO_KERB_KEY";
+      break;
+    case SEC_E_NO_PA_DATA:
+      txt = "SEC_E_NO_PA_DATA";
+      break;
+    case SEC_E_NO_S4U_PROT_SUPPORT:
+      txt = "SEC_E_NO_S4U_PROT_SUPPORT";
+      break;
+    case SEC_E_NO_TGT_REPLY:
+      txt = "SEC_E_NO_TGT_REPLY";
+      break;
+    case SEC_E_OUT_OF_SEQUENCE:
+      txt = "SEC_E_OUT_OF_SEQUENCE";
+      break;
+    case SEC_E_PKINIT_CLIENT_FAILURE:
+      txt = "SEC_E_PKINIT_CLIENT_FAILURE";
+      break;
+    case SEC_E_PKINIT_NAME_MISMATCH:
+      txt = "SEC_E_PKINIT_NAME_MISMATCH";
+      break;
+    case SEC_E_POLICY_NLTM_ONLY:
+      txt = "SEC_E_POLICY_NLTM_ONLY";
+      break;
+    case SEC_E_QOP_NOT_SUPPORTED:
+      txt = "SEC_E_QOP_NOT_SUPPORTED";
+      break;
+    case SEC_E_REVOCATION_OFFLINE_C:
+      txt = "SEC_E_REVOCATION_OFFLINE_C";
+      break;
+    case SEC_E_REVOCATION_OFFLINE_KDC:
+      txt = "SEC_E_REVOCATION_OFFLINE_KDC";
+      break;
+    case SEC_E_SECPKG_NOT_FOUND:
+      txt = "SEC_E_SECPKG_NOT_FOUND";
+      break;
+    case SEC_E_SECURITY_QOS_FAILED:
+      txt = "SEC_E_SECURITY_QOS_FAILED";
+      break;
+    case SEC_E_SHUTDOWN_IN_PROGRESS:
+      txt = "SEC_E_SHUTDOWN_IN_PROGRESS";
+      break;
+    case SEC_E_SMARTCARD_CERT_EXPIRED:
+      txt = "SEC_E_SMARTCARD_CERT_EXPIRED";
+      break;
+    case SEC_E_SMARTCARD_CERT_REVOKED:
+      txt = "SEC_E_SMARTCARD_CERT_REVOKED";
+      break;
+    case SEC_E_SMARTCARD_LOGON_REQUIRED:
+      txt = "SEC_E_SMARTCARD_LOGON_REQUIRED";
+      break;
+    case SEC_E_STRONG_CRYPTO_NOT_SUPPORTED:
+      txt = "SEC_E_STRONG_CRYPTO_NOT_SUPPORTED";
+      break;
+    case SEC_E_TARGET_UNKNOWN:
+      txt = "SEC_E_TARGET_UNKNOWN";
+      break;
+    case SEC_E_TIME_SKEW:
+      txt = "SEC_E_TIME_SKEW";
+      break;
+    case SEC_E_TOO_MANY_PRINCIPALS:
+      txt = "SEC_E_TOO_MANY_PRINCIPALS";
+      break;
+    case SEC_E_UNFINISHED_CONTEXT_DELETED:
+      txt = "SEC_E_UNFINISHED_CONTEXT_DELETED";
+      break;
+    case SEC_E_UNKNOWN_CREDENTIALS:
+      txt = "SEC_E_UNKNOWN_CREDENTIALS";
+      break;
+    case SEC_E_UNSUPPORTED_FUNCTION:
+      txt = "SEC_E_UNSUPPORTED_FUNCTION";
+      break;
+    case SEC_E_UNSUPPORTED_PREAUTH:
+      txt = "SEC_E_UNSUPPORTED_PREAUTH";
+      break;
+    case SEC_E_UNTRUSTED_ROOT:
+      txt = "SEC_E_UNTRUSTED_ROOT";
+      break;
+    case SEC_E_WRONG_CREDENTIAL_HANDLE:
+      txt = "SEC_E_WRONG_CREDENTIAL_HANDLE";
+      break;
+    case SEC_E_WRONG_PRINCIPAL:
+      txt = "SEC_E_WRONG_PRINCIPAL";
+      break;
+    case SEC_I_COMPLETE_AND_CONTINUE:
+      txt = "SEC_I_COMPLETE_AND_CONTINUE";
+      break;
+    case SEC_I_COMPLETE_NEEDED:
+      txt = "SEC_I_COMPLETE_NEEDED";
+      break;
+    case SEC_I_CONTEXT_EXPIRED:
+      txt = "SEC_I_CONTEXT_EXPIRED";
+      break;
+    case SEC_I_CONTINUE_NEEDED:
+      txt = "SEC_I_CONTINUE_NEEDED";
+      break;
+    case SEC_I_INCOMPLETE_CREDENTIALS:
+      txt = "SEC_I_INCOMPLETE_CREDENTIALS";
+      break;
+    case SEC_I_LOCAL_LOGON:
+      txt = "SEC_I_LOCAL_LOGON";
+      break;
+    case SEC_I_NO_LSA_CONTEXT:
+      txt = "SEC_I_NO_LSA_CONTEXT";
+      break;
+    case SEC_I_RENEGOTIATE:
+      txt = "SEC_I_RENEGOTIATE";
+      break;
+    case SEC_I_SIGNATURE_NEEDED:
+      txt = "SEC_I_SIGNATURE_NEEDED";
+      break;
+    default:
+      txt = "Unknown error";
+  }
+
+  if(err == SEC_E_OK)
+    strncpy(outbuf, txt, outmax);
+  else {
+    str = txtbuf;
+    snprintf(txtbuf, sizeof(txtbuf), "%s (0x%04X%04X)",
+             txt, (err >> 16) & 0xffff, err & 0xffff);
+    txtbuf[sizeof(txtbuf)-1] = '\0';
+
+#ifdef _WIN32_WCE
+    {
+      wchar_t wbuf[256];
+      wbuf[0] = L'\0';
+
+      if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+                       FORMAT_MESSAGE_IGNORE_INSERTS,
+                       NULL, err, LANG_NEUTRAL,
+                       wbuf, sizeof(wbuf)/sizeof(wchar_t), NULL)) {
+        wcstombs(msgbuf,wbuf,sizeof(msgbuf)-1);
+        msg_formatted = TRUE;
+      }
+    }
+#else
+    if(FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
+                      FORMAT_MESSAGE_IGNORE_INSERTS,
+                      NULL, err, LANG_NEUTRAL,
+                      msgbuf, sizeof(msgbuf)-1, NULL)) {
+      msg_formatted = TRUE;
+    }
+#endif
+    if(msg_formatted) {
+      msgbuf[sizeof(msgbuf)-1] = '\0';
+      /* strip trailing '\r\n' or '\n' */
+      if((p = strrchr(msgbuf,'\n')) != NULL && (p - msgbuf) >= 2)
+         *p = '\0';
+      if((p = strrchr(msgbuf,'\r')) != NULL && (p - msgbuf) >= 1)
+         *p = '\0';
+      msg = msgbuf;
+    }
+    if(msg)
+      snprintf(outbuf, outmax, "%s - %s", str, msg);
+    else
+      strncpy(outbuf, str, outmax);
+  }
+
+  if(old_errno != ERRNO)
+    SET_ERRNO(old_errno);
+
+#else
+
+  if(err == SEC_E_OK)
+    txt = "No error";
+  else
+    txt = "Error";
+
+  strncpy(outbuf, txt, outmax);
+
+#endif
+
+  outbuf[outmax] = '\0';
+
+  return outbuf;
+}
+#endif /* USE_WINDOWS_SSPI */
diff --git a/lib/curl_strtok.c b/lib/curl_strtok.c
new file mode 100644 (file)
index 0000000..33bdd96
--- /dev/null
@@ -0,0 +1,66 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifndef HAVE_STRTOK_R
+#include <stddef.h>
+
+#include "curl_strtok.h"
+
+char *
+Curl_strtok_r(char *ptr, const char *sep, char **end)
+{
+  if(!ptr)
+    /* we got NULL input so then we get our last position instead */
+    ptr = *end;
+
+  /* pass all letters that are including in the separator string */
+  while(*ptr && strchr(sep, *ptr))
+    ++ptr;
+
+  if(*ptr) {
+    /* so this is where the next piece of string starts */
+    char *start = ptr;
+
+    /* set the end pointer to the first byte after the start */
+    *end = start + 1;
+
+    /* scan through the string to find where it ends, it ends on a
+       null byte or a character that exists in the separator string */
+    while(**end && !strchr(sep, **end))
+      ++*end;
+
+    if(**end) {
+      /* the end is not a null byte */
+      **end = '\0';  /* zero terminate it! */
+      ++*end;        /* advance the last pointer to beyond the null byte */
+    }
+
+    return start; /* return the position where the string starts */
+  }
+
+  /* we ended up on a null byte, there are no more strings to find! */
+  return NULL;
+}
+
+#endif /* this was only compiled if strtok_r wasn't present */
diff --git a/lib/curl_strtoofft.c b/lib/curl_strtoofft.c
new file mode 100644 (file)
index 0000000..d203d9c
--- /dev/null
@@ -0,0 +1,188 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include "curl_strtoofft.h"
+
+/*
+ * NOTE:
+ *
+ * In the ISO C standard (IEEE Std 1003.1), there is a strtoimax() function we
+ * could use in case strtoll() doesn't exist...  See
+ * http://www.opengroup.org/onlinepubs/009695399/functions/strtoimax.html
+ */
+
+#ifdef NEED_CURL_STRTOLL
+
+/* Range tests can be used for alphanum decoding if characters are consecutive,
+   like in ASCII. Else an array is scanned. Determine this condition now. */
+
+#if('9' - '0') != 9 || ('Z' - 'A') != 25 || ('z' - 'a') != 25
+
+#define NO_RANGE_TEST
+
+static const char valchars[] =
+            "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+#endif
+
+static int get_char(char c, int base);
+
+/**
+ * Emulated version of the strtoll function.  This extracts a long long
+ * value from the given input string and returns it.
+ */
+curl_off_t
+curlx_strtoll(const char *nptr, char **endptr, int base)
+{
+  char *end;
+  int is_negative = 0;
+  int overflow;
+  int i;
+  curl_off_t value = 0;
+  curl_off_t newval;
+
+  /* Skip leading whitespace. */
+  end = (char *)nptr;
+  while(ISSPACE(end[0])) {
+    end++;
+  }
+
+  /* Handle the sign, if any. */
+  if(end[0] == '-') {
+    is_negative = 1;
+    end++;
+  }
+  else if(end[0] == '+') {
+    end++;
+  }
+  else if(end[0] == '\0') {
+    /* We had nothing but perhaps some whitespace -- there was no number. */
+    if(endptr) {
+      *endptr = end;
+    }
+    return 0;
+  }
+
+  /* Handle special beginnings, if present and allowed. */
+  if(end[0] == '0' && end[1] == 'x') {
+    if(base == 16 || base == 0) {
+      end += 2;
+      base = 16;
+    }
+  }
+  else if(end[0] == '0') {
+    if(base == 8 || base == 0) {
+      end++;
+      base = 8;
+    }
+  }
+
+  /* Matching strtol, if the base is 0 and it doesn't look like
+   * the number is octal or hex, we assume it's base 10.
+   */
+  if(base == 0) {
+    base = 10;
+  }
+
+  /* Loop handling digits. */
+  value = 0;
+  overflow = 0;
+  for(i = get_char(end[0], base);
+      i != -1;
+      end++, i = get_char(end[0], base)) {
+    newval = base * value + i;
+    if(newval < value) {
+      /* We've overflowed. */
+      overflow = 1;
+      break;
+    }
+    else
+      value = newval;
+  }
+
+  if(!overflow) {
+    if(is_negative) {
+      /* Fix the sign. */
+      value *= -1;
+    }
+  }
+  else {
+    if(is_negative)
+      value = CURL_OFF_T_MIN;
+    else
+      value = CURL_OFF_T_MAX;
+
+    SET_ERRNO(ERANGE);
+  }
+
+  if(endptr)
+    *endptr = end;
+
+  return value;
+}
+
+/**
+ * Returns the value of c in the given base, or -1 if c cannot
+ * be interpreted properly in that base (i.e., is out of range,
+ * is a null, etc.).
+ *
+ * @param c     the character to interpret according to base
+ * @param base  the base in which to interpret c
+ *
+ * @return  the value of c in base, or -1 if c isn't in range
+ */
+static int get_char(char c, int base)
+{
+#ifndef NO_RANGE_TEST
+  int value = -1;
+  if(c <= '9' && c >= '0') {
+    value = c - '0';
+  }
+  else if(c <= 'Z' && c >= 'A') {
+    value = c - 'A' + 10;
+  }
+  else if(c <= 'z' && c >= 'a') {
+    value = c - 'a' + 10;
+  }
+#else
+  const char * cp;
+  int value;
+
+  cp = memchr(valchars, c, 10 + 26 + 26);
+
+  if(!cp)
+    return -1;
+
+  value = cp - valchars;
+
+  if(value >= 10 + 26)
+    value -= 26;                /* Lowercase. */
+#endif
+
+  if(value >= base) {
+    value = -1;
+  }
+
+  return value;
+}
+#endif  /* Only present if we need strtoll, but don't have it. */
diff --git a/lib/curl_telnet.c b/lib/curl_telnet.c
new file mode 100644 (file)
index 0000000..54eab1c
--- /dev/null
@@ -0,0 +1,1678 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_TELNET
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#include "curl_urldata.h"
+#include <curl/curl.h>
+#include "curl_transfer.h"
+#include "curl_sendf.h"
+#include "curl_telnet.h"
+#include "curl_connect.h"
+#include "curl_progress.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#define  TELOPTS
+#define  TELCMDS
+
+#include "curl_arpa_telnet.h"
+#include "curl_memory.h"
+#include "curl_select.h"
+#include "curl_strequal.h"
+#include "curl_rawstr.h"
+#include "curl_warnless.h"
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+#define SUBBUFSIZE 512
+
+#define CURL_SB_CLEAR(x)  x->subpointer = x->subbuffer
+#define CURL_SB_TERM(x)                                 \
+  do {                                                  \
+    x->subend = x->subpointer;                          \
+    CURL_SB_CLEAR(x);                                   \
+  } WHILE_FALSE
+#define CURL_SB_ACCUM(x,c)                                   \
+  do {                                                       \
+    if(x->subpointer < (x->subbuffer+sizeof x->subbuffer))   \
+      *x->subpointer++ = (c);                                \
+  } WHILE_FALSE
+
+#define  CURL_SB_GET(x) ((*x->subpointer++)&0xff)
+#define  CURL_SB_PEEK(x)   ((*x->subpointer)&0xff)
+#define  CURL_SB_EOF(x) (x->subpointer >= x->subend)
+#define  CURL_SB_LEN(x) (x->subend - x->subpointer)
+
+#ifdef CURL_DISABLE_VERBOSE_STRINGS
+#define printoption(a,b,c,d)  Curl_nop_stmt
+#endif
+
+#ifdef USE_WINSOCK
+typedef FARPROC WSOCK2_FUNC;
+static CURLcode check_wsock2 ( struct SessionHandle *data );
+#endif
+
+static
+CURLcode telrcv(struct connectdata *,
+                const unsigned char *inbuf, /* Data received from socket */
+                ssize_t count);             /* Number of bytes received */
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static void printoption(struct SessionHandle *data,
+                        const char *direction,
+                        int cmd, int option);
+#endif
+
+static void negotiate(struct connectdata *);
+static void send_negotiation(struct connectdata *, int cmd, int option);
+static void set_local_option(struct connectdata *, int cmd, int option);
+static void set_remote_option(struct connectdata *, int cmd, int option);
+
+static void printsub(struct SessionHandle *data,
+                     int direction, unsigned char *pointer,
+                     size_t length);
+static void suboption(struct connectdata *);
+static void sendsuboption(struct connectdata *conn, int option);
+
+static CURLcode telnet_do(struct connectdata *conn, bool *done);
+static CURLcode telnet_done(struct connectdata *conn,
+                                 CURLcode, bool premature);
+static CURLcode send_telnet_data(struct connectdata *conn,
+                                 char *buffer, ssize_t nread);
+
+/* For negotiation compliant to RFC 1143 */
+#define CURL_NO          0
+#define CURL_YES         1
+#define CURL_WANTYES     2
+#define CURL_WANTNO      3
+
+#define CURL_EMPTY       0
+#define CURL_OPPOSITE    1
+
+/*
+ * Telnet receiver states for fsm
+ */
+typedef enum
+{
+   CURL_TS_DATA = 0,
+   CURL_TS_IAC,
+   CURL_TS_WILL,
+   CURL_TS_WONT,
+   CURL_TS_DO,
+   CURL_TS_DONT,
+   CURL_TS_CR,
+   CURL_TS_SB,   /* sub-option collection */
+   CURL_TS_SE   /* looking for sub-option end */
+} TelnetReceive;
+
+struct TELNET {
+  int please_negotiate;
+  int already_negotiated;
+  int us[256];
+  int usq[256];
+  int us_preferred[256];
+  int him[256];
+  int himq[256];
+  int him_preferred[256];
+  int subnegotiation[256];
+  char subopt_ttype[32];             /* Set with suboption TTYPE */
+  char subopt_xdisploc[128];         /* Set with suboption XDISPLOC */
+  unsigned short subopt_wsx;         /* Set with suboption NAWS */
+  unsigned short subopt_wsy;         /* Set with suboption NAWS */
+  struct curl_slist *telnet_vars;    /* Environment variables */
+
+  /* suboptions */
+  unsigned char subbuffer[SUBBUFSIZE];
+  unsigned char *subpointer, *subend;      /* buffer for sub-options */
+
+  TelnetReceive telrcv_state;
+};
+
+
+/*
+ * TELNET protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_telnet = {
+  "TELNET",                             /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  telnet_do,                            /* do_it */
+  telnet_done,                          /* done */
+  ZERO_NULL,                            /* do_more */
+  ZERO_NULL,                            /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  PORT_TELNET,                          /* defport */
+  CURLPROTO_TELNET,                     /* protocol */
+  PROTOPT_NONE | PROTOPT_NOURLQUERY     /* flags */
+};
+
+
+#ifdef USE_WINSOCK
+static CURLcode
+check_wsock2 ( struct SessionHandle *data )
+{
+  int err;
+  WORD wVersionRequested;
+  WSADATA wsaData;
+
+  DEBUGASSERT(data);
+
+  /* telnet requires at least WinSock 2.0 so ask for it. */
+  wVersionRequested = MAKEWORD(2, 0);
+
+  err = WSAStartup(wVersionRequested, &wsaData);
+
+  /* We must've called this once already, so this call */
+  /* should always succeed.  But, just in case... */
+  if(err != 0) {
+    failf(data,"WSAStartup failed (%d)",err);
+    return CURLE_FAILED_INIT;
+  }
+
+  /* We have to have a WSACleanup call for every successful */
+  /* WSAStartup call. */
+  WSACleanup();
+
+  /* Check that our version is supported */
+  if(LOBYTE(wsaData.wVersion) != LOBYTE(wVersionRequested) ||
+      HIBYTE(wsaData.wVersion) != HIBYTE(wVersionRequested)) {
+      /* Our version isn't supported */
+      failf(data,"insufficient winsock version to support "
+            "telnet");
+      return CURLE_FAILED_INIT;
+  }
+
+  /* Our version is supported */
+  return CURLE_OK;
+}
+#endif
+
+static
+CURLcode init_telnet(struct connectdata *conn)
+{
+  struct TELNET *tn;
+
+  tn = calloc(1, sizeof(struct TELNET));
+  if(!tn)
+    return CURLE_OUT_OF_MEMORY;
+
+  conn->data->state.proto.telnet = (void *)tn; /* make us known */
+
+  tn->telrcv_state = CURL_TS_DATA;
+
+  /* Init suboptions */
+  CURL_SB_CLEAR(tn);
+
+  /* Set the options we want by default */
+  tn->us_preferred[CURL_TELOPT_SGA] = CURL_YES;
+  tn->him_preferred[CURL_TELOPT_SGA] = CURL_YES;
+
+  /* To be compliant with previous releases of libcurl
+     we enable this option by default. This behaviour
+         can be changed thanks to the "BINARY" option in
+         CURLOPT_TELNETOPTIONS
+  */
+  tn->us_preferred[CURL_TELOPT_BINARY] = CURL_YES;
+  tn->him_preferred[CURL_TELOPT_BINARY] = CURL_YES;
+
+  /* We must allow the server to echo what we sent
+         but it is not necessary to request the server
+         to do so (it might forces the server to close
+         the connection). Hence, we ignore ECHO in the
+         negotiate function
+  */
+  tn->him_preferred[CURL_TELOPT_ECHO] = CURL_YES;
+
+  /* Set the subnegotiation fields to send information
+    just after negotiation passed (do/will)
+
+     Default values are (0,0) initialized by calloc.
+     According to the RFC1013 it is valid:
+     A value equal to zero is acceptable for the width (or height),
+         and means that no character width (or height) is being sent.
+         In this case, the width (or height) that will be assumed by the
+         Telnet server is operating system specific (it will probably be
+         based upon the terminal type information that may have been sent
+         using the TERMINAL TYPE Telnet option). */
+  tn->subnegotiation[CURL_TELOPT_NAWS] = CURL_YES;
+  return CURLE_OK;
+}
+
+static void negotiate(struct connectdata *conn)
+{
+  int i;
+  struct TELNET *tn = (struct TELNET *) conn->data->state.proto.telnet;
+
+  for(i = 0;i < CURL_NTELOPTS;i++) {
+    if(i==CURL_TELOPT_ECHO)
+      continue;
+
+    if(tn->us_preferred[i] == CURL_YES)
+      set_local_option(conn, i, CURL_YES);
+
+    if(tn->him_preferred[i] == CURL_YES)
+      set_remote_option(conn, i, CURL_YES);
+  }
+}
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static void printoption(struct SessionHandle *data,
+                        const char *direction, int cmd, int option)
+{
+  const char *fmt;
+  const char *opt;
+
+  if(data->set.verbose) {
+    if(cmd == CURL_IAC) {
+      if(CURL_TELCMD_OK(option))
+        infof(data, "%s IAC %s\n", direction, CURL_TELCMD(option));
+      else
+        infof(data, "%s IAC %d\n", direction, option);
+    }
+    else {
+      fmt = (cmd == CURL_WILL) ? "WILL" : (cmd == CURL_WONT) ? "WONT" :
+        (cmd == CURL_DO) ? "DO" : (cmd == CURL_DONT) ? "DONT" : 0;
+      if(fmt) {
+        if(CURL_TELOPT_OK(option))
+          opt = CURL_TELOPT(option);
+        else if(option == CURL_TELOPT_EXOPL)
+          opt = "EXOPL";
+        else
+          opt = NULL;
+
+        if(opt)
+          infof(data, "%s %s %s\n", direction, fmt, opt);
+        else
+          infof(data, "%s %s %d\n", direction, fmt, option);
+      }
+      else
+        infof(data, "%s %d %d\n", direction, cmd, option);
+    }
+  }
+}
+#endif
+
+static void send_negotiation(struct connectdata *conn, int cmd, int option)
+{
+   unsigned char buf[3];
+   ssize_t bytes_written;
+   int err;
+   struct SessionHandle *data = conn->data;
+
+   buf[0] = CURL_IAC;
+   buf[1] = (unsigned char)cmd;
+   buf[2] = (unsigned char)option;
+
+   bytes_written = swrite(conn->sock[FIRSTSOCKET], buf, 3);
+   if(bytes_written < 0) {
+     err = SOCKERRNO;
+     failf(data,"Sending data failed (%d)",err);
+   }
+
+   printoption(conn->data, "SENT", cmd, option);
+}
+
+static
+void set_remote_option(struct connectdata *conn, int option, int newstate)
+{
+  struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
+  if(newstate == CURL_YES) {
+    switch(tn->him[option]) {
+    case CURL_NO:
+      tn->him[option] = CURL_WANTYES;
+      send_negotiation(conn, CURL_DO, option);
+      break;
+
+    case CURL_YES:
+      /* Already enabled */
+      break;
+
+    case CURL_WANTNO:
+      switch(tn->himq[option]) {
+      case CURL_EMPTY:
+        /* Already negotiating for CURL_YES, queue the request */
+        tn->himq[option] = CURL_OPPOSITE;
+        break;
+      case CURL_OPPOSITE:
+        /* Error: already queued an enable request */
+        break;
+      }
+      break;
+
+    case CURL_WANTYES:
+      switch(tn->himq[option]) {
+      case CURL_EMPTY:
+        /* Error: already negotiating for enable */
+        break;
+      case CURL_OPPOSITE:
+        tn->himq[option] = CURL_EMPTY;
+        break;
+      }
+      break;
+    }
+  }
+  else { /* NO */
+    switch(tn->him[option]) {
+    case CURL_NO:
+      /* Already disabled */
+      break;
+
+    case CURL_YES:
+      tn->him[option] = CURL_WANTNO;
+      send_negotiation(conn, CURL_DONT, option);
+      break;
+
+    case CURL_WANTNO:
+      switch(tn->himq[option]) {
+      case CURL_EMPTY:
+        /* Already negotiating for NO */
+        break;
+      case CURL_OPPOSITE:
+        tn->himq[option] = CURL_EMPTY;
+        break;
+      }
+      break;
+
+    case CURL_WANTYES:
+      switch(tn->himq[option]) {
+      case CURL_EMPTY:
+        tn->himq[option] = CURL_OPPOSITE;
+        break;
+      case CURL_OPPOSITE:
+        break;
+      }
+      break;
+    }
+  }
+}
+
+static
+void rec_will(struct connectdata *conn, int option)
+{
+  struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
+  switch(tn->him[option]) {
+  case CURL_NO:
+    if(tn->him_preferred[option] == CURL_YES) {
+      tn->him[option] = CURL_YES;
+      send_negotiation(conn, CURL_DO, option);
+    }
+    else
+      send_negotiation(conn, CURL_DONT, option);
+
+    break;
+
+  case CURL_YES:
+    /* Already enabled */
+    break;
+
+  case CURL_WANTNO:
+    switch(tn->himq[option]) {
+    case CURL_EMPTY:
+      /* Error: DONT answered by WILL */
+      tn->him[option] = CURL_NO;
+      break;
+    case CURL_OPPOSITE:
+      /* Error: DONT answered by WILL */
+      tn->him[option] = CURL_YES;
+      tn->himq[option] = CURL_EMPTY;
+      break;
+    }
+    break;
+
+  case CURL_WANTYES:
+    switch(tn->himq[option]) {
+    case CURL_EMPTY:
+      tn->him[option] = CURL_YES;
+      break;
+    case CURL_OPPOSITE:
+      tn->him[option] = CURL_WANTNO;
+      tn->himq[option] = CURL_EMPTY;
+      send_negotiation(conn, CURL_DONT, option);
+      break;
+    }
+    break;
+  }
+}
+
+static
+void rec_wont(struct connectdata *conn, int option)
+{
+  struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
+  switch(tn->him[option]) {
+  case CURL_NO:
+    /* Already disabled */
+    break;
+
+  case CURL_YES:
+    tn->him[option] = CURL_NO;
+    send_negotiation(conn, CURL_DONT, option);
+    break;
+
+  case CURL_WANTNO:
+    switch(tn->himq[option]) {
+    case CURL_EMPTY:
+      tn->him[option] = CURL_NO;
+      break;
+
+    case CURL_OPPOSITE:
+      tn->him[option] = CURL_WANTYES;
+      tn->himq[option] = CURL_EMPTY;
+      send_negotiation(conn, CURL_DO, option);
+      break;
+    }
+    break;
+
+  case CURL_WANTYES:
+    switch(tn->himq[option]) {
+    case CURL_EMPTY:
+      tn->him[option] = CURL_NO;
+      break;
+    case CURL_OPPOSITE:
+      tn->him[option] = CURL_NO;
+      tn->himq[option] = CURL_EMPTY;
+      break;
+    }
+    break;
+  }
+}
+
+static void
+set_local_option(struct connectdata *conn, int option, int newstate)
+{
+  struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
+  if(newstate == CURL_YES) {
+    switch(tn->us[option]) {
+    case CURL_NO:
+      tn->us[option] = CURL_WANTYES;
+      send_negotiation(conn, CURL_WILL, option);
+      break;
+
+    case CURL_YES:
+      /* Already enabled */
+      break;
+
+    case CURL_WANTNO:
+      switch(tn->usq[option]) {
+      case CURL_EMPTY:
+        /* Already negotiating for CURL_YES, queue the request */
+        tn->usq[option] = CURL_OPPOSITE;
+        break;
+      case CURL_OPPOSITE:
+        /* Error: already queued an enable request */
+        break;
+      }
+      break;
+
+    case CURL_WANTYES:
+      switch(tn->usq[option]) {
+      case CURL_EMPTY:
+        /* Error: already negotiating for enable */
+        break;
+      case CURL_OPPOSITE:
+        tn->usq[option] = CURL_EMPTY;
+        break;
+      }
+      break;
+    }
+  }
+  else { /* NO */
+    switch(tn->us[option]) {
+    case CURL_NO:
+      /* Already disabled */
+      break;
+
+    case CURL_YES:
+      tn->us[option] = CURL_WANTNO;
+      send_negotiation(conn, CURL_WONT, option);
+      break;
+
+    case CURL_WANTNO:
+      switch(tn->usq[option]) {
+      case CURL_EMPTY:
+        /* Already negotiating for NO */
+        break;
+      case CURL_OPPOSITE:
+        tn->usq[option] = CURL_EMPTY;
+        break;
+      }
+      break;
+
+    case CURL_WANTYES:
+      switch(tn->usq[option]) {
+      case CURL_EMPTY:
+        tn->usq[option] = CURL_OPPOSITE;
+        break;
+      case CURL_OPPOSITE:
+        break;
+      }
+      break;
+    }
+  }
+}
+
+static
+void rec_do(struct connectdata *conn, int option)
+{
+  struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
+  switch(tn->us[option]) {
+  case CURL_NO:
+    if(tn->us_preferred[option] == CURL_YES) {
+      tn->us[option] = CURL_YES;
+      send_negotiation(conn, CURL_WILL, option);
+      if(tn->subnegotiation[option] == CURL_YES)
+        /* transmission of data option */
+        sendsuboption(conn, option);
+    }
+    else if(tn->subnegotiation[option] == CURL_YES) {
+      /* send information to achieve this option*/
+      tn->us[option] = CURL_YES;
+      send_negotiation(conn, CURL_WILL, option);
+      sendsuboption(conn, option);
+    }
+    else
+      send_negotiation(conn, CURL_WONT, option);
+    break;
+
+  case CURL_YES:
+    /* Already enabled */
+    break;
+
+  case CURL_WANTNO:
+    switch(tn->usq[option]) {
+    case CURL_EMPTY:
+      /* Error: DONT answered by WILL */
+      tn->us[option] = CURL_NO;
+      break;
+    case CURL_OPPOSITE:
+      /* Error: DONT answered by WILL */
+      tn->us[option] = CURL_YES;
+      tn->usq[option] = CURL_EMPTY;
+      break;
+    }
+    break;
+
+  case CURL_WANTYES:
+    switch(tn->usq[option]) {
+    case CURL_EMPTY:
+      tn->us[option] = CURL_YES;
+      if(tn->subnegotiation[option] == CURL_YES) {
+        /* transmission of data option */
+        sendsuboption(conn, option);
+      }
+      break;
+    case CURL_OPPOSITE:
+      tn->us[option] = CURL_WANTNO;
+      tn->himq[option] = CURL_EMPTY;
+      send_negotiation(conn, CURL_WONT, option);
+      break;
+    }
+    break;
+  }
+}
+
+static
+void rec_dont(struct connectdata *conn, int option)
+{
+  struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
+  switch(tn->us[option]) {
+  case CURL_NO:
+    /* Already disabled */
+    break;
+
+  case CURL_YES:
+    tn->us[option] = CURL_NO;
+    send_negotiation(conn, CURL_WONT, option);
+    break;
+
+  case CURL_WANTNO:
+    switch(tn->usq[option]) {
+    case CURL_EMPTY:
+      tn->us[option] = CURL_NO;
+      break;
+
+    case CURL_OPPOSITE:
+      tn->us[option] = CURL_WANTYES;
+      tn->usq[option] = CURL_EMPTY;
+      send_negotiation(conn, CURL_WILL, option);
+      break;
+    }
+    break;
+
+  case CURL_WANTYES:
+    switch(tn->usq[option]) {
+    case CURL_EMPTY:
+      tn->us[option] = CURL_NO;
+      break;
+    case CURL_OPPOSITE:
+      tn->us[option] = CURL_NO;
+      tn->usq[option] = CURL_EMPTY;
+      break;
+    }
+    break;
+  }
+}
+
+
+static void printsub(struct SessionHandle *data,
+                     int direction,             /* '<' or '>' */
+                     unsigned char *pointer,    /* where suboption data is */
+                     size_t length)             /* length of suboption data */
+{
+  unsigned int i = 0;
+  unsigned short *pval;
+
+  if(data->set.verbose) {
+    if(direction) {
+      infof(data, "%s IAC SB ", (direction == '<')? "RCVD":"SENT");
+      if(length >= 3) {
+        int j;
+
+        i = pointer[length-2];
+        j = pointer[length-1];
+
+        if(i != CURL_IAC || j != CURL_SE) {
+          infof(data, "(terminated by ");
+          if(CURL_TELOPT_OK(i))
+            infof(data, "%s ", CURL_TELOPT(i));
+          else if(CURL_TELCMD_OK(i))
+            infof(data, "%s ", CURL_TELCMD(i));
+          else
+            infof(data, "%u ", i);
+          if(CURL_TELOPT_OK(j))
+            infof(data, "%s", CURL_TELOPT(j));
+          else if(CURL_TELCMD_OK(j))
+            infof(data, "%s", CURL_TELCMD(j));
+          else
+            infof(data, "%d", j);
+          infof(data, ", not IAC SE!) ");
+        }
+      }
+      length -= 2;
+    }
+    if(length < 1) {
+      infof(data, "(Empty suboption?)");
+      return;
+    }
+
+    if(CURL_TELOPT_OK(pointer[0])) {
+      switch(pointer[0]) {
+      case CURL_TELOPT_TTYPE:
+      case CURL_TELOPT_XDISPLOC:
+      case CURL_TELOPT_NEW_ENVIRON:
+      case CURL_TELOPT_NAWS:
+        infof(data, "%s", CURL_TELOPT(pointer[0]));
+        break;
+      default:
+        infof(data, "%s (unsupported)", CURL_TELOPT(pointer[0]));
+        break;
+      }
+    }
+    else
+      infof(data, "%d (unknown)", pointer[i]);
+
+    switch(pointer[0]) {
+    case CURL_TELOPT_NAWS:
+      pval = (unsigned short*)(pointer+1);
+      infof(data, "Width: %hu ; Height: %hu",
+            ntohs(pval[0]), ntohs(pval[1]));
+      break;
+    default:
+      switch(pointer[1]) {
+      case CURL_TELQUAL_IS:
+        infof(data, " IS");
+        break;
+      case CURL_TELQUAL_SEND:
+        infof(data, " SEND");
+        break;
+      case CURL_TELQUAL_INFO:
+        infof(data, " INFO/REPLY");
+        break;
+      case CURL_TELQUAL_NAME:
+        infof(data, " NAME");
+        break;
+      }
+
+      switch(pointer[0]) {
+      case CURL_TELOPT_TTYPE:
+      case CURL_TELOPT_XDISPLOC:
+        pointer[length] = 0;
+        infof(data, " \"%s\"", &pointer[2]);
+        break;
+      case CURL_TELOPT_NEW_ENVIRON:
+        if(pointer[1] == CURL_TELQUAL_IS) {
+          infof(data, " ");
+          for(i = 3;i < length;i++) {
+            switch(pointer[i]) {
+            case CURL_NEW_ENV_VAR:
+              infof(data, ", ");
+              break;
+            case CURL_NEW_ENV_VALUE:
+              infof(data, " = ");
+              break;
+            default:
+              infof(data, "%c", pointer[i]);
+              break;
+            }
+          }
+        }
+        break;
+      default:
+        for(i = 2; i < length; i++)
+          infof(data, " %.2x", pointer[i]);
+        break;
+      }
+    }
+    if(direction)
+      infof(data, "\n");
+  }
+}
+
+static CURLcode check_telnet_options(struct connectdata *conn)
+{
+  struct curl_slist *head;
+  struct curl_slist *beg;
+  char option_keyword[128];
+  char option_arg[256];
+  struct SessionHandle *data = conn->data;
+  struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
+  CURLcode result = CURLE_OK;
+  int binary_option;
+
+  /* Add the user name as an environment variable if it
+     was given on the command line */
+  if(conn->bits.user_passwd) {
+    snprintf(option_arg, sizeof(option_arg), "USER,%s", conn->user);
+    beg = curl_slist_append(tn->telnet_vars, option_arg);
+    if(!beg) {
+      curl_slist_free_all(tn->telnet_vars);
+      tn->telnet_vars = NULL;
+      return CURLE_OUT_OF_MEMORY;
+    }
+    tn->telnet_vars = beg;
+    tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
+  }
+
+  for(head = data->set.telnet_options; head; head=head->next) {
+    if(sscanf(head->data, "%127[^= ]%*[ =]%255s",
+              option_keyword, option_arg) == 2) {
+
+      /* Terminal type */
+      if(Curl_raw_equal(option_keyword, "TTYPE")) {
+        strncpy(tn->subopt_ttype, option_arg, 31);
+        tn->subopt_ttype[31] = 0; /* String termination */
+        tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES;
+        continue;
+      }
+
+      /* Display variable */
+      if(Curl_raw_equal(option_keyword, "XDISPLOC")) {
+        strncpy(tn->subopt_xdisploc, option_arg, 127);
+        tn->subopt_xdisploc[127] = 0; /* String termination */
+        tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES;
+        continue;
+      }
+
+      /* Environment variable */
+      if(Curl_raw_equal(option_keyword, "NEW_ENV")) {
+        beg = curl_slist_append(tn->telnet_vars, option_arg);
+        if(!beg) {
+          result = CURLE_OUT_OF_MEMORY;
+          break;
+        }
+        tn->telnet_vars = beg;
+        tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
+        continue;
+      }
+
+          /* Window Size */
+      if(Curl_raw_equal(option_keyword, "WS")) {
+        if(sscanf(option_arg, "%hu%*[xX]%hu",
+                  &tn->subopt_wsx, &tn->subopt_wsy) == 2)
+          tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES;
+        else {
+          failf(data, "Syntax error in telnet option: %s", head->data);
+          result = CURLE_TELNET_OPTION_SYNTAX;
+          break;
+        }
+        continue;
+      }
+
+      /* To take care or not of the 8th bit in data exchange */
+      if(Curl_raw_equal(option_keyword, "BINARY")) {
+        binary_option=atoi(option_arg);
+        if(binary_option!=1) {
+          tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO;
+          tn->him_preferred[CURL_TELOPT_BINARY] = CURL_NO;
+        }
+        continue;
+      }
+
+      failf(data, "Unknown telnet option %s", head->data);
+      result = CURLE_UNKNOWN_TELNET_OPTION;
+      break;
+    }
+    else {
+      failf(data, "Syntax error in telnet option: %s", head->data);
+      result = CURLE_TELNET_OPTION_SYNTAX;
+      break;
+    }
+  }
+
+  if(result) {
+    curl_slist_free_all(tn->telnet_vars);
+    tn->telnet_vars = NULL;
+  }
+
+  return result;
+}
+
+/*
+ * suboption()
+ *
+ * Look at the sub-option buffer, and try to be helpful to the other
+ * side.
+ */
+
+static void suboption(struct connectdata *conn)
+{
+  struct curl_slist *v;
+  unsigned char temp[2048];
+  ssize_t bytes_written;
+  size_t len;
+  size_t tmplen;
+  int err;
+  char varname[128];
+  char varval[128];
+  struct SessionHandle *data = conn->data;
+  struct TELNET *tn = (struct TELNET *)data->state.proto.telnet;
+
+  printsub(data, '<', (unsigned char *)tn->subbuffer, CURL_SB_LEN(tn)+2);
+  switch (CURL_SB_GET(tn)) {
+    case CURL_TELOPT_TTYPE:
+      len = strlen(tn->subopt_ttype) + 4 + 2;
+      snprintf((char *)temp, sizeof(temp),
+               "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_TTYPE,
+               CURL_TELQUAL_IS, tn->subopt_ttype, CURL_IAC, CURL_SE);
+      bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
+      if(bytes_written < 0) {
+        err = SOCKERRNO;
+        failf(data,"Sending data failed (%d)",err);
+      }
+      printsub(data, '>', &temp[2], len-2);
+      break;
+    case CURL_TELOPT_XDISPLOC:
+      len = strlen(tn->subopt_xdisploc) + 4 + 2;
+      snprintf((char *)temp, sizeof(temp),
+               "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_XDISPLOC,
+               CURL_TELQUAL_IS, tn->subopt_xdisploc, CURL_IAC, CURL_SE);
+      bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
+      if(bytes_written < 0) {
+        err = SOCKERRNO;
+        failf(data,"Sending data failed (%d)",err);
+      }
+      printsub(data, '>', &temp[2], len-2);
+      break;
+    case CURL_TELOPT_NEW_ENVIRON:
+      snprintf((char *)temp, sizeof(temp),
+               "%c%c%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_NEW_ENVIRON,
+               CURL_TELQUAL_IS);
+      len = 4;
+
+      for(v = tn->telnet_vars;v;v = v->next) {
+        tmplen = (strlen(v->data) + 1);
+        /* Add the variable only if it fits */
+        if(len + tmplen < (int)sizeof(temp)-6) {
+          sscanf(v->data, "%127[^,],%127s", varname, varval);
+          snprintf((char *)&temp[len], sizeof(temp) - len,
+                   "%c%s%c%s", CURL_NEW_ENV_VAR, varname,
+                   CURL_NEW_ENV_VALUE, varval);
+          len += tmplen;
+        }
+      }
+      snprintf((char *)&temp[len], sizeof(temp) - len,
+               "%c%c", CURL_IAC, CURL_SE);
+      len += 2;
+      bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
+      if(bytes_written < 0) {
+        err = SOCKERRNO;
+        failf(data,"Sending data failed (%d)",err);
+      }
+      printsub(data, '>', &temp[2], len-2);
+      break;
+  }
+  return;
+}
+
+
+/*
+ * sendsuboption()
+ *
+ * Send suboption information to the server side.
+ */
+
+static void sendsuboption(struct connectdata *conn, int option)
+{
+  ssize_t bytes_written;
+  int err;
+  unsigned short x, y;
+  unsigned char*uc1, *uc2;
+
+  struct SessionHandle *data = conn->data;
+  struct TELNET *tn = (struct TELNET *)data->state.proto.telnet;
+
+  switch (option) {
+  case CURL_TELOPT_NAWS:
+    /* We prepare data to be sent */
+    CURL_SB_CLEAR(tn);
+    CURL_SB_ACCUM(tn, CURL_IAC);
+    CURL_SB_ACCUM(tn, CURL_SB);
+    CURL_SB_ACCUM(tn, CURL_TELOPT_NAWS);
+    /* We must deal either with litte or big endien processors */
+    /* Window size must be sent according to the 'network order' */
+    x=htons(tn->subopt_wsx);
+    y=htons(tn->subopt_wsy);
+    uc1 = (unsigned char*)&x;
+    uc2 = (unsigned char*)&y;
+    CURL_SB_ACCUM(tn, uc1[0]);
+    CURL_SB_ACCUM(tn, uc1[1]);
+    CURL_SB_ACCUM(tn, uc2[0]);
+    CURL_SB_ACCUM(tn, uc2[1]);
+
+    CURL_SB_ACCUM(tn, CURL_IAC);
+    CURL_SB_ACCUM(tn, CURL_SE);
+    CURL_SB_TERM(tn);
+    /* data suboption is now ready */
+
+    printsub(data, '>', (unsigned char *)tn->subbuffer+2,
+             CURL_SB_LEN(tn)-2);
+
+    /* we send the header of the suboption... */
+    bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer, 3);
+    if(bytes_written < 0) {
+      err = SOCKERRNO;
+      failf(data, "Sending data failed (%d)", err);
+    }
+    /* ... then the window size with the send_telnet_data() function
+       to deal with 0xFF cases ... */
+    send_telnet_data(conn, (char *)tn->subbuffer+3, 4);
+    /* ... and the footer */
+    bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer+7, 2);
+    if(bytes_written < 0) {
+      err = SOCKERRNO;
+      failf(data, "Sending data failed (%d)", err);
+    }
+    break;
+  }
+}
+
+
+static
+CURLcode telrcv(struct connectdata *conn,
+                const unsigned char *inbuf, /* Data received from socket */
+                ssize_t count)              /* Number of bytes received */
+{
+  unsigned char c;
+  CURLcode result;
+  int in = 0;
+  int startwrite=-1;
+  struct SessionHandle *data = conn->data;
+  struct TELNET *tn = (struct TELNET *)data->state.proto.telnet;
+
+#define startskipping()                                       \
+  if(startwrite >= 0) {                                       \
+    result = Curl_client_write(conn,                          \
+                               CLIENTWRITE_BODY,              \
+                               (char *)&inbuf[startwrite],    \
+                               in-startwrite);                \
+    if(result != CURLE_OK)                                    \
+      return result;                                          \
+  }                                                           \
+  startwrite = -1
+
+#define writebyte() \
+    if(startwrite < 0) \
+      startwrite = in
+
+#define bufferflush() startskipping()
+
+  while(count--) {
+    c = inbuf[in];
+
+    switch (tn->telrcv_state) {
+    case CURL_TS_CR:
+      tn->telrcv_state = CURL_TS_DATA;
+      if(c == '\0') {
+        startskipping();
+        break;   /* Ignore \0 after CR */
+      }
+      writebyte();
+      break;
+
+    case CURL_TS_DATA:
+      if(c == CURL_IAC) {
+        tn->telrcv_state = CURL_TS_IAC;
+        startskipping();
+        break;
+      }
+      else if(c == '\r')
+        tn->telrcv_state = CURL_TS_CR;
+      writebyte();
+      break;
+
+    case CURL_TS_IAC:
+    process_iac:
+      DEBUGASSERT(startwrite < 0);
+      switch (c) {
+      case CURL_WILL:
+        tn->telrcv_state = CURL_TS_WILL;
+        break;
+      case CURL_WONT:
+        tn->telrcv_state = CURL_TS_WONT;
+        break;
+      case CURL_DO:
+        tn->telrcv_state = CURL_TS_DO;
+        break;
+      case CURL_DONT:
+        tn->telrcv_state = CURL_TS_DONT;
+        break;
+      case CURL_SB:
+        CURL_SB_CLEAR(tn);
+        tn->telrcv_state = CURL_TS_SB;
+        break;
+      case CURL_IAC:
+        tn->telrcv_state = CURL_TS_DATA;
+        writebyte();
+        break;
+      case CURL_DM:
+      case CURL_NOP:
+      case CURL_GA:
+      default:
+        tn->telrcv_state = CURL_TS_DATA;
+        printoption(data, "RCVD", CURL_IAC, c);
+        break;
+      }
+      break;
+
+      case CURL_TS_WILL:
+        printoption(data, "RCVD", CURL_WILL, c);
+        tn->please_negotiate = 1;
+        rec_will(conn, c);
+        tn->telrcv_state = CURL_TS_DATA;
+        break;
+
+      case CURL_TS_WONT:
+        printoption(data, "RCVD", CURL_WONT, c);
+        tn->please_negotiate = 1;
+        rec_wont(conn, c);
+        tn->telrcv_state = CURL_TS_DATA;
+        break;
+
+      case CURL_TS_DO:
+        printoption(data, "RCVD", CURL_DO, c);
+        tn->please_negotiate = 1;
+        rec_do(conn, c);
+        tn->telrcv_state = CURL_TS_DATA;
+        break;
+
+      case CURL_TS_DONT:
+        printoption(data, "RCVD", CURL_DONT, c);
+        tn->please_negotiate = 1;
+        rec_dont(conn, c);
+        tn->telrcv_state = CURL_TS_DATA;
+        break;
+
+      case CURL_TS_SB:
+        if(c == CURL_IAC)
+          tn->telrcv_state = CURL_TS_SE;
+        else
+          CURL_SB_ACCUM(tn,c);
+        break;
+
+      case CURL_TS_SE:
+        if(c != CURL_SE) {
+          if(c != CURL_IAC) {
+            /*
+             * This is an error.  We only expect to get "IAC IAC" or "IAC SE".
+             * Several things may have happened.  An IAC was not doubled, the
+             * IAC SE was left off, or another option got inserted into the
+             * suboption are all possibilities.  If we assume that the IAC was
+             * not doubled, and really the IAC SE was left off, we could get
+             * into an infinate loop here.  So, instead, we terminate the
+             * suboption, and process the partial suboption if we can.
+             */
+            CURL_SB_ACCUM(tn, CURL_IAC);
+            CURL_SB_ACCUM(tn, c);
+            tn->subpointer -= 2;
+            CURL_SB_TERM(tn);
+
+            printoption(data, "In SUBOPTION processing, RCVD", CURL_IAC, c);
+            suboption(conn);   /* handle sub-option */
+            tn->telrcv_state = CURL_TS_IAC;
+            goto process_iac;
+          }
+          CURL_SB_ACCUM(tn,c);
+          tn->telrcv_state = CURL_TS_SB;
+        }
+        else
+        {
+          CURL_SB_ACCUM(tn, CURL_IAC);
+          CURL_SB_ACCUM(tn, CURL_SE);
+          tn->subpointer -= 2;
+          CURL_SB_TERM(tn);
+          suboption(conn);   /* handle sub-option */
+          tn->telrcv_state = CURL_TS_DATA;
+        }
+        break;
+    }
+    ++in;
+  }
+  bufferflush();
+  return CURLE_OK;
+}
+
+/* Escape and send a telnet data block */
+/* TODO: write large chunks of data instead of one byte at a time */
+static CURLcode send_telnet_data(struct connectdata *conn,
+                                 char *buffer, ssize_t nread)
+{
+  unsigned char outbuf[2];
+  ssize_t bytes_written, total_written;
+  int out_count;
+  CURLcode rc = CURLE_OK;
+
+  while(rc == CURLE_OK && nread--) {
+    outbuf[0] = *buffer++;
+    out_count = 1;
+    if(outbuf[0] == CURL_IAC)
+      outbuf[out_count++] = CURL_IAC;
+
+    total_written = 0;
+    do {
+      /* Make sure socket is writable to avoid EWOULDBLOCK condition */
+      struct pollfd pfd[1];
+      pfd[0].fd = conn->sock[FIRSTSOCKET];
+      pfd[0].events = POLLOUT;
+      switch (Curl_poll(pfd, 1, -1)) {
+        case -1:                    /* error, abort writing */
+        case 0:                     /* timeout (will never happen) */
+          rc = CURLE_SEND_ERROR;
+          break;
+        default:                    /* write! */
+          bytes_written = 0;
+          rc = Curl_write(conn, conn->sock[FIRSTSOCKET], outbuf+total_written,
+                          out_count-total_written, &bytes_written);
+          total_written += bytes_written;
+          break;
+      }
+    /* handle partial write */
+    } while(rc == CURLE_OK && total_written < out_count);
+  }
+  return rc;
+}
+
+static CURLcode telnet_done(struct connectdata *conn,
+                                 CURLcode status, bool premature)
+{
+  struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
+  (void)status; /* unused */
+  (void)premature; /* not used */
+
+  if(!tn)
+    return CURLE_OK;
+
+  curl_slist_free_all(tn->telnet_vars);
+  tn->telnet_vars = NULL;
+
+  Curl_safefree(conn->data->state.proto.telnet);
+
+  return CURLE_OK;
+}
+
+static CURLcode telnet_do(struct connectdata *conn, bool *done)
+{
+  CURLcode code;
+  struct SessionHandle *data = conn->data;
+  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
+#ifdef USE_WINSOCK
+  HMODULE wsock2;
+  WSOCK2_FUNC close_event_func;
+  WSOCK2_FUNC create_event_func;
+  WSOCK2_FUNC event_select_func;
+  WSOCK2_FUNC enum_netevents_func;
+  WSAEVENT event_handle;
+  WSANETWORKEVENTS events;
+  HANDLE stdin_handle;
+  HANDLE objs[2];
+  DWORD  obj_count;
+  DWORD  wait_timeout;
+  DWORD waitret;
+  DWORD readfile_read;
+  int err;
+#else
+  int interval_ms;
+  struct pollfd pfd[2];
+  int poll_cnt;
+  curl_off_t total_dl = 0;
+  curl_off_t total_ul = 0;
+#endif
+  ssize_t nread;
+  struct timeval now;
+  bool keepon = TRUE;
+  char *buf = data->state.buffer;
+  struct TELNET *tn;
+
+  *done = TRUE; /* unconditionally */
+
+  code = init_telnet(conn);
+  if(code)
+    return code;
+
+  tn = (struct TELNET *)data->state.proto.telnet;
+
+  code = check_telnet_options(conn);
+  if(code)
+    return code;
+
+#ifdef USE_WINSOCK
+  /*
+  ** This functionality only works with WinSock >= 2.0.  So,
+  ** make sure have it.
+  */
+  code = check_wsock2(data);
+  if(code)
+    return code;
+
+  /* OK, so we have WinSock 2.0.  We need to dynamically */
+  /* load ws2_32.dll and get the function pointers we need. */
+  wsock2 = LoadLibrary(TEXT("WS2_32.DLL"));
+  if(wsock2 == NULL) {
+    failf(data,"failed to load WS2_32.DLL (%d)", ERRNO);
+    return CURLE_FAILED_INIT;
+  }
+
+  /* Grab a pointer to WSACreateEvent */
+  create_event_func = GetProcAddress(wsock2,"WSACreateEvent");
+  if(create_event_func == NULL) {
+    failf(data,"failed to find WSACreateEvent function (%d)",
+          ERRNO);
+    FreeLibrary(wsock2);
+    return CURLE_FAILED_INIT;
+  }
+
+  /* And WSACloseEvent */
+  close_event_func = GetProcAddress(wsock2,"WSACloseEvent");
+  if(close_event_func == NULL) {
+    failf(data,"failed to find WSACloseEvent function (%d)",
+          ERRNO);
+    FreeLibrary(wsock2);
+    return CURLE_FAILED_INIT;
+  }
+
+  /* And WSAEventSelect */
+  event_select_func = GetProcAddress(wsock2,"WSAEventSelect");
+  if(event_select_func == NULL) {
+    failf(data,"failed to find WSAEventSelect function (%d)",
+          ERRNO);
+    FreeLibrary(wsock2);
+    return CURLE_FAILED_INIT;
+  }
+
+  /* And WSAEnumNetworkEvents */
+  enum_netevents_func = GetProcAddress(wsock2,"WSAEnumNetworkEvents");
+  if(enum_netevents_func == NULL) {
+    failf(data,"failed to find WSAEnumNetworkEvents function (%d)",
+          ERRNO);
+    FreeLibrary(wsock2);
+    return CURLE_FAILED_INIT;
+  }
+
+  /* We want to wait for both stdin and the socket. Since
+  ** the select() function in winsock only works on sockets
+  ** we have to use the WaitForMultipleObjects() call.
+  */
+
+  /* First, create a sockets event object */
+  event_handle = (WSAEVENT)create_event_func();
+  if(event_handle == WSA_INVALID_EVENT) {
+    failf(data,"WSACreateEvent failed (%d)", SOCKERRNO);
+    FreeLibrary(wsock2);
+    return CURLE_FAILED_INIT;
+  }
+
+  /* Tell winsock what events we want to listen to */
+  if(event_select_func(sockfd, event_handle, FD_READ|FD_CLOSE) ==
+     SOCKET_ERROR) {
+    close_event_func(event_handle);
+    FreeLibrary(wsock2);
+    return CURLE_OK;
+  }
+
+  /* The get the Windows file handle for stdin */
+  stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
+
+  /* Create the list of objects to wait for */
+  objs[0] = event_handle;
+  objs[1] = stdin_handle;
+
+  /* If stdin_handle is a pipe, use PeekNamedPipe() method to check it,
+     else use the old WaitForMultipleObjects() way */
+  if(GetFileType(stdin_handle) == FILE_TYPE_PIPE ||
+     data->set.is_fread_set) {
+    /* Don't wait for stdin_handle, just wait for event_handle */
+    obj_count = 1;
+    /* Check stdin_handle per 100 milliseconds */
+    wait_timeout = 100;
+  }
+  else {
+    obj_count = 2;
+    wait_timeout = 1000;
+  }
+
+  /* Keep on listening and act on events */
+  while(keepon) {
+    waitret = WaitForMultipleObjects(obj_count, objs, FALSE, wait_timeout);
+    switch(waitret) {
+    case WAIT_TIMEOUT:
+    {
+      for(;;) {
+        if(obj_count == 1) {
+          /* read from user-supplied method */
+          code = (int)conn->fread_func(buf, 1, BUFSIZE - 1, conn->fread_in);
+          if(code == CURL_READFUNC_ABORT) {
+            keepon = FALSE;
+            code = CURLE_READ_ERROR;
+            break;
+          }
+
+          if(code == CURL_READFUNC_PAUSE)
+            break;
+
+          if(code == 0)                        /* no bytes */
+            break;
+
+          readfile_read = code; /* fall thru with number of bytes read */
+        }
+        else {
+          /* read from stdin */
+          if(!PeekNamedPipe(stdin_handle, NULL, 0, NULL,
+                            &readfile_read, NULL)) {
+            keepon = FALSE;
+            code = CURLE_READ_ERROR;
+            break;
+          }
+
+          if(!readfile_read)
+            break;
+
+          if(!ReadFile(stdin_handle, buf, sizeof(data->state.buffer),
+                       &readfile_read, NULL)) {
+            keepon = FALSE;
+            code = CURLE_READ_ERROR;
+            break;
+          }
+        }
+
+        code = send_telnet_data(conn, buf, readfile_read);
+        if(code) {
+          keepon = FALSE;
+          break;
+        }
+      }
+    }
+    break;
+
+    case WAIT_OBJECT_0 + 1:
+    {
+      if(!ReadFile(stdin_handle, buf, sizeof(data->state.buffer),
+                   &readfile_read, NULL)) {
+        keepon = FALSE;
+        code = CURLE_READ_ERROR;
+        break;
+      }
+
+      code = send_telnet_data(conn, buf, readfile_read);
+      if(code) {
+        keepon = FALSE;
+        break;
+      }
+    }
+    break;
+
+    case WAIT_OBJECT_0:
+
+      if(SOCKET_ERROR == enum_netevents_func(sockfd, event_handle, &events)) {
+        if((err = SOCKERRNO) != EINPROGRESS) {
+          infof(data,"WSAEnumNetworkEvents failed (%d)", err);
+          keepon = FALSE;
+          code = CURLE_READ_ERROR;
+        }
+        break;
+      }
+      if(events.lNetworkEvents & FD_READ) {
+        /* read data from network */
+        code = Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread);
+        /* read would've blocked. Loop again */
+        if(code == CURLE_AGAIN)
+          break;
+        /* returned not-zero, this an error */
+        else if(code) {
+          keepon = FALSE;
+          break;
+        }
+        /* returned zero but actually received 0 or less here,
+           the server closed the connection and we bail out */
+        else if(nread <= 0) {
+          keepon = FALSE;
+          break;
+        }
+
+        code = telrcv(conn, (unsigned char *)buf, nread);
+        if(code) {
+          keepon = FALSE;
+          break;
+        }
+
+        /* Negotiate if the peer has started negotiating,
+           otherwise don't. We don't want to speak telnet with
+           non-telnet servers, like POP or SMTP. */
+        if(tn->please_negotiate && !tn->already_negotiated) {
+          negotiate(conn);
+          tn->already_negotiated = 1;
+        }
+      }
+      if(events.lNetworkEvents & FD_CLOSE) {
+        keepon = FALSE;
+      }
+      break;
+
+    }
+
+    if(data->set.timeout) {
+      now = Curl_tvnow();
+      if(Curl_tvdiff(now, conn->created) >= data->set.timeout) {
+        failf(data, "Time-out");
+        code = CURLE_OPERATION_TIMEDOUT;
+        keepon = FALSE;
+      }
+    }
+  }
+
+  /* We called WSACreateEvent, so call WSACloseEvent */
+  if(!close_event_func(event_handle)) {
+    infof(data,"WSACloseEvent failed (%d)", SOCKERRNO);
+  }
+
+  /* "Forget" pointers into the library we're about to free */
+  create_event_func = NULL;
+  close_event_func = NULL;
+  event_select_func = NULL;
+  enum_netevents_func = NULL;
+
+  /* We called LoadLibrary, so call FreeLibrary */
+  if(!FreeLibrary(wsock2))
+    infof(data,"FreeLibrary(wsock2) failed (%d)", ERRNO);
+#else
+  pfd[0].fd = sockfd;
+  pfd[0].events = POLLIN;
+
+  if(conn->fread_func != (curl_read_callback)fread) {
+    poll_cnt = 1;
+    interval_ms = 100; /* poll user-supplied read function */
+  }
+  else {
+    /* really using fread, so infile is a FILE* */
+    pfd[1].fd = fileno((FILE *)conn->fread_in);
+    pfd[1].events = POLLIN;
+    poll_cnt = 2;
+    interval_ms = 1 * 1000;
+  }
+
+  while(keepon) {
+    switch (Curl_poll(pfd, poll_cnt, interval_ms)) {
+    case -1:                    /* error, stop reading */
+      keepon = FALSE;
+      continue;
+    case 0:                     /* timeout */
+      pfd[0].revents = 0;
+      pfd[1].revents = 0;
+      /* fall through */
+    default:                    /* read! */
+      if(pfd[0].revents & POLLIN) {
+        /* read data from network */
+        code = Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread);
+        /* read would've blocked. Loop again */
+        if(code == CURLE_AGAIN)
+          break;
+        /* returned not-zero, this an error */
+        else if(code) {
+          keepon = FALSE;
+          break;
+        }
+        /* returned zero but actually received 0 or less here,
+           the server closed the connection and we bail out */
+        else if(nread <= 0) {
+          keepon = FALSE;
+          break;
+        }
+
+        total_dl += nread;
+        Curl_pgrsSetDownloadCounter(data, total_dl);
+        code = telrcv(conn, (unsigned char *)buf, nread);
+        if(code) {
+          keepon = FALSE;
+          break;
+        }
+
+        /* Negotiate if the peer has started negotiating,
+           otherwise don't. We don't want to speak telnet with
+           non-telnet servers, like POP or SMTP. */
+        if(tn->please_negotiate && !tn->already_negotiated) {
+          negotiate(conn);
+          tn->already_negotiated = 1;
+        }
+      }
+
+      nread = 0;
+      if(poll_cnt == 2) {
+        if(pfd[1].revents & POLLIN) { /* read from in file */
+          nread = read(pfd[1].fd, buf, BUFSIZE - 1);
+        }
+      }
+      else {
+        /* read from user-supplied method */
+        nread = (int)conn->fread_func(buf, 1, BUFSIZE - 1, conn->fread_in);
+        if(nread == CURL_READFUNC_ABORT) {
+          keepon = FALSE;
+          break;
+        }
+        if(nread == CURL_READFUNC_PAUSE)
+          break;
+      }
+
+      if(nread > 0) {
+        code = send_telnet_data(conn, buf, nread);
+        if(code) {
+          keepon = FALSE;
+          break;
+        }
+        total_ul += nread;
+        Curl_pgrsSetUploadCounter(data, total_ul);
+      }
+      else if(nread < 0)
+        keepon = FALSE;
+
+      break;
+    } /* poll switch statement */
+
+    if(data->set.timeout) {
+      now = Curl_tvnow();
+      if(Curl_tvdiff(now, conn->created) >= data->set.timeout) {
+        failf(data, "Time-out");
+        code = CURLE_OPERATION_TIMEDOUT;
+        keepon = FALSE;
+      }
+    }
+
+    if(Curl_pgrsUpdate(conn)) {
+      code = CURLE_ABORTED_BY_CALLBACK;
+      break;
+    }
+  }
+#endif
+  /* mark this as "no further transfer wanted" */
+  Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+
+  return code;
+}
+#endif
diff --git a/lib/curl_tftp.c b/lib/curl_tftp.c
new file mode 100644 (file)
index 0000000..1af246e
--- /dev/null
@@ -0,0 +1,1500 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_TFTP
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#include "curl_urldata.h"
+#include <curl/curl.h>
+#include "curl_transfer.h"
+#include "curl_sendf.h"
+#include "curl_tftp.h"
+#include "curl_progress.h"
+#include "curl_connect.h"
+#include "curl_strerror.h"
+#include "curl_sockaddr.h" /* required for Curl_sockaddr_storage */
+#include "curl_multiif.h"
+#include "curl_url.h"
+#include "curl_rawstr.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+#include "curl_select.h"
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/* RFC2348 allows the block size to be negotiated */
+#define TFTP_BLKSIZE_DEFAULT 512
+#define TFTP_BLKSIZE_MIN 8
+#define TFTP_BLKSIZE_MAX 65464
+#define TFTP_OPTION_BLKSIZE "blksize"
+
+/* from RFC2349: */
+#define TFTP_OPTION_TSIZE    "tsize"
+#define TFTP_OPTION_INTERVAL "timeout"
+
+typedef enum {
+  TFTP_MODE_NETASCII=0,
+  TFTP_MODE_OCTET
+} tftp_mode_t;
+
+typedef enum {
+  TFTP_STATE_START=0,
+  TFTP_STATE_RX,
+  TFTP_STATE_TX,
+  TFTP_STATE_FIN
+} tftp_state_t;
+
+typedef enum {
+  TFTP_EVENT_NONE = -1,
+  TFTP_EVENT_INIT = 0,
+  TFTP_EVENT_RRQ = 1,
+  TFTP_EVENT_WRQ = 2,
+  TFTP_EVENT_DATA = 3,
+  TFTP_EVENT_ACK = 4,
+  TFTP_EVENT_ERROR = 5,
+  TFTP_EVENT_OACK = 6,
+  TFTP_EVENT_TIMEOUT
+} tftp_event_t;
+
+typedef enum {
+  TFTP_ERR_UNDEF=0,
+  TFTP_ERR_NOTFOUND,
+  TFTP_ERR_PERM,
+  TFTP_ERR_DISKFULL,
+  TFTP_ERR_ILLEGAL,
+  TFTP_ERR_UNKNOWNID,
+  TFTP_ERR_EXISTS,
+  TFTP_ERR_NOSUCHUSER,  /* This will never be triggered by this code */
+
+  /* The remaining error codes are internal to curl */
+  TFTP_ERR_NONE = -100,
+  TFTP_ERR_TIMEOUT,
+  TFTP_ERR_NORESPONSE
+} tftp_error_t;
+
+typedef struct tftp_packet {
+  unsigned char *data;
+} tftp_packet_t;
+
+typedef struct tftp_state_data {
+  tftp_state_t    state;
+  tftp_mode_t     mode;
+  tftp_error_t    error;
+  tftp_event_t    event;
+  struct connectdata      *conn;
+  curl_socket_t   sockfd;
+  int             retries;
+  int             retry_time;
+  int             retry_max;
+  time_t          start_time;
+  time_t          max_time;
+  time_t          rx_time;
+  unsigned short  block;
+  struct Curl_sockaddr_storage   local_addr;
+  struct Curl_sockaddr_storage   remote_addr;
+  curl_socklen_t  remote_addrlen;
+  int             rbytes;
+  int             sbytes;
+  int             blksize;
+  int             requested_blksize;
+  tftp_packet_t   rpacket;
+  tftp_packet_t   spacket;
+} tftp_state_data_t;
+
+
+/* Forward declarations */
+static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event) ;
+static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event) ;
+static CURLcode tftp_connect(struct connectdata *conn, bool *done);
+static CURLcode tftp_disconnect(struct connectdata *conn,
+                                bool dead_connection);
+static CURLcode tftp_do(struct connectdata *conn, bool *done);
+static CURLcode tftp_done(struct connectdata *conn,
+                          CURLcode, bool premature);
+static CURLcode tftp_setup_connection(struct connectdata * conn);
+static CURLcode tftp_multi_statemach(struct connectdata *conn, bool *done);
+static CURLcode tftp_doing(struct connectdata *conn, bool *dophase_done);
+static int tftp_getsock(struct connectdata *conn, curl_socket_t *socks,
+                        int numsocks);
+static CURLcode tftp_translate_code(tftp_error_t error);
+
+
+/*
+ * TFTP protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_tftp = {
+  "TFTP",                               /* scheme */
+  tftp_setup_connection,                /* setup_connection */
+  tftp_do,                              /* do_it */
+  tftp_done,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  tftp_connect,                         /* connect_it */
+  tftp_multi_statemach,                 /* connecting */
+  tftp_doing,                           /* doing */
+  tftp_getsock,                         /* proto_getsock */
+  tftp_getsock,                         /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  tftp_disconnect,                      /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  PORT_TFTP,                            /* defport */
+  CURLPROTO_TFTP,                       /* protocol */
+  PROTOPT_NONE | PROTOPT_NOURLQUERY     /* flags */
+};
+
+/**********************************************************
+ *
+ * tftp_set_timeouts -
+ *
+ * Set timeouts based on state machine state.
+ * Use user provided connect timeouts until DATA or ACK
+ * packet is received, then use user-provided transfer timeouts
+ *
+ *
+ **********************************************************/
+static CURLcode tftp_set_timeouts(tftp_state_data_t *state)
+{
+  time_t maxtime, timeout;
+  long timeout_ms;
+  bool start = (state->state == TFTP_STATE_START) ? TRUE : FALSE;
+
+  time(&state->start_time);
+
+  /* Compute drop-dead time */
+  timeout_ms = Curl_timeleft(state->conn->data, NULL, start);
+
+  if(timeout_ms < 0) {
+    /* time-out, bail out, go home */
+    failf(state->conn->data, "Connection time-out");
+    return CURLE_OPERATION_TIMEDOUT;
+  }
+
+  if(start) {
+
+    maxtime = (time_t)(timeout_ms + 500) / 1000;
+    state->max_time = state->start_time+maxtime;
+
+    /* Set per-block timeout to total */
+    timeout = maxtime ;
+
+    /* Average restart after 5 seconds */
+    state->retry_max = (int)timeout/5;
+
+    if(state->retry_max < 1)
+      /* avoid division by zero below */
+      state->retry_max = 1;
+
+    /* Compute the re-start interval to suit the timeout */
+    state->retry_time = (int)timeout/state->retry_max;
+    if(state->retry_time<1)
+      state->retry_time=1;
+
+  }
+  else {
+    if(timeout_ms > 0)
+      maxtime = (time_t)(timeout_ms + 500) / 1000;
+    else
+      maxtime = 3600;
+
+    state->max_time = state->start_time+maxtime;
+
+    /* Set per-block timeout to total */
+    timeout = maxtime;
+
+    /* Average reposting an ACK after 5 seconds */
+    state->retry_max = (int)timeout/5;
+  }
+  /* But bound the total number */
+  if(state->retry_max<3)
+    state->retry_max=3;
+
+  if(state->retry_max>50)
+    state->retry_max=50;
+
+  /* Compute the re-ACK interval to suit the timeout */
+  state->retry_time = (int)(timeout/state->retry_max);
+  if(state->retry_time<1)
+    state->retry_time=1;
+
+  infof(state->conn->data,
+        "set timeouts for state %d; Total %ld, retry %d maxtry %d\n",
+        (int)state->state, (long)(state->max_time-state->start_time),
+        state->retry_time, state->retry_max);
+
+  /* init RX time */
+  time(&state->rx_time);
+
+  return CURLE_OK;
+}
+
+/**********************************************************
+ *
+ * tftp_set_send_first
+ *
+ * Event handler for the START state
+ *
+ **********************************************************/
+
+static void setpacketevent(tftp_packet_t *packet, unsigned short num)
+{
+  packet->data[0] = (unsigned char)(num >> 8);
+  packet->data[1] = (unsigned char)(num & 0xff);
+}
+
+
+static void setpacketblock(tftp_packet_t *packet, unsigned short num)
+{
+  packet->data[2] = (unsigned char)(num >> 8);
+  packet->data[3] = (unsigned char)(num & 0xff);
+}
+
+static unsigned short getrpacketevent(const tftp_packet_t *packet)
+{
+  return (unsigned short)((packet->data[0] << 8) | packet->data[1]);
+}
+
+static unsigned short getrpacketblock(const tftp_packet_t *packet)
+{
+  return (unsigned short)((packet->data[2] << 8) | packet->data[3]);
+}
+
+static size_t Curl_strnlen(const char *string, size_t maxlen)
+{
+  const char *end = memchr (string, '\0', maxlen);
+  return end ? (size_t) (end - string) : maxlen;
+}
+
+static const char *tftp_option_get(const char *buf, size_t len,
+                                   const char **option, const char **value)
+{
+  size_t loc;
+
+  loc = Curl_strnlen( buf, len );
+  loc++; /* NULL term */
+
+  if(loc >= len)
+    return NULL;
+  *option = buf;
+
+  loc += Curl_strnlen( buf+loc, len-loc );
+  loc++; /* NULL term */
+
+  if(loc > len)
+    return NULL;
+  *value = &buf[strlen(*option) + 1];
+
+  return &buf[loc];
+}
+
+static CURLcode tftp_parse_option_ack(tftp_state_data_t *state,
+                                      const char *ptr, int len)
+{
+  const char *tmp = ptr;
+  struct SessionHandle *data = state->conn->data;
+
+  /* if OACK doesn't contain blksize option, the default (512) must be used */
+  state->blksize = TFTP_BLKSIZE_DEFAULT;
+
+  while(tmp < ptr + len) {
+    const char *option, *value;
+
+    tmp = tftp_option_get(tmp, ptr + len - tmp, &option, &value);
+    if(tmp == NULL) {
+      failf(data, "Malformed ACK packet, rejecting");
+      return CURLE_TFTP_ILLEGAL;
+    }
+
+    infof(data, "got option=(%s) value=(%s)\n", option, value);
+
+    if(checkprefix(option, TFTP_OPTION_BLKSIZE)) {
+      long blksize;
+
+      blksize = strtol( value, NULL, 10 );
+
+      if(!blksize) {
+        failf(data, "invalid blocksize value in OACK packet");
+        return CURLE_TFTP_ILLEGAL;
+      }
+      else if(blksize > TFTP_BLKSIZE_MAX) {
+        failf(data, "%s (%d)", "blksize is larger than max supported",
+              TFTP_BLKSIZE_MAX);
+        return CURLE_TFTP_ILLEGAL;
+      }
+      else if(blksize < TFTP_BLKSIZE_MIN) {
+        failf(data, "%s (%d)", "blksize is smaller than min supported",
+              TFTP_BLKSIZE_MIN);
+        return CURLE_TFTP_ILLEGAL;
+      }
+      else if(blksize > state->requested_blksize) {
+        /* could realloc pkt buffers here, but the spec doesn't call out
+         * support for the server requesting a bigger blksize than the client
+         * requests */
+        failf(data, "%s (%ld)",
+              "server requested blksize larger than allocated", blksize);
+        return CURLE_TFTP_ILLEGAL;
+      }
+
+      state->blksize = (int)blksize;
+      infof(data, "%s (%d) %s (%d)\n", "blksize parsed from OACK",
+            state->blksize, "requested", state->requested_blksize);
+    }
+    else if(checkprefix(option, TFTP_OPTION_TSIZE)) {
+      long tsize = 0;
+
+      tsize = strtol( value, NULL, 10 );
+      infof(data, "%s (%ld)\n", "tsize parsed from OACK", tsize);
+
+      /* tsize should be ignored on upload: Who cares about the size of the
+         remote file? */
+      if(!data->set.upload) {
+        if(!tsize) {
+          failf(data, "invalid tsize -:%s:- value in OACK packet", value);
+          return CURLE_TFTP_ILLEGAL;
+        }
+        Curl_pgrsSetDownloadSize(data, tsize);
+      }
+    }
+  }
+
+  return CURLE_OK;
+}
+
+static size_t tftp_option_add(tftp_state_data_t *state, size_t csize,
+                              char *buf, const char *option)
+{
+  if(( strlen(option) + csize + 1 ) > (size_t)state->blksize)
+    return 0;
+  strcpy(buf, option);
+  return( strlen(option) + 1 );
+}
+
+static CURLcode tftp_connect_for_tx(tftp_state_data_t *state,
+                                    tftp_event_t event)
+{
+  CURLcode res;
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+  struct SessionHandle *data = state->conn->data;
+
+  infof(data, "%s\n", "Connected for transmit");
+#endif
+  state->state = TFTP_STATE_TX;
+  res = tftp_set_timeouts(state);
+  if(res != CURLE_OK)
+    return(res);
+  return tftp_tx(state, event);
+}
+
+static CURLcode tftp_connect_for_rx(tftp_state_data_t *state,
+                                    tftp_event_t event)
+{
+  CURLcode res;
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+  struct SessionHandle *data = state->conn->data;
+
+  infof(data, "%s\n", "Connected for receive");
+#endif
+  state->state = TFTP_STATE_RX;
+  res = tftp_set_timeouts(state);
+  if(res != CURLE_OK)
+    return(res);
+  return tftp_rx(state, event);
+}
+
+static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event)
+{
+  size_t sbytes;
+  ssize_t senddata;
+  const char *mode = "octet";
+  char *filename;
+  char buf[64];
+  struct SessionHandle *data = state->conn->data;
+  CURLcode res = CURLE_OK;
+
+  /* Set ascii mode if -B flag was used */
+  if(data->set.prefer_ascii)
+    mode = "netascii";
+
+  switch(event) {
+
+  case TFTP_EVENT_INIT:    /* Send the first packet out */
+  case TFTP_EVENT_TIMEOUT: /* Resend the first packet out */
+    /* Increment the retry counter, quit if over the limit */
+    state->retries++;
+    if(state->retries>state->retry_max) {
+      state->error = TFTP_ERR_NORESPONSE;
+      state->state = TFTP_STATE_FIN;
+      return res;
+    }
+
+    if(data->set.upload) {
+      /* If we are uploading, send an WRQ */
+      setpacketevent(&state->spacket, TFTP_EVENT_WRQ);
+      state->conn->data->req.upload_fromhere =
+        (char *)state->spacket.data+4;
+      if(data->set.infilesize != -1)
+        Curl_pgrsSetUploadSize(data, data->set.infilesize);
+    }
+    else {
+      /* If we are downloading, send an RRQ */
+      setpacketevent(&state->spacket, TFTP_EVENT_RRQ);
+    }
+    /* As RFC3617 describes the separator slash is not actually part of the
+       file name so we skip the always-present first letter of the path
+       string. */
+    filename = curl_easy_unescape(data, &state->conn->data->state.path[1], 0,
+                                  NULL);
+    if(!filename)
+      return CURLE_OUT_OF_MEMORY;
+
+    snprintf((char *)state->spacket.data+2,
+             state->blksize,
+             "%s%c%s%c", filename, '\0',  mode, '\0');
+    sbytes = 4 + strlen(filename) + strlen(mode);
+
+    /* add tsize option */
+    if(data->set.upload && (data->set.infilesize != -1))
+      snprintf( buf, sizeof(buf), "%" FORMAT_OFF_T, data->set.infilesize );
+    else
+      strcpy(buf, "0"); /* the destination is large enough */
+
+    sbytes += tftp_option_add(state, sbytes,
+                              (char *)state->spacket.data+sbytes,
+                              TFTP_OPTION_TSIZE);
+    sbytes += tftp_option_add(state, sbytes,
+                              (char *)state->spacket.data+sbytes, buf);
+    /* add blksize option */
+    snprintf( buf, sizeof(buf), "%d", state->requested_blksize );
+    sbytes += tftp_option_add(state, sbytes,
+                              (char *)state->spacket.data+sbytes,
+                              TFTP_OPTION_BLKSIZE);
+    sbytes += tftp_option_add(state, sbytes,
+                              (char *)state->spacket.data+sbytes, buf );
+
+    /* add timeout option */
+    snprintf( buf, sizeof(buf), "%d", state->retry_time);
+    sbytes += tftp_option_add(state, sbytes,
+                              (char *)state->spacket.data+sbytes,
+                              TFTP_OPTION_INTERVAL);
+    sbytes += tftp_option_add(state, sbytes,
+                              (char *)state->spacket.data+sbytes, buf );
+
+    /* the typecase for the 3rd argument is mostly for systems that do
+       not have a size_t argument, like older unixes that want an 'int' */
+    senddata = sendto(state->sockfd, (void *)state->spacket.data,
+                      (SEND_TYPE_ARG3)sbytes, 0,
+                      state->conn->ip_addr->ai_addr,
+                      state->conn->ip_addr->ai_addrlen);
+    if(senddata != (ssize_t)sbytes) {
+      failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
+    }
+    Curl_safefree(filename);
+    break;
+
+  case TFTP_EVENT_OACK:
+    if(data->set.upload) {
+      res = tftp_connect_for_tx(state, event);
+    }
+    else {
+      res = tftp_connect_for_rx(state, event);
+    }
+    break;
+
+  case TFTP_EVENT_ACK: /* Connected for transmit */
+    res = tftp_connect_for_tx(state, event);
+    break;
+
+  case TFTP_EVENT_DATA: /* Connected for receive */
+    res = tftp_connect_for_rx(state, event);
+    break;
+
+  case TFTP_EVENT_ERROR:
+    state->state = TFTP_STATE_FIN;
+    break;
+
+  default:
+    failf(state->conn->data, "tftp_send_first: internal error");
+    break;
+  }
+  return res;
+}
+
+/* the next blocknum is x + 1 but it needs to wrap at an unsigned 16bit
+   boundary */
+#define NEXT_BLOCKNUM(x) (((x)+1)&0xffff)
+
+/**********************************************************
+ *
+ * tftp_rx
+ *
+ * Event handler for the RX state
+ *
+ **********************************************************/
+static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event)
+{
+  ssize_t sbytes;
+  int rblock;
+  struct SessionHandle *data = state->conn->data;
+
+  switch(event) {
+
+  case TFTP_EVENT_DATA:
+    /* Is this the block we expect? */
+    rblock = getrpacketblock(&state->rpacket);
+    if(NEXT_BLOCKNUM(state->block) == rblock) {
+      /* This is the expected block.  Reset counters and ACK it. */
+      state->retries = 0;
+    }
+    else if(state->block == rblock) {
+      /* This is the last recently received block again. Log it and ACK it
+         again. */
+      infof(data, "Received last DATA packet block %d again.\n", rblock);
+    }
+    else {
+      /* totally unexpected, just log it */
+      infof(data,
+            "Received unexpected DATA packet block %d, expecting block %d\n",
+            rblock, NEXT_BLOCKNUM(state->block));
+      break;
+    }
+
+    /* ACK this block. */
+    state->block = (unsigned short)rblock;
+    setpacketevent(&state->spacket, TFTP_EVENT_ACK);
+    setpacketblock(&state->spacket, state->block);
+    sbytes = sendto(state->sockfd, (void *)state->spacket.data,
+                    4, SEND_4TH_ARG,
+                    (struct sockaddr *)&state->remote_addr,
+                    state->remote_addrlen);
+    if(sbytes < 0) {
+      failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
+      return CURLE_SEND_ERROR;
+    }
+
+    /* Check if completed (That is, a less than full packet is received) */
+    if(state->rbytes < (ssize_t)state->blksize+4) {
+      state->state = TFTP_STATE_FIN;
+    }
+    else {
+      state->state = TFTP_STATE_RX;
+    }
+    time(&state->rx_time);
+    break;
+
+  case TFTP_EVENT_OACK:
+    /* ACK option acknowledgement so we can move on to data */
+    state->block = 0;
+    state->retries = 0;
+    setpacketevent(&state->spacket, TFTP_EVENT_ACK);
+    setpacketblock(&state->spacket, state->block);
+    sbytes = sendto(state->sockfd, (void *)state->spacket.data,
+                    4, SEND_4TH_ARG,
+                    (struct sockaddr *)&state->remote_addr,
+                    state->remote_addrlen);
+    if(sbytes < 0) {
+      failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
+      return CURLE_SEND_ERROR;
+    }
+
+    /* we're ready to RX data */
+    state->state = TFTP_STATE_RX;
+    time(&state->rx_time);
+    break;
+
+  case TFTP_EVENT_TIMEOUT:
+    /* Increment the retry count and fail if over the limit */
+    state->retries++;
+    infof(data,
+          "Timeout waiting for block %d ACK.  Retries = %d\n",
+          NEXT_BLOCKNUM(state->block), state->retries);
+    if(state->retries > state->retry_max) {
+      state->error = TFTP_ERR_TIMEOUT;
+      state->state = TFTP_STATE_FIN;
+    }
+    else {
+      /* Resend the previous ACK */
+      sbytes = sendto(state->sockfd, (void *)state->spacket.data,
+                      4, SEND_4TH_ARG,
+                      (struct sockaddr *)&state->remote_addr,
+                      state->remote_addrlen);
+      if(sbytes<0) {
+        failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
+        return CURLE_SEND_ERROR;
+      }
+    }
+    break;
+
+  case TFTP_EVENT_ERROR:
+    setpacketevent(&state->spacket, TFTP_EVENT_ERROR);
+    setpacketblock(&state->spacket, state->block);
+    (void)sendto(state->sockfd, (void *)state->spacket.data,
+                 4, SEND_4TH_ARG,
+                 (struct sockaddr *)&state->remote_addr,
+                 state->remote_addrlen);
+    /* don't bother with the return code, but if the socket is still up we
+     * should be a good TFTP client and let the server know we're done */
+    state->state = TFTP_STATE_FIN;
+    break;
+
+  default:
+    failf(data, "%s", "tftp_rx: internal error");
+    return CURLE_TFTP_ILLEGAL; /* not really the perfect return code for
+                                  this */
+  }
+  return CURLE_OK;
+}
+
+/**********************************************************
+ *
+ * tftp_tx
+ *
+ * Event handler for the TX state
+ *
+ **********************************************************/
+static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event)
+{
+  struct SessionHandle *data = state->conn->data;
+  ssize_t sbytes;
+  int rblock;
+  CURLcode res = CURLE_OK;
+  struct SingleRequest *k = &data->req;
+
+  switch(event) {
+
+  case TFTP_EVENT_ACK:
+  case TFTP_EVENT_OACK:
+    if(event == TFTP_EVENT_ACK) {
+      /* Ack the packet */
+      rblock = getrpacketblock(&state->rpacket);
+
+      if(rblock != state->block &&
+         /* There's a bug in tftpd-hpa that causes it to send us an ack for
+          * 65535 when the block number wraps to 0. So when we're expecting
+          * 0, also accept 65535. See
+          * http://syslinux.zytor.com/archives/2010-September/015253.html
+          * */
+         !(state->block == 0 && rblock == 65535)) {
+        /* This isn't the expected block.  Log it and up the retry counter */
+        infof(data, "Received ACK for block %d, expecting %d\n",
+              rblock, state->block);
+        state->retries++;
+        /* Bail out if over the maximum */
+        if(state->retries>state->retry_max) {
+          failf(data, "tftp_tx: giving up waiting for block %d ack",
+                state->block);
+          res = CURLE_SEND_ERROR;
+        }
+        else {
+          /* Re-send the data packet */
+          sbytes = sendto(state->sockfd, (void *)state->spacket.data,
+                          4+state->sbytes, SEND_4TH_ARG,
+                          (struct sockaddr *)&state->remote_addr,
+                          state->remote_addrlen);
+          /* Check all sbytes were sent */
+          if(sbytes<0) {
+            failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
+            res = CURLE_SEND_ERROR;
+          }
+        }
+        return res;
+      }
+      /* This is the expected packet.  Reset the counters and send the next
+         block */
+      time(&state->rx_time);
+      state->block++;
+    }
+    else
+      state->block = 1; /* first data block is 1 when using OACK */
+
+    state->retries = 0;
+    setpacketevent(&state->spacket, TFTP_EVENT_DATA);
+    setpacketblock(&state->spacket, state->block);
+    if(state->block > 1 && state->sbytes < (int)state->blksize) {
+      state->state = TFTP_STATE_FIN;
+      return CURLE_OK;
+    }
+    res = Curl_fillreadbuffer(state->conn, state->blksize, &state->sbytes);
+    if(res)
+      return res;
+    sbytes = sendto(state->sockfd, (void *)state->spacket.data,
+                    4+state->sbytes, SEND_4TH_ARG,
+                    (struct sockaddr *)&state->remote_addr,
+                    state->remote_addrlen);
+    /* Check all sbytes were sent */
+    if(sbytes<0) {
+      failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
+      return CURLE_SEND_ERROR;
+    }
+    /* Update the progress meter */
+    k->writebytecount += state->sbytes;
+    Curl_pgrsSetUploadCounter(data, k->writebytecount);
+    break;
+
+  case TFTP_EVENT_TIMEOUT:
+    /* Increment the retry counter and log the timeout */
+    state->retries++;
+    infof(data, "Timeout waiting for block %d ACK. "
+          " Retries = %d\n", NEXT_BLOCKNUM(state->block), state->retries);
+    /* Decide if we've had enough */
+    if(state->retries > state->retry_max) {
+      state->error = TFTP_ERR_TIMEOUT;
+      state->state = TFTP_STATE_FIN;
+    }
+    else {
+      /* Re-send the data packet */
+      sbytes = sendto(state->sockfd, (void *)state->spacket.data,
+                      4+state->sbytes, SEND_4TH_ARG,
+                      (struct sockaddr *)&state->remote_addr,
+                      state->remote_addrlen);
+      /* Check all sbytes were sent */
+      if(sbytes<0) {
+        failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
+        return CURLE_SEND_ERROR;
+      }
+      /* since this was a re-send, we remain at the still byte position */
+      Curl_pgrsSetUploadCounter(data, k->writebytecount);
+    }
+    break;
+
+  case TFTP_EVENT_ERROR:
+    state->state = TFTP_STATE_FIN;
+    setpacketevent(&state->spacket, TFTP_EVENT_ERROR);
+    setpacketblock(&state->spacket, state->block);
+    (void)sendto(state->sockfd, (void *)state->spacket.data, 4, SEND_4TH_ARG,
+                 (struct sockaddr *)&state->remote_addr,
+                 state->remote_addrlen);
+    /* don't bother with the return code, but if the socket is still up we
+     * should be a good TFTP client and let the server know we're done */
+    state->state = TFTP_STATE_FIN;
+    break;
+
+  default:
+    failf(data, "tftp_tx: internal error, event: %i", (int)(event));
+    break;
+  }
+
+  return res;
+}
+
+/**********************************************************
+ *
+ * tftp_translate_code
+ *
+ * Translate internal error codes to CURL error codes
+ *
+ **********************************************************/
+static CURLcode tftp_translate_code(tftp_error_t error)
+{
+  CURLcode code = CURLE_OK;
+
+  if(error != TFTP_ERR_NONE) {
+    switch(error) {
+    case TFTP_ERR_NOTFOUND:
+      code = CURLE_TFTP_NOTFOUND;
+      break;
+    case TFTP_ERR_PERM:
+      code = CURLE_TFTP_PERM;
+      break;
+    case TFTP_ERR_DISKFULL:
+      code = CURLE_REMOTE_DISK_FULL;
+      break;
+    case TFTP_ERR_UNDEF:
+    case TFTP_ERR_ILLEGAL:
+      code = CURLE_TFTP_ILLEGAL;
+      break;
+    case TFTP_ERR_UNKNOWNID:
+      code = CURLE_TFTP_UNKNOWNID;
+      break;
+    case TFTP_ERR_EXISTS:
+      code = CURLE_REMOTE_FILE_EXISTS;
+      break;
+    case TFTP_ERR_NOSUCHUSER:
+      code = CURLE_TFTP_NOSUCHUSER;
+      break;
+    case TFTP_ERR_TIMEOUT:
+      code = CURLE_OPERATION_TIMEDOUT;
+      break;
+    case TFTP_ERR_NORESPONSE:
+      code = CURLE_COULDNT_CONNECT;
+      break;
+    default:
+      code= CURLE_ABORTED_BY_CALLBACK;
+      break;
+    }
+  }
+  else {
+    code = CURLE_OK;
+  }
+
+  return(code);
+}
+
+/**********************************************************
+ *
+ * tftp_state_machine
+ *
+ * The tftp state machine event dispatcher
+ *
+ **********************************************************/
+static CURLcode tftp_state_machine(tftp_state_data_t *state,
+                                   tftp_event_t event)
+{
+  CURLcode res = CURLE_OK;
+  struct SessionHandle *data = state->conn->data;
+  switch(state->state) {
+  case TFTP_STATE_START:
+    DEBUGF(infof(data, "TFTP_STATE_START\n"));
+    res = tftp_send_first(state, event);
+    break;
+  case TFTP_STATE_RX:
+    DEBUGF(infof(data, "TFTP_STATE_RX\n"));
+    res = tftp_rx(state, event);
+    break;
+  case TFTP_STATE_TX:
+    DEBUGF(infof(data, "TFTP_STATE_TX\n"));
+    res = tftp_tx(state, event);
+    break;
+  case TFTP_STATE_FIN:
+    infof(data, "%s\n", "TFTP finished");
+    break;
+  default:
+    DEBUGF(infof(data, "STATE: %d\n", state->state));
+    failf(data, "%s", "Internal state machine error");
+    res = CURLE_TFTP_ILLEGAL;
+    break;
+  }
+  return res;
+}
+
+/**********************************************************
+ *
+ * tftp_disconnect
+ *
+ * The disconnect callback
+ *
+ **********************************************************/
+static CURLcode tftp_disconnect(struct connectdata *conn, bool dead_connection)
+{
+  tftp_state_data_t *state = conn->proto.tftpc;
+  (void) dead_connection;
+
+  /* done, free dynamically allocated pkt buffers */
+  if(state) {
+    Curl_safefree(state->rpacket.data);
+    Curl_safefree(state->spacket.data);
+    free(state);
+  }
+
+  return CURLE_OK;
+}
+
+/**********************************************************
+ *
+ * tftp_connect
+ *
+ * The connect callback
+ *
+ **********************************************************/
+static CURLcode tftp_connect(struct connectdata *conn, bool *done)
+{
+  CURLcode code;
+  tftp_state_data_t *state;
+  int blksize, rc;
+
+  blksize = TFTP_BLKSIZE_DEFAULT;
+
+  /* If there already is a protocol-specific struct allocated for this
+     sessionhandle, deal with it */
+  Curl_reset_reqproto(conn);
+
+  state = conn->proto.tftpc = calloc(1, sizeof(tftp_state_data_t));
+  if(!state)
+    return CURLE_OUT_OF_MEMORY;
+
+  /* alloc pkt buffers based on specified blksize */
+  if(conn->data->set.tftp_blksize) {
+    blksize = (int)conn->data->set.tftp_blksize;
+    if(blksize > TFTP_BLKSIZE_MAX || blksize < TFTP_BLKSIZE_MIN )
+      return CURLE_TFTP_ILLEGAL;
+  }
+
+  if(!state->rpacket.data) {
+    state->rpacket.data = calloc(1, blksize + 2 + 2);
+
+    if(!state->rpacket.data)
+      return CURLE_OUT_OF_MEMORY;
+  }
+
+  if(!state->spacket.data) {
+    state->spacket.data = calloc(1, blksize + 2 + 2);
+
+    if(!state->spacket.data)
+      return CURLE_OUT_OF_MEMORY;
+  }
+
+  conn->bits.close = TRUE; /* we don't keep TFTP connections up bascially
+                              because there's none or very little gain for UDP
+                           */
+
+  state->conn = conn;
+  state->sockfd = state->conn->sock[FIRSTSOCKET];
+  state->state = TFTP_STATE_START;
+  state->error = TFTP_ERR_NONE;
+  state->blksize = TFTP_BLKSIZE_DEFAULT;
+  state->requested_blksize = blksize;
+
+  ((struct sockaddr *)&state->local_addr)->sa_family =
+    (unsigned short)(conn->ip_addr->ai_family);
+
+  tftp_set_timeouts(state);
+
+  if(!conn->bits.bound) {
+    /* If not already bound, bind to any interface, random UDP port. If it is
+     * reused or a custom local port was desired, this has already been done!
+     *
+     * We once used the size of the local_addr struct as the third argument
+     * for bind() to better work with IPv6 or whatever size the struct could
+     * have, but we learned that at least Tru64, AIX and IRIX *requires* the
+     * size of that argument to match the exact size of a 'sockaddr_in' struct
+     * when running IPv4-only.
+     *
+     * Therefore we use the size from the address we connected to, which we
+     * assume uses the same IP version and thus hopefully this works for both
+     * IPv4 and IPv6...
+     */
+    rc = bind(state->sockfd, (struct sockaddr *)&state->local_addr,
+              conn->ip_addr->ai_addrlen);
+    if(rc) {
+      failf(conn->data, "bind() failed; %s",
+            Curl_strerror(conn, SOCKERRNO));
+      return CURLE_COULDNT_CONNECT;
+    }
+    conn->bits.bound = TRUE;
+  }
+
+  Curl_pgrsStartNow(conn->data);
+
+  *done = TRUE;
+  code = CURLE_OK;
+  return(code);
+}
+
+/**********************************************************
+ *
+ * tftp_done
+ *
+ * The done callback
+ *
+ **********************************************************/
+static CURLcode tftp_done(struct connectdata *conn, CURLcode status,
+                          bool premature)
+{
+  CURLcode code = CURLE_OK;
+  tftp_state_data_t *state = (tftp_state_data_t *)conn->proto.tftpc;
+
+  (void)status; /* unused */
+  (void)premature; /* not used */
+
+  if(Curl_pgrsDone(conn))
+    return CURLE_ABORTED_BY_CALLBACK;
+
+  /* If we have encountered an error */
+  code = tftp_translate_code(state->error);
+
+  return code;
+}
+
+/**********************************************************
+ *
+ * tftp_getsock
+ *
+ * The getsock callback
+ *
+ **********************************************************/
+static int tftp_getsock(struct connectdata *conn, curl_socket_t *socks,
+                        int numsocks)
+{
+  if(!numsocks)
+    return GETSOCK_BLANK;
+
+  socks[0] = conn->sock[FIRSTSOCKET];
+
+  return GETSOCK_READSOCK(0);
+}
+
+/**********************************************************
+ *
+ * tftp_receive_packet
+ *
+ * Called once select fires and data is ready on the socket
+ *
+ **********************************************************/
+static CURLcode tftp_receive_packet(struct connectdata *conn)
+{
+  struct Curl_sockaddr_storage fromaddr;
+  curl_socklen_t        fromlen;
+  CURLcode              result = CURLE_OK;
+  struct SessionHandle  *data = conn->data;
+  tftp_state_data_t     *state = (tftp_state_data_t *)conn->proto.tftpc;
+  struct SingleRequest  *k = &data->req;
+
+  /* Receive the packet */
+  fromlen = sizeof(fromaddr);
+  state->rbytes = (int)recvfrom(state->sockfd,
+                                (void *)state->rpacket.data,
+                                state->blksize+4,
+                                0,
+                                (struct sockaddr *)&fromaddr,
+                                &fromlen);
+  if(state->remote_addrlen==0) {
+    memcpy(&state->remote_addr, &fromaddr, fromlen);
+    state->remote_addrlen = fromlen;
+  }
+
+  /* Sanity check packet length */
+  if(state->rbytes < 4) {
+    failf(data, "Received too short packet");
+    /* Not a timeout, but how best to handle it? */
+    state->event = TFTP_EVENT_TIMEOUT;
+  }
+  else {
+    /* The event is given by the TFTP packet time */
+    state->event = (tftp_event_t)getrpacketevent(&state->rpacket);
+
+    switch(state->event) {
+    case TFTP_EVENT_DATA:
+      /* Don't pass to the client empty or retransmitted packets */
+      if(state->rbytes > 4 &&
+         (NEXT_BLOCKNUM(state->block) == getrpacketblock(&state->rpacket))) {
+        result = Curl_client_write(conn, CLIENTWRITE_BODY,
+                                   (char *)state->rpacket.data+4,
+                                   state->rbytes-4);
+        if(result) {
+          tftp_state_machine(state, TFTP_EVENT_ERROR);
+          return result;
+        }
+        k->bytecount += state->rbytes-4;
+        Curl_pgrsSetDownloadCounter(data, (curl_off_t) k->bytecount);
+      }
+      break;
+    case TFTP_EVENT_ERROR:
+      state->error = (tftp_error_t)getrpacketblock(&state->rpacket);
+      infof(data, "%s\n", (const char *)state->rpacket.data+4);
+      break;
+    case TFTP_EVENT_ACK:
+      break;
+    case TFTP_EVENT_OACK:
+      result = tftp_parse_option_ack(state,
+                                     (const char *)state->rpacket.data+2,
+                                     state->rbytes-2);
+      if(result)
+        return result;
+      break;
+    case TFTP_EVENT_RRQ:
+    case TFTP_EVENT_WRQ:
+    default:
+      failf(data, "%s", "Internal error: Unexpected packet");
+      break;
+    }
+
+    /* Update the progress meter */
+    if(Curl_pgrsUpdate(conn)) {
+      tftp_state_machine(state, TFTP_EVENT_ERROR);
+      return CURLE_ABORTED_BY_CALLBACK;
+    }
+  }
+  return result;
+}
+
+/**********************************************************
+ *
+ * tftp_state_timeout
+ *
+ * Check if timeouts have been reached
+ *
+ **********************************************************/
+static long tftp_state_timeout(struct connectdata *conn, tftp_event_t *event)
+{
+  time_t                current;
+  tftp_state_data_t     *state = (tftp_state_data_t *)conn->proto.tftpc;
+
+  if(event)
+    *event = TFTP_EVENT_NONE;
+
+  time(&current);
+  if(current > state->max_time) {
+    DEBUGF(infof(conn->data, "timeout: %ld > %ld\n",
+                 (long)current, (long)state->max_time));
+    state->error = TFTP_ERR_TIMEOUT;
+    state->state = TFTP_STATE_FIN;
+    return 0;
+  }
+  else if(current > state->rx_time+state->retry_time) {
+    if(event)
+      *event = TFTP_EVENT_TIMEOUT;
+    time(&state->rx_time); /* update even though we received nothing */
+  }
+
+  /* there's a typecast below here since 'time_t' may in fact be larger than
+     'long', but we estimate that a 'long' will still be able to hold number
+     of seconds even if "only" 32 bit */
+  return (long)(state->max_time - current);
+}
+
+
+/**********************************************************
+ *
+ * tftp_easy_statemach
+ *
+ * Handle easy request until completion
+ *
+ **********************************************************/
+static CURLcode tftp_easy_statemach(struct connectdata *conn)
+{
+  int                   rc;
+  int                   check_time = 0;
+  CURLcode              result = CURLE_OK;
+  struct SessionHandle  *data = conn->data;
+  tftp_state_data_t     *state = (tftp_state_data_t *)conn->proto.tftpc;
+  curl_socket_t         fd_read;
+  long                  timeout_ms;
+  struct SingleRequest  *k = &data->req;
+  struct timeval        transaction_start = Curl_tvnow();
+
+  k->start = transaction_start;
+  k->now = transaction_start;
+
+  /* Run the TFTP State Machine */
+  for(; (state->state != TFTP_STATE_FIN) && (result == CURLE_OK); ) {
+
+    timeout_ms = state->retry_time * 1000;
+
+    if(data->set.upload) {
+      if(data->set.max_send_speed &&
+          (data->progress.ulspeed > data->set.max_send_speed)) {
+        fd_read = CURL_SOCKET_BAD;
+        timeout_ms = Curl_sleep_time(data->set.max_send_speed,
+                                     data->progress.ulspeed, state->blksize);
+      }
+      else {
+        fd_read = state->sockfd;
+      }
+    }
+    else {
+      if(data->set.max_recv_speed &&
+         (data->progress.dlspeed > data->set.max_recv_speed)) {
+        fd_read = CURL_SOCKET_BAD;
+        timeout_ms = Curl_sleep_time(data->set.max_recv_speed,
+                                     data->progress.dlspeed, state->blksize);
+      }
+      else
+        fd_read = state->sockfd;
+    }
+
+    if(data->set.timeout) {
+      timeout_ms = data->set.timeout - Curl_tvdiff(k->now, k->start);
+      if(timeout_ms > state->retry_time * 1000)
+        timeout_ms = state->retry_time * 1000;
+      else if(timeout_ms < 0)
+        timeout_ms = 0;
+    }
+
+
+    /* Wait until ready to read or timeout occurs */
+    rc = Curl_socket_ready(fd_read, CURL_SOCKET_BAD, timeout_ms);
+
+    k->now = Curl_tvnow();
+
+    /* Force a progress callback if it's been too long */
+    if(Curl_tvdiff(k->now, k->start) >= data->set.timeout) {
+      if(Curl_pgrsUpdate(conn)) {
+        tftp_state_machine(state, TFTP_EVENT_ERROR);
+        return CURLE_ABORTED_BY_CALLBACK;
+      }
+      k->start = k->now;
+    }
+
+    if(rc == -1) {
+      /* bail out */
+      int error = SOCKERRNO;
+      failf(data, "%s", Curl_strerror(conn, error));
+      state->event = TFTP_EVENT_ERROR;
+    }
+    else {
+
+      if(rc==0) {
+        /* A timeout occurred, but our timeout is variable, so maybe
+           just continue? */
+        long rtms = state->retry_time * 1000;
+        if(Curl_tvdiff(k->now, transaction_start) > rtms) {
+          state->event = TFTP_EVENT_TIMEOUT;
+          /* Force a look at transfer timeouts */
+          check_time = 1;
+        }
+        else {
+          continue; /* skip state machine */
+        }
+      }
+      else {
+        result = tftp_receive_packet(conn);
+        if(result == CURLE_OK)
+          transaction_start = Curl_tvnow();
+
+        if(k->bytecountp)
+          *k->bytecountp = k->bytecount; /* read count */
+        if(k->writebytecountp)
+          *k->writebytecountp = k->writebytecount; /* write count */
+      }
+    }
+
+    if(check_time) {
+      tftp_state_timeout(conn, NULL);
+      check_time = 0;
+    }
+
+    if(result)
+      return(result);
+
+    result = tftp_state_machine(state, state->event);
+  }
+
+  /* Tell curl we're done */
+  Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+
+  return(result);
+}
+
+/**********************************************************
+ *
+ * tftp_multi_statemach
+ *
+ * Handle single RX socket event and return
+ *
+ **********************************************************/
+static CURLcode tftp_multi_statemach(struct connectdata *conn, bool *done)
+{
+  int                   rc;
+  tftp_event_t          event;
+  CURLcode              result = CURLE_OK;
+  struct SessionHandle  *data = conn->data;
+  tftp_state_data_t     *state = (tftp_state_data_t *)conn->proto.tftpc;
+  long                  timeout_ms = tftp_state_timeout(conn, &event);
+
+  *done = FALSE;
+
+  if(timeout_ms <= 0) {
+    failf(data, "TFTP response timeout");
+    return CURLE_OPERATION_TIMEDOUT;
+  }
+  else if(event != TFTP_EVENT_NONE) {
+    result = tftp_state_machine(state, event);
+    if(result != CURLE_OK)
+      return(result);
+    *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE;
+    if(*done)
+      /* Tell curl we're done */
+      Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+  }
+  else {
+    /* no timeouts to handle, check our socket */
+    rc = Curl_socket_ready(state->sockfd, CURL_SOCKET_BAD, 0);
+
+    if(rc == -1) {
+      /* bail out */
+      int error = SOCKERRNO;
+      failf(data, "%s", Curl_strerror(conn, error));
+      state->event = TFTP_EVENT_ERROR;
+    }
+    else if(rc != 0) {
+      result = tftp_receive_packet(conn);
+      if(result != CURLE_OK)
+        return(result);
+      result = tftp_state_machine(state, state->event);
+      if(result != CURLE_OK)
+        return(result);
+      *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE;
+      if(*done)
+        /* Tell curl we're done */
+        Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+    }
+    /* if rc == 0, then select() timed out */
+  }
+
+  return result;
+}
+
+/**********************************************************
+ *
+ * tftp_doing
+ *
+ * Called from curl_multi.c while DOing
+ *
+ **********************************************************/
+static CURLcode tftp_doing(struct connectdata *conn, bool *dophase_done)
+{
+  CURLcode result;
+  result = tftp_multi_statemach(conn, dophase_done);
+
+  if(*dophase_done) {
+    DEBUGF(infof(conn->data, "DO phase is complete\n"));
+  }
+  return result;
+}
+
+/**********************************************************
+ *
+ * tftp_peform
+ *
+ * Entry point for transfer from tftp_do, sarts state mach
+ *
+ **********************************************************/
+static CURLcode tftp_perform(struct connectdata *conn, bool *dophase_done)
+{
+  CURLcode              result = CURLE_OK;
+  tftp_state_data_t     *state = (tftp_state_data_t *)conn->proto.tftpc;
+
+  *dophase_done = FALSE;
+
+  result = tftp_state_machine(state, TFTP_EVENT_INIT);
+
+  if(state->state == TFTP_STATE_FIN || result != CURLE_OK)
+    return(result);
+
+  if(conn->data->state.used_interface == Curl_if_multi)
+    tftp_multi_statemach(conn, dophase_done);
+  else {
+    result = tftp_easy_statemach(conn);
+    *dophase_done = TRUE; /* with the easy interface we are done here */
+  }
+
+  if(*dophase_done)
+    DEBUGF(infof(conn->data, "DO phase is complete\n"));
+
+  return result;
+}
+
+
+/**********************************************************
+ *
+ * tftp_do
+ *
+ * The do callback
+ *
+ * This callback initiates the TFTP transfer
+ *
+ **********************************************************/
+
+static CURLcode tftp_do(struct connectdata *conn, bool *done)
+{
+  tftp_state_data_t     *state;
+  CURLcode              code;
+
+  *done = FALSE;
+
+  /*
+    Since connections can be re-used between SessionHandles, this might be a
+    connection already existing but on a fresh SessionHandle struct so we must
+    make sure we have a good 'struct TFTP' to play with. For new connections,
+    the struct TFTP is allocated and setup in the tftp_connect() function.
+  */
+  Curl_reset_reqproto(conn);
+
+  if(!conn->proto.tftpc) {
+    code = tftp_connect(conn, done);
+    if(code)
+      return code;
+  }
+  state = (tftp_state_data_t *)conn->proto.tftpc;
+
+  code = tftp_perform(conn, done);
+
+  /* If tftp_perform() returned an error, use that for return code. If it
+     was OK, see if tftp_translate_code() has an error. */
+  if(code == CURLE_OK)
+    /* If we have encountered an internal tftp error, translate it. */
+    code = tftp_translate_code(state->error);
+
+  return code;
+}
+
+static CURLcode tftp_setup_connection(struct connectdata * conn)
+{
+  struct SessionHandle *data = conn->data;
+  char * type;
+  char command;
+
+  conn->socktype = SOCK_DGRAM;   /* UDP datagram based */
+
+  /* TFTP URLs support an extension like ";mode=<typecode>" that
+   * we'll try to get now! */
+  type = strstr(data->state.path, ";mode=");
+
+  if(!type)
+    type = strstr(conn->host.rawalloc, ";mode=");
+
+  if(type) {
+    *type = 0;                   /* it was in the middle of the hostname */
+    command = Curl_raw_toupper(type[6]);
+
+    switch (command) {
+    case 'A': /* ASCII mode */
+    case 'N': /* NETASCII mode */
+      data->set.prefer_ascii = TRUE;
+      break;
+
+    case 'O': /* octet mode */
+    case 'I': /* binary mode */
+    default:
+      /* switch off ASCII */
+      data->set.prefer_ascii = FALSE;
+      break;
+    }
+  }
+
+  return CURLE_OK;
+}
+#endif
diff --git a/lib/curl_timeval.c b/lib/curl_timeval.c
new file mode 100644 (file)
index 0000000..8e4c7bd
--- /dev/null
@@ -0,0 +1,134 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_timeval.h"
+
+#if defined(WIN32) && !defined(MSDOS)
+
+struct timeval curlx_tvnow(void)
+{
+  /*
+  ** GetTickCount() is available on _all_ Windows versions from W95 up
+  ** to nowadays. Returns milliseconds elapsed since last system boot,
+  ** increases monotonically and wraps once 49.7 days have elapsed.
+  */
+  struct timeval now;
+  DWORD milliseconds = GetTickCount();
+  now.tv_sec = milliseconds / 1000;
+  now.tv_usec = (milliseconds % 1000) * 1000;
+  return now;
+}
+
+#elif defined(HAVE_CLOCK_GETTIME_MONOTONIC)
+
+struct timeval curlx_tvnow(void)
+{
+  /*
+  ** clock_gettime() is granted to be increased monotonically when the
+  ** monotonic clock is queried. Time starting point is unspecified, it
+  ** could be the system start-up time, the Epoch, or something else,
+  ** in any case the time starting point does not change once that the
+  ** system has started up.
+  */
+  struct timeval now;
+  struct timespec tsnow;
+  if(0 == clock_gettime(CLOCK_MONOTONIC, &tsnow)) {
+    now.tv_sec = tsnow.tv_sec;
+    now.tv_usec = tsnow.tv_nsec / 1000;
+  }
+  /*
+  ** Even when the configure process has truly detected monotonic clock
+  ** availability, it might happen that it is not actually available at
+  ** run-time. When this occurs simply fallback to other time source.
+  */
+#ifdef HAVE_GETTIMEOFDAY
+  else
+    (void)gettimeofday(&now, NULL);
+#else
+  else {
+    now.tv_sec = (long)time(NULL);
+    now.tv_usec = 0;
+  }
+#endif
+  return now;
+}
+
+#elif defined(HAVE_GETTIMEOFDAY)
+
+struct timeval curlx_tvnow(void)
+{
+  /*
+  ** gettimeofday() is not granted to be increased monotonically, due to
+  ** clock drifting and external source time synchronization it can jump
+  ** forward or backward in time.
+  */
+  struct timeval now;
+  (void)gettimeofday(&now, NULL);
+  return now;
+}
+
+#else
+
+struct timeval curlx_tvnow(void)
+{
+  /*
+  ** time() returns the value of time in seconds since the Epoch.
+  */
+  struct timeval now;
+  now.tv_sec = (long)time(NULL);
+  now.tv_usec = 0;
+  return now;
+}
+
+#endif
+
+/*
+ * Make sure that the first argument is the more recent time, as otherwise
+ * we'll get a weird negative time-diff back...
+ *
+ * Returns: the time difference in number of milliseconds.
+ */
+long curlx_tvdiff(struct timeval newer, struct timeval older)
+{
+  return (newer.tv_sec-older.tv_sec)*1000+
+    (newer.tv_usec-older.tv_usec)/1000;
+}
+
+/*
+ * Same as curlx_tvdiff but with full usec resolution.
+ *
+ * Returns: the time difference in seconds with subsecond resolution.
+ */
+double curlx_tvdiff_secs(struct timeval newer, struct timeval older)
+{
+  if(newer.tv_sec != older.tv_sec)
+    return (double)(newer.tv_sec-older.tv_sec)+
+      (double)(newer.tv_usec-older.tv_usec)/1000000.0;
+  else
+    return (double)(newer.tv_usec-older.tv_usec)/1000000.0;
+}
+
+/* return the number of seconds in the given input timeval struct */
+long Curl_tvlong(struct timeval t1)
+{
+  return t1.tv_sec;
+}
diff --git a/lib/curl_transfer.c b/lib/curl_transfer.c
new file mode 100644 (file)
index 0000000..a1dee1d
--- /dev/null
@@ -0,0 +1,2338 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include "curl_strtoofft.h"
+#include "curl_strequal.h"
+#include "curl_rawstr.h"
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#ifndef HAVE_SOCKET
+#error "We can't compile without socket() support!"
+#endif
+
+#include "curl_urldata.h"
+#include <curl/curl.h>
+#include "curl_netrc.h"
+
+#include "curl_content_encoding.h"
+#include "curl_hostip.h"
+#include "curl_transfer.h"
+#include "curl_sendf.h"
+#include "curl_speedcheck.h"
+#include "curl_progress.h"
+#include "curl_http.h"
+#include "curl_url.h"
+#include "curl_getinfo.h"
+#include "curl_sslgen.h"
+#include "curl_http_digest.h"
+#include "curl_ntlm.h"
+#include "curl_http_negotiate.h"
+#include "curl_share.h"
+#include "curl_memory.h"
+#include "curl_select.h"
+#include "curl_multiif.h"
+#include "curl_connect.h"
+#include "curl_non_ascii.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+#define CURL_TIMEOUT_EXPECT_100 1000 /* counting ms here */
+
+/*
+ * This function will call the read callback to fill our buffer with data
+ * to upload.
+ */
+CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp)
+{
+  struct SessionHandle *data = conn->data;
+  size_t buffersize = (size_t)bytes;
+  int nread;
+#ifdef CURL_DOES_CONVERSIONS
+  bool sending_http_headers = FALSE;
+
+  if((conn->handler->protocol&(CURLPROTO_HTTP|CURLPROTO_RTSP)) &&
+     (data->state.proto.http->sending == HTTPSEND_REQUEST)) {
+    /* We're sending the HTTP request headers, not the data.
+       Remember that so we don't re-translate them into garbage. */
+    sending_http_headers = TRUE;
+  }
+#endif
+
+  if(data->req.upload_chunky) {
+    /* if chunked Transfer-Encoding */
+    buffersize -= (8 + 2 + 2);   /* 32bit hex + CRLF + CRLF */
+    data->req.upload_fromhere += (8 + 2); /* 32bit hex + CRLF */
+  }
+
+  /* this function returns a size_t, so we typecast to int to prevent warnings
+     with picky compilers */
+  nread = (int)conn->fread_func(data->req.upload_fromhere, 1,
+                                buffersize, conn->fread_in);
+
+  if(nread == CURL_READFUNC_ABORT) {
+    failf(data, "operation aborted by callback");
+    *nreadp = 0;
+    return CURLE_ABORTED_BY_CALLBACK;
+  }
+  else if(nread == CURL_READFUNC_PAUSE) {
+    struct SingleRequest *k = &data->req;
+    /* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */
+    k->keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */
+    if(data->req.upload_chunky) {
+      /* Back out the preallocation done above */
+      data->req.upload_fromhere -= (8 + 2);
+    }
+    *nreadp = 0;
+    return CURLE_OK; /* nothing was read */
+  }
+  else if((size_t)nread > buffersize) {
+    /* the read function returned a too large value */
+    *nreadp = 0;
+    failf(data, "read function returned funny value");
+    return CURLE_READ_ERROR;
+  }
+
+  if(!data->req.forbidchunk && data->req.upload_chunky) {
+    /* if chunked Transfer-Encoding
+     *    build chunk:
+     *
+     *        <HEX SIZE> CRLF
+     *        <DATA> CRLF
+     */
+    /* On non-ASCII platforms the <DATA> may or may not be
+       translated based on set.prefer_ascii while the protocol
+       portion must always be translated to the network encoding.
+       To further complicate matters, line end conversion might be
+       done later on, so we need to prevent CRLFs from becoming
+       CRCRLFs if that's the case.  To do this we use bare LFs
+       here, knowing they'll become CRLFs later on.
+     */
+
+    char hexbuffer[11];
+    const char *endofline_native;
+    const char *endofline_network;
+    int hexlen;
+
+    if(
+#ifdef CURL_DO_LINEEND_CONV
+       (data->set.prefer_ascii) ||
+#endif
+       (data->set.crlf)) {
+      /* \n will become \r\n later on */
+      endofline_native  = "\n";
+      endofline_network = "\x0a";
+    }
+    else {
+      endofline_native  = "\r\n";
+      endofline_network = "\x0d\x0a";
+    }
+    hexlen = snprintf(hexbuffer, sizeof(hexbuffer),
+                      "%x%s", nread, endofline_native);
+
+    /* move buffer pointer */
+    data->req.upload_fromhere -= hexlen;
+    nread += hexlen;
+
+    /* copy the prefix to the buffer, leaving out the NUL */
+    memcpy(data->req.upload_fromhere, hexbuffer, hexlen);
+
+    /* always append ASCII CRLF to the data */
+    memcpy(data->req.upload_fromhere + nread,
+           endofline_network,
+           strlen(endofline_network));
+
+#ifdef CURL_DOES_CONVERSIONS
+    CURLcode res;
+    int length;
+    if(data->set.prefer_ascii) {
+      /* translate the protocol and data */
+      length = nread;
+    }
+    else {
+      /* just translate the protocol portion */
+      length = strlen(hexbuffer);
+    }
+    res = Curl_convert_to_network(data, data->req.upload_fromhere, length);
+    /* Curl_convert_to_network calls failf if unsuccessful */
+    if(res)
+      return(res);
+#endif /* CURL_DOES_CONVERSIONS */
+
+    if((nread - hexlen) == 0)
+      /* mark this as done once this chunk is transferred */
+      data->req.upload_done = TRUE;
+
+    nread+=(int)strlen(endofline_native); /* for the added end of line */
+  }
+#ifdef CURL_DOES_CONVERSIONS
+  else if((data->set.prefer_ascii) && (!sending_http_headers)) {
+    CURLcode res;
+    res = Curl_convert_to_network(data, data->req.upload_fromhere, nread);
+    /* Curl_convert_to_network calls failf if unsuccessful */
+    if(res != CURLE_OK)
+      return(res);
+  }
+#endif /* CURL_DOES_CONVERSIONS */
+
+  *nreadp = nread;
+
+  return CURLE_OK;
+}
+
+
+/*
+ * Curl_readrewind() rewinds the read stream. This is typically used for HTTP
+ * POST/PUT with multi-pass authentication when a sending was denied and a
+ * resend is necessary.
+ */
+CURLcode Curl_readrewind(struct connectdata *conn)
+{
+  struct SessionHandle *data = conn->data;
+
+  conn->bits.rewindaftersend = FALSE; /* we rewind now */
+
+  /* explicitly switch off sending data on this connection now since we are
+     about to restart a new transfer and thus we want to avoid inadvertently
+     sending more data on the existing connection until the next transfer
+     starts */
+  data->req.keepon &= ~KEEP_SEND;
+
+  /* We have sent away data. If not using CURLOPT_POSTFIELDS or
+     CURLOPT_HTTPPOST, call app to rewind
+  */
+  if(data->set.postfields ||
+     (data->set.httpreq == HTTPREQ_POST_FORM))
+    ; /* do nothing */
+  else {
+    if(data->set.seek_func) {
+      int err;
+
+      err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET);
+      if(err) {
+        failf(data, "seek callback returned error %d", (int)err);
+        return CURLE_SEND_FAIL_REWIND;
+      }
+    }
+    else if(data->set.ioctl_func) {
+      curlioerr err;
+
+      err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD,
+                                   data->set.ioctl_client);
+      infof(data, "the ioctl callback returned %d\n", (int)err);
+
+      if(err) {
+        /* FIXME: convert to a human readable error message */
+        failf(data, "ioctl callback returned error %d", (int)err);
+        return CURLE_SEND_FAIL_REWIND;
+      }
+    }
+    else {
+      /* If no CURLOPT_READFUNCTION is used, we know that we operate on a
+         given FILE * stream and we can actually attempt to rewind that
+         ourselves with fseek() */
+      if(data->set.fread_func == (curl_read_callback)fread) {
+        if(-1 != fseek(data->set.in, 0, SEEK_SET))
+          /* successful rewind */
+          return CURLE_OK;
+      }
+
+      /* no callback set or failure above, makes us fail at once */
+      failf(data, "necessary data rewind wasn't possible");
+      return CURLE_SEND_FAIL_REWIND;
+    }
+  }
+  return CURLE_OK;
+}
+
+static int data_pending(const struct connectdata *conn)
+{
+  /* in the case of libssh2, we can never be really sure that we have emptied
+     its internal buffers so we MUST always try until we get EAGAIN back */
+  return conn->handler->protocol&(CURLPROTO_SCP|CURLPROTO_SFTP) ||
+    Curl_ssl_data_pending(conn, FIRSTSOCKET);
+}
+
+static void read_rewind(struct connectdata *conn,
+                        size_t thismuch)
+{
+  DEBUGASSERT(conn->read_pos >= thismuch);
+
+  conn->read_pos -= thismuch;
+  conn->bits.stream_was_rewound = TRUE;
+
+#ifdef DEBUGBUILD
+  {
+    char buf[512 + 1];
+    size_t show;
+
+    show = CURLMIN(conn->buf_len - conn->read_pos, sizeof(buf)-1);
+    if(conn->master_buffer) {
+      memcpy(buf, conn->master_buffer + conn->read_pos, show);
+      buf[show] = '\0';
+    }
+    else {
+      buf[0] = '\0';
+    }
+
+    DEBUGF(infof(conn->data,
+                 "Buffer after stream rewind (read_pos = %zu): [%s]\n",
+                 conn->read_pos, buf));
+  }
+#endif
+}
+
+/*
+ * Check to see if CURLOPT_TIMECONDITION was met by comparing the time of the
+ * remote document with the time provided by CURLOPT_TIMEVAL
+ */
+bool Curl_meets_timecondition(struct SessionHandle *data, time_t timeofdoc)
+{
+  if((timeofdoc == 0) || (data->set.timevalue == 0))
+    return TRUE;
+
+  switch(data->set.timecondition) {
+  case CURL_TIMECOND_IFMODSINCE:
+  default:
+    if(timeofdoc <= data->set.timevalue) {
+      infof(data,
+            "The requested document is not new enough\n");
+      data->info.timecond = TRUE;
+      return FALSE;
+    }
+    break;
+  case CURL_TIMECOND_IFUNMODSINCE:
+    if(timeofdoc >= data->set.timevalue) {
+      infof(data,
+            "The requested document is not old enough\n");
+      data->info.timecond = TRUE;
+      return FALSE;
+    }
+    break;
+  }
+
+  return TRUE;
+}
+
+/*
+ * Go ahead and do a read if we have a readable socket or if
+ * the stream was rewound (in which case we have data in a
+ * buffer)
+ */
+static CURLcode readwrite_data(struct SessionHandle *data,
+                               struct connectdata *conn,
+                               struct SingleRequest *k,
+                               int *didwhat, bool *done)
+{
+  CURLcode result = CURLE_OK;
+  ssize_t nread; /* number of bytes read */
+  size_t excess = 0; /* excess bytes read */
+  bool is_empty_data = FALSE;
+  bool readmore = FALSE; /* used by RTP to signal for more data */
+
+  *done = FALSE;
+
+  /* This is where we loop until we have read everything there is to
+     read or we get a CURLE_AGAIN */
+  do {
+    size_t buffersize = data->set.buffer_size?
+      data->set.buffer_size : BUFSIZE;
+    size_t bytestoread = buffersize;
+
+    if(k->size != -1 && !k->header) {
+      /* make sure we don't read "too much" if we can help it since we
+         might be pipelining and then someone else might want to read what
+         follows! */
+      curl_off_t totalleft = k->size - k->bytecount;
+      if(totalleft < (curl_off_t)bytestoread)
+        bytestoread = (size_t)totalleft;
+    }
+
+    if(bytestoread) {
+      /* receive data from the network! */
+      result = Curl_read(conn, conn->sockfd, k->buf, bytestoread, &nread);
+
+      /* read would've blocked */
+      if(CURLE_AGAIN == result)
+        break; /* get out of loop */
+
+      if(result>0)
+        return result;
+    }
+    else {
+      /* read nothing but since we wanted nothing we consider this an OK
+         situation to proceed from */
+      nread = 0;
+    }
+
+    if((k->bytecount == 0) && (k->writebytecount == 0)) {
+      Curl_pgrsTime(data, TIMER_STARTTRANSFER);
+      if(k->exp100 > EXP100_SEND_DATA)
+        /* set time stamp to compare with when waiting for the 100 */
+        k->start100 = Curl_tvnow();
+    }
+
+    *didwhat |= KEEP_RECV;
+    /* indicates data of zero size, i.e. empty file */
+    is_empty_data = ((nread == 0) && (k->bodywrites == 0)) ? TRUE : FALSE;
+
+    /* NUL terminate, allowing string ops to be used */
+    if(0 < nread || is_empty_data) {
+      k->buf[nread] = 0;
+    }
+    else if(0 >= nread) {
+      /* if we receive 0 or less here, the server closed the connection
+         and we bail out from this! */
+      DEBUGF(infof(data, "nread <= 0, server closed connection, bailing\n"));
+      k->keepon &= ~KEEP_RECV;
+      break;
+    }
+
+    /* Default buffer to use when we write the buffer, it may be changed
+       in the flow below before the actual storing is done. */
+    k->str = k->buf;
+
+    if(conn->handler->readwrite) {
+      result = conn->handler->readwrite(data, conn, &nread, &readmore);
+      if(result)
+        return result;
+      if(readmore)
+        break;
+    }
+
+#ifndef CURL_DISABLE_HTTP
+    /* Since this is a two-state thing, we check if we are parsing
+       headers at the moment or not. */
+    if(k->header) {
+      /* we are in parse-the-header-mode */
+      bool stop_reading = FALSE;
+      result = Curl_http_readwrite_headers(data, conn, &nread, &stop_reading);
+      if(result)
+        return result;
+
+      if(conn->handler->readwrite &&
+         (k->maxdownload <= 0 && nread > 0)) {
+        result = conn->handler->readwrite(data, conn, &nread, &readmore);
+        if(result)
+          return result;
+        if(readmore)
+          break;
+      }
+
+      if(stop_reading) {
+        /* We've stopped dealing with input, get out of the do-while loop */
+
+        if(nread > 0) {
+          if(conn->data->multi && Curl_multi_canPipeline(conn->data->multi)) {
+            infof(data,
+                  "Rewinding stream by : %zd"
+                  " bytes on url %s (zero-length body)\n",
+                  nread, data->state.path);
+            read_rewind(conn, (size_t)nread);
+          }
+          else {
+            infof(data,
+                  "Excess found in a non pipelined read:"
+                  " excess = %zd"
+                  " url = %s (zero-length body)\n",
+                  nread, data->state.path);
+          }
+        }
+
+        break;
+      }
+    }
+#endif /* CURL_DISABLE_HTTP */
+
+
+    /* This is not an 'else if' since it may be a rest from the header
+       parsing, where the beginning of the buffer is headers and the end
+       is non-headers. */
+    if(k->str && !k->header && (nread > 0 || is_empty_data)) {
+
+#ifndef CURL_DISABLE_HTTP
+      if(0 == k->bodywrites && !is_empty_data) {
+        /* These checks are only made the first time we are about to
+           write a piece of the body */
+        if(conn->handler->protocol&(CURLPROTO_HTTP|CURLPROTO_RTSP)) {
+          /* HTTP-only checks */
+
+          if(data->req.newurl) {
+            if(conn->bits.close) {
+              /* Abort after the headers if "follow Location" is set
+                 and we're set to close anyway. */
+              k->keepon &= ~KEEP_RECV;
+              *done = TRUE;
+              return CURLE_OK;
+            }
+            /* We have a new url to load, but since we want to be able
+               to re-use this connection properly, we read the full
+               response in "ignore more" */
+            k->ignorebody = TRUE;
+            infof(data, "Ignoring the response-body\n");
+          }
+          if(data->state.resume_from && !k->content_range &&
+             (data->set.httpreq==HTTPREQ_GET) &&
+             !k->ignorebody) {
+            /* we wanted to resume a download, although the server doesn't
+             * seem to support this and we did this with a GET (if it
+             * wasn't a GET we did a POST or PUT resume) */
+            failf(data, "HTTP server doesn't seem to support "
+                  "byte ranges. Cannot resume.");
+            return CURLE_RANGE_ERROR;
+          }
+
+          if(data->set.timecondition && !data->state.range) {
+            /* A time condition has been set AND no ranges have been
+               requested. This seems to be what chapter 13.3.4 of
+               RFC 2616 defines to be the correct action for a
+               HTTP/1.1 client */
+
+            if(!Curl_meets_timecondition(data, k->timeofdoc)) {
+              *done = TRUE;
+              /* we abort the transfer before it is completed == we ruin the
+                 re-use ability. Close the connection */
+              conn->bits.close = TRUE;
+              return CURLE_OK;
+            }
+          } /* we have a time condition */
+
+        } /* this is HTTP or RTSP */
+      } /* this is the first time we write a body part */
+#endif /* CURL_DISABLE_HTTP */
+
+      k->bodywrites++;
+
+      /* pass data to the debug function before it gets "dechunked" */
+      if(data->set.verbose) {
+        if(k->badheader) {
+          Curl_debug(data, CURLINFO_DATA_IN, data->state.headerbuff,
+                     (size_t)k->hbuflen, conn);
+          if(k->badheader == HEADER_PARTHEADER)
+            Curl_debug(data, CURLINFO_DATA_IN,
+                       k->str, (size_t)nread, conn);
+        }
+        else
+          Curl_debug(data, CURLINFO_DATA_IN,
+                     k->str, (size_t)nread, conn);
+      }
+
+#ifndef CURL_DISABLE_HTTP
+      if(k->chunk) {
+        /*
+         * Here comes a chunked transfer flying and we need to decode this
+         * properly.  While the name says read, this function both reads
+         * and writes away the data. The returned 'nread' holds the number
+         * of actual data it wrote to the client.
+         */
+
+        CHUNKcode res =
+          Curl_httpchunk_read(conn, k->str, nread, &nread);
+
+        if(CHUNKE_OK < res) {
+          if(CHUNKE_WRITE_ERROR == res) {
+            failf(data, "Failed writing data");
+            return CURLE_WRITE_ERROR;
+          }
+          failf(data, "Problem (%d) in the Chunked-Encoded data", (int)res);
+          return CURLE_RECV_ERROR;
+        }
+        else if(CHUNKE_STOP == res) {
+          size_t dataleft;
+          /* we're done reading chunks! */
+          k->keepon &= ~KEEP_RECV; /* read no more */
+
+          /* There are now possibly N number of bytes at the end of the
+             str buffer that weren't written to the client.
+
+             We DO care about this data if we are pipelining.
+             Push it back to be read on the next pass. */
+
+          dataleft = conn->chunk.dataleft;
+          if(dataleft != 0) {
+            infof(conn->data, "Leftovers after chunking: %zu bytes\n",
+                  dataleft);
+            if(conn->data->multi &&
+               Curl_multi_canPipeline(conn->data->multi)) {
+              /* only attempt the rewind if we truly are pipelining */
+              infof(conn->data, "Rewinding %zu bytes\n",dataleft);
+              read_rewind(conn, dataleft);
+            }
+          }
+        }
+        /* If it returned OK, we just keep going */
+      }
+#endif   /* CURL_DISABLE_HTTP */
+
+      /* Account for body content stored in the header buffer */
+      if(k->badheader && !k->ignorebody) {
+        DEBUGF(infof(data, "Increasing bytecount by %zu from hbuflen\n",
+                     k->hbuflen));
+        k->bytecount += k->hbuflen;
+      }
+
+      if((-1 != k->maxdownload) &&
+         (k->bytecount + nread >= k->maxdownload)) {
+
+        excess = (size_t)(k->bytecount + nread - k->maxdownload);
+        if(excess > 0 && !k->ignorebody) {
+          if(conn->data->multi && Curl_multi_canPipeline(conn->data->multi)) {
+            /* The 'excess' amount below can't be more than BUFSIZE which
+               always will fit in a size_t */
+            infof(data,
+                  "Rewinding stream by : %zu"
+                  " bytes on url %s (size = %" FORMAT_OFF_T
+                  ", maxdownload = %" FORMAT_OFF_T
+                  ", bytecount = %" FORMAT_OFF_T ", nread = %zd)\n",
+                  excess, data->state.path,
+                  k->size, k->maxdownload, k->bytecount, nread);
+            read_rewind(conn, excess);
+          }
+          else {
+            infof(data,
+                  "Excess found in a non pipelined read:"
+                  " excess = %zu"
+                  ", size = %" FORMAT_OFF_T
+                  ", maxdownload = %" FORMAT_OFF_T
+                  ", bytecount = %" FORMAT_OFF_T "\n",
+                  excess, k->size, k->maxdownload, k->bytecount);
+          }
+        }
+
+        nread = (ssize_t) (k->maxdownload - k->bytecount);
+        if(nread < 0 ) /* this should be unusual */
+          nread = 0;
+
+        k->keepon &= ~KEEP_RECV; /* we're done reading */
+      }
+
+      k->bytecount += nread;
+
+      Curl_pgrsSetDownloadCounter(data, k->bytecount);
+
+      if(!k->chunk && (nread || k->badheader || is_empty_data)) {
+        /* If this is chunky transfer, it was already written */
+
+        if(k->badheader && !k->ignorebody) {
+          /* we parsed a piece of data wrongly assuming it was a header
+             and now we output it as body instead */
+
+          /* Don't let excess data pollute body writes */
+          if(k->maxdownload == -1 || (curl_off_t)k->hbuflen <= k->maxdownload)
+            result = Curl_client_write(conn, CLIENTWRITE_BODY,
+                                       data->state.headerbuff,
+                                       k->hbuflen);
+          else
+            result = Curl_client_write(conn, CLIENTWRITE_BODY,
+                                       data->state.headerbuff,
+                                       (size_t)k->maxdownload);
+
+          if(result)
+            return result;
+        }
+        if(k->badheader < HEADER_ALLBAD) {
+          /* This switch handles various content encodings. If there's an
+             error here, be sure to check over the almost identical code
+             in curl_http_chunks.c.
+             Make sure that ALL_CONTENT_ENCODINGS contains all the
+             encodings handled here. */
+#ifdef HAVE_LIBZ
+          switch (conn->data->set.http_ce_skip ?
+                  IDENTITY : k->auto_decoding) {
+          case IDENTITY:
+#endif
+            /* This is the default when the server sends no
+               Content-Encoding header. See Curl_readwrite_init; the
+               memset() call initializes k->auto_decoding to zero. */
+            if(!k->ignorebody) {
+
+#ifndef CURL_DISABLE_POP3
+              if(conn->handler->protocol&CURLPROTO_POP3)
+                result = Curl_pop3_write(conn, k->str, nread);
+              else
+#endif /* CURL_DISABLE_POP3 */
+
+                result = Curl_client_write(conn, CLIENTWRITE_BODY, k->str,
+                                           nread);
+            }
+#ifdef HAVE_LIBZ
+            break;
+
+          case DEFLATE:
+            /* Assume CLIENTWRITE_BODY; headers are not encoded. */
+            if(!k->ignorebody)
+              result = Curl_unencode_deflate_write(conn, k, nread);
+            break;
+
+          case GZIP:
+            /* Assume CLIENTWRITE_BODY; headers are not encoded. */
+            if(!k->ignorebody)
+              result = Curl_unencode_gzip_write(conn, k, nread);
+            break;
+
+          case COMPRESS:
+          default:
+            failf (data, "Unrecognized content encoding type. "
+                   "libcurl understands `identity', `deflate' and `gzip' "
+                   "content encodings.");
+            result = CURLE_BAD_CONTENT_ENCODING;
+            break;
+          }
+#endif
+        }
+        k->badheader = HEADER_NORMAL; /* taken care of now */
+
+        if(result)
+          return result;
+      }
+
+    } /* if(! header and data to read ) */
+
+    if(conn->handler->readwrite &&
+       (excess > 0 && !conn->bits.stream_was_rewound)) {
+      /* Parse the excess data */
+      k->str += nread;
+      nread = (ssize_t)excess;
+
+      result = conn->handler->readwrite(data, conn, &nread, &readmore);
+      if(result)
+        return result;
+
+      if(readmore)
+        k->keepon |= KEEP_RECV; /* we're not done reading */
+      break;
+    }
+
+    if(is_empty_data) {
+      /* if we received nothing, the server closed the connection and we
+         are done */
+      k->keepon &= ~KEEP_RECV;
+    }
+
+  } while(data_pending(conn));
+
+  if(((k->keepon & (KEEP_RECV|KEEP_SEND)) == KEEP_SEND) &&
+     conn->bits.close ) {
+    /* When we've read the entire thing and the close bit is set, the server
+       may now close the connection. If there's now any kind of sending going
+       on from our side, we need to stop that immediately. */
+    infof(data, "we are done reading and this is set to close, stop send\n");
+    k->keepon &= ~KEEP_SEND; /* no writing anymore either */
+  }
+
+  return CURLE_OK;
+}
+
+/*
+ * Send data to upload to the server, when the socket is writable.
+ */
+static CURLcode readwrite_upload(struct SessionHandle *data,
+                                 struct connectdata *conn,
+                                 struct SingleRequest *k,
+                                 int *didwhat)
+{
+  ssize_t i, si;
+  ssize_t bytes_written;
+  CURLcode result;
+  ssize_t nread; /* number of bytes read */
+  bool sending_http_headers = FALSE;
+
+  if((k->bytecount == 0) && (k->writebytecount == 0))
+    Curl_pgrsTime(data, TIMER_STARTTRANSFER);
+
+  *didwhat |= KEEP_SEND;
+
+  /*
+   * We loop here to do the READ and SEND loop until we run out of
+   * data to send or until we get EWOULDBLOCK back
+   *
+   * FIXME: above comment is misleading. Currently no looping is
+   * actually done in do-while loop below.
+   */
+  do {
+
+    /* only read more data if there's no upload data already
+       present in the upload buffer */
+    if(0 == data->req.upload_present) {
+      /* init the "upload from here" pointer */
+      data->req.upload_fromhere = k->uploadbuf;
+
+      if(!k->upload_done) {
+        /* HTTP pollution, this should be written nicer to become more
+           protocol agnostic. */
+        int fillcount;
+
+        if((k->exp100 == EXP100_SENDING_REQUEST) &&
+           (data->state.proto.http->sending == HTTPSEND_BODY)) {
+          /* If this call is to send body data, we must take some action:
+             We have sent off the full HTTP 1.1 request, and we shall now
+             go into the Expect: 100 state and await such a header */
+          k->exp100 = EXP100_AWAITING_CONTINUE; /* wait for the header */
+          k->keepon &= ~KEEP_SEND;         /* disable writing */
+          k->start100 = Curl_tvnow();       /* timeout count starts now */
+          *didwhat &= ~KEEP_SEND;  /* we didn't write anything actually */
+
+          /* set a timeout for the multi interface */
+          Curl_expire(data, CURL_TIMEOUT_EXPECT_100);
+          break;
+        }
+
+        if(conn->handler->protocol&(CURLPROTO_HTTP|CURLPROTO_RTSP)) {
+          if(data->state.proto.http->sending == HTTPSEND_REQUEST)
+            /* We're sending the HTTP request headers, not the data.
+               Remember that so we don't change the line endings. */
+            sending_http_headers = TRUE;
+          else
+            sending_http_headers = FALSE;
+        }
+
+        result = Curl_fillreadbuffer(conn, BUFSIZE, &fillcount);
+        if(result)
+          return result;
+
+        nread = (ssize_t)fillcount;
+      }
+      else
+        nread = 0; /* we're done uploading/reading */
+
+      if(!nread && (k->keepon & KEEP_SEND_PAUSE)) {
+        /* this is a paused transfer */
+        break;
+      }
+      else if(nread<=0) {
+        /* done */
+        k->keepon &= ~KEEP_SEND; /* we're done writing */
+
+        if(conn->bits.rewindaftersend) {
+          result = Curl_readrewind(conn);
+          if(result)
+            return result;
+        }
+        break;
+      }
+
+      /* store number of bytes available for upload */
+      data->req.upload_present = nread;
+
+#ifndef CURL_DISABLE_SMTP
+      if(conn->handler->protocol & CURLPROTO_SMTP) {
+        result = Curl_smtp_escape_eob(conn, nread);
+        if(result)
+          return result;
+      }
+      else
+#endif /* CURL_DISABLE_SMTP */
+
+      /* convert LF to CRLF if so asked */
+      if((!sending_http_headers) && (
+#ifdef CURL_DO_LINEEND_CONV
+         /* always convert if we're FTPing in ASCII mode */
+         (data->set.prefer_ascii) ||
+#endif
+         (data->set.crlf))) {
+        if(data->state.scratch == NULL)
+          data->state.scratch = malloc(2*BUFSIZE);
+        if(data->state.scratch == NULL) {
+          failf (data, "Failed to alloc scratch buffer!");
+          return CURLE_OUT_OF_MEMORY;
+        }
+        /*
+         * ASCII/EBCDIC Note: This is presumably a text (not binary)
+         * transfer so the data should already be in ASCII.
+         * That means the hex values for ASCII CR (0x0d) & LF (0x0a)
+         * must be used instead of the escape sequences \r & \n.
+         */
+        for(i = 0, si = 0; i < nread; i++, si++) {
+          if(data->req.upload_fromhere[i] == 0x0a) {
+            data->state.scratch[si++] = 0x0d;
+            data->state.scratch[si] = 0x0a;
+            if(!data->set.crlf) {
+              /* we're here only because FTP is in ASCII mode...
+                 bump infilesize for the LF we just added */
+              data->set.infilesize++;
+            }
+          }
+          else
+            data->state.scratch[si] = data->req.upload_fromhere[i];
+        }
+        if(si != nread) {
+          /* only perform the special operation if we really did replace
+             anything */
+          nread = si;
+
+          /* upload from the new (replaced) buffer instead */
+          data->req.upload_fromhere = data->state.scratch;
+
+          /* set the new amount too */
+          data->req.upload_present = nread;
+        }
+      }
+    } /* if 0 == data->req.upload_present */
+    else {
+      /* We have a partial buffer left from a previous "round". Use
+         that instead of reading more data */
+    }
+
+    /* write to socket (send away data) */
+    result = Curl_write(conn,
+                        conn->writesockfd,     /* socket to send to */
+                        data->req.upload_fromhere, /* buffer pointer */
+                        data->req.upload_present,  /* buffer size */
+                        &bytes_written);           /* actually sent */
+
+    if(result)
+      return result;
+
+    if(data->set.verbose)
+      /* show the data before we change the pointer upload_fromhere */
+      Curl_debug(data, CURLINFO_DATA_OUT, data->req.upload_fromhere,
+                 (size_t)bytes_written, conn);
+
+    k->writebytecount += bytes_written;
+
+    if(k->writebytecount == data->set.infilesize) {
+      /* we have sent all data we were supposed to */
+      k->upload_done = TRUE;
+      infof(data, "We are completely uploaded and fine\n");
+    }
+
+    if(data->req.upload_present != bytes_written) {
+      /* we only wrote a part of the buffer (if anything), deal with it! */
+
+      /* store the amount of bytes left in the buffer to write */
+      data->req.upload_present -= bytes_written;
+
+      /* advance the pointer where to find the buffer when the next send
+         is to happen */
+      data->req.upload_fromhere += bytes_written;
+    }
+    else {
+      /* we've uploaded that buffer now */
+      data->req.upload_fromhere = k->uploadbuf;
+      data->req.upload_present = 0; /* no more bytes left */
+
+      if(k->upload_done) {
+        /* switch off writing, we're done! */
+        k->keepon &= ~KEEP_SEND; /* we're done writing */
+      }
+    }
+
+    Curl_pgrsSetUploadCounter(data, k->writebytecount);
+
+  } WHILE_FALSE; /* just to break out from! */
+
+  return CURLE_OK;
+}
+
+/*
+ * Curl_readwrite() is the low-level function to be called when data is to
+ * be read and written to/from the connection.
+ */
+CURLcode Curl_readwrite(struct connectdata *conn,
+                        bool *done)
+{
+  struct SessionHandle *data = conn->data;
+  struct SingleRequest *k = &data->req;
+  CURLcode result;
+  int didwhat=0;
+
+  curl_socket_t fd_read;
+  curl_socket_t fd_write;
+  int select_res = conn->cselect_bits;
+
+  conn->cselect_bits = 0;
+
+  /* only use the proper socket if the *_HOLD bit is not set simultaneously as
+     then we are in rate limiting state in that transfer direction */
+
+  if((k->keepon & KEEP_RECVBITS) == KEEP_RECV)
+    fd_read = conn->sockfd;
+  else
+    fd_read = CURL_SOCKET_BAD;
+
+  if((k->keepon & KEEP_SENDBITS) == KEEP_SEND)
+    fd_write = conn->writesockfd;
+  else
+    fd_write = CURL_SOCKET_BAD;
+
+  if(!select_res) /* Call for select()/poll() only, if read/write/error
+                     status is not known. */
+    select_res = Curl_socket_ready(fd_read, fd_write, 0);
+
+  if(select_res == CURL_CSELECT_ERR) {
+    failf(data, "select/poll returned error");
+    return CURLE_SEND_ERROR;
+  }
+
+  /* We go ahead and do a read if we have a readable socket or if
+     the stream was rewound (in which case we have data in a
+     buffer) */
+  if((k->keepon & KEEP_RECV) &&
+     ((select_res & CURL_CSELECT_IN) || conn->bits.stream_was_rewound)) {
+
+    result = readwrite_data(data, conn, k, &didwhat, done);
+    if(result || *done)
+      return result;
+  }
+
+  /* If we still have writing to do, we check if we have a writable socket. */
+  if((k->keepon & KEEP_SEND) && (select_res & CURL_CSELECT_OUT)) {
+    /* write */
+
+    result = readwrite_upload(data, conn, k, &didwhat);
+    if(result)
+      return result;
+  }
+
+  k->now = Curl_tvnow();
+  if(didwhat) {
+    /* Update read/write counters */
+    if(k->bytecountp)
+      *k->bytecountp = k->bytecount; /* read count */
+    if(k->writebytecountp)
+      *k->writebytecountp = k->writebytecount; /* write count */
+  }
+  else {
+    /* no read no write, this is a timeout? */
+    if(k->exp100 == EXP100_AWAITING_CONTINUE) {
+      /* This should allow some time for the header to arrive, but only a
+         very short time as otherwise it'll be too much wasted time too
+         often. */
+
+      /* Quoting RFC2616, section "8.2.3 Use of the 100 (Continue) Status":
+
+         Therefore, when a client sends this header field to an origin server
+         (possibly via a proxy) from which it has never seen a 100 (Continue)
+         status, the client SHOULD NOT wait for an indefinite period before
+         sending the request body.
+
+      */
+
+      long ms = Curl_tvdiff(k->now, k->start100);
+      if(ms > CURL_TIMEOUT_EXPECT_100) {
+        /* we've waited long enough, continue anyway */
+        k->exp100 = EXP100_SEND_DATA;
+        k->keepon |= KEEP_SEND;
+        infof(data, "Done waiting for 100-continue\n");
+      }
+    }
+  }
+
+  if(Curl_pgrsUpdate(conn))
+    result = CURLE_ABORTED_BY_CALLBACK;
+  else
+    result = Curl_speedcheck(data, k->now);
+  if(result)
+    return result;
+
+  if(k->keepon) {
+    if(0 > Curl_timeleft(data, &k->now, FALSE)) {
+      if(k->size != -1) {
+        failf(data, "Operation timed out after %ld milliseconds with %"
+              FORMAT_OFF_T " out of %" FORMAT_OFF_T " bytes received",
+              Curl_tvdiff(k->now, data->progress.t_startsingle), k->bytecount,
+              k->size);
+      }
+      else {
+        failf(data, "Operation timed out after %ld milliseconds with %"
+              FORMAT_OFF_T " bytes received",
+              Curl_tvdiff(k->now, data->progress.t_startsingle), k->bytecount);
+      }
+      return CURLE_OPERATION_TIMEDOUT;
+    }
+  }
+  else {
+    /*
+     * The transfer has been performed. Just make some general checks before
+     * returning.
+     */
+
+    if(!(data->set.opt_no_body) && (k->size != -1) &&
+       (k->bytecount != k->size) &&
+#ifdef CURL_DO_LINEEND_CONV
+       /* Most FTP servers don't adjust their file SIZE response for CRLFs,
+          so we'll check to see if the discrepancy can be explained
+          by the number of CRLFs we've changed to LFs.
+       */
+       (k->bytecount != (k->size + data->state.crlf_conversions)) &&
+#endif /* CURL_DO_LINEEND_CONV */
+       !data->req.newurl) {
+      failf(data, "transfer closed with %" FORMAT_OFF_T
+            " bytes remaining to read",
+            k->size - k->bytecount);
+      return CURLE_PARTIAL_FILE;
+    }
+    else if(!(data->set.opt_no_body) &&
+            k->chunk &&
+            (conn->chunk.state != CHUNK_STOP)) {
+      /*
+       * In chunked mode, return an error if the connection is closed prior to
+       * the empty (terminating) chunk is read.
+       *
+       * The condition above used to check for
+       * conn->proto.http->chunk.datasize != 0 which is true after reading
+       * *any* chunk, not just the empty chunk.
+       *
+       */
+      failf(data, "transfer closed with outstanding read data remaining");
+      return CURLE_PARTIAL_FILE;
+    }
+    if(Curl_pgrsUpdate(conn))
+      return CURLE_ABORTED_BY_CALLBACK;
+  }
+
+  /* Now update the "done" boolean we return */
+  *done = (0 == (k->keepon&(KEEP_RECV|KEEP_SEND|
+                            KEEP_RECV_PAUSE|KEEP_SEND_PAUSE))) ? TRUE : FALSE;
+
+  return CURLE_OK;
+}
+
+/*
+ * Curl_single_getsock() gets called by the multi interface code when the app
+ * has requested to get the sockets for the current connection. This function
+ * will then be called once for every connection that the multi interface
+ * keeps track of. This function will only be called for connections that are
+ * in the proper state to have this information available.
+ */
+int Curl_single_getsock(const struct connectdata *conn,
+                        curl_socket_t *sock, /* points to numsocks number
+                                                of sockets */
+                        int numsocks)
+{
+  const struct SessionHandle *data = conn->data;
+  int bitmap = GETSOCK_BLANK;
+  unsigned sockindex = 0;
+
+  if(conn->handler->perform_getsock)
+    return conn->handler->perform_getsock(conn, sock, numsocks);
+
+  if(numsocks < 2)
+    /* simple check but we might need two slots */
+    return GETSOCK_BLANK;
+
+  /* don't include HOLD and PAUSE connections */
+  if((data->req.keepon & KEEP_RECVBITS) == KEEP_RECV) {
+
+    DEBUGASSERT(conn->sockfd != CURL_SOCKET_BAD);
+
+    bitmap |= GETSOCK_READSOCK(sockindex);
+    sock[sockindex] = conn->sockfd;
+  }
+
+  /* don't include HOLD and PAUSE connections */
+  if((data->req.keepon & KEEP_SENDBITS) == KEEP_SEND) {
+
+    if((conn->sockfd != conn->writesockfd) ||
+       !(data->req.keepon & KEEP_RECV)) {
+      /* only if they are not the same socket or we didn't have a readable
+         one, we increase index */
+      if(data->req.keepon & KEEP_RECV)
+        sockindex++; /* increase index if we need two entries */
+
+      DEBUGASSERT(conn->writesockfd != CURL_SOCKET_BAD);
+
+      sock[sockindex] = conn->writesockfd;
+    }
+
+    bitmap |= GETSOCK_WRITESOCK(sockindex);
+  }
+
+  return bitmap;
+}
+
+/*
+ * Determine optimum sleep time based on configured rate, current rate,
+ * and packet size.
+ * Returns value in milliseconds.
+ *
+ * The basic idea is to adjust the desired rate up/down in this method
+ * based on whether we are running too slow or too fast.  Then, calculate
+ * how many milliseconds to wait for the next packet to achieve this new
+ * rate.
+ */
+long Curl_sleep_time(curl_off_t rate_bps, curl_off_t cur_rate_bps,
+                             int pkt_size)
+{
+  curl_off_t min_sleep = 0;
+  curl_off_t rv = 0;
+
+  if(rate_bps == 0)
+    return 0;
+
+  /* If running faster than about .1% of the desired speed, slow
+   * us down a bit.  Use shift instead of division as the 0.1%
+   * cutoff is arbitrary anyway.
+   */
+  if(cur_rate_bps > (rate_bps + (rate_bps >> 10))) {
+    /* running too fast, decrease target rate by 1/64th of rate */
+    rate_bps -= rate_bps >> 6;
+    min_sleep = 1;
+  }
+  else if(cur_rate_bps < (rate_bps - (rate_bps >> 10))) {
+    /* running too slow, increase target rate by 1/64th of rate */
+    rate_bps += rate_bps >> 6;
+  }
+
+  /* Determine number of milliseconds to wait until we do
+   * the next packet at the adjusted rate.  We should wait
+   * longer when using larger packets, for instance.
+   */
+  rv = ((curl_off_t)((pkt_size * 8) * 1000) / rate_bps);
+
+  /* Catch rounding errors and always slow down at least 1ms if
+   * we are running too fast.
+   */
+  if(rv < min_sleep)
+    rv = min_sleep;
+
+  /* Bound value to fit in 'long' on 32-bit platform.  That's
+   * plenty long enough anyway!
+   */
+  if(rv > 0x7fffffff)
+    rv = 0x7fffffff;
+
+  return (long)rv;
+}
+
+
+/*
+ * Transfer()
+ *
+ * This function is what performs the actual transfer. It is capable of doing
+ * both ways simultaneously.  The transfer must already have been setup by a
+ * call to Curl_setup_transfer().
+ *
+ * Note that headers are created in a preallocated buffer of a default size.
+ * That buffer can be enlarged on demand, but it is never shrunken again.
+ *
+ */
+
+static CURLcode
+Transfer(struct connectdata *conn)
+{
+  CURLcode result;
+  struct SessionHandle *data = conn->data;
+  struct SingleRequest *k = &data->req;
+  bool done=FALSE;
+  bool first=TRUE;
+  long timeout_ms;
+  int buffersize;
+  long totmp;
+
+  if((conn->sockfd == CURL_SOCKET_BAD) &&
+     (conn->writesockfd == CURL_SOCKET_BAD))
+    /* nothing to read, nothing to write, we're already OK! */
+    return CURLE_OK;
+
+  /* we want header and/or body, if neither then don't do this! */
+  if(!k->getheader && data->set.opt_no_body)
+    return CURLE_OK;
+
+  while(!done) {
+    curl_socket_t fd_read = conn->sockfd;
+    curl_socket_t fd_write = conn->writesockfd;
+    int keepon = k->keepon;
+    timeout_ms = 1000;
+
+    if(conn->waitfor) {
+      /* if waitfor is set, get the RECV and SEND bits from that but keep the
+         other bits */
+      keepon &= ~ (KEEP_RECV|KEEP_SEND);
+      keepon |= conn->waitfor & (KEEP_RECV|KEEP_SEND);
+    }
+
+    /* limit-rate logic: if speed exceeds threshold, then do not include fd in
+       select set. The current speed is recalculated in each Curl_readwrite()
+       call */
+    if((keepon & KEEP_SEND) &&
+        (!data->set.max_send_speed ||
+         (data->progress.ulspeed < data->set.max_send_speed) )) {
+      k->keepon &= ~KEEP_SEND_HOLD;
+    }
+    else {
+      if(data->set.upload && data->set.max_send_speed &&
+         (data->progress.ulspeed > data->set.max_send_speed) ) {
+        /* calculate upload rate-limitation timeout. */
+        buffersize = (int)(data->set.buffer_size ?
+                           data->set.buffer_size : BUFSIZE);
+        totmp = Curl_sleep_time(data->set.max_send_speed,
+                                data->progress.ulspeed, buffersize);
+        if(totmp < timeout_ms)
+          timeout_ms = totmp;
+      }
+      fd_write = CURL_SOCKET_BAD;
+      if(keepon & KEEP_SEND)
+        k->keepon |= KEEP_SEND_HOLD; /* hold it */
+    }
+
+    if((keepon & KEEP_RECV) &&
+        (!data->set.max_recv_speed ||
+         (data->progress.dlspeed < data->set.max_recv_speed)) ) {
+      k->keepon &= ~KEEP_RECV_HOLD;
+    }
+    else {
+      if((!data->set.upload) && data->set.max_recv_speed &&
+         (data->progress.dlspeed > data->set.max_recv_speed)) {
+        /* Calculate download rate-limitation timeout. */
+        buffersize = (int)(data->set.buffer_size ?
+                           data->set.buffer_size : BUFSIZE);
+        totmp = Curl_sleep_time(data->set.max_recv_speed,
+                                data->progress.dlspeed, buffersize);
+        if(totmp < timeout_ms)
+          timeout_ms = totmp;
+      }
+      fd_read = CURL_SOCKET_BAD;
+      if(keepon & KEEP_RECV)
+        k->keepon |= KEEP_RECV_HOLD; /* hold it */
+    }
+
+    /* pause logic. Don't check descriptors for paused connections */
+    if(k->keepon & KEEP_RECV_PAUSE)
+      fd_read = CURL_SOCKET_BAD;
+    if(k->keepon & KEEP_SEND_PAUSE)
+      fd_write = CURL_SOCKET_BAD;
+
+    /* The *_HOLD and *_PAUSE logic is necessary since even though there might
+       be no traffic during the select interval, we still call
+       Curl_readwrite() for the timeout case and if we limit transfer speed we
+       must make sure that this function doesn't transfer anything while in
+       HOLD status.
+
+       The no timeout for the first round is for the protocols for which data
+       has already been slurped off the socket and thus waiting for action
+       won't work since it'll wait even though there is already data present
+       to work with. */
+    if(first &&
+       ((fd_read != CURL_SOCKET_BAD) || (fd_write != CURL_SOCKET_BAD)))
+      /* if this is the first lap and one of the file descriptors is fine
+         to work with, skip the timeout */
+      timeout_ms = 0;
+    else {
+      totmp = Curl_timeleft(data, &k->now, FALSE);
+      if(totmp < 0)
+        return CURLE_OPERATION_TIMEDOUT;
+      else if(!totmp)
+        totmp = 1000;
+
+      if(totmp < timeout_ms)
+        timeout_ms = totmp;
+    }
+
+    switch (Curl_socket_ready(fd_read, fd_write, timeout_ms)) {
+    case -1: /* select() error, stop reading */
+#ifdef EINTR
+      /* The EINTR is not serious, and it seems you might get this more
+         often when using the lib in a multi-threaded environment! */
+      if(SOCKERRNO == EINTR)
+        continue;
+#endif
+      return CURLE_RECV_ERROR;  /* indicate a network problem */
+    case 0:  /* timeout */
+    default: /* readable descriptors */
+
+      result = Curl_readwrite(conn, &done);
+      /* "done" signals to us if the transfer(s) are ready */
+      break;
+    }
+    if(result)
+      return result;
+
+    first = FALSE; /* not the first lap anymore */
+  }
+
+  return CURLE_OK;
+}
+
+
+/*
+ * Curl_pretransfer() is called immediately before a transfer starts.
+ */
+CURLcode Curl_pretransfer(struct SessionHandle *data)
+{
+  CURLcode res;
+  if(!data->change.url) {
+    /* we can't do anything without URL */
+    failf(data, "No URL set!");
+    return CURLE_URL_MALFORMAT;
+  }
+
+  /* Init the SSL session ID cache here. We do it here since we want to do it
+     after the *_setopt() calls (that could specify the size of the cache) but
+     before any transfer takes place. */
+  res = Curl_ssl_initsessions(data, data->set.ssl.max_ssl_sessions);
+  if(res)
+    return res;
+
+  data->set.followlocation=0; /* reset the location-follow counter */
+  data->state.this_is_a_follow = FALSE; /* reset this */
+  data->state.errorbuf = FALSE; /* no error has occurred */
+  data->state.httpversion = 0; /* don't assume any particular server version */
+
+  data->state.ssl_connect_retry = FALSE;
+
+  data->state.authproblem = FALSE;
+  data->state.authhost.want = data->set.httpauth;
+  data->state.authproxy.want = data->set.proxyauth;
+  Curl_safefree(data->info.wouldredirect);
+  data->info.wouldredirect = NULL;
+
+  /* If there is a list of cookie files to read, do it now! */
+  if(data->change.cookielist)
+    Curl_cookie_loadfiles(data);
+
+  /* If there is a list of host pairs to deal with */
+  if(data->change.resolve)
+    res = Curl_loadhostpairs(data);
+
+  if(!res) {
+    /* Allow data->set.use_port to set which port to use. This needs to be
+     * disabled for example when we follow Location: headers to URLs using
+     * different ports! */
+    data->state.allow_port = TRUE;
+
+#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(HAVE_MSG_NOSIGNAL)
+    /*************************************************************
+     * Tell signal handler to ignore SIGPIPE
+     *************************************************************/
+    if(!data->set.no_signal)
+      data->state.prev_signal = signal(SIGPIPE, SIG_IGN);
+#endif
+
+    Curl_initinfo(data); /* reset session-specific information "variables" */
+    Curl_pgrsStartNow(data);
+
+    if(data->set.timeout)
+      Curl_expire(data, data->set.timeout);
+
+    if(data->set.connecttimeout)
+      Curl_expire(data, data->set.connecttimeout);
+
+    /* In case the handle is re-used and an authentication method was picked
+       in the session we need to make sure we only use the one(s) we now
+       consider to be fine */
+    data->state.authhost.picked &= data->state.authhost.want;
+    data->state.authproxy.picked &= data->state.authproxy.want;
+  }
+
+  return res;
+}
+
+/*
+ * Curl_posttransfer() is called immediately after a transfer ends
+ */
+CURLcode Curl_posttransfer(struct SessionHandle *data)
+{
+#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(HAVE_MSG_NOSIGNAL)
+  /* restore the signal handler for SIGPIPE before we get back */
+  if(!data->set.no_signal)
+    signal(SIGPIPE, data->state.prev_signal);
+#else
+  (void)data; /* unused parameter */
+#endif
+
+  return CURLE_OK;
+}
+
+#ifndef CURL_DISABLE_HTTP
+/*
+ * strlen_url() returns the length of the given URL if the spaces within the
+ * URL were properly URL encoded.
+ */
+static size_t strlen_url(const char *url)
+{
+  const char *ptr;
+  size_t newlen=0;
+  bool left=TRUE; /* left side of the ? */
+
+  for(ptr=url; *ptr; ptr++) {
+    switch(*ptr) {
+    case '?':
+      left=FALSE;
+      /* fall through */
+    default:
+      newlen++;
+      break;
+    case ' ':
+      if(left)
+        newlen+=3;
+      else
+        newlen++;
+      break;
+    }
+  }
+  return newlen;
+}
+
+/* strcpy_url() copies a url to a output buffer and URL-encodes the spaces in
+ * the source URL accordingly.
+ */
+static void strcpy_url(char *output, const char *url)
+{
+  /* we must add this with whitespace-replacing */
+  bool left=TRUE;
+  const char *iptr;
+  char *optr = output;
+  for(iptr = url;    /* read from here */
+      *iptr;         /* until zero byte */
+      iptr++) {
+    switch(*iptr) {
+    case '?':
+      left=FALSE;
+      /* fall through */
+    default:
+      *optr++=*iptr;
+      break;
+    case ' ':
+      if(left) {
+        *optr++='%'; /* add a '%' */
+        *optr++='2'; /* add a '2' */
+        *optr++='0'; /* add a '0' */
+      }
+      else
+        *optr++='+'; /* add a '+' here */
+      break;
+    }
+  }
+  *optr=0; /* zero terminate output buffer */
+
+}
+
+/*
+ * Returns true if the given URL is absolute (as opposed to relative)
+ */
+static bool is_absolute_url(const char *url)
+{
+  char prot[16]; /* URL protocol string storage */
+  char letter;   /* used for a silly sscanf */
+
+  return (2 == sscanf(url, "%15[^?&/:]://%c", prot, &letter)) ? TRUE : FALSE;
+}
+
+/*
+ * Concatenate a relative URL to a base URL making it absolute.
+ * URL-encodes any spaces.
+ * The returned pointer must be freed by the caller unless NULL
+ * (returns NULL on out of memory).
+ */
+static char *concat_url(const char *base, const char *relurl)
+{
+  /***
+   TRY to append this new path to the old URL
+   to the right of the host part. Oh crap, this is doomed to cause
+   problems in the future...
+  */
+  char *newest;
+  char *protsep;
+  char *pathsep;
+  size_t newlen;
+
+  const char *useurl = relurl;
+  size_t urllen;
+
+  /* we must make our own copy of the URL to play with, as it may
+     point to read-only data */
+  char *url_clone=strdup(base);
+
+  if(!url_clone)
+    return NULL; /* skip out of this NOW */
+
+  /* protsep points to the start of the host name */
+  protsep=strstr(url_clone, "//");
+  if(!protsep)
+    protsep=url_clone;
+  else
+    protsep+=2; /* pass the slashes */
+
+  if('/' != relurl[0]) {
+    int level=0;
+
+    /* First we need to find out if there's a ?-letter in the URL,
+       and cut it and the right-side of that off */
+    pathsep = strchr(protsep, '?');
+    if(pathsep)
+      *pathsep=0;
+
+    /* we have a relative path to append to the last slash if there's one
+       available, or if the new URL is just a query string (starts with a
+       '?')  we append the new one at the end of the entire currently worked
+       out URL */
+    if(useurl[0] != '?') {
+      pathsep = strrchr(protsep, '/');
+      if(pathsep)
+        *pathsep=0;
+    }
+
+    /* Check if there's any slash after the host name, and if so, remember
+       that position instead */
+    pathsep = strchr(protsep, '/');
+    if(pathsep)
+      protsep = pathsep+1;
+    else
+      protsep = NULL;
+
+    /* now deal with one "./" or any amount of "../" in the newurl
+       and act accordingly */
+
+    if((useurl[0] == '.') && (useurl[1] == '/'))
+      useurl+=2; /* just skip the "./" */
+
+    while((useurl[0] == '.') &&
+          (useurl[1] == '.') &&
+          (useurl[2] == '/')) {
+      level++;
+      useurl+=3; /* pass the "../" */
+    }
+
+    if(protsep) {
+      while(level--) {
+        /* cut off one more level from the right of the original URL */
+        pathsep = strrchr(protsep, '/');
+        if(pathsep)
+          *pathsep=0;
+        else {
+          *protsep=0;
+          break;
+        }
+      }
+    }
+  }
+  else {
+    /* We got a new absolute path for this server */
+
+    if((relurl[0] == '/') && (relurl[1] == '/')) {
+      /* the new URL starts with //, just keep the protocol part from the
+         original one */
+      *protsep=0;
+      useurl = &relurl[2]; /* we keep the slashes from the original, so we
+                              skip the new ones */
+    }
+    else {
+      /* cut off the original URL from the first slash, or deal with URLs
+         without slash */
+      pathsep = strchr(protsep, '/');
+      if(pathsep) {
+        /* When people use badly formatted URLs, such as
+           "http://www.url.com?dir=/home/daniel" we must not use the first
+           slash, if there's a ?-letter before it! */
+        char *sep = strchr(protsep, '?');
+        if(sep && (sep < pathsep))
+          pathsep = sep;
+        *pathsep=0;
+      }
+      else {
+        /* There was no slash. Now, since we might be operating on a badly
+           formatted URL, such as "http://www.url.com?id=2380" which doesn't
+           use a slash separator as it is supposed to, we need to check for a
+           ?-letter as well! */
+        pathsep = strchr(protsep, '?');
+        if(pathsep)
+          *pathsep=0;
+      }
+    }
+  }
+
+  /* If the new part contains a space, this is a mighty stupid redirect
+     but we still make an effort to do "right". To the left of a '?'
+     letter we replace each space with %20 while it is replaced with '+'
+     on the right side of the '?' letter.
+  */
+  newlen = strlen_url(useurl);
+
+  urllen = strlen(url_clone);
+
+  newest = malloc(urllen + 1 + /* possible slash */
+                  newlen + 1 /* zero byte */);
+
+  if(!newest) {
+    free(url_clone); /* don't leak this */
+    return NULL;
+  }
+
+  /* copy over the root url part */
+  memcpy(newest, url_clone, urllen);
+
+  /* check if we need to append a slash */
+  if(('/' == useurl[0]) || (protsep && !*protsep) || ('?' == useurl[0]))
+    ;
+  else
+    newest[urllen++]='/';
+
+  /* then append the new piece on the right side */
+  strcpy_url(&newest[urllen], useurl);
+
+  free(url_clone);
+
+  return newest;
+}
+#endif /* CURL_DISABLE_HTTP */
+
+/*
+ * Curl_follow() handles the URL redirect magic. Pass in the 'newurl' string
+ * as given by the remote server and set up the new URL to request.
+ */
+CURLcode Curl_follow(struct SessionHandle *data,
+                     char *newurl, /* this 'newurl' is the Location: string,
+                                      and it must be malloc()ed before passed
+                                      here */
+                     followtype type) /* see curl_transfer.h */
+{
+#ifdef CURL_DISABLE_HTTP
+  (void)data;
+  (void)newurl;
+  (void)type;
+  /* Location: following will not happen when HTTP is disabled */
+  return CURLE_TOO_MANY_REDIRECTS;
+#else
+
+  /* Location: redirect */
+  bool disallowport = FALSE;
+
+  if(type == FOLLOW_REDIR) {
+    if((data->set.maxredirs != -1) &&
+        (data->set.followlocation >= data->set.maxredirs)) {
+      failf(data,"Maximum (%ld) redirects followed", data->set.maxredirs);
+      return CURLE_TOO_MANY_REDIRECTS;
+    }
+
+    /* mark the next request as a followed location: */
+    data->state.this_is_a_follow = TRUE;
+
+    data->set.followlocation++; /* count location-followers */
+
+    if(data->set.http_auto_referer) {
+      /* We are asked to automatically set the previous URL as the referer
+         when we get the next URL. We pick the ->url field, which may or may
+         not be 100% correct */
+
+      if(data->change.referer_alloc) {
+        Curl_safefree(data->change.referer);
+        data->change.referer_alloc = FALSE;
+      }
+
+      data->change.referer = strdup(data->change.url);
+      if(!data->change.referer)
+        return CURLE_OUT_OF_MEMORY;
+      data->change.referer_alloc = TRUE; /* yes, free this later */
+    }
+  }
+
+  if(!is_absolute_url(newurl))  {
+    /***
+     *DANG* this is an RFC 2068 violation. The URL is supposed
+     to be absolute and this doesn't seem to be that!
+     */
+    char *absolute = concat_url(data->change.url, newurl);
+    if(!absolute)
+      return CURLE_OUT_OF_MEMORY;
+    free(newurl);
+    newurl = absolute;
+  }
+  else {
+    /* This is an absolute URL, don't allow the custom port number */
+    disallowport = TRUE;
+
+    if(strchr(newurl, ' ')) {
+      /* This new URL contains at least one space, this is a mighty stupid
+         redirect but we still make an effort to do "right". */
+      char *newest;
+      size_t newlen = strlen_url(newurl);
+
+      newest = malloc(newlen+1); /* get memory for this */
+      if(!newest)
+        return CURLE_OUT_OF_MEMORY;
+      strcpy_url(newest, newurl); /* create a space-free URL */
+
+      free(newurl); /* that was no good */
+      newurl = newest; /* use this instead now */
+    }
+
+  }
+
+  if(type == FOLLOW_FAKE) {
+    /* we're only figuring out the new url if we would've followed locations
+       but now we're done so we can get out! */
+    data->info.wouldredirect = newurl;
+    return CURLE_OK;
+  }
+
+  if(disallowport)
+    data->state.allow_port = FALSE;
+
+  if(data->change.url_alloc) {
+    Curl_safefree(data->change.url);
+    data->change.url_alloc = FALSE;
+  }
+
+  data->change.url = newurl;
+  data->change.url_alloc = TRUE;
+  newurl = NULL; /* don't free! */
+
+  infof(data, "Issue another request to this URL: '%s'\n", data->change.url);
+
+  /*
+   * We get here when the HTTP code is 300-399 (and 401). We need to perform
+   * differently based on exactly what return code there was.
+   *
+   * News from 7.10.6: we can also get here on a 401 or 407, in case we act on
+   * a HTTP (proxy-) authentication scheme other than Basic.
+   */
+  switch(data->info.httpcode) {
+    /* 401 - Act on a WWW-Authenticate, we keep on moving and do the
+       Authorization: XXXX header in the HTTP request code snippet */
+    /* 407 - Act on a Proxy-Authenticate, we keep on moving and do the
+       Proxy-Authorization: XXXX header in the HTTP request code snippet */
+    /* 300 - Multiple Choices */
+    /* 306 - Not used */
+    /* 307 - Temporary Redirect */
+  default:  /* for all above (and the unknown ones) */
+    /* Some codes are explicitly mentioned since I've checked RFC2616 and they
+     * seem to be OK to POST to.
+     */
+    break;
+  case 301: /* Moved Permanently */
+    /* (quote from RFC2616, section 10.3.2):
+     *
+     * When automatically redirecting a POST request after receiving a 301
+     * status code, some existing HTTP/1.0 user agents will erroneously change
+     * it into a GET request.
+     *
+     * ----
+     *
+     * As most of the important user agents do this obvious RFC2616 violation,
+     * many webservers expect this. So these servers often answers to a POST
+     * request with an error page.  To be sure that libcurl gets the page that
+     * most user agents would get, libcurl has to force GET.
+     *
+     * This behavior can be overridden with CURLOPT_POSTREDIR.
+     */
+    if((data->set.httpreq == HTTPREQ_POST
+        || data->set.httpreq == HTTPREQ_POST_FORM)
+       && !(data->set.keep_post & CURL_REDIR_POST_301)) {
+      infof(data,
+            "Violate RFC 2616/10.3.2 and switch from POST to GET\n");
+      data->set.httpreq = HTTPREQ_GET;
+    }
+    break;
+  case 302: /* Found */
+    /* (From 10.3.3)
+
+    Note: RFC 1945 and RFC 2068 specify that the client is not allowed
+    to change the method on the redirected request.  However, most
+    existing user agent implementations treat 302 as if it were a 303
+    response, performing a GET on the Location field-value regardless
+    of the original request method. The status codes 303 and 307 have
+    been added for servers that wish to make unambiguously clear which
+    kind of reaction is expected of the client.
+
+    (From 10.3.4)
+
+    Note: Many pre-HTTP/1.1 user agents do not understand the 303
+    status. When interoperability with such clients is a concern, the
+    302 status code may be used instead, since most user agents react
+    to a 302 response as described here for 303.
+
+    This behavior can be overridden with CURLOPT_POSTREDIR
+    */
+    if((data->set.httpreq == HTTPREQ_POST
+        || data->set.httpreq == HTTPREQ_POST_FORM)
+       && !(data->set.keep_post & CURL_REDIR_POST_302)) {
+      infof(data,
+            "Violate RFC 2616/10.3.3 and switch from POST to GET\n");
+      data->set.httpreq = HTTPREQ_GET;
+    }
+    break;
+
+  case 303: /* See Other */
+    /* Disable both types of POSTs, unless the user explicitely
+       asks for POST after POST */
+    if(data->set.httpreq != HTTPREQ_GET
+      && !(data->set.keep_post & CURL_REDIR_POST_303)) {
+      data->set.httpreq = HTTPREQ_GET; /* enforce GET request */
+      infof(data, "Disables POST, goes with %s\n",
+            data->set.opt_no_body?"HEAD":"GET");
+    }
+    break;
+  case 304: /* Not Modified */
+    /* 304 means we did a conditional request and it was "Not modified".
+     * We shouldn't get any Location: header in this response!
+     */
+    break;
+  case 305: /* Use Proxy */
+    /* (quote from RFC2616, section 10.3.6):
+     * "The requested resource MUST be accessed through the proxy given
+     * by the Location field. The Location field gives the URI of the
+     * proxy.  The recipient is expected to repeat this single request
+     * via the proxy. 305 responses MUST only be generated by origin
+     * servers."
+     */
+    break;
+  }
+  Curl_pgrsTime(data, TIMER_REDIRECT);
+  Curl_pgrsResetTimesSizes(data);
+
+  return CURLE_OK;
+#endif /* CURL_DISABLE_HTTP */
+}
+
+static CURLcode
+connect_host(struct SessionHandle *data,
+             struct connectdata **conn)
+{
+  CURLcode res = CURLE_OK;
+
+  bool async;
+  bool protocol_done=TRUE; /* will be TRUE always since this is only used
+                                within the easy interface */
+  Curl_pgrsTime(data, TIMER_STARTSINGLE);
+  res = Curl_connect(data, conn, &async, &protocol_done);
+
+  if((CURLE_OK == res) && async) {
+    /* Now, if async is TRUE here, we need to wait for the name
+       to resolve */
+    res = Curl_resolver_wait_resolv(*conn, NULL);
+    if(CURLE_OK == res) {
+      /* Resolved, continue with the connection */
+      res = Curl_async_resolved(*conn, &protocol_done);
+      if(res)
+        *conn = NULL;
+    }
+    else {
+      /* if we can't resolve, we kill this "connection" now */
+      (void)Curl_disconnect(*conn, /* dead_connection */ FALSE);
+      *conn = NULL;
+    }
+  }
+
+  return res;
+}
+
+CURLcode
+Curl_reconnect_request(struct connectdata **connp)
+{
+  CURLcode result = CURLE_OK;
+  struct connectdata *conn = *connp;
+  struct SessionHandle *data = conn->data;
+
+  /* This was a re-use of a connection and we got a write error in the
+   * DO-phase. Then we DISCONNECT this connection and have another attempt to
+   * CONNECT and then DO again! The retry cannot possibly find another
+   * connection to re-use, since we only keep one possible connection for
+   * each.  */
+
+  infof(data, "Re-used connection seems dead, get a new one\n");
+
+  conn->bits.close = TRUE; /* enforce close of this connection */
+  result = Curl_done(&conn, result, FALSE); /* we are so done with this */
+
+  /* conn may no longer be a good pointer, clear it to avoid mistakes by
+     parent functions */
+  *connp = NULL;
+
+  /*
+   * According to bug report #1330310. We need to check for CURLE_SEND_ERROR
+   * here as well. I figure this could happen when the request failed on a FTP
+   * connection and thus Curl_done() itself tried to use the connection
+   * (again). Slight Lack of feedback in the report, but I don't think this
+   * extra check can do much harm.
+   */
+  if((CURLE_OK == result) || (CURLE_SEND_ERROR == result)) {
+    bool async;
+    bool protocol_done = TRUE;
+
+    /* Now, redo the connect and get a new connection */
+    result = Curl_connect(data, connp, &async, &protocol_done);
+    if(CURLE_OK == result) {
+      /* We have connected or sent away a name resolve query fine */
+
+      conn = *connp; /* setup conn to again point to something nice */
+      if(async) {
+        /* Now, if async is TRUE here, we need to wait for the name
+           to resolve */
+        result = Curl_resolver_wait_resolv(conn, NULL);
+        if(result)
+          return result;
+
+        /* Resolved, continue with the connection */
+        result = Curl_async_resolved(conn, &protocol_done);
+        if(result)
+          return result;
+      }
+    }
+  }
+
+  return result;
+}
+
+/* Returns CURLE_OK *and* sets '*url' if a request retry is wanted.
+
+   NOTE: that the *url is malloc()ed. */
+CURLcode Curl_retry_request(struct connectdata *conn,
+                            char **url)
+{
+  struct SessionHandle *data = conn->data;
+
+  *url = NULL;
+
+  /* if we're talking upload, we can't do the checks below, unless the protocol
+     is HTTP as when uploading over HTTP we will still get a response */
+  if(data->set.upload &&
+     !(conn->handler->protocol&(CURLPROTO_HTTP|CURLPROTO_RTSP)))
+    return CURLE_OK;
+
+  if(/* workaround for broken TLS servers */ data->state.ssl_connect_retry ||
+      ((data->req.bytecount +
+        data->req.headerbytecount == 0) &&
+        conn->bits.reuse &&
+        !data->set.opt_no_body &&
+        data->set.rtspreq != RTSPREQ_RECEIVE)) {
+    /* We got no data, we attempted to re-use a connection and yet we want a
+       "body". This might happen if the connection was left alive when we were
+       done using it before, but that was closed when we wanted to read from
+       it again. Bad luck. Retry the same request on a fresh connect! */
+    infof(conn->data, "Connection died, retrying a fresh connect\n");
+    *url = strdup(conn->data->change.url);
+    if(!*url)
+      return CURLE_OUT_OF_MEMORY;
+
+    conn->bits.close = TRUE; /* close this connection */
+    conn->bits.retry = TRUE; /* mark this as a connection we're about
+                                to retry. Marking it this way should
+                                prevent i.e HTTP transfers to return
+                                error just because nothing has been
+                                transferred! */
+
+
+    if((conn->handler->protocol&CURLPROTO_HTTP) &&
+       data->state.proto.http->writebytecount)
+      return Curl_readrewind(conn);
+  }
+  return CURLE_OK;
+}
+
+static CURLcode Curl_do_perform(struct SessionHandle *data)
+{
+  CURLcode res;
+  CURLcode res2;
+  struct connectdata *conn=NULL;
+  char *newurl = NULL; /* possibly a new URL to follow to! */
+  followtype follow = FOLLOW_NONE;
+
+  data->state.used_interface = Curl_if_easy;
+
+  res = Curl_pretransfer(data);
+  if(res)
+    return res;
+
+  /*
+   * It is important that there is NO 'return' from this function at any other
+   * place than falling down to the end of the function! This is because we
+   * have cleanup stuff that must be done before we get back, and that is only
+   * performed after this do-while loop.
+   */
+
+  for(;;) {
+    res = connect_host(data, &conn);   /* primary connection */
+
+    if(res == CURLE_OK) {
+      bool do_done;
+      if(data->set.connect_only) {
+        /* keep connection open for application to use the socket */
+        conn->bits.close = FALSE;
+        res = Curl_done(&conn, CURLE_OK, FALSE);
+        break;
+      }
+      res = Curl_do(&conn, &do_done);
+
+      if(res == CURLE_OK) {
+        if(conn->data->set.wildcardmatch) {
+          if(conn->data->wildcard.state == CURLWC_DONE ||
+             conn->data->wildcard.state == CURLWC_SKIP) {
+            /* keep connection open for application to use the socket */
+            conn->bits.close = FALSE;
+            res = Curl_done(&conn, CURLE_OK, FALSE);
+            break;
+          }
+        }
+        res = Transfer(conn); /* now fetch that URL please */
+        if((res == CURLE_OK) || (res == CURLE_RECV_ERROR)) {
+          bool retry = FALSE;
+          CURLcode rc = Curl_retry_request(conn, &newurl);
+          if(rc)
+            res = rc;
+          else
+            retry = (newurl?TRUE:FALSE);
+
+          if(retry) {
+            /* we know (newurl != NULL) at this point */
+            res = CURLE_OK;
+            follow = FOLLOW_RETRY;
+          }
+          else if(res == CURLE_OK) {
+            /*
+             * We must duplicate the new URL here as the connection data may
+             * be free()ed in the Curl_done() function. We prefer the newurl
+             * one since that's used for redirects or just further requests
+             * for retries or multi-stage HTTP auth methods etc.
+             */
+            if(data->req.newurl) {
+              follow = FOLLOW_REDIR;
+              newurl = strdup(data->req.newurl);
+              if(!newurl)
+                res = CURLE_OUT_OF_MEMORY;
+            }
+            else if(data->req.location) {
+              follow = FOLLOW_FAKE;
+              newurl = strdup(data->req.location);
+              if(!newurl)
+                res = CURLE_OUT_OF_MEMORY;
+            }
+          }
+
+          /* in the above cases where 'newurl' gets assigned, we have a fresh
+           * allocated memory pointed to */
+        }
+        if(res != CURLE_OK) {
+          /* The transfer phase returned error, we mark the connection to get
+           * closed to prevent being re-used. This is because we can't
+           * possibly know if the connection is in a good shape or not now. */
+          conn->bits.close = TRUE;
+
+          if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) {
+            /* if we failed anywhere, we must clean up the secondary socket if
+               it was used */
+            Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
+            conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
+          }
+        }
+
+        /* Always run Curl_done(), even if some of the previous calls
+           failed, but return the previous (original) error code */
+        res2 = Curl_done(&conn, res, FALSE);
+
+        if(CURLE_OK == res)
+          res = res2;
+      }
+      else if(conn)
+        /* Curl_do() failed, clean up left-overs in the done-call, but note
+           that at some cases the conn pointer is NULL when Curl_do() failed
+           and the connection cache is very small so only call Curl_done() if
+           conn is still "alive". */
+        /* ignore return code since we already have an error to return */
+        (void)Curl_done(&conn, res, FALSE);
+
+      /*
+       * Important: 'conn' cannot be used here, since it may have been closed
+       * in 'Curl_done' or other functions.
+       */
+
+      if((res == CURLE_OK) && follow) {
+        res = Curl_follow(data, newurl, follow);
+        if(CURLE_OK == res) {
+          /* if things went fine, Curl_follow() freed or otherwise took
+             responsibility for the newurl pointer */
+          newurl = NULL;
+          if(follow >= FOLLOW_RETRY) {
+            follow = FOLLOW_NONE;
+            continue;
+          }
+          /* else we break out of the loop below */
+        }
+      }
+    }
+    break; /* it only reaches here when this shouldn't loop */
+
+  } /* loop if Location: */
+
+  if(newurl)
+    free(newurl);
+
+  if(res && !data->state.errorbuf) {
+    /*
+     * As an extra precaution: if no error string has been set and there was
+     * an error, use the strerror() string or if things are so bad that not
+     * even that is good, set a bad string that mentions the error code.
+     */
+    const char *str = curl_easy_strerror(res);
+    if(!str)
+      failf(data, "unspecified error %d", (int)res);
+    else
+      failf(data, "%s", str);
+  }
+
+  /* run post-transfer unconditionally, but don't clobber the return code if
+     we already have an error code recorder */
+  res2 = Curl_posttransfer(data);
+  if(!res && res2)
+    res = res2;
+
+  return res;
+}
+
+/*
+ * Curl_perform() is the internal high-level function that gets called by the
+ * external curl_easy_perform() function. It inits, performs and cleans up a
+ * single file transfer.
+ */
+CURLcode Curl_perform(struct SessionHandle *data)
+{
+  CURLcode res;
+  if(!data->set.wildcardmatch)
+    return Curl_do_perform(data);
+
+  /* init main wildcard structures */
+  res = Curl_wildcard_init(&data->wildcard);
+  if(res)
+    return res;
+
+  res = Curl_do_perform(data);
+  if(res) {
+    Curl_wildcard_dtor(&data->wildcard);
+    return res;
+  }
+
+  /* wildcard loop */
+  while(!res && data->wildcard.state != CURLWC_DONE)
+    res = Curl_do_perform(data);
+
+  Curl_wildcard_dtor(&data->wildcard);
+
+  /* wildcard download finished or failed */
+  data->wildcard.state = CURLWC_INIT;
+  return res;
+}
+
+/*
+ * Curl_setup_transfer() is called to setup some basic properties for the
+ * upcoming transfer.
+ */
+void
+Curl_setup_transfer(
+  struct connectdata *conn, /* connection data */
+  int sockindex,            /* socket index to read from or -1 */
+  curl_off_t size,          /* -1 if unknown at this point */
+  bool getheader,           /* TRUE if header parsing is wanted */
+  curl_off_t *bytecountp,   /* return number of bytes read or NULL */
+  int writesockindex,       /* socket index to write to, it may very well be
+                               the same we read from. -1 disables */
+  curl_off_t *writecountp   /* return number of bytes written or NULL */
+  )
+{
+  struct SessionHandle *data;
+  struct SingleRequest *k;
+
+  DEBUGASSERT(conn != NULL);
+
+  data = conn->data;
+  k = &data->req;
+
+  DEBUGASSERT((sockindex <= 1) && (sockindex >= -1));
+
+  /* now copy all input parameters */
+  conn->sockfd = sockindex == -1 ?
+      CURL_SOCKET_BAD : conn->sock[sockindex];
+  conn->writesockfd = writesockindex == -1 ?
+      CURL_SOCKET_BAD:conn->sock[writesockindex];
+  k->getheader = getheader;
+
+  k->size = size;
+  k->bytecountp = bytecountp;
+  k->writebytecountp = writecountp;
+
+  /* The code sequence below is placed in this function just because all
+     necessary input is not always known in do_complete() as this function may
+     be called after that */
+
+  if(!k->getheader) {
+    k->header = FALSE;
+    if(size > 0)
+      Curl_pgrsSetDownloadSize(data, size);
+  }
+  /* we want header and/or body, if neither then don't do this! */
+  if(k->getheader || !data->set.opt_no_body) {
+
+    if(conn->sockfd != CURL_SOCKET_BAD)
+      k->keepon |= KEEP_RECV;
+
+    if(conn->writesockfd != CURL_SOCKET_BAD) {
+      /* HTTP 1.1 magic:
+
+         Even if we require a 100-return code before uploading data, we might
+         need to write data before that since the REQUEST may not have been
+         finished sent off just yet.
+
+         Thus, we must check if the request has been sent before we set the
+         state info where we wait for the 100-return code
+      */
+      if((data->state.expect100header) &&
+         (data->state.proto.http->sending == HTTPSEND_BODY)) {
+        /* wait with write until we either got 100-continue or a timeout */
+        k->exp100 = EXP100_AWAITING_CONTINUE;
+        k->start100 = Curl_tvnow();
+
+        /* set a timeout for the multi interface */
+        Curl_expire(data, CURL_TIMEOUT_EXPECT_100);
+      }
+      else {
+        if(data->state.expect100header)
+          /* when we've sent off the rest of the headers, we must await a
+             100-continue but first finish sending the request */
+          k->exp100 = EXP100_SENDING_REQUEST;
+
+        /* enable the write bit when we're not waiting for continue */
+        k->keepon |= KEEP_SEND;
+      }
+    } /* if(conn->writesockfd != CURL_SOCKET_BAD) */
+  } /* if(k->getheader || !data->set.opt_no_body) */
+
+}
diff --git a/lib/curl_url.c b/lib/curl_url.c
new file mode 100644 (file)
index 0000000..52badc5
--- /dev/null
@@ -0,0 +1,5423 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#ifndef HAVE_SOCKET
+#error "We can't compile without socket() support!"
+#endif
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#ifdef USE_LIBIDN
+#include <idna.h>
+#include <tld.h>
+#include <stringprep.h>
+#ifdef HAVE_IDN_FREE_H
+#include <idn-free.h>
+#else
+/* prototype from idn-free.h, not provided by libidn 0.4.5's make install! */
+void idn_free (void *ptr);
+#endif
+#ifndef HAVE_IDN_FREE
+/* if idn_free() was not found in this version of libidn use free() instead */
+#define idn_free(x) (free)(x)
+#endif
+#elif defined(USE_WIN32_IDN)
+/* prototype for curl_win32_idn_to_ascii() */
+int curl_win32_idn_to_ascii(const char *in, char **out);
+#endif  /* USE_LIBIDN */
+
+#include "curl_urldata.h"
+#include "curl_netrc.h"
+
+#include "curl_formdata.h"
+#include "curl_sslgen.h"
+#include "curl_hostip.h"
+#include "curl_transfer.h"
+#include "curl_sendf.h"
+#include "curl_progress.h"
+#include "curl_cookie.h"
+#include "curl_strequal.h"
+#include "curl_strerror.h"
+#include "curl_escape.h"
+#include "curl_strtok.h"
+#include "curl_share.h"
+#include "curl_content_encoding.h"
+#include "curl_http_digest.h"
+#include "curl_http_negotiate.h"
+#include "curl_select.h"
+#include "curl_multiif.h"
+#include "curl_easyif.h"
+#include "curl_speedcheck.h"
+#include "curl_rawstr.h"
+#include "curl_warnless.h"
+#include "curl_non_ascii.h"
+#include "curl_inet_pton.h"
+
+/* And now for the protocols */
+#include "curl_ftp.h"
+#include "curl_dict.h"
+#include "curl_telnet.h"
+#include "curl_tftp.h"
+#include "curl_http.h"
+#include "curl_file.h"
+#include "curl_ldap.h"
+#include "curl_ssh.h"
+#include "curl_imap.h"
+#include "curl_url.h"
+#include "curl_connect.h"
+#include "curl_inet_ntop.h"
+#include "curl_ntlm.h"
+#include "curl_ntlm_wb.h"
+#include "curl_socks.h"
+#include "curl_rtmp.h"
+#include "curl_gopher.h"
+#include "curl_http_proxy.h"
+#include "curl_bundles.h"
+#include "curl_conncache.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+/* Local static prototypes */
+static bool ConnectionKillOne(struct SessionHandle *data);
+static void conn_free(struct connectdata *conn);
+static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke);
+static CURLcode do_init(struct connectdata *conn);
+static CURLcode parse_url_userpass(struct SessionHandle *data,
+                                   struct connectdata *conn,
+                                   char *user, char *passwd);
+/*
+ * Protocol table.
+ */
+
+static const struct Curl_handler * const protocols[] = {
+
+#ifndef CURL_DISABLE_HTTP
+  &Curl_handler_http,
+#endif
+
+#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP)
+  &Curl_handler_https,
+#endif
+
+#ifndef CURL_DISABLE_FTP
+  &Curl_handler_ftp,
+#endif
+
+#if defined(USE_SSL) && !defined(CURL_DISABLE_FTP)
+  &Curl_handler_ftps,
+#endif
+
+#ifndef CURL_DISABLE_TELNET
+  &Curl_handler_telnet,
+#endif
+
+#ifndef CURL_DISABLE_DICT
+  &Curl_handler_dict,
+#endif
+
+#ifndef CURL_DISABLE_LDAP
+  &Curl_handler_ldap,
+#if !defined(CURL_DISABLE_LDAPS) && \
+    ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \
+     (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL)))
+  &Curl_handler_ldaps,
+#endif
+#endif
+
+#ifndef CURL_DISABLE_FILE
+  &Curl_handler_file,
+#endif
+
+#ifndef CURL_DISABLE_TFTP
+  &Curl_handler_tftp,
+#endif
+
+#ifdef USE_LIBSSH2
+  &Curl_handler_scp,
+  &Curl_handler_sftp,
+#endif
+
+#ifndef CURL_DISABLE_IMAP
+  &Curl_handler_imap,
+#ifdef USE_SSL
+  &Curl_handler_imaps,
+#endif
+#endif
+
+#ifndef CURL_DISABLE_POP3
+  &Curl_handler_pop3,
+#ifdef USE_SSL
+  &Curl_handler_pop3s,
+#endif
+#endif
+
+#ifndef CURL_DISABLE_SMTP
+  &Curl_handler_smtp,
+#ifdef USE_SSL
+  &Curl_handler_smtps,
+#endif
+#endif
+
+#ifndef CURL_DISABLE_RTSP
+  &Curl_handler_rtsp,
+#endif
+
+#ifndef CURL_DISABLE_GOPHER
+  &Curl_handler_gopher,
+#endif
+
+#ifdef USE_LIBRTMP
+  &Curl_handler_rtmp,
+  &Curl_handler_rtmpt,
+  &Curl_handler_rtmpe,
+  &Curl_handler_rtmpte,
+  &Curl_handler_rtmps,
+  &Curl_handler_rtmpts,
+#endif
+
+  (struct Curl_handler *) NULL
+};
+
+/*
+ * Dummy handler for undefined protocol schemes.
+ */
+
+static const struct Curl_handler Curl_handler_dummy = {
+  "<no protocol>",                      /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  ZERO_NULL,                            /* do_it */
+  ZERO_NULL,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  ZERO_NULL,                            /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  0,                                    /* defport */
+  0,                                    /* protocol */
+  PROTOPT_NONE                          /* flags */
+};
+
+static void close_connections(struct SessionHandle *data)
+{
+  /* Loop through all open connections and kill them one by one */
+  bool killed;
+  do {
+    killed = ConnectionKillOne(data);
+  } while(killed);
+}
+
+void Curl_freeset(struct SessionHandle * data)
+{
+  /* Free all dynamic strings stored in the data->set substructure. */
+  enum dupstring i;
+  for(i=(enum dupstring)0; i < STRING_LAST; i++)
+    Curl_safefree(data->set.str[i]);
+
+  if(data->change.referer_alloc) {
+    Curl_safefree(data->change.referer);
+    data->change.referer_alloc = FALSE;
+  }
+  data->change.referer = NULL;
+}
+
+static CURLcode setstropt(char **charp, char * s)
+{
+  /* Release the previous storage at `charp' and replace by a dynamic storage
+     copy of `s'. Return CURLE_OK or CURLE_OUT_OF_MEMORY. */
+
+  Curl_safefree(*charp);
+
+  if(s) {
+    s = strdup(s);
+
+    if(!s)
+      return CURLE_OUT_OF_MEMORY;
+
+    *charp = s;
+  }
+
+  return CURLE_OK;
+}
+
+static CURLcode setstropt_userpwd(char *option, char **user_storage,
+                                  char **pwd_storage)
+{
+  char* separator;
+  CURLcode result = CURLE_OK;
+
+  if(!option) {
+    /* we treat a NULL passed in as a hint to clear existing info */
+    Curl_safefree(*user_storage);
+    *user_storage = (char *) NULL;
+    Curl_safefree(*pwd_storage);
+    *pwd_storage = (char *) NULL;
+    return CURLE_OK;
+  }
+
+  separator = strchr(option, ':');
+  if(separator != NULL) {
+
+    /* store username part of option */
+    char * p;
+    size_t username_len = (size_t)(separator-option);
+    p = malloc(username_len+1);
+    if(!p)
+      result = CURLE_OUT_OF_MEMORY;
+    else {
+      memcpy(p, option, username_len);
+      p[username_len] = '\0';
+      Curl_safefree(*user_storage);
+      *user_storage = p;
+    }
+
+    /* store password part of option */
+    if(result == CURLE_OK)
+      result = setstropt(pwd_storage, separator+1);
+  }
+  else {
+    result = setstropt(user_storage, option);
+  }
+  return result;
+}
+
+CURLcode Curl_dupset(struct SessionHandle * dst, struct SessionHandle * src)
+{
+  CURLcode r = CURLE_OK;
+  enum dupstring i;
+
+  /* Copy src->set into dst->set first, then deal with the strings
+     afterwards */
+  dst->set = src->set;
+
+  /* clear all string pointers first */
+  memset(dst->set.str, 0, STRING_LAST * sizeof(char *));
+
+  /* duplicate all strings */
+  for(i=(enum dupstring)0; i< STRING_LAST; i++) {
+    r = setstropt(&dst->set.str[i], src->set.str[i]);
+    if(r != CURLE_OK)
+      break;
+  }
+
+  /* If a failure occurred, freeing has to be performed externally. */
+  return r;
+}
+
+/*
+ * This is the internal function curl_easy_cleanup() calls. This should
+ * cleanup and free all resources associated with this sessionhandle.
+ *
+ * NOTE: if we ever add something that attempts to write to a socket or
+ * similar here, we must ignore SIGPIPE first. It is currently only done
+ * when curl_easy_perform() is invoked.
+ */
+
+CURLcode Curl_close(struct SessionHandle *data)
+{
+  struct Curl_multi *m;
+
+  if(!data)
+    return CURLE_OK;
+
+  Curl_expire(data, 0); /* shut off timers */
+
+  m = data->multi;
+
+  if(m)
+    /* This handle is still part of a multi handle, take care of this first
+       and detach this handle from there. */
+    curl_multi_remove_handle(data->multi, data);
+
+  /* Destroy the timeout list that is held in the easy handle. It is
+     /normally/ done by curl_multi_remove_handle() but this is "just in
+     case" */
+  if(data->state.timeoutlist) {
+    Curl_llist_destroy(data->state.timeoutlist, NULL);
+    data->state.timeoutlist = NULL;
+  }
+
+  data->magic = 0; /* force a clear AFTER the possibly enforced removal from
+                      the multi handle, since that function uses the magic
+                      field! */
+
+  if(data->state.conn_cache) {
+    if(data->state.conn_cache->type == CONNCACHE_PRIVATE) {
+      /* close all connections still alive that are in the private connection
+         cache, as we no longer have the pointer left to the shared one. */
+      close_connections(data);
+      Curl_conncache_destroy(data->state.conn_cache);
+      data->state.conn_cache = NULL;
+    }
+  }
+
+  if(data->dns.hostcachetype == HCACHE_PRIVATE)
+    Curl_hostcache_destroy(data);
+
+  if(data->state.rangestringalloc)
+    free(data->state.range);
+
+  /* Free the pathbuffer */
+  Curl_safefree(data->state.pathbuffer);
+  data->state.path = NULL;
+
+  Curl_safefree(data->state.proto.generic);
+
+  /* Close down all open SSL info and sessions */
+  Curl_ssl_close_all(data);
+  Curl_safefree(data->state.first_host);
+  Curl_safefree(data->state.scratch);
+  Curl_ssl_free_certinfo(data);
+
+  if(data->change.referer_alloc) {
+    Curl_safefree(data->change.referer);
+    data->change.referer_alloc = FALSE;
+  }
+  data->change.referer = NULL;
+
+  if(data->change.url_alloc) {
+    Curl_safefree(data->change.url);
+    data->change.url_alloc = FALSE;
+  }
+  data->change.url = NULL;
+
+  Curl_safefree(data->state.headerbuff);
+
+  Curl_flush_cookies(data, 1);
+
+  Curl_digest_cleanup(data);
+
+  Curl_safefree(data->info.contenttype);
+  Curl_safefree(data->info.wouldredirect);
+
+  /* this destroys the channel and we cannot use it anymore after this */
+  Curl_resolver_cleanup(data->state.resolver);
+
+  Curl_convert_close(data);
+
+  /* No longer a dirty share, if it exists */
+  if(data->share) {
+    Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
+    data->share->dirty--;
+    Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
+  }
+
+  Curl_freeset(data);
+  free(data);
+  return CURLE_OK;
+}
+
+/*
+ * Initialize the UserDefined fields within a SessionHandle.
+ * This may be safely called on a new or existing SessionHandle.
+ */
+CURLcode Curl_init_userdefined(struct UserDefined *set)
+{
+  CURLcode res = CURLE_OK;
+
+  set->out = stdout; /* default output to stdout */
+  set->in  = stdin;  /* default input from stdin */
+  set->err  = stderr;  /* default stderr to stderr */
+
+  /* use fwrite as default function to store output */
+  set->fwrite_func = (curl_write_callback)fwrite;
+
+  /* use fread as default function to read input */
+  set->fread_func = (curl_read_callback)fread;
+  set->is_fread_set = 0;
+  set->is_fwrite_set = 0;
+
+  set->seek_func = ZERO_NULL;
+  set->seek_client = ZERO_NULL;
+
+  /* conversion callbacks for non-ASCII hosts */
+  set->convfromnetwork = ZERO_NULL;
+  set->convtonetwork   = ZERO_NULL;
+  set->convfromutf8    = ZERO_NULL;
+
+  set->infilesize = -1;      /* we don't know any size */
+  set->postfieldsize = -1;   /* unknown size */
+  set->maxredirs = -1;       /* allow any amount by default */
+
+  set->httpreq = HTTPREQ_GET; /* Default HTTP request */
+  set->rtspreq = RTSPREQ_OPTIONS; /* Default RTSP request */
+  set->ftp_use_epsv = TRUE;   /* FTP defaults to EPSV operations */
+  set->ftp_use_eprt = TRUE;   /* FTP defaults to EPRT operations */
+  set->ftp_use_pret = FALSE;  /* mainly useful for drftpd servers */
+  set->ftp_filemethod = FTPFILE_MULTICWD;
+
+  set->dns_cache_timeout = 60; /* Timeout every 60 seconds by default */
+
+  /* Set the default size of the SSL session ID cache */
+  set->ssl.max_ssl_sessions = 5;
+
+  set->proxyport = CURL_DEFAULT_PROXY_PORT; /* from curl_url.h */
+  set->proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */
+  set->httpauth = CURLAUTH_BASIC;  /* defaults to basic */
+  set->proxyauth = CURLAUTH_BASIC; /* defaults to basic */
+
+  /* make libcurl quiet by default: */
+  set->hide_progress = TRUE;  /* CURLOPT_NOPROGRESS changes these */
+
+  /*
+   * libcurl 7.10 introduced SSL verification *by default*! This needs to be
+   * switched off unless wanted.
+   */
+  set->ssl.verifypeer = TRUE;
+  set->ssl.verifyhost = TRUE;
+#ifdef USE_TLS_SRP
+  set->ssl.authtype = CURL_TLSAUTH_NONE;
+#endif
+  set->ssh_auth_types = CURLSSH_AUTH_DEFAULT; /* defaults to any auth
+                                                      type */
+  set->ssl.sessionid = TRUE; /* session ID caching enabled by default */
+
+  set->new_file_perms = 0644;    /* Default permissions */
+  set->new_directory_perms = 0755; /* Default permissions */
+
+  /* for the *protocols fields we don't use the CURLPROTO_ALL convenience
+     define since we internally only use the lower 16 bits for the passed
+     in bitmask to not conflict with the private bits */
+  set->allowed_protocols = CURLPROTO_ALL;
+  set->redir_protocols =
+    CURLPROTO_ALL & ~(CURLPROTO_FILE|CURLPROTO_SCP); /* not FILE or SCP */
+
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+  /*
+   * disallow unprotected protection negotiation NEC reference implementation
+   * seem not to follow rfc1961 section 4.3/4.4
+   */
+  set->socks5_gssapi_nec = FALSE;
+  /* set default gssapi service name */
+  res = setstropt(&set->str[STRING_SOCKS5_GSSAPI_SERVICE],
+                  (char *) CURL_DEFAULT_SOCKS5_GSSAPI_SERVICE);
+  if(res != CURLE_OK)
+    return res;
+#endif
+
+  /* This is our preferred CA cert bundle/path since install time */
+#if defined(CURL_CA_BUNDLE)
+  res = setstropt(&set->str[STRING_SSL_CAFILE], (char *) CURL_CA_BUNDLE);
+#elif defined(CURL_CA_PATH)
+  res = setstropt(&set->str[STRING_SSL_CAPATH], (char *) CURL_CA_PATH);
+#endif
+
+  set->wildcardmatch  = FALSE;
+  set->chunk_bgn      = ZERO_NULL;
+  set->chunk_end      = ZERO_NULL;
+
+  /* tcp keepalives are disabled by default, but provide reasonable values for
+   * the interval and idle times.
+   */
+  set->tcp_keepalive = FALSE;
+  set->tcp_keepintvl = 60;
+  set->tcp_keepidle = 60;
+
+  return res;
+}
+
+/**
+ * Curl_open()
+ *
+ * @param curl is a pointer to a sessionhandle pointer that gets set by this
+ * function.
+ * @return CURLcode
+ */
+
+CURLcode Curl_open(struct SessionHandle **curl)
+{
+  CURLcode res = CURLE_OK;
+  struct SessionHandle *data;
+  CURLcode status;
+
+  /* Very simple start-up: alloc the struct, init it with zeroes and return */
+  data = calloc(1, sizeof(struct SessionHandle));
+  if(!data) {
+    /* this is a very serious error */
+    DEBUGF(fprintf(stderr, "Error: calloc of SessionHandle failed\n"));
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  data->magic = CURLEASY_MAGIC_NUMBER;
+
+  status = Curl_resolver_init(&data->state.resolver);
+  if(status) {
+    DEBUGF(fprintf(stderr, "Error: resolver_init failed\n"));
+    free(data);
+    return status;
+  }
+
+  /* We do some initial setup here, all those fields that can't be just 0 */
+
+  data->state.headerbuff = malloc(HEADERSIZE);
+  if(!data->state.headerbuff) {
+    DEBUGF(fprintf(stderr, "Error: malloc of headerbuff failed\n"));
+    res = CURLE_OUT_OF_MEMORY;
+  }
+  else {
+    Curl_easy_initHandleData(data);
+    res = Curl_init_userdefined(&data->set);
+
+    data->state.headersize=HEADERSIZE;
+
+    Curl_convert_init(data);
+
+    /* most recent connection is not yet defined */
+    data->state.lastconnect = NULL;
+
+    data->progress.flags |= PGRS_HIDE;
+    data->state.current_speed = -1; /* init to negative == impossible */
+
+    data->wildcard.state = CURLWC_INIT;
+    data->wildcard.filelist = NULL;
+    data->set.fnmatch = ZERO_NULL;
+    /* This no longer creates a connection cache here. It is instead made on
+       the first call to curl_easy_perform() or when the handle is added to a
+       multi stack. */
+  }
+
+
+  if(res) {
+    Curl_resolver_cleanup(data->state.resolver);
+    if(data->state.headerbuff)
+      free(data->state.headerbuff);
+    Curl_freeset(data);
+    free(data);
+    data = NULL;
+  }
+  else
+    *curl = data;
+
+  return res;
+}
+
+CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
+                     va_list param)
+{
+  char *argptr;
+  CURLcode result = CURLE_OK;
+  long arg;
+#ifndef CURL_DISABLE_HTTP
+  curl_off_t bigsize;
+#endif
+
+  switch(option) {
+  case CURLOPT_DNS_CACHE_TIMEOUT:
+    data->set.dns_cache_timeout = va_arg(param, long);
+    break;
+  case CURLOPT_DNS_USE_GLOBAL_CACHE:
+    /* remember we want this enabled */
+    arg = va_arg(param, long);
+    data->set.global_dns_cache = (0 != arg)?TRUE:FALSE;
+    break;
+  case CURLOPT_SSL_CIPHER_LIST:
+    /* set a list of cipher we want to use in the SSL connection */
+    result = setstropt(&data->set.str[STRING_SSL_CIPHER_LIST],
+                       va_arg(param, char *));
+    break;
+
+  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.
+     */
+    result = setstropt(&data->set.str[STRING_SSL_RANDOM_FILE],
+                       va_arg(param, char *));
+    break;
+  case CURLOPT_EGDSOCKET:
+    /*
+     * The Entropy Gathering Daemon socket pathname
+     */
+    result = setstropt(&data->set.str[STRING_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.
+     */
+    data->set.maxconnects = va_arg(param, long);
+    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->set.reuse_forbid = (0 != 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->set.reuse_fresh = (0 != 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.
+     */
+    data->set.verbose = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+  case CURLOPT_HEADER:
+    /*
+     * Set to include the header in the general data output stream.
+     */
+    data->set.include_header = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+  case CURLOPT_NOPROGRESS:
+    /*
+     * Shut off the internal supported progress meter
+     */
+    data->set.hide_progress = (0 != va_arg(param, long))?TRUE:FALSE;
+    if(data->set.hide_progress)
+      data->progress.flags |= PGRS_HIDE;
+    else
+      data->progress.flags &= ~PGRS_HIDE;
+    break;
+  case CURLOPT_NOBODY:
+    /*
+     * Do not include the body part in the output data stream.
+     */
+    data->set.opt_no_body = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+  case CURLOPT_FAILONERROR:
+    /*
+     * Don't output the >=300 error code HTML-page, but instead only
+     * return error.
+     */
+    data->set.http_fail_on_error = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+  case CURLOPT_UPLOAD:
+  case CURLOPT_PUT:
+    /*
+     * We want to sent data to the remote host. If this is HTTP, that equals
+     * using the PUT request.
+     */
+    data->set.upload = (0 != va_arg(param, long))?TRUE:FALSE;
+    if(data->set.upload) {
+      /* If this is HTTP, PUT is what's needed to "upload" */
+      data->set.httpreq = HTTPREQ_PUT;
+      data->set.opt_no_body = FALSE; /* this is implied */
+    }
+    else
+      /* In HTTP, the opposite of upload is GET (unless NOBODY is true as
+         then this can be changed to HEAD later on) */
+      data->set.httpreq = HTTPREQ_GET;
+    break;
+  case CURLOPT_FILETIME:
+    /*
+     * Try to get the file time of the remote document. The time will
+     * later (possibly) become available using curl_easy_getinfo().
+     */
+    data->set.get_filetime = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+  case CURLOPT_FTP_CREATE_MISSING_DIRS:
+    /*
+     * An FTP option that modifies an upload to create missing directories on
+     * the server.
+     */
+    switch(va_arg(param, long)) {
+    case 0:
+      data->set.ftp_create_missing_dirs = 0;
+      break;
+    case 1:
+      data->set.ftp_create_missing_dirs = 1;
+      break;
+    case 2:
+      data->set.ftp_create_missing_dirs = 2;
+      break;
+    default:
+      /* reserve other values for future use */
+      result = CURLE_UNKNOWN_OPTION;
+      break;
+    }
+    break;
+  case CURLOPT_SERVER_RESPONSE_TIMEOUT:
+    /*
+     * Option that specifies how quickly an server response must be obtained
+     * before it is considered failure. For pingpong protocols.
+     */
+    data->set.server_response_timeout = va_arg( param , long ) * 1000;
+    break;
+  case CURLOPT_TFTP_BLKSIZE:
+    /*
+     * TFTP option that specifies the block size to use for data transmission
+     */
+    data->set.tftp_blksize = va_arg(param, long);
+    break;
+  case CURLOPT_DIRLISTONLY:
+    /*
+     * An option that changes the command to one that asks for a list
+     * only, no file info details.
+     */
+    data->set.ftp_list_only = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+  case CURLOPT_APPEND:
+    /*
+     * We want to upload and append to an existing file.
+     */
+    data->set.ftp_append = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+  case CURLOPT_FTP_FILEMETHOD:
+    /*
+     * How do access files over FTP.
+     */
+    data->set.ftp_filemethod = (curl_ftpfile)va_arg(param, long);
+    break;
+  case CURLOPT_NETRC:
+    /*
+     * Parse the $HOME/.netrc file
+     */
+    data->set.use_netrc = (enum CURL_NETRC_OPTION)va_arg(param, long);
+    break;
+  case CURLOPT_NETRC_FILE:
+    /*
+     * Use this file instead of the $HOME/.netrc file
+     */
+    result = setstropt(&data->set.str[STRING_NETRC_FILE],
+                       va_arg(param, char *));
+    break;
+  case CURLOPT_TRANSFERTEXT:
+    /*
+     * This option was previously named 'FTPASCII'. Renamed to work with
+     * more protocols than merely FTP.
+     *
+     * Transfer using ASCII (instead of BINARY).
+     */
+    data->set.prefer_ascii = (0 != 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->set.timecondition = (curl_TimeCond)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->set.timevalue = (time_t)va_arg(param, long);
+    break;
+  case CURLOPT_SSLVERSION:
+    /*
+     * Set explicit SSL version to try to connect with, as some SSL
+     * implementations are lame.
+     */
+    data->set.ssl.version = va_arg(param, long);
+    break;
+
+#ifndef CURL_DISABLE_HTTP
+  case CURLOPT_AUTOREFERER:
+    /*
+     * Switch on automatic referer that gets set if curl follows locations.
+     */
+    data->set.http_auto_referer = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+
+  case CURLOPT_ACCEPT_ENCODING:
+    /*
+     * String to use at the value of Accept-Encoding header.
+     *
+     * If the encoding is set to "" we use an Accept-Encoding header that
+     * encompasses all the encodings we support.
+     * If the encoding is set to NULL we don't send an Accept-Encoding header
+     * and ignore an received Content-Encoding header.
+     *
+     */
+    argptr = va_arg(param, char *);
+    result = setstropt(&data->set.str[STRING_ENCODING],
+                       (argptr && !*argptr)?
+                       (char *) ALL_CONTENT_ENCODINGS: argptr);
+    break;
+
+  case CURLOPT_TRANSFER_ENCODING:
+    data->set.http_transfer_encoding = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+
+  case CURLOPT_FOLLOWLOCATION:
+    /*
+     * Follow Location: header hints on a HTTP-server.
+     */
+    data->set.http_follow_location = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+
+  case CURLOPT_UNRESTRICTED_AUTH:
+    /*
+     * Send authentication (user+password) when following locations, even when
+     * hostname changed.
+     */
+    data->set.http_disable_hostname_check_before_authentication =
+      (0 != va_arg(param, long))?TRUE:FALSE;
+    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->set.maxredirs = va_arg(param, long);
+    break;
+
+  case CURLOPT_POSTREDIR:
+  {
+    /*
+     * Set the behaviour of POST when redirecting
+     * CURL_REDIR_GET_ALL - POST is changed to GET after 301 and 302
+     * CURL_REDIR_POST_301 - POST is kept as POST after 301
+     * CURL_REDIR_POST_302 - POST is kept as POST after 302
+     * CURL_REDIR_POST_303 - POST is kept as POST after 303
+     * CURL_REDIR_POST_ALL - POST is kept as POST after 301, 302 and 303
+     * other - POST is kept as POST after 301 and 302
+     */
+    int postRedir = curlx_sltosi(va_arg(param, long));
+    data->set.keep_post = postRedir & CURL_REDIR_POST_ALL;
+  }
+  break;
+
+  case CURLOPT_POST:
+    /* Does this option serve a purpose anymore? Yes it does, when
+       CURLOPT_POSTFIELDS isn't used and the POST data is read off the
+       callback! */
+    if(va_arg(param, long)) {
+      data->set.httpreq = HTTPREQ_POST;
+      data->set.opt_no_body = FALSE; /* this is implied */
+    }
+    else
+      data->set.httpreq = HTTPREQ_GET;
+    break;
+
+  case CURLOPT_COPYPOSTFIELDS:
+    /*
+     * A string with POST data. Makes curl HTTP POST. Even if it is NULL.
+     * If needed, CURLOPT_POSTFIELDSIZE must have been set prior to
+     *  CURLOPT_COPYPOSTFIELDS and not altered later.
+     */
+    argptr = va_arg(param, char *);
+
+    if(!argptr || data->set.postfieldsize == -1)
+      result = setstropt(&data->set.str[STRING_COPYPOSTFIELDS], argptr);
+    else {
+      /*
+       *  Check that requested length does not overflow the size_t type.
+       */
+
+      if((data->set.postfieldsize < 0) ||
+         ((sizeof(curl_off_t) != sizeof(size_t)) &&
+          (data->set.postfieldsize > (curl_off_t)((size_t)-1))))
+        result = CURLE_OUT_OF_MEMORY;
+      else {
+        char * p;
+
+        (void) setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL);
+
+        /* Allocate even when size == 0. This satisfies the need of possible
+           later address compare to detect the COPYPOSTFIELDS mode, and
+           to mark that postfields is used rather than read function or
+           form data.
+        */
+        p = malloc((size_t)(data->set.postfieldsize?
+                            data->set.postfieldsize:1));
+
+        if(!p)
+          result = CURLE_OUT_OF_MEMORY;
+        else {
+          if(data->set.postfieldsize)
+            memcpy(p, argptr, (size_t)data->set.postfieldsize);
+
+          data->set.str[STRING_COPYPOSTFIELDS] = p;
+        }
+      }
+    }
+
+    data->set.postfields = data->set.str[STRING_COPYPOSTFIELDS];
+    data->set.httpreq = HTTPREQ_POST;
+    break;
+
+  case CURLOPT_POSTFIELDS:
+    /*
+     * Like above, but use static data instead of copying it.
+     */
+    data->set.postfields = va_arg(param, void *);
+    /* Release old copied data. */
+    (void) setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL);
+    data->set.httpreq = HTTPREQ_POST;
+    break;
+
+  case CURLOPT_POSTFIELDSIZE:
+    /*
+     * The size of the POSTFIELD data to prevent libcurl to do strlen() to
+     * figure it out. Enables binary posts.
+     */
+    bigsize = va_arg(param, long);
+
+    if(data->set.postfieldsize < bigsize &&
+       data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) {
+      /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */
+      (void) setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL);
+      data->set.postfields = NULL;
+    }
+
+    data->set.postfieldsize = bigsize;
+    break;
+
+  case CURLOPT_POSTFIELDSIZE_LARGE:
+    /*
+     * The size of the POSTFIELD data to prevent libcurl to do strlen() to
+     * figure it out. Enables binary posts.
+     */
+    bigsize = va_arg(param, curl_off_t);
+
+    if(data->set.postfieldsize < bigsize &&
+       data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) {
+      /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */
+      (void) setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL);
+      data->set.postfields = NULL;
+    }
+
+    data->set.postfieldsize = bigsize;
+    break;
+
+  case CURLOPT_HTTPPOST:
+    /*
+     * Set to make us do HTTP POST
+     */
+    data->set.httppost = va_arg(param, struct curl_httppost *);
+    data->set.httpreq = HTTPREQ_POST_FORM;
+    data->set.opt_no_body = FALSE; /* this is implied */
+    break;
+
+  case CURLOPT_REFERER:
+    /*
+     * String to set in the HTTP Referer: field.
+     */
+    if(data->change.referer_alloc) {
+      Curl_safefree(data->change.referer);
+      data->change.referer_alloc = FALSE;
+    }
+    result = setstropt(&data->set.str[STRING_SET_REFERER],
+                       va_arg(param, char *));
+    data->change.referer = data->set.str[STRING_SET_REFERER];
+    break;
+
+  case CURLOPT_USERAGENT:
+    /*
+     * String to use in the HTTP User-Agent field
+     */
+    result = setstropt(&data->set.str[STRING_USERAGENT],
+                       va_arg(param, char *));
+    break;
+
+  case CURLOPT_HTTPHEADER:
+    /*
+     * Set a list with HTTP headers to use (or replace internals with)
+     */
+    data->set.headers = va_arg(param, struct curl_slist *);
+    break;
+
+  case CURLOPT_HTTP200ALIASES:
+    /*
+     * Set a list of aliases for HTTP 200 in response header
+     */
+    data->set.http200aliases = va_arg(param, struct curl_slist *);
+    break;
+
+#if !defined(CURL_DISABLE_COOKIES)
+  case CURLOPT_COOKIE:
+    /*
+     * Cookie string to send to the remote server in the request.
+     */
+    result = setstropt(&data->set.str[STRING_COOKIE],
+                       va_arg(param, char *));
+    break;
+
+  case CURLOPT_COOKIEFILE:
+    /*
+     * Set cookie file to read and parse. Can be used multiple times.
+     */
+    argptr = (char *)va_arg(param, void *);
+    if(argptr) {
+      struct curl_slist *cl;
+      /* append the cookie file name to the list of file names, and deal with
+         them later */
+      cl = curl_slist_append(data->change.cookielist, argptr);
+      if(!cl) {
+        curl_slist_free_all(data->change.cookielist);
+        data->change.cookielist = NULL;
+        return CURLE_OUT_OF_MEMORY;
+      }
+      data->change.cookielist = cl; /* store the list for later use */
+    }
+    break;
+
+  case CURLOPT_COOKIEJAR:
+    /*
+     * Set cookie file name to dump all cookies to when we're done.
+     */
+    result = setstropt(&data->set.str[STRING_COOKIEJAR],
+                       va_arg(param, char *));
+
+    /*
+     * Activate the cookie parser. This may or may not already
+     * have been made.
+     */
+    data->cookies = Curl_cookie_init(data, NULL, data->cookies,
+                                     data->set.cookiesession);
+    break;
+
+  case CURLOPT_COOKIESESSION:
+    /*
+     * Set this option to TRUE to start a new "cookie session". It will
+     * prevent the forthcoming read-cookies-from-file actions to accept
+     * cookies that are marked as being session cookies, as they belong to a
+     * previous session.
+     *
+     * In the original Netscape cookie spec, "session cookies" are cookies
+     * with no expire date set. RFC2109 describes the same action if no
+     * 'Max-Age' is set and RFC2965 includes the RFC2109 description and adds
+     * a 'Discard' action that can enforce the discard even for cookies that
+     * have a Max-Age.
+     *
+     * We run mostly with the original cookie spec, as hardly anyone implements
+     * anything else.
+     */
+    data->set.cookiesession = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+
+  case CURLOPT_COOKIELIST:
+    argptr = va_arg(param, char *);
+
+    if(argptr == NULL)
+      break;
+
+    if(Curl_raw_equal(argptr, "ALL")) {
+      /* clear all cookies */
+      Curl_cookie_clearall(data->cookies);
+      break;
+    }
+    else if(Curl_raw_equal(argptr, "SESS")) {
+      /* clear session cookies */
+      Curl_cookie_clearsess(data->cookies);
+      break;
+    }
+    else if(Curl_raw_equal(argptr, "FLUSH")) {
+      /* flush cookies to file */
+      Curl_flush_cookies(data, 0);
+      break;
+    }
+
+    if(!data->cookies)
+      /* if cookie engine was not running, activate it */
+      data->cookies = Curl_cookie_init(data, NULL, NULL, TRUE);
+
+    argptr = strdup(argptr);
+    if(!argptr) {
+      result = CURLE_OUT_OF_MEMORY;
+      break;
+    }
+
+    if(checkprefix("Set-Cookie:", argptr))
+      /* HTTP Header format line */
+      Curl_cookie_add(data, data->cookies, TRUE, argptr + 11, NULL, NULL);
+
+    else
+      /* Netscape format line */
+      Curl_cookie_add(data, data->cookies, FALSE, argptr, NULL, NULL);
+
+    free(argptr);
+    break;
+#endif /* CURL_DISABLE_COOKIES */
+
+  case CURLOPT_HTTPGET:
+    /*
+     * Set to force us do HTTP GET
+     */
+    if(va_arg(param, long)) {
+      data->set.httpreq = HTTPREQ_GET;
+      data->set.upload = FALSE; /* switch off upload */
+      data->set.opt_no_body = FALSE; /* this is implied */
+    }
+    break;
+
+  case CURLOPT_HTTP_VERSION:
+    /*
+     * This sets a requested HTTP version to be used. The value is one of
+     * the listed enums in curl/curl.h.
+     */
+    data->set.httpversion = va_arg(param, long);
+    break;
+
+  case CURLOPT_HTTPAUTH:
+    /*
+     * Set HTTP Authentication type BITMASK.
+     */
+  {
+    int bitcheck;
+    bool authbits;
+    unsigned long auth = va_arg(param, unsigned long);
+
+    if(auth == CURLAUTH_NONE) {
+      data->set.httpauth = auth;
+      break;
+    }
+
+    /* the DIGEST_IE bit is only used to set a special marker, for all the
+       rest we need to handle it as normal DIGEST */
+    data->state.authhost.iestyle = (auth & CURLAUTH_DIGEST_IE)?TRUE:FALSE;
+
+    if(auth & CURLAUTH_DIGEST_IE) {
+      auth |= CURLAUTH_DIGEST; /* set standard digest bit */
+      auth &= ~CURLAUTH_DIGEST_IE; /* unset ie digest bit */
+    }
+
+    /* switch off bits we can't support */
+#ifndef USE_NTLM
+    auth &= ~CURLAUTH_NTLM;    /* no NTLM support */
+    auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */
+#elif !defined(NTLM_WB_ENABLED)
+    auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */
+#endif
+#ifndef USE_HTTP_NEGOTIATE
+    auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI or
+                                       WINDOWS_SSPI */
+#endif
+
+    /* check if any auth bit lower than CURLAUTH_ONLY is still set */
+    bitcheck = 0;
+    authbits = FALSE;
+    while(bitcheck < 31) {
+      if(auth & (1UL << bitcheck++)) {
+        authbits = TRUE;
+        break;
+      }
+    }
+    if(!authbits)
+      return CURLE_NOT_BUILT_IN; /* no supported types left! */
+
+    data->set.httpauth = auth;
+  }
+  break;
+
+#endif   /* CURL_DISABLE_HTTP */
+
+  case CURLOPT_CUSTOMREQUEST:
+    /*
+     * Set a custom string to use as request
+     */
+    result = setstropt(&data->set.str[STRING_CUSTOMREQUEST],
+                       va_arg(param, char *));
+
+    /* we don't set
+       data->set.httpreq = HTTPREQ_CUSTOM;
+       here, we continue as if we were using the already set type
+       and this just changes the actual request keyword */
+    break;
+
+#ifndef CURL_DISABLE_PROXY
+  case CURLOPT_HTTPPROXYTUNNEL:
+    /*
+     * Tunnel operations through the proxy instead of normal proxy use
+     */
+    data->set.tunnel_thru_httpproxy = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+
+  case CURLOPT_PROXYPORT:
+    /*
+     * Explicitly set HTTP proxy port number.
+     */
+    data->set.proxyport = va_arg(param, long);
+    break;
+
+  case CURLOPT_PROXYAUTH:
+    /*
+     * Set HTTP Authentication type BITMASK.
+     */
+  {
+    int bitcheck;
+    bool authbits;
+    unsigned long auth = va_arg(param, unsigned long);
+
+    if(auth == CURLAUTH_NONE) {
+      data->set.proxyauth = auth;
+      break;
+    }
+
+    /* the DIGEST_IE bit is only used to set a special marker, for all the
+       rest we need to handle it as normal DIGEST */
+    data->state.authproxy.iestyle = (auth & CURLAUTH_DIGEST_IE)?TRUE:FALSE;
+
+    if(auth & CURLAUTH_DIGEST_IE) {
+      auth |= CURLAUTH_DIGEST; /* set standard digest bit */
+      auth &= ~CURLAUTH_DIGEST_IE; /* unset ie digest bit */
+    }
+    /* switch off bits we can't support */
+#ifndef USE_NTLM
+    auth &= ~CURLAUTH_NTLM;    /* no NTLM support */
+    auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */
+#elif !defined(NTLM_WB_ENABLED)
+    auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */
+#endif
+#ifndef USE_HTTP_NEGOTIATE
+    auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI or
+                                       WINDOWS_SSPI */
+#endif
+
+    /* check if any auth bit lower than CURLAUTH_ONLY is still set */
+    bitcheck = 0;
+    authbits = FALSE;
+    while(bitcheck < 31) {
+      if(auth & (1UL << bitcheck++)) {
+        authbits = TRUE;
+        break;
+      }
+    }
+    if(!authbits)
+      return CURLE_NOT_BUILT_IN; /* no supported types left! */
+
+    data->set.proxyauth = auth;
+  }
+  break;
+
+  case CURLOPT_PROXY:
+    /*
+     * Set proxy server:port to use as HTTP proxy.
+     *
+     * If the proxy is set to "" we explicitly say that we don't want to use a
+     * proxy (even though there might be environment variables saying so).
+     *
+     * Setting it to NULL, means no proxy but allows the environment variables
+     * to decide for us.
+     */
+    result = setstropt(&data->set.str[STRING_PROXY],
+                       va_arg(param, char *));
+    break;
+
+  case CURLOPT_PROXYTYPE:
+    /*
+     * Set proxy type. HTTP/HTTP_1_0/SOCKS4/SOCKS4a/SOCKS5/SOCKS5_HOSTNAME
+     */
+    data->set.proxytype = (curl_proxytype)va_arg(param, long);
+    break;
+
+  case CURLOPT_PROXY_TRANSFER_MODE:
+    /*
+     * set transfer mode (;type=<a|i>) when doing FTP via an HTTP proxy
+     */
+    switch (va_arg(param, long)) {
+    case 0:
+      data->set.proxy_transfer_mode = FALSE;
+      break;
+    case 1:
+      data->set.proxy_transfer_mode = TRUE;
+      break;
+    default:
+      /* reserve other values for future use */
+      result = CURLE_UNKNOWN_OPTION;
+      break;
+    }
+    break;
+#endif   /* CURL_DISABLE_PROXY */
+
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+  case CURLOPT_SOCKS5_GSSAPI_SERVICE:
+    /*
+     * Set gssapi service name
+     */
+    result = setstropt(&data->set.str[STRING_SOCKS5_GSSAPI_SERVICE],
+                       va_arg(param, char *));
+    break;
+
+  case CURLOPT_SOCKS5_GSSAPI_NEC:
+    /*
+     * set flag for nec socks5 support
+     */
+    data->set.socks5_gssapi_nec = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+#endif
+
+  case CURLOPT_WRITEHEADER:
+    /*
+     * Custom pointer to pass the header write callback function
+     */
+    data->set.writeheader = (void *)va_arg(param, void *);
+    break;
+  case CURLOPT_ERRORBUFFER:
+    /*
+     * Error buffer provided by the caller to get the human readable
+     * error string in.
+     */
+    data->set.errorbuffer = va_arg(param, char *);
+    break;
+  case CURLOPT_FILE:
+    /*
+     * FILE pointer to write to or include in the data write callback
+     */
+    data->set.out = va_arg(param, FILE *);
+    break;
+  case CURLOPT_FTPPORT:
+    /*
+     * Use FTP PORT, this also specifies which IP address to use
+     */
+    result = setstropt(&data->set.str[STRING_FTPPORT],
+                       va_arg(param, char *));
+    data->set.ftp_use_port = (NULL != data->set.str[STRING_FTPPORT]) ?
+                             TRUE:FALSE;
+    break;
+
+  case CURLOPT_FTP_USE_EPRT:
+    data->set.ftp_use_eprt = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+
+  case CURLOPT_FTP_USE_EPSV:
+    data->set.ftp_use_epsv = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+
+  case CURLOPT_FTP_USE_PRET:
+    data->set.ftp_use_pret = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+
+  case CURLOPT_FTP_SSL_CCC:
+    data->set.ftp_ccc = (curl_ftpccc)va_arg(param, long);
+    break;
+
+  case CURLOPT_FTP_SKIP_PASV_IP:
+    /*
+     * Enable or disable FTP_SKIP_PASV_IP, which will disable/enable the
+     * bypass of the IP address in PASV responses.
+     */
+    data->set.ftp_skip_ip = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+
+  case CURLOPT_INFILE:
+    /*
+     * FILE pointer to read the file to be uploaded from. Or possibly
+     * used as argument to the read callback.
+     */
+    data->set.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->set.infilesize = va_arg(param, long);
+    break;
+  case CURLOPT_INFILESIZE_LARGE:
+    /*
+     * If known, this should inform curl about the file size of the
+     * to-be-uploaded file.
+     */
+    data->set.infilesize = va_arg(param, curl_off_t);
+    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->set.low_speed_limit=va_arg(param, long);
+    break;
+  case CURLOPT_MAX_SEND_SPEED_LARGE:
+    /*
+     * When transfer uploads are faster then CURLOPT_MAX_SEND_SPEED_LARGE
+     * bytes per second the transfer is throttled..
+     */
+    data->set.max_send_speed=va_arg(param, curl_off_t);
+    break;
+  case CURLOPT_MAX_RECV_SPEED_LARGE:
+    /*
+     * When receiving data faster than CURLOPT_MAX_RECV_SPEED_LARGE bytes per
+     * second the transfer is throttled..
+     */
+    data->set.max_recv_speed=va_arg(param, curl_off_t);
+    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->set.low_speed_time=va_arg(param, long);
+    break;
+  case CURLOPT_URL:
+    /*
+     * The URL to fetch.
+     */
+    if(data->change.url_alloc) {
+      /* the already set URL is allocated, free it first! */
+      Curl_safefree(data->change.url);
+      data->change.url_alloc = FALSE;
+    }
+    result = setstropt(&data->set.str[STRING_SET_URL],
+                       va_arg(param, char *));
+    data->change.url = data->set.str[STRING_SET_URL];
+    break;
+  case CURLOPT_PORT:
+    /*
+     * The port number to use when getting the URL
+     */
+    data->set.use_port = va_arg(param, long);
+    break;
+  case CURLOPT_TIMEOUT:
+    /*
+     * The maximum time you allow curl to use for a single transfer
+     * operation.
+     */
+    data->set.timeout = va_arg(param, long) * 1000L;
+    break;
+
+  case CURLOPT_TIMEOUT_MS:
+    data->set.timeout = va_arg(param, long);
+    break;
+
+  case CURLOPT_CONNECTTIMEOUT:
+    /*
+     * The maximum time you allow curl to use to connect.
+     */
+    data->set.connecttimeout = va_arg(param, long) * 1000L;
+    break;
+
+  case CURLOPT_CONNECTTIMEOUT_MS:
+    data->set.connecttimeout = va_arg(param, long);
+    break;
+
+  case CURLOPT_ACCEPTTIMEOUT_MS:
+    /*
+     * The maximum time you allow curl to wait for server connect
+     */
+    data->set.accepttimeout = va_arg(param, long);
+    break;
+
+  case CURLOPT_USERPWD:
+    /*
+     * user:password to use in the operation
+     */
+    result = setstropt_userpwd(va_arg(param, char *),
+                               &data->set.str[STRING_USERNAME],
+                               &data->set.str[STRING_PASSWORD]);
+    break;
+  case CURLOPT_USERNAME:
+    /*
+     * authentication user name to use in the operation
+     */
+    result = setstropt(&data->set.str[STRING_USERNAME],
+                       va_arg(param, char *));
+    break;
+  case CURLOPT_PASSWORD:
+    /*
+     * authentication password to use in the operation
+     */
+    result = setstropt(&data->set.str[STRING_PASSWORD],
+                       va_arg(param, char *));
+    break;
+  case CURLOPT_POSTQUOTE:
+    /*
+     * List of RAW FTP commands to use after a transfer
+     */
+    data->set.postquote = va_arg(param, struct curl_slist *);
+    break;
+  case CURLOPT_PREQUOTE:
+    /*
+     * List of RAW FTP commands to use prior to RETR (Wesley Laxton)
+     */
+    data->set.prequote = va_arg(param, struct curl_slist *);
+    break;
+  case CURLOPT_QUOTE:
+    /*
+     * List of RAW FTP commands to use before a transfer
+     */
+    data->set.quote = va_arg(param, struct curl_slist *);
+    break;
+  case CURLOPT_RESOLVE:
+    /*
+     * List of NAME:[address] names to populate the DNS cache with
+     * Prefix the NAME with dash (-) to _remove_ the name from the cache.
+     *
+     * Names added with this API will remain in the cache until explicitly
+     * removed or the handle is cleaned up.
+     *
+     * This API can remove any name from the DNS cache, but only entries
+     * that aren't actually in use right now will be pruned immediately.
+     */
+    data->set.resolve = va_arg(param, struct curl_slist *);
+    data->change.resolve = data->set.resolve;
+    break;
+  case CURLOPT_PROGRESSFUNCTION:
+    /*
+     * Progress callback function
+     */
+    data->set.fprogress = va_arg(param, curl_progress_callback);
+    if(data->set.fprogress)
+      data->progress.callback = TRUE; /* no longer internal */
+    else
+      data->progress.callback = FALSE; /* NULL enforces internal */
+
+    break;
+  case CURLOPT_PROGRESSDATA:
+    /*
+     * Custom client data to pass to the progress callback
+     */
+    data->set.progress_client = va_arg(param, void *);
+    break;
+
+#ifndef CURL_DISABLE_PROXY
+  case CURLOPT_PROXYUSERPWD:
+    /*
+     * user:password needed to use the proxy
+     */
+    result = setstropt_userpwd(va_arg(param, char *),
+                               &data->set.str[STRING_PROXYUSERNAME],
+                               &data->set.str[STRING_PROXYPASSWORD]);
+    break;
+  case CURLOPT_PROXYUSERNAME:
+    /*
+     * authentication user name to use in the operation
+     */
+    result = setstropt(&data->set.str[STRING_PROXYUSERNAME],
+                       va_arg(param, char *));
+    break;
+  case CURLOPT_PROXYPASSWORD:
+    /*
+     * authentication password to use in the operation
+     */
+    result = setstropt(&data->set.str[STRING_PROXYPASSWORD],
+                       va_arg(param, char *));
+    break;
+  case CURLOPT_NOPROXY:
+    /*
+     * proxy exception list
+     */
+    result = setstropt(&data->set.str[STRING_NOPROXY],
+                       va_arg(param, char *));
+    break;
+#endif
+
+  case CURLOPT_RANGE:
+    /*
+     * What range of the file you want to transfer
+     */
+    result = setstropt(&data->set.str[STRING_SET_RANGE],
+                       va_arg(param, char *));
+    break;
+  case CURLOPT_RESUME_FROM:
+    /*
+     * Resume transfer at the give file position
+     */
+    data->set.set_resume_from = va_arg(param, long);
+    break;
+  case CURLOPT_RESUME_FROM_LARGE:
+    /*
+     * Resume transfer at the give file position
+     */
+    data->set.set_resume_from = va_arg(param, curl_off_t);
+    break;
+  case CURLOPT_DEBUGFUNCTION:
+    /*
+     * stderr write callback.
+     */
+    data->set.fdebug = va_arg(param, curl_debug_callback);
+    /*
+     * if the callback provided is NULL, it'll use the default callback
+     */
+    break;
+  case CURLOPT_DEBUGDATA:
+    /*
+     * Set to a void * that should receive all error writes. This
+     * defaults to CURLOPT_STDERR for normal operations.
+     */
+    data->set.debugdata = va_arg(param, void *);
+    break;
+  case CURLOPT_STDERR:
+    /*
+     * Set to a FILE * that should receive all error writes. This
+     * defaults to stderr for normal operations.
+     */
+    data->set.err = va_arg(param, FILE *);
+    if(!data->set.err)
+      data->set.err = stderr;
+    break;
+  case CURLOPT_HEADERFUNCTION:
+    /*
+     * Set header write callback
+     */
+    data->set.fwrite_header = va_arg(param, curl_write_callback);
+    break;
+  case CURLOPT_WRITEFUNCTION:
+    /*
+     * Set data write callback
+     */
+    data->set.fwrite_func = va_arg(param, curl_write_callback);
+    if(!data->set.fwrite_func) {
+      data->set.is_fwrite_set = 0;
+      /* When set to NULL, reset to our internal default function */
+      data->set.fwrite_func = (curl_write_callback)fwrite;
+    }
+    else
+      data->set.is_fwrite_set = 1;
+    break;
+  case CURLOPT_READFUNCTION:
+    /*
+     * Read data callback
+     */
+    data->set.fread_func = va_arg(param, curl_read_callback);
+    if(!data->set.fread_func) {
+      data->set.is_fread_set = 0;
+      /* When set to NULL, reset to our internal default function */
+      data->set.fread_func = (curl_read_callback)fread;
+    }
+    else
+      data->set.is_fread_set = 1;
+    break;
+  case CURLOPT_SEEKFUNCTION:
+    /*
+     * Seek callback. Might be NULL.
+     */
+    data->set.seek_func = va_arg(param, curl_seek_callback);
+    break;
+  case CURLOPT_SEEKDATA:
+    /*
+     * Seek control callback. Might be NULL.
+     */
+    data->set.seek_client = va_arg(param, void *);
+    break;
+  case CURLOPT_CONV_FROM_NETWORK_FUNCTION:
+    /*
+     * "Convert from network encoding" callback
+     */
+    data->set.convfromnetwork = va_arg(param, curl_conv_callback);
+    break;
+  case CURLOPT_CONV_TO_NETWORK_FUNCTION:
+    /*
+     * "Convert to network encoding" callback
+     */
+    data->set.convtonetwork = va_arg(param, curl_conv_callback);
+    break;
+  case CURLOPT_CONV_FROM_UTF8_FUNCTION:
+    /*
+     * "Convert from UTF-8 encoding" callback
+     */
+    data->set.convfromutf8 = va_arg(param, curl_conv_callback);
+    break;
+  case CURLOPT_IOCTLFUNCTION:
+    /*
+     * I/O control callback. Might be NULL.
+     */
+    data->set.ioctl_func = va_arg(param, curl_ioctl_callback);
+    break;
+  case CURLOPT_IOCTLDATA:
+    /*
+     * I/O control data pointer. Might be NULL.
+     */
+    data->set.ioctl_client = va_arg(param, void *);
+    break;
+  case CURLOPT_SSLCERT:
+    /*
+     * String that holds file name of the SSL certificate to use
+     */
+    result = setstropt(&data->set.str[STRING_CERT],
+                       va_arg(param, char *));
+    break;
+  case CURLOPT_SSLCERTTYPE:
+    /*
+     * String that holds file type of the SSL certificate to use
+     */
+    result = setstropt(&data->set.str[STRING_CERT_TYPE],
+                       va_arg(param, char *));
+    break;
+  case CURLOPT_SSLKEY:
+    /*
+     * String that holds file name of the SSL key to use
+     */
+    result = setstropt(&data->set.str[STRING_KEY],
+                       va_arg(param, char *));
+    break;
+  case CURLOPT_SSLKEYTYPE:
+    /*
+     * String that holds file type of the SSL key to use
+     */
+    result = setstropt(&data->set.str[STRING_KEY_TYPE],
+                       va_arg(param, char *));
+    break;
+  case CURLOPT_KEYPASSWD:
+    /*
+     * String that holds the SSL or SSH private key password.
+     */
+    result = setstropt(&data->set.str[STRING_KEY_PASSWD],
+                       va_arg(param, char *));
+    break;
+  case CURLOPT_SSLENGINE:
+    /*
+     * String that holds the SSL crypto engine.
+     */
+    argptr = va_arg(param, char *);
+    if(argptr && argptr[0])
+      result = Curl_ssl_set_engine(data, argptr);
+    break;
+
+  case CURLOPT_SSLENGINE_DEFAULT:
+    /*
+     * flag to set engine as default.
+     */
+    result = Curl_ssl_set_engine_default(data);
+    break;
+  case CURLOPT_CRLF:
+    /*
+     * Kludgy option to enable CRLF conversions. Subject for removal.
+     */
+    data->set.crlf = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+
+  case CURLOPT_INTERFACE:
+    /*
+     * Set what interface or address/hostname to bind the socket to when
+     * performing an operation and thus what from-IP your connection will use.
+     */
+    result = setstropt(&data->set.str[STRING_DEVICE],
+                       va_arg(param, char *));
+    break;
+  case CURLOPT_LOCALPORT:
+    /*
+     * Set what local port to bind the socket to when performing an operation.
+     */
+    data->set.localport = curlx_sltous(va_arg(param, long));
+    break;
+  case CURLOPT_LOCALPORTRANGE:
+    /*
+     * Set number of local ports to try, starting with CURLOPT_LOCALPORT.
+     */
+    data->set.localportrange = curlx_sltosi(va_arg(param, long));
+    break;
+  case CURLOPT_KRBLEVEL:
+    /*
+     * A string that defines the kerberos security level.
+     */
+    result = setstropt(&data->set.str[STRING_KRB_LEVEL],
+                       va_arg(param, char *));
+    data->set.krb = (NULL != data->set.str[STRING_KRB_LEVEL])?TRUE:FALSE;
+    break;
+  case CURLOPT_GSSAPI_DELEGATION:
+    /*
+     * GSSAPI credential delegation
+     */
+    data->set.gssapi_delegation = va_arg(param, long);
+    break;
+  case CURLOPT_SSL_VERIFYPEER:
+    /*
+     * Enable peer SSL verifying.
+     */
+    data->set.ssl.verifypeer = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+  case CURLOPT_SSL_VERIFYHOST:
+    /*
+     * Enable verification of the host name in the peer certificate
+     */
+    arg = va_arg(param, long);
+
+    /* Obviously people are not reading documentation and too many thought
+       this argument took a boolean when it wasn't and misused it. We thus ban
+       1 as a sensible input and we warn about its use. Then we only have the
+       2 action internally stored as TRUE. */
+
+    if(1 == arg) {
+      failf(data, "CURLOPT_SSL_VERIFYHOST no longer supports 1 as value!");
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    }
+
+    data->set.ssl.verifyhost = (0 != arg)?TRUE:FALSE;
+    break;
+#ifdef USE_SSLEAY
+    /* since these two options are only possible to use on an OpenSSL-
+       powered libcurl we #ifdef them on this condition so that libcurls
+       built against other SSL libs will return a proper error when trying
+       to set this option! */
+  case CURLOPT_SSL_CTX_FUNCTION:
+    /*
+     * Set a SSL_CTX callback
+     */
+    data->set.ssl.fsslctx = va_arg(param, curl_ssl_ctx_callback);
+    break;
+  case CURLOPT_SSL_CTX_DATA:
+    /*
+     * Set a SSL_CTX callback parameter pointer
+     */
+    data->set.ssl.fsslctxp = va_arg(param, void *);
+    break;
+  case CURLOPT_CERTINFO:
+    data->set.ssl.certinfo = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+#endif
+  case CURLOPT_CAINFO:
+    /*
+     * Set CA info for SSL connection. Specify file name of the CA certificate
+     */
+    result = setstropt(&data->set.str[STRING_SSL_CAFILE],
+                       va_arg(param, char *));
+    break;
+  case CURLOPT_CAPATH:
+    /*
+     * Set CA path info for SSL connection. Specify directory name of the CA
+     * certificates which have been prepared using openssl c_rehash utility.
+     */
+    /* This does not work on windows. */
+    result = setstropt(&data->set.str[STRING_SSL_CAPATH],
+                       va_arg(param, char *));
+    break;
+  case CURLOPT_CRLFILE:
+    /*
+     * Set CRL file info for SSL connection. Specify file name of the CRL
+     * to check certificates revocation
+     */
+    result = setstropt(&data->set.str[STRING_SSL_CRLFILE],
+                       va_arg(param, char *));
+    break;
+  case CURLOPT_ISSUERCERT:
+    /*
+     * Set Issuer certificate file
+     * to check certificates issuer
+     */
+    result = setstropt(&data->set.str[STRING_SSL_ISSUERCERT],
+                       va_arg(param, char *));
+    break;
+  case CURLOPT_TELNETOPTIONS:
+    /*
+     * Set a linked list of telnet options
+     */
+    data->set.telnet_options = va_arg(param, struct curl_slist *);
+    break;
+
+  case CURLOPT_BUFFERSIZE:
+    /*
+     * The application kindly asks for a differently sized receive buffer.
+     * If it seems reasonable, we'll use it.
+     */
+    data->set.buffer_size = va_arg(param, long);
+
+    if((data->set.buffer_size> (BUFSIZE -1 )) ||
+       (data->set.buffer_size < 1))
+      data->set.buffer_size = 0; /* huge internal default */
+
+    break;
+
+  case CURLOPT_NOSIGNAL:
+    /*
+     * The application asks not to set any signal() or alarm() handlers,
+     * even when using a timeout.
+     */
+    data->set.no_signal = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+
+  case CURLOPT_SHARE:
+  {
+    struct Curl_share *set;
+    set = va_arg(param, struct Curl_share *);
+
+    /* disconnect from old share, if any */
+    if(data->share) {
+      Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
+
+      if(data->dns.hostcachetype == HCACHE_SHARED) {
+        data->dns.hostcache = NULL;
+        data->dns.hostcachetype = HCACHE_NONE;
+      }
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
+      if(data->share->cookies == data->cookies)
+        data->cookies = NULL;
+#endif
+
+      if(data->share->sslsession == data->state.session)
+        data->state.session = NULL;
+
+      data->share->dirty--;
+
+      Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
+      data->share = NULL;
+    }
+
+    /* use new share if it set */
+    data->share = set;
+    if(data->share) {
+
+      Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
+
+      data->share->dirty++;
+
+      if(data->share->hostcache) {
+        /* use shared host cache, first free the private one if any */
+        if(data->dns.hostcachetype == HCACHE_PRIVATE)
+          Curl_hostcache_destroy(data);
+
+        data->dns.hostcache = data->share->hostcache;
+        data->dns.hostcachetype = HCACHE_SHARED;
+      }
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
+      if(data->share->cookies) {
+        /* use shared cookie list, first free own one if any */
+        if(data->cookies)
+          Curl_cookie_cleanup(data->cookies);
+        /* enable cookies since we now use a share that uses cookies! */
+        data->cookies = data->share->cookies;
+      }
+#endif   /* CURL_DISABLE_HTTP */
+      if(data->share->sslsession) {
+        data->set.ssl.max_ssl_sessions = data->share->max_ssl_sessions;
+        data->state.session = data->share->sslsession;
+      }
+      Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
+
+    }
+    /* check for host cache not needed,
+     * it will be done by curl_easy_perform */
+  }
+  break;
+
+  case CURLOPT_PRIVATE:
+    /*
+     * Set private data pointer.
+     */
+    data->set.private_data = va_arg(param, void *);
+    break;
+
+  case CURLOPT_MAXFILESIZE:
+    /*
+     * Set the maximum size of a file to download.
+     */
+    data->set.max_filesize = va_arg(param, long);
+    break;
+
+#ifdef USE_SSL
+  case CURLOPT_USE_SSL:
+    /*
+     * Make transfers attempt to use SSL/TLS.
+     */
+    data->set.use_ssl = (curl_usessl)va_arg(param, long);
+    break;
+
+  case CURLOPT_SSL_OPTIONS:
+    arg = va_arg(param, long);
+    data->set.ssl_enable_beast = arg&CURLSSLOPT_ALLOW_BEAST?TRUE:FALSE;
+    break;
+
+#endif
+  case CURLOPT_FTPSSLAUTH:
+    /*
+     * Set a specific auth for FTP-SSL transfers.
+     */
+    data->set.ftpsslauth = (curl_ftpauth)va_arg(param, long);
+    break;
+
+  case CURLOPT_IPRESOLVE:
+    data->set.ipver = va_arg(param, long);
+    break;
+
+  case CURLOPT_MAXFILESIZE_LARGE:
+    /*
+     * Set the maximum size of a file to download.
+     */
+    data->set.max_filesize = va_arg(param, curl_off_t);
+    break;
+
+  case CURLOPT_TCP_NODELAY:
+    /*
+     * Enable or disable TCP_NODELAY, which will disable/enable the Nagle
+     * algorithm
+     */
+    data->set.tcp_nodelay = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+
+  case CURLOPT_FTP_ACCOUNT:
+    result = setstropt(&data->set.str[STRING_FTP_ACCOUNT],
+                       va_arg(param, char *));
+    break;
+
+  case CURLOPT_IGNORE_CONTENT_LENGTH:
+    data->set.ignorecl = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+
+  case CURLOPT_CONNECT_ONLY:
+    /*
+     * No data transfer, set up connection and let application use the socket
+     */
+    data->set.connect_only = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+
+  case CURLOPT_FTP_ALTERNATIVE_TO_USER:
+    result = setstropt(&data->set.str[STRING_FTP_ALTERNATIVE_TO_USER],
+                       va_arg(param, char *));
+    break;
+
+  case CURLOPT_SOCKOPTFUNCTION:
+    /*
+     * socket callback function: called after socket() but before connect()
+     */
+    data->set.fsockopt = va_arg(param, curl_sockopt_callback);
+    break;
+
+  case CURLOPT_SOCKOPTDATA:
+    /*
+     * socket callback data pointer. Might be NULL.
+     */
+    data->set.sockopt_client = va_arg(param, void *);
+    break;
+
+  case CURLOPT_OPENSOCKETFUNCTION:
+    /*
+     * open/create socket callback function: called instead of socket(),
+     * before connect()
+     */
+    data->set.fopensocket = va_arg(param, curl_opensocket_callback);
+    break;
+
+  case CURLOPT_OPENSOCKETDATA:
+    /*
+     * socket callback data pointer. Might be NULL.
+     */
+    data->set.opensocket_client = va_arg(param, void *);
+    break;
+
+  case CURLOPT_CLOSESOCKETFUNCTION:
+    /*
+     * close socket callback function: called instead of close()
+     * when shutting down a connection
+     */
+    data->set.fclosesocket = va_arg(param, curl_closesocket_callback);
+    break;
+
+  case CURLOPT_CLOSESOCKETDATA:
+    /*
+     * socket callback data pointer. Might be NULL.
+     */
+    data->set.closesocket_client = va_arg(param, void *);
+    break;
+
+  case CURLOPT_SSL_SESSIONID_CACHE:
+    data->set.ssl.sessionid = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+
+#ifdef USE_LIBSSH2
+    /* we only include SSH options if explicitly built to support SSH */
+  case CURLOPT_SSH_AUTH_TYPES:
+    data->set.ssh_auth_types = va_arg(param, long);
+    break;
+
+  case CURLOPT_SSH_PUBLIC_KEYFILE:
+    /*
+     * Use this file instead of the $HOME/.ssh/id_dsa.pub file
+     */
+    result = setstropt(&data->set.str[STRING_SSH_PUBLIC_KEY],
+                       va_arg(param, char *));
+    break;
+
+  case CURLOPT_SSH_PRIVATE_KEYFILE:
+    /*
+     * Use this file instead of the $HOME/.ssh/id_dsa file
+     */
+    result = setstropt(&data->set.str[STRING_SSH_PRIVATE_KEY],
+                       va_arg(param, char *));
+    break;
+  case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5:
+    /*
+     * Option to allow for the MD5 of the host public key to be checked
+     * for validation purposes.
+     */
+    result = setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5],
+                       va_arg(param, char *));
+    break;
+#ifdef HAVE_LIBSSH2_KNOWNHOST_API
+  case CURLOPT_SSH_KNOWNHOSTS:
+    /*
+     * Store the file name to read known hosts from.
+     */
+    result = setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS],
+                       va_arg(param, char *));
+    break;
+
+  case CURLOPT_SSH_KEYFUNCTION:
+    /* setting to NULL is fine since the curl_ssh.c functions themselves will
+       then rever to use the internal default */
+    data->set.ssh_keyfunc = va_arg(param, curl_sshkeycallback);
+    break;
+
+  case CURLOPT_SSH_KEYDATA:
+    /*
+     * Custom client data to pass to the SSH keyfunc callback
+     */
+    data->set.ssh_keyfunc_userp = va_arg(param, void *);
+    break;
+#endif /* HAVE_LIBSSH2_KNOWNHOST_API */
+
+#endif /* USE_LIBSSH2 */
+
+  case CURLOPT_HTTP_TRANSFER_DECODING:
+    /*
+     * disable libcurl transfer encoding is used
+     */
+    data->set.http_te_skip = (0 == va_arg(param, long))?TRUE:FALSE;
+    break;
+
+  case CURLOPT_HTTP_CONTENT_DECODING:
+    /*
+     * raw data passed to the application when content encoding is used
+     */
+    data->set.http_ce_skip = (0 == va_arg(param, long))?TRUE:FALSE;
+    break;
+
+  case CURLOPT_NEW_FILE_PERMS:
+    /*
+     * Uses these permissions instead of 0644
+     */
+    data->set.new_file_perms = va_arg(param, long);
+    break;
+
+  case CURLOPT_NEW_DIRECTORY_PERMS:
+    /*
+     * Uses these permissions instead of 0755
+     */
+    data->set.new_directory_perms = va_arg(param, long);
+    break;
+
+  case CURLOPT_ADDRESS_SCOPE:
+    /*
+     * We always get longs when passed plain numericals, but for this value we
+     * know that an unsigned int will always hold the value so we blindly
+     * typecast to this type
+     */
+    data->set.scope = curlx_sltoui(va_arg(param, long));
+    break;
+
+  case CURLOPT_PROTOCOLS:
+    /* set the bitmask for the protocols that are allowed to be used for the
+       transfer, which thus helps the app which takes URLs from users or other
+       external inputs and want to restrict what protocol(s) to deal
+       with. Defaults to CURLPROTO_ALL. */
+    data->set.allowed_protocols = va_arg(param, long);
+    break;
+
+  case CURLOPT_REDIR_PROTOCOLS:
+    /* set the bitmask for the protocols that libcurl is allowed to follow to,
+       as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs
+       to be set in both bitmasks to be allowed to get redirected to. Defaults
+       to all protocols except FILE and SCP. */
+    data->set.redir_protocols = va_arg(param, long);
+    break;
+
+  case CURLOPT_MAIL_FROM:
+    result = setstropt(&data->set.str[STRING_MAIL_FROM],
+                       va_arg(param, char *));
+    break;
+
+  case CURLOPT_MAIL_AUTH:
+    result = setstropt(&data->set.str[STRING_MAIL_AUTH],
+                       va_arg(param, char *));
+    break;
+
+  case CURLOPT_MAIL_RCPT:
+    /* get a list of mail recipients */
+    data->set.mail_rcpt = va_arg(param, struct curl_slist *);
+    break;
+
+  case CURLOPT_RTSP_REQUEST:
+    {
+      /*
+       * Set the RTSP request method (OPTIONS, SETUP, PLAY, etc...)
+       * Would this be better if the RTSPREQ_* were just moved into here?
+       */
+      long curl_rtspreq = va_arg(param, long);
+      Curl_RtspReq rtspreq = RTSPREQ_NONE;
+      switch(curl_rtspreq) {
+        case CURL_RTSPREQ_OPTIONS:
+          rtspreq = RTSPREQ_OPTIONS;
+          break;
+
+        case CURL_RTSPREQ_DESCRIBE:
+          rtspreq = RTSPREQ_DESCRIBE;
+          break;
+
+        case CURL_RTSPREQ_ANNOUNCE:
+          rtspreq = RTSPREQ_ANNOUNCE;
+          break;
+
+        case CURL_RTSPREQ_SETUP:
+          rtspreq = RTSPREQ_SETUP;
+          break;
+
+        case CURL_RTSPREQ_PLAY:
+          rtspreq = RTSPREQ_PLAY;
+          break;
+
+        case CURL_RTSPREQ_PAUSE:
+          rtspreq = RTSPREQ_PAUSE;
+          break;
+
+        case CURL_RTSPREQ_TEARDOWN:
+          rtspreq = RTSPREQ_TEARDOWN;
+          break;
+
+        case CURL_RTSPREQ_GET_PARAMETER:
+          rtspreq = RTSPREQ_GET_PARAMETER;
+          break;
+
+        case CURL_RTSPREQ_SET_PARAMETER:
+          rtspreq = RTSPREQ_SET_PARAMETER;
+          break;
+
+        case CURL_RTSPREQ_RECORD:
+          rtspreq = RTSPREQ_RECORD;
+          break;
+
+        case CURL_RTSPREQ_RECEIVE:
+          rtspreq = RTSPREQ_RECEIVE;
+          break;
+        default:
+          rtspreq = RTSPREQ_NONE;
+      }
+
+      data->set.rtspreq = rtspreq;
+    break;
+    }
+
+
+  case CURLOPT_RTSP_SESSION_ID:
+    /*
+     * Set the RTSP Session ID manually. Useful if the application is
+     * resuming a previously established RTSP session
+     */
+    result = setstropt(&data->set.str[STRING_RTSP_SESSION_ID],
+                       va_arg(param, char *));
+    break;
+
+  case CURLOPT_RTSP_STREAM_URI:
+    /*
+     * Set the Stream URI for the RTSP request. Unless the request is
+     * for generic server options, the application will need to set this.
+     */
+    result = setstropt(&data->set.str[STRING_RTSP_STREAM_URI],
+                       va_arg(param, char *));
+    break;
+
+  case CURLOPT_RTSP_TRANSPORT:
+    /*
+     * The content of the Transport: header for the RTSP request
+     */
+    result = setstropt(&data->set.str[STRING_RTSP_TRANSPORT],
+                       va_arg(param, char *));
+    break;
+
+  case CURLOPT_RTSP_CLIENT_CSEQ:
+    /*
+     * Set the CSEQ number to issue for the next RTSP request. Useful if the
+     * application is resuming a previously broken connection. The CSEQ
+     * will increment from this new number henceforth.
+     */
+    data->state.rtsp_next_client_CSeq = va_arg(param, long);
+    break;
+
+  case CURLOPT_RTSP_SERVER_CSEQ:
+    /* Same as the above, but for server-initiated requests */
+    data->state.rtsp_next_client_CSeq = va_arg(param, long);
+    break;
+
+  case CURLOPT_INTERLEAVEDATA:
+    data->set.rtp_out = va_arg(param, void *);
+    break;
+  case CURLOPT_INTERLEAVEFUNCTION:
+    /* Set the user defined RTP write function */
+    data->set.fwrite_rtp = va_arg(param, curl_write_callback);
+    break;
+
+  case CURLOPT_WILDCARDMATCH:
+    data->set.wildcardmatch = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+  case CURLOPT_CHUNK_BGN_FUNCTION:
+    data->set.chunk_bgn = va_arg(param, curl_chunk_bgn_callback);
+    break;
+  case CURLOPT_CHUNK_END_FUNCTION:
+    data->set.chunk_end = va_arg(param, curl_chunk_end_callback);
+    break;
+  case CURLOPT_FNMATCH_FUNCTION:
+    data->set.fnmatch = va_arg(param, curl_fnmatch_callback);
+    break;
+  case CURLOPT_CHUNK_DATA:
+    data->wildcard.customptr = va_arg(param, void *);
+    break;
+  case CURLOPT_FNMATCH_DATA:
+    data->set.fnmatch_data = va_arg(param, void *);
+    break;
+#ifdef USE_TLS_SRP
+  case CURLOPT_TLSAUTH_USERNAME:
+    result = setstropt(&data->set.str[STRING_TLSAUTH_USERNAME],
+                       va_arg(param, char *));
+    if(data->set.str[STRING_TLSAUTH_USERNAME] && !data->set.ssl.authtype)
+      data->set.ssl.authtype = CURL_TLSAUTH_SRP; /* default to SRP */
+    break;
+  case CURLOPT_TLSAUTH_PASSWORD:
+    result = setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD],
+                       va_arg(param, char *));
+    if(data->set.str[STRING_TLSAUTH_USERNAME] && !data->set.ssl.authtype)
+      data->set.ssl.authtype = CURL_TLSAUTH_SRP; /* default to SRP */
+    break;
+  case CURLOPT_TLSAUTH_TYPE:
+    if(strnequal((char *)va_arg(param, char *), "SRP", strlen("SRP")))
+      data->set.ssl.authtype = CURL_TLSAUTH_SRP;
+    else
+      data->set.ssl.authtype = CURL_TLSAUTH_NONE;
+    break;
+#endif
+  case CURLOPT_DNS_SERVERS:
+    result = Curl_set_dns_servers(data, va_arg(param, char *));
+    break;
+
+  case CURLOPT_TCP_KEEPALIVE:
+    data->set.tcp_keepalive = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+  case CURLOPT_TCP_KEEPIDLE:
+    data->set.tcp_keepidle = va_arg(param, long);
+    break;
+  case CURLOPT_TCP_KEEPINTVL:
+    data->set.tcp_keepintvl = va_arg(param, long);
+    break;
+
+  default:
+    /* unknown tag and its companion, just ignore: */
+    result = CURLE_UNKNOWN_OPTION;
+    break;
+  }
+
+  return result;
+}
+
+static void conn_free(struct connectdata *conn)
+{
+  if(!conn)
+    return;
+
+  /* possible left-overs from the async name resolvers */
+  Curl_resolver_cancel(conn);
+
+  /* close the SSL stuff before we close any sockets since they will/may
+     write to the sockets */
+  Curl_ssl_close(conn, FIRSTSOCKET);
+  Curl_ssl_close(conn, SECONDARYSOCKET);
+
+  /* close possibly still open sockets */
+  if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
+    Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
+  if(CURL_SOCKET_BAD != conn->sock[FIRSTSOCKET])
+    Curl_closesocket(conn, conn->sock[FIRSTSOCKET]);
+
+#if defined(USE_NTLM) && defined(NTLM_WB_ENABLED)
+  Curl_ntlm_wb_cleanup(conn);
+#endif
+
+  Curl_safefree(conn->user);
+  Curl_safefree(conn->passwd);
+  Curl_safefree(conn->proxyuser);
+  Curl_safefree(conn->proxypasswd);
+  Curl_safefree(conn->allocptr.proxyuserpwd);
+  Curl_safefree(conn->allocptr.uagent);
+  Curl_safefree(conn->allocptr.userpwd);
+  Curl_safefree(conn->allocptr.accept_encoding);
+  Curl_safefree(conn->allocptr.te);
+  Curl_safefree(conn->allocptr.rangeline);
+  Curl_safefree(conn->allocptr.ref);
+  Curl_safefree(conn->allocptr.host);
+  Curl_safefree(conn->allocptr.cookiehost);
+  Curl_safefree(conn->allocptr.rtsp_transport);
+  Curl_safefree(conn->trailer);
+  Curl_safefree(conn->host.rawalloc); /* host name buffer */
+  Curl_safefree(conn->proxy.rawalloc); /* proxy name buffer */
+  Curl_safefree(conn->master_buffer);
+
+  Curl_llist_destroy(conn->send_pipe, NULL);
+  Curl_llist_destroy(conn->recv_pipe, NULL);
+  Curl_llist_destroy(conn->pend_pipe, NULL);
+  Curl_llist_destroy(conn->done_pipe, NULL);
+
+  conn->send_pipe = NULL;
+  conn->recv_pipe = NULL;
+  conn->pend_pipe = NULL;
+  conn->done_pipe = NULL;
+
+  Curl_safefree(conn->localdev);
+  Curl_free_ssl_config(&conn->ssl_config);
+
+  free(conn); /* free all the connection oriented data */
+}
+
+CURLcode Curl_disconnect(struct connectdata *conn, bool dead_connection)
+{
+  struct SessionHandle *data;
+  if(!conn)
+    return CURLE_OK; /* this is closed and fine already */
+  data = conn->data;
+
+  if(!data) {
+    DEBUGF(fprintf(stderr, "DISCONNECT without easy handle, ignoring\n"));
+    return CURLE_OK;
+  }
+
+  if(conn->dns_entry != NULL) {
+    Curl_resolv_unlock(data, conn->dns_entry);
+    conn->dns_entry = NULL;
+  }
+
+  Curl_hostcache_prune(data); /* kill old DNS cache entries */
+
+  {
+    int has_host_ntlm = (conn->ntlm.state != NTLMSTATE_NONE);
+    int has_proxy_ntlm = (conn->proxyntlm.state != NTLMSTATE_NONE);
+
+    /* Authentication data is a mix of connection-related and sessionhandle-
+       related stuff. NTLM is connection-related so when we close the shop
+       we shall forget. */
+
+    if(has_host_ntlm) {
+      data->state.authhost.done = FALSE;
+      data->state.authhost.picked =
+        data->state.authhost.want;
+    }
+
+    if(has_proxy_ntlm) {
+      data->state.authproxy.done = FALSE;
+      data->state.authproxy.picked =
+        data->state.authproxy.want;
+    }
+
+    if(has_host_ntlm || has_proxy_ntlm) {
+      data->state.authproblem = FALSE;
+
+      Curl_http_ntlm_cleanup(conn);
+    }
+  }
+
+  /* Cleanup possible redirect junk */
+  if(data->req.newurl) {
+    free(data->req.newurl);
+    data->req.newurl = NULL;
+  }
+
+  if(conn->handler->disconnect)
+    /* This is set if protocol-specific cleanups should be made */
+    conn->handler->disconnect(conn, dead_connection);
+
+    /* unlink ourselves! */
+  infof(data, "Closing connection %d\n", conn->connection_id);
+  Curl_conncache_remove_conn(data->state.conn_cache, conn);
+
+#if defined(USE_LIBIDN)
+  if(conn->host.encalloc)
+    idn_free(conn->host.encalloc); /* encoded host name buffer, must be freed
+                                      with idn_free() since this was allocated
+                                      by libidn */
+  if(conn->proxy.encalloc)
+    idn_free(conn->proxy.encalloc); /* encoded proxy name buffer, must be
+                                       freed with idn_free() since this was
+                                       allocated by libidn */
+#elif defined(USE_WIN32_IDN)
+  free(conn->host.encalloc); /* encoded host name buffer, must be freed with
+                                idn_free() since this was allocated by
+                                curl_win32_idn_to_ascii */
+  if(conn->proxy.encalloc)
+    free(conn->proxy.encalloc); /* encoded proxy name buffer, must be freed
+                                   with idn_free() since this was allocated by
+                                   curl_win32_idn_to_ascii */
+#endif
+
+  Curl_ssl_close(conn, FIRSTSOCKET);
+
+  /* Indicate to all handles on the pipe that we're dead */
+  if(Curl_isPipeliningEnabled(data)) {
+    signalPipeClose(conn->send_pipe, TRUE);
+    signalPipeClose(conn->recv_pipe, TRUE);
+    signalPipeClose(conn->pend_pipe, TRUE);
+    signalPipeClose(conn->done_pipe, FALSE);
+  }
+
+  conn_free(conn);
+  data->state.current_conn = NULL;
+  Curl_speedinit(data);
+
+  return CURLE_OK;
+}
+
+/*
+ * 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(curl_socket_t sock)
+{
+  int sval;
+  bool ret_val = TRUE;
+
+  sval = Curl_socket_ready(sock, CURL_SOCKET_BAD, 0);
+  if(sval == 0)
+    /* timeout */
+    ret_val = FALSE;
+
+  return ret_val;
+}
+
+static bool IsPipeliningPossible(const struct SessionHandle *handle,
+                                 const struct connectdata *conn)
+{
+  if((conn->handler->protocol & CURLPROTO_HTTP) &&
+     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;
+}
+
+bool Curl_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 *pipeline)
+{
+  if(!Curl_llist_insert_next(pipeline, pipeline->tail, data))
+    return CURLE_OUT_OF_MEMORY;
+  return CURLE_OK;
+}
+
+int Curl_removeHandleFromPipeline(struct SessionHandle *handle,
+                                  struct curl_llist *pipeline)
+{
+  struct curl_llist_element *curr;
+
+  curr = pipeline->head;
+  while(curr) {
+    if(curr->ptr == handle) {
+      Curl_llist_remove(pipeline, curr, NULL);
+      return 1; /* we removed a handle */
+    }
+    curr = curr->next;
+  }
+
+  return 0;
+}
+
+#if 0 /* this code is saved here as it is useful for debugging purposes */
+static void Curl_printPipeline(struct curl_llist *pipeline)
+{
+  struct curl_llist_element *curr;
+
+  curr = pipeline->head;
+  while(curr) {
+    struct SessionHandle *data = (struct SessionHandle *) curr->ptr;
+    infof(data, "Handle in pipeline: %s\n", data->state.path);
+    curr = curr->next;
+  }
+}
+#endif
+
+static struct SessionHandle* gethandleathead(struct curl_llist *pipeline)
+{
+  struct curl_llist_element *curr = pipeline->head;
+  if(curr) {
+    return (struct SessionHandle *) curr->ptr;
+  }
+
+  return NULL;
+}
+
+/* remove the specified connection from all (possible) pipelines and related
+   queues */
+void Curl_getoff_all_pipelines(struct SessionHandle *data,
+                               struct connectdata *conn)
+{
+  bool recv_head = (conn->readchannel_inuse &&
+    (gethandleathead(conn->recv_pipe) == data)) ? TRUE : FALSE;
+
+  bool send_head = (conn->writechannel_inuse &&
+    (gethandleathead(conn->send_pipe) == data)) ? TRUE : FALSE;
+
+  if(Curl_removeHandleFromPipeline(data, conn->recv_pipe) && recv_head)
+    conn->readchannel_inuse = FALSE;
+  if(Curl_removeHandleFromPipeline(data, conn->send_pipe) && send_head)
+    conn->writechannel_inuse = FALSE;
+  Curl_removeHandleFromPipeline(data, conn->pend_pipe);
+  Curl_removeHandleFromPipeline(data, conn->done_pipe);
+}
+
+static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke)
+{
+  struct curl_llist_element *curr;
+
+  if(!pipeline)
+    return;
+
+  curr = pipeline->head;
+  while(curr) {
+    struct curl_llist_element *next = curr->next;
+    struct SessionHandle *data = (struct SessionHandle *) curr->ptr;
+
+#ifdef DEBUGBUILD /* debug-only code */
+    if(data->magic != CURLEASY_MAGIC_NUMBER) {
+      /* MAJOR BADNESS */
+      infof(data, "signalPipeClose() found BAAD easy handle\n");
+    }
+#endif
+
+    if(pipe_broke)
+      data->state.pipe_broke = TRUE;
+    Curl_multi_handlePipeBreak(data);
+    Curl_llist_remove(pipeline, curr, NULL);
+    curr = next;
+  }
+}
+
+
+/*
+ * 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.
+ */
+static bool
+ConnectionExists(struct SessionHandle *data,
+                 struct connectdata *needle,
+                 struct connectdata **usethis)
+{
+  struct connectdata *check;
+  struct connectdata *chosen = 0;
+  bool canPipeline = IsPipeliningPossible(data, needle);
+  bool wantNTLM = (data->state.authhost.want==CURLAUTH_NTLM) ||
+                  (data->state.authhost.want==CURLAUTH_NTLM_WB) ? TRUE : FALSE;
+  struct connectbundle *bundle;
+
+  /* Look up the bundle with all the connections to this
+     particular host */
+  bundle = Curl_conncache_find_bundle(data->state.conn_cache,
+                                      needle->host.name);
+  if(bundle) {
+    struct curl_llist_element *curr;
+
+    infof(data, "Found bundle for host %s: %p\n", needle->host.name, bundle);
+
+    curr = bundle->conn_list->head;
+    while(curr) {
+      bool match = FALSE;
+      bool credentialsMatch = FALSE;
+      size_t pipeLen;
+
+      /*
+       * Note that if we use a HTTP proxy, we check connections to that
+       * proxy and not to the actual remote server.
+       */
+      check = curr->ptr;
+      curr = curr->next;
+
+      pipeLen = check->send_pipe->size + check->recv_pipe->size;
+
+      if(!pipeLen && !check->inuse) {
+        /* The check for a dead socket makes sense only if there are no
+           handles in pipeline and the connection isn't already marked in
+           use */
+        bool dead;
+        if(check->handler->protocol & CURLPROTO_RTSP)
+          /* RTSP is a special case due to RTP interleaving */
+          dead = Curl_rtsp_connisdead(check);
+        else
+          dead = SocketIsDead(check->sock[FIRSTSOCKET]);
+
+        if(dead) {
+          check->data = data;
+          infof(data, "Connection %d seems to be dead!\n",
+                check->connection_id);
+
+          /* disconnect resources */
+          Curl_disconnect(check, /* dead_connection */ TRUE);
+          continue;
+        }
+      }
+
+      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, check))
+            continue;
+        }
+        else if(rh) {
+          if(!IsPipeliningPossible(rh, check))
+            continue;
+        }
+#ifdef DEBUGBUILD
+      if(pipeLen > MAX_PIPELINE_LENGTH) {
+        infof(data, "BAD! Connection #%ld has too big pipeline!\n",
+              check->connection_id);
+      }
+#endif
+      }
+      else {
+        if(pipeLen > 0) {
+          /* can only happen within multi handles, and means that another easy
+             handle is using this connection */
+          continue;
+        }
+
+        if(Curl_resolver_asynch()) {
+          /* ip_addr_str[0] is NUL only if the resolving of the name hasn't
+             completed yet and until then we don't re-use this connection */
+          if(!check->ip_addr_str[0]) {
+            infof(data,
+                  "Connection #%ld is still name resolving, can't reuse\n",
+                  check->connection_id);
+            continue;
+          }
+        }
+
+        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->connection_id);
+#ifdef DEBUGBUILD
+          if(check->recv_pipe->size > 0) {
+            infof(data,
+                  "BAD! Unconnected #%ld has a non-empty recv pipeline!\n",
+                  check->connection_id);
+          }
+#endif
+          continue;
+        }
+      }
+
+      if((needle->handler->flags&PROTOPT_SSL) !=
+         (check->handler->flags&PROTOPT_SSL))
+        /* don't do mixed SSL and non-SSL connections */
+        if(!(needle->handler->protocol & check->handler->protocol))
+          /* except protocols that have been upgraded via TLS */
+          continue;
+
+      if(needle->handler->flags&PROTOPT_SSL) {
+        if((data->set.ssl.verifypeer != check->verifypeer) ||
+           (data->set.ssl.verifyhost != check->verifyhost))
+          continue;
+      }
+
+      if(needle->bits.proxy != check->bits.proxy)
+        /* don't do mixed proxy and non-proxy connections */
+        continue;
+
+      if(!canPipeline && check->inuse)
+        /* this request can't be pipelined but the checked connection is
+           already in use so we skip it */
+        continue;
+
+      if(needle->localdev || needle->localport) {
+        /* If we are bound to a specific local end (IP+port), we must not
+           re-use a random other one, although if we didn't ask for a
+           particular one we can reuse one that was bound.
+
+           This comparison is a bit rough and too strict. Since the input
+           parameters can be specified in numerous ways and still end up the
+           same it would take a lot of processing to make it really accurate.
+           Instead, this matching will assume that re-uses of bound connections
+           will most likely also re-use the exact same binding parameters and
+           missing out a few edge cases shouldn't hurt anyone very much.
+        */
+        if((check->localport != needle->localport) ||
+           (check->localportrange != needle->localportrange) ||
+           !check->localdev ||
+           !needle->localdev ||
+           strcmp(check->localdev, needle->localdev))
+          continue;
+      }
+
+      if(!needle->bits.httpproxy || needle->handler->flags&PROTOPT_SSL ||
+         (needle->bits.httpproxy && check->bits.httpproxy &&
+          needle->bits.tunnel_proxy && check->bits.tunnel_proxy &&
+          Curl_raw_equal(needle->proxy.name, check->proxy.name) &&
+          (needle->port == check->port))) {
+        /* The requested connection does not use a HTTP proxy or it uses SSL or
+           it is a non-SSL protocol tunneled over the same http proxy name and
+           port number or it is a non-SSL protocol which is allowed to be
+           upgraded via TLS */
+
+        if((Curl_raw_equal(needle->handler->scheme, check->handler->scheme) ||
+            needle->handler->protocol & check->handler->protocol) &&
+           Curl_raw_equal(needle->host.name, check->host.name) &&
+           needle->remote_port == check->remote_port) {
+          if(needle->handler->flags & PROTOPT_SSL) {
+            /* This is a SSL connection so verify that we're using the same
+               SSL options as well */
+            if(!Curl_ssl_config_matches(&needle->ssl_config,
+                                        &check->ssl_config)) {
+              DEBUGF(infof(data,
+                           "Connection #%ld has different SSL parameters, "
+                           "can't reuse\n",
+                           check->connection_id));
+              continue;
+            }
+            else if(check->ssl[FIRSTSOCKET].state != ssl_connection_complete) {
+              DEBUGF(infof(data,
+                           "Connection #%ld has not started SSL connect, "
+                           "can't reuse\n",
+                           check->connection_id));
+              continue;
+            }
+          }
+          if((needle->handler->protocol & CURLPROTO_FTP) ||
+             ((needle->handler->protocol & CURLPROTO_HTTP) && wantNTLM)) {
+            /* 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;
+            }
+            credentialsMatch = TRUE;
+          }
+          match = TRUE;
+        }
+      }
+      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) &&
+           (needle->bits.tunnel_proxy == check->bits.tunnel_proxy) &&
+           Curl_raw_equal(needle->proxy.name, check->proxy.name) &&
+           needle->port == check->port) {
+          /* This is the same proxy connection, use it! */
+          match = TRUE;
+        }
+      }
+
+      if(match) {
+        chosen = check;
+
+        /* If we are not looking for an NTLM connection, we can choose this one
+           immediately. */
+        if(!wantNTLM)
+          break;
+
+        /* Otherwise, check if this is already authenticating with the right
+           credentials. If not, keep looking so that we can reuse NTLM
+           connections if possible. (Especially we must reuse the same
+           connection if partway through a handshake!) */
+        if(credentialsMatch && chosen->ntlm.state != NTLMSTATE_NONE)
+          break;
+      }
+    }
+  }
+
+  if(chosen) {
+    chosen->inuse = TRUE; /* mark this as being in use so that no other
+                            handle in a multi stack may nick it */
+    *usethis = chosen;
+    return TRUE; /* yes, we found one to use! */
+  }
+
+  return FALSE; /* no matching connecting exists */
+}
+
+/*
+ * This function kills and removes an existing connection in the connection
+ * cache. The connection that has been unused for the longest time.
+ *
+ * Returns FALSE if it can't find any unused connection to kill.
+ */
+static bool
+ConnectionKillOne(struct SessionHandle *data)
+{
+  struct conncache *bc = data->state.conn_cache;
+  struct curl_hash_iterator iter;
+  struct curl_llist_element *curr;
+  struct curl_hash_element *he;
+  long highscore=-1;
+  long score;
+  struct timeval now;
+  struct connectdata *conn_candidate = NULL;
+  struct connectbundle *bundle;
+
+  now = Curl_tvnow();
+
+  Curl_hash_start_iterate(bc->hash, &iter);
+
+  he = Curl_hash_next_element(&iter);
+  while(he) {
+    struct connectdata *conn;
+
+    bundle = he->ptr;
+
+    curr = bundle->conn_list->head;
+    while(curr) {
+      conn = curr->ptr;
+
+      if(!conn->inuse) {
+        /* Set higher score for the age passed since the connection was used */
+        score = Curl_tvdiff(now, conn->now);
+
+        if(score > highscore) {
+          highscore = score;
+          conn_candidate = conn;
+        }
+      }
+      curr = curr->next;
+    }
+
+    he = Curl_hash_next_element(&iter);
+  }
+
+  if(conn_candidate) {
+    /* Set the connection's owner correctly */
+    conn_candidate->data = data;
+
+    bundle = conn_candidate->bundle;
+
+    /* the winner gets the honour of being disconnected */
+    (void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE);
+
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+/* this connection can now be marked 'idle' */
+static void
+ConnectionDone(struct connectdata *conn)
+{
+  conn->inuse = FALSE;
+}
+
+/*
+ * The given input connection struct pointer is to be stored in the connection
+ * cache. If the cache is already full, least interesting existing connection
+ * (if any) gets closed.
+ *
+ * The given connection should be unique. That must've been checked prior to
+ * this call.
+ */
+static CURLcode ConnectionStore(struct SessionHandle *data,
+                                struct connectdata *conn)
+{
+  static int connection_id_counter = 0;
+
+  CURLcode result;
+
+  /* Assign a number to the connection for easier tracking in the log
+     output */
+  conn->connection_id = connection_id_counter++;
+
+  result = Curl_conncache_add_conn(data->state.conn_cache, conn);
+  if(result != CURLE_OK)
+    conn->connection_id = -1;
+
+  return result;
+}
+
+/* after a TCP connection to the proxy has been verified, this function does
+   the next magic step.
+
+   Note: this function's sub-functions call failf()
+
+*/
+CURLcode Curl_connected_proxy(struct connectdata *conn)
+{
+  switch(conn->proxytype) {
+#ifndef CURL_DISABLE_PROXY
+  case CURLPROXY_SOCKS5:
+  case CURLPROXY_SOCKS5_HOSTNAME:
+    return Curl_SOCKS5(conn->proxyuser, conn->proxypasswd,
+                       conn->host.name, conn->remote_port,
+                       FIRSTSOCKET, conn);
+
+  case CURLPROXY_SOCKS4:
+    return Curl_SOCKS4(conn->proxyuser, conn->host.name,
+                       conn->remote_port, FIRSTSOCKET, conn, FALSE);
+
+  case CURLPROXY_SOCKS4A:
+    return Curl_SOCKS4(conn->proxyuser, conn->host.name,
+                       conn->remote_port, FIRSTSOCKET, conn, TRUE);
+
+#endif /* CURL_DISABLE_PROXY */
+  case CURLPROXY_HTTP:
+  case CURLPROXY_HTTP_1_0:
+    /* do nothing here. handled later. */
+    break;
+  default:
+    break;
+  } /* switch proxytype */
+
+  return CURLE_OK;
+}
+
+static CURLcode ConnectPlease(struct SessionHandle *data,
+                              struct connectdata *conn,
+                              bool *connected)
+{
+  CURLcode result;
+  Curl_addrinfo *addr;
+#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 %ld (#%ld)\n",
+        conn->bits.proxy?"proxy ":"",
+        hostname, conn->port, conn->connection_id);
+#else
+  (void)data;
+#endif
+
+  /*************************************************************
+   * Connect to server/proxy
+   *************************************************************/
+  result= Curl_connecthost(conn,
+                           conn->dns_entry,
+                           &conn->sock[FIRSTSOCKET],
+                           &addr,
+                           connected);
+  if(CURLE_OK == result) {
+    /* All is cool, we store the current information */
+    conn->ip_addr = addr;
+
+    if(*connected) {
+      result = Curl_connected_proxy(conn);
+      if(!result) {
+        conn->bits.tcpconnect[FIRSTSOCKET] = TRUE;
+        Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */
+      }
+    }
+  }
+
+  if(result)
+    *connected = FALSE; /* mark it as not connected */
+
+  return result;
+}
+
+/*
+ * verboseconnect() displays verbose information after a connect
+ */
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+void Curl_verboseconnect(struct connectdata *conn)
+{
+  if(conn->data->set.verbose)
+    infof(conn->data, "Connected to %s (%s) port %ld (#%ld)\n",
+          conn->bits.proxy ? conn->proxy.dispname : conn->host.dispname,
+          conn->ip_addr_str, conn->port, conn->connection_id);
+}
+#endif
+
+int Curl_protocol_getsock(struct connectdata *conn,
+                          curl_socket_t *socks,
+                          int numsocks)
+{
+  if(conn->handler->proto_getsock)
+    return conn->handler->proto_getsock(conn, socks, numsocks);
+  return GETSOCK_BLANK;
+}
+
+int Curl_doing_getsock(struct connectdata *conn,
+                       curl_socket_t *socks,
+                       int numsocks)
+{
+  if(conn && conn->handler->doing_getsock)
+    return conn->handler->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->handler->connecting) {
+    *done = FALSE;
+    result = conn->handler->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->handler->doing) {
+    *done = FALSE;
+    result = conn->handler->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)
+{
+  CURLcode result=CURLE_OK;
+  struct SessionHandle *data = conn->data;
+
+  *protocol_done = FALSE;
+
+  if(conn->bits.tcpconnect[FIRSTSOCKET] && 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->handler->connecting)
+      *protocol_done = TRUE;
+
+    return CURLE_OK;
+  }
+
+  Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */
+  Curl_verboseconnect(conn);
+
+  if(!conn->bits.protoconnstart) {
+
+    /* 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();
+
+    result = Curl_proxy_connect(conn);
+    if(result)
+      return result;
+
+    if(conn->handler->connect_it) {
+      /* is there a protocol-specific connect() procedure? */
+
+      /* Call the protocol-specific connect function */
+      result = conn->handler->connect_it(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;
+  }
+
+  return result; /* pass back status */
+}
+
+/*
+ * Helpers for IDNA convertions.
+ */
+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;
+}
+
+#ifdef USE_LIBIDN
+/*
+ * Check if characters in hostname is allowed in Top Level Domain.
+ */
+static bool tld_check_name(struct SessionHandle *data,
+                           const char *ace_hostname)
+{
+  size_t err_pos;
+  char *uc_name = NULL;
+  int rc;
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+  const char *tld_errmsg = "<no msg>";
+#else
+  (void)data;
+#endif
+
+  /* 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)
+    return FALSE;
+
+  rc = tld_check_lz(uc_name, &err_pos, NULL);
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+#ifdef HAVE_TLD_STRERROR
+  if(rc != TLD_SUCCESS)
+    tld_errmsg = tld_strerror((Tld_rc)rc);
+#endif
+  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);
+  else if(rc != TLD_SUCCESS)
+    infof(data, "WARNING: TLD check for %s failed; %s\n",
+          uc_name, tld_errmsg);
+#endif /* CURL_DISABLE_VERBOSE_STRINGS */
+  if(uc_name)
+     idn_free(uc_name);
+  if(rc != TLD_SUCCESS)
+    return FALSE;
+
+  return TRUE;
+}
+#endif
+
+/*
+ * Perform any necessary IDN conversion of hostname
+ */
+static void fix_hostname(struct SessionHandle *data,
+                         struct connectdata *conn, struct hostname *host)
+{
+#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 */
+  host->dispname = host->name;
+  if(!is_ASCII_name(host->name)) {
+#ifdef USE_LIBIDN
+  /*************************************************************
+   * Check name for non-ASCII and convert hostname to ACE form.
+   *************************************************************/
+  if(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));
+    else {
+      /* tld_check_name() displays a warning if the host name contains
+         "illegal" characters for this TLD */
+      (void)tld_check_name(data, ace_hostname);
+
+      host->encalloc = ace_hostname;
+      /* change the name pointer to point to the encoded hostname */
+      host->name = host->encalloc;
+    }
+  }
+#elif defined(USE_WIN32_IDN)
+  /*************************************************************
+   * Check name for non-ASCII and convert hostname to ACE form.
+   *************************************************************/
+    char *ace_hostname = NULL;
+    int rc = curl_win32_idn_to_ascii(host->name, &ace_hostname);
+    if(rc == 0)
+      infof(data, "Failed to convert %s to ACE;\n",
+            host->name);
+    else {
+      host->encalloc = ace_hostname;
+      /* change the name pointer to point to the encoded hostname */
+      host->name = host->encalloc;
+    }
+#else
+    infof(data, "IDN support not present, can't parse Unicode domains\n");
+#endif
+  }
+}
+
+static void llist_dtor(void *user, void *element)
+{
+  (void)user;
+  (void)element;
+  /* Do nothing */
+}
+
+/*
+ * Allocate and initialize a new connectdata object.
+ */
+static struct connectdata *allocate_conn(struct SessionHandle *data)
+{
+  struct connectdata *conn = calloc(1, sizeof(struct connectdata));
+  if(!conn)
+    return NULL;
+
+  conn->handler = &Curl_handler_dummy;  /* Be sure we have a handler defined
+                                           already from start to avoid NULL
+                                           situations and checks */
+
+  /* and we setup a few fields in case we end up actually using this struct */
+
+  conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD;     /* no file descriptor */
+  conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
+  conn->connection_id = -1;    /* no ID */
+  conn->port = -1; /* unknown at this point */
+
+  /* Default protocol-independent behavior doesn't support persistent
+     connections, so we set this to force-close. Protocols that support
+     this need to set this to FALSE in their "curl_do" functions. */
+  conn->bits.close = TRUE;
+
+  /* Store creation time to help future close decision making */
+  conn->created = Curl_tvnow();
+
+  conn->data = data; /* Setup the association between this connection
+                        and the SessionHandle */
+
+  conn->proxytype = data->set.proxytype; /* type */
+
+#ifdef CURL_DISABLE_PROXY
+
+  conn->bits.proxy = FALSE;
+  conn->bits.httpproxy = FALSE;
+  conn->bits.proxy_user_passwd = FALSE;
+  conn->bits.tunnel_proxy = FALSE;
+
+#else /* CURL_DISABLE_PROXY */
+
+  /* note that these two proxy bits are now just on what looks to be
+     requested, they may be altered down the road */
+  conn->bits.proxy = (data->set.str[STRING_PROXY] &&
+                      *data->set.str[STRING_PROXY])?TRUE:FALSE;
+  conn->bits.httpproxy = (conn->bits.proxy &&
+                          (conn->proxytype == CURLPROXY_HTTP ||
+                           conn->proxytype == CURLPROXY_HTTP_1_0))?TRUE:FALSE;
+  conn->bits.proxy_user_passwd =
+    (NULL != data->set.str[STRING_PROXYUSERNAME])?TRUE:FALSE;
+  conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy;
+
+#endif /* CURL_DISABLE_PROXY */
+
+  conn->bits.user_passwd = (NULL != data->set.str[STRING_USERNAME])?TRUE:FALSE;
+  conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
+  conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;
+
+  conn->verifypeer = data->set.ssl.verifypeer;
+  conn->verifyhost = data->set.ssl.verifyhost;
+
+  conn->ip_version = data->set.ipver;
+
+#if defined(USE_NTLM) && defined(NTLM_WB_ENABLED)
+  conn->ntlm_auth_hlpr_socket = CURL_SOCKET_BAD;
+  conn->ntlm_auth_hlpr_pid = 0;
+  conn->challenge_header = NULL;
+  conn->response_header = NULL;
+#endif
+
+  if(data->multi && Curl_multi_canPipeline(data->multi) &&
+      !conn->master_buffer) {
+    /* Allocate master_buffer to be used for pipelining */
+    conn->master_buffer = calloc(BUFSIZE, sizeof (char));
+    if(!conn->master_buffer)
+      goto error;
+  }
+
+  /* Initialize the pipeline lists */
+  conn->send_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
+  conn->recv_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
+  conn->pend_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
+  conn->done_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
+  if(!conn->send_pipe || !conn->recv_pipe || !conn->pend_pipe ||
+     !conn->done_pipe)
+    goto error;
+
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+  conn->data_prot = PROT_CLEAR;
+#endif
+
+  /* Store the local bind parameters that will be used for this connection */
+  if(data->set.str[STRING_DEVICE]) {
+    conn->localdev = strdup(data->set.str[STRING_DEVICE]);
+    if(!conn->localdev)
+      goto error;
+  }
+  conn->localportrange = data->set.localportrange;
+  conn->localport = data->set.localport;
+
+  /* the close socket stuff needs to be copied to the connection struct as
+     it may live on without (this specific) SessionHandle */
+  conn->fclosesocket = data->set.fclosesocket;
+  conn->closesocket_client = data->set.closesocket_client;
+
+  return conn;
+  error:
+
+  Curl_llist_destroy(conn->send_pipe, NULL);
+  Curl_llist_destroy(conn->recv_pipe, NULL);
+  Curl_llist_destroy(conn->pend_pipe, NULL);
+  Curl_llist_destroy(conn->done_pipe, NULL);
+
+  conn->send_pipe = NULL;
+  conn->recv_pipe = NULL;
+  conn->pend_pipe = NULL;
+  conn->done_pipe = NULL;
+
+  Curl_safefree(conn->master_buffer);
+  Curl_safefree(conn->localdev);
+  Curl_safefree(conn);
+  return NULL;
+}
+
+static CURLcode findprotocol(struct SessionHandle *data,
+                             struct connectdata *conn,
+                             const char *protostr)
+{
+  const struct Curl_handler * const *pp;
+  const struct Curl_handler *p;
+
+  /* Scan protocol handler table and match against 'protostr' to set a few
+     variables based on the URL. Now that the handler may be changed later
+     when the protocol specific setup function is called. */
+  for(pp = protocols; (p = *pp) != NULL; pp++) {
+    if(Curl_raw_equal(p->scheme, protostr)) {
+      /* Protocol found in table. Check if allowed */
+      if(!(data->set.allowed_protocols & p->protocol))
+        /* nope, get out */
+        break;
+
+      /* it is allowed for "normal" request, now do an extra check if this is
+         the result of a redirect */
+      if(data->state.this_is_a_follow &&
+         !(data->set.redir_protocols & p->protocol))
+        /* nope, get out */
+        break;
+
+      /* Perform setup complement if some. */
+      conn->handler = conn->given = p;
+
+      /* 'port' and 'remote_port' are set in setup_connection_internals() */
+      return CURLE_OK;
+    }
+  }
+
+
+  /* The protocol was not found in the table, but we don't have to assign it
+     to anything since it is already assigned to a dummy-struct in the
+     create_conn() function when the connectdata struct is allocated. */
+  failf(data, "Protocol %s not supported or disabled in " LIBCURL_NAME,
+        protostr);
+
+  return CURLE_UNSUPPORTED_PROTOCOL;
+}
+
+/*
+ * Parse URL and fill in the relevant members of the connection struct.
+ */
+static CURLcode parseurlandfillconn(struct SessionHandle *data,
+                                    struct connectdata *conn,
+                                    bool *prot_missing,
+                                    char *user,
+                                    char *passwd)
+{
+  char *at;
+  char *fragment;
+  char *path = data->state.path;
+  char *query;
+  int rc;
+  char protobuf[16];
+  const char *protop;
+  CURLcode result;
+
+  *prot_missing = FALSE;
+
+  /*************************************************************
+   * 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]",
+                  protobuf, path)) &&
+     Curl_raw_equal(protobuf, "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);
+    }
+    /*
+     * 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
+     */
+    if(path[0] != '/') {
+      /* the URL included a host name, we ignore host names in file:// URLs
+         as the standards don't define what to do with them */
+      char *ptr=strchr(path, '/');
+      if(ptr) {
+        /* there was a slash present
+
+           RFC1738 (section 3.1, page 5) says:
+
+           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.
+
+           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,
+           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 */
+          ptr++;
+
+        /* This cannot be made with strcpy, as the memory chunks overlap! */
+        memmove(path, ptr, strlen(ptr)+1);
+      }
+    }
+
+    protop = "file"; /* protocol string */
+  }
+  else {
+    /* clear path */
+    path[0]=0;
+
+    if(2 > sscanf(data->change.url,
+                   "%15[^\n:]://%[^\n/?]%[^\n]",
+                   protobuf,
+                   conn->host.name, path)) {
+
+      /*
+       * The URL was badly formatted, let's try the browser-style _without_
+       * protocol specified like 'http://'.
+       */
+      rc = sscanf(data->change.url, "%[^\n/?]%[^\n]", conn->host.name, path);
+      if(1 > rc) {
+        /*
+         * We couldn't even get this format.
+         * djgpp 2.04 has a sscanf() bug where 'conn->host.name' is
+         * assigned, but the return value is EOF!
+         */
+#if defined(__DJGPP__) && (DJGPP_MINOR == 4)
+        if(!(rc == -1 && *conn->host.name))
+#endif
+        {
+          failf(data, "<url> malformed");
+          return CURLE_URL_MALFORMAT;
+        }
+      }
+
+      /*
+       * 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/curl_version.c too! */
+
+      if(checkprefix("FTP.", conn->host.name))
+        protop = "ftp";
+      else if(checkprefix("DICT.", conn->host.name))
+        protop = "DICT";
+      else if(checkprefix("LDAP.", conn->host.name))
+        protop = "LDAP";
+      else if(checkprefix("IMAP.", conn->host.name))
+        protop = "IMAP";
+      else {
+        protop = "http";
+      }
+
+      *prot_missing = TRUE; /* not given in URL */
+    }
+    else
+      protop = protobuf;
+  }
+
+  /* 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)
+    query = strchr(at+1, '?');
+  else
+    query = strchr(conn->host.name, '?');
+
+  if(query) {
+    /* 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(query);
+    size_t pathlen = strlen(path);
+
+    /* 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 */
+    memcpy(path+1, query, hostlen);
+
+    path[0]='/'; /* prepend the missing slash */
+
+    *query=0; /* now cut off the hostname at the ? */
+  }
+  else if(!path[0]) {
+    /* if there's no path set, use a single slash */
+    strcpy(path, "/");
+  }
+
+  /* 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 '?'.
+   */
+  if(path[0] == '?') {
+    /* 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] = '/';
+  }
+
+  /*************************************************************
+   * Parse a user name and password in the URL and strip it out
+   * of the host name
+   *************************************************************/
+  result = parse_url_userpass(data, conn, user, passwd);
+  if(result != CURLE_OK)
+    return result;
+
+  if(conn->host.name[0] == '[') {
+    /* This looks like an IPv6 address literal.  See if there is an address
+       scope.  */
+    char *percent = strstr (conn->host.name, "%25");
+    if(percent) {
+      char *endp;
+      unsigned long scope = strtoul (percent + 3, &endp, 10);
+      if(*endp == ']') {
+        /* The address scope was well formed.  Knock it out of the
+           hostname. */
+        memmove(percent, endp, strlen(endp)+1);
+        if(!data->state.this_is_a_follow)
+          /* Don't honour a scope given in a Location: header */
+          conn->scope = (unsigned int)scope;
+      }
+      else
+        infof(data, "Invalid IPv6 address format\n");
+    }
+  }
+
+  if(data->set.scope)
+    /* Override any scope that was set above.  */
+    conn->scope = data->set.scope;
+
+  /* Remove the fragment part of the path. Per RFC 2396, this is always the
+     last part of the URI. We are looking for the first '#' so that we deal
+     gracefully with non conformant URI such as http://example.com#foo#bar. */
+  fragment = strchr(path, '#');
+  if(fragment) {
+    *fragment = 0;
+
+    /* we know the path part ended with a fragment, so we know the full URL
+       string does too and we need to cut it off from there so it isn't used
+       over proxy */
+    fragment = strchr(data->change.url, '#');
+    if(fragment)
+      *fragment = 0;
+  }
+
+  /*
+   * So if the URL was A://B/C#D,
+   *   protop is A
+   *   conn->host.name is B
+   *   data->state.path is /C
+   */
+
+  return findprotocol(data, conn, protop);
+}
+
+/*
+ * If we're doing a resumed transfer, we need to setup our stuff
+ * properly.
+ */
+static CURLcode setup_range(struct SessionHandle *data)
+{
+  struct UrlState *s = &data->state;
+  s->resume_from = data->set.set_resume_from;
+  if(s->resume_from || data->set.str[STRING_SET_RANGE]) {
+    if(s->rangestringalloc)
+      free(s->range);
+
+    if(s->resume_from)
+      s->range = aprintf("%" FORMAT_OFF_TU "-", s->resume_from);
+    else
+      s->range = strdup(data->set.str[STRING_SET_RANGE]);
+
+    s->rangestringalloc = (s->range)?TRUE:FALSE;
+
+    if(!s->range)
+      return CURLE_OUT_OF_MEMORY;
+
+    /* tell ourselves to fetch this range */
+    s->use_range = TRUE;        /* enable range download */
+  }
+  else
+    s->use_range = FALSE; /* disable range download */
+
+  return CURLE_OK;
+}
+
+
+/***************************************************************
+* Setup connection internals specific to the requested protocol.
+* This MUST get called after proxy magic has been figured out.
+***************************************************************/
+static CURLcode setup_connection_internals(struct connectdata *conn)
+{
+  const struct Curl_handler * p;
+  CURLcode result;
+
+  conn->socktype = SOCK_STREAM; /* most of them are TCP streams */
+
+  /* Scan protocol handler table. */
+
+  /* Perform setup complement if some. */
+  p = conn->handler;
+
+  if(p->setup_connection) {
+    result = (*p->setup_connection)(conn);
+
+    if(result != CURLE_OK)
+      return result;
+
+    p = conn->handler;              /* May have changed. */
+  }
+
+  if(conn->port < 0)
+    /* we check for -1 here since if proxy was detected already, this
+       was very likely already set to the proxy port */
+    conn->port = p->defport;
+  conn->remote_port = (unsigned short)conn->given->defport;
+
+  return CURLE_OK;
+}
+
+#ifndef CURL_DISABLE_PROXY
+/****************************************************************
+* Checks if the host is in the noproxy list. returns true if it matches
+* and therefore the proxy should NOT be used.
+****************************************************************/
+static bool check_noproxy(const char* name, const char* no_proxy)
+{
+  /* no_proxy=domain1.dom,host.domain2.dom
+   *   (a comma-separated list of hosts which should
+   *   not be proxied, or an asterisk to override
+   *   all proxy variables)
+   */
+  size_t tok_start;
+  size_t tok_end;
+  const char* separator = ", ";
+  size_t no_proxy_len;
+  size_t namelen;
+  char *endptr;
+
+  if(no_proxy && no_proxy[0]) {
+    if(Curl_raw_equal("*", no_proxy)) {
+      return TRUE;
+    }
+
+    /* NO_PROXY was specified and it wasn't just an asterisk */
+
+    no_proxy_len = strlen(no_proxy);
+    endptr = strchr(name, ':');
+    if(endptr)
+      namelen = endptr - name;
+    else
+      namelen = strlen(name);
+
+    for(tok_start = 0; tok_start < no_proxy_len; tok_start = tok_end + 1) {
+      while(tok_start < no_proxy_len &&
+            strchr(separator, no_proxy[tok_start]) != NULL) {
+        /* Look for the beginning of the token. */
+        ++tok_start;
+      }
+
+      if(tok_start == no_proxy_len)
+        break; /* It was all trailing separator chars, no more tokens. */
+
+      for(tok_end = tok_start; tok_end < no_proxy_len &&
+            strchr(separator, no_proxy[tok_end]) == NULL; ++tok_end)
+        /* Look for the end of the token. */
+        ;
+
+      /* To match previous behaviour, where it was necessary to specify
+       * ".local.com" to prevent matching "notlocal.com", we will leave
+       * the '.' off.
+       */
+      if(no_proxy[tok_start] == '.')
+        ++tok_start;
+
+      if((tok_end - tok_start) <= namelen) {
+        /* Match the last part of the name to the domain we are checking. */
+        const char *checkn = name + namelen - (tok_end - tok_start);
+        if(Curl_raw_nequal(no_proxy + tok_start, checkn,
+                           tok_end - tok_start)) {
+          if((tok_end - tok_start) == namelen || *(checkn - 1) == '.') {
+            /* We either have an exact match, or the previous character is a .
+             * so it is within the same domain, so no proxy for this host.
+             */
+            return TRUE;
+          }
+        }
+      } /* if((tok_end - tok_start) <= namelen) */
+    } /* for(tok_start = 0; tok_start < no_proxy_len;
+         tok_start = tok_end + 1) */
+  } /* NO_PROXY was specified and it wasn't just an asterisk */
+
+  return FALSE;
+}
+
+/****************************************************************
+* Detect what (if any) proxy to use. Remember that this selects a host
+* name and is not limited to HTTP proxies only.
+* The returned pointer must be freed by the caller (unless NULL)
+****************************************************************/
+static char *detect_proxy(struct connectdata *conn)
+{
+  char *proxy = NULL;
+
+#ifndef CURL_DISABLE_HTTP
+  /* If proxy was not specified, we check for default proxy environment
+   * variables, to enable i.e Lynx compliance:
+   *
+   * http_proxy=http://some.server.dom:port/
+   * https_proxy=http://some.server.dom:port/
+   * ftp_proxy=http://some.server.dom:port/
+   * no_proxy=domain1.dom,host.domain2.dom
+   *   (a comma-separated list of hosts which should
+   *   not be proxied, or an asterisk to override
+   *   all proxy variables)
+   * all_proxy=http://some.server.dom:port/
+   *   (seems to exist for the CERN www lib. Probably
+   *   the first to check for.)
+   *
+   * For compatibility, the all-uppercase versions of these variables are
+   * checked if the lowercase versions don't exist.
+   */
+  char *no_proxy=NULL;
+  char proxy_env[128];
+
+  no_proxy=curl_getenv("no_proxy");
+  if(!no_proxy)
+    no_proxy=curl_getenv("NO_PROXY");
+
+  if(!check_noproxy(conn->host.name, no_proxy)) {
+    /* It was not listed as without proxy */
+    const char *protop = conn->handler->scheme;
+    char *envp = proxy_env;
+    char *prox;
+
+    /* Now, build <protocol>_proxy and check for such a one to use */
+    while(*protop)
+      *envp++ = (char)tolower((int)*protop++);
+
+    /* append _proxy */
+    strcpy(envp, "_proxy");
+
+    /* read the protocol proxy: */
+    prox=curl_getenv(proxy_env);
+
+    /*
+     * We don't try the uppercase version of HTTP_PROXY because of
+     * security reasons:
+     *
+     * When curl is used in a webserver application
+     * environment (cgi or php), this environment variable can
+     * be controlled by the web server user by setting the
+     * http header 'Proxy:' to some value.
+     *
+     * This can cause 'internal' http/ftp requests to be
+     * arbitrarily redirected by any external attacker.
+     */
+    if(!prox && !Curl_raw_equal("http_proxy", proxy_env)) {
+      /* There was no lowercase variable, try the uppercase version: */
+      Curl_strntoupper(proxy_env, proxy_env, sizeof(proxy_env));
+      prox=curl_getenv(proxy_env);
+    }
+
+    if(prox && *prox) { /* don't count "" strings */
+      proxy = prox; /* use this */
+    }
+    else {
+      proxy = curl_getenv("all_proxy"); /* default proxy to use */
+      if(!proxy)
+        proxy=curl_getenv("ALL_PROXY");
+    }
+  } /* if(!check_noproxy(conn->host.name, no_proxy)) - it wasn't specified
+       non-proxy */
+  if(no_proxy)
+    free(no_proxy);
+
+#else /* !CURL_DISABLE_HTTP */
+
+  (void)conn;
+#endif /* CURL_DISABLE_HTTP */
+
+  return proxy;
+}
+
+/*
+ * If this is supposed to use a proxy, we need to figure out the proxy
+ * host name, so that we can re-use an existing connection
+ * that may exist registered to the same proxy host.
+ * proxy will be freed before this function returns.
+ */
+static CURLcode parse_proxy(struct SessionHandle *data,
+                            struct connectdata *conn, char *proxy)
+{
+  char *prox_portno;
+  char *endofprot;
+
+  /* We use 'proxyptr' to point to the proxy name from now on... */
+  char *proxyptr;
+  char *portptr;
+  char *atsign;
+
+  /* We do the proxy host string parsing here. We want the host name and the
+   * port name. Accept a protocol:// prefix
+   */
+
+  /* Parse the protocol part if present */
+  endofprot = strstr(proxy, "://");
+  if(endofprot) {
+    proxyptr = endofprot+3;
+    if(checkprefix("socks5h", proxy))
+      conn->proxytype = CURLPROXY_SOCKS5_HOSTNAME;
+    else if(checkprefix("socks5", proxy))
+      conn->proxytype = CURLPROXY_SOCKS5;
+    else if(checkprefix("socks4a", proxy))
+      conn->proxytype = CURLPROXY_SOCKS4A;
+    else if(checkprefix("socks4", proxy) || checkprefix("socks", proxy))
+      conn->proxytype = CURLPROXY_SOCKS4;
+    /* Any other xxx:// : change to http proxy */
+  }
+  else
+    proxyptr = proxy; /* No xxx:// head: It's a HTTP proxy */
+
+  /* Is there a username and password given in this proxy url? */
+  atsign = strchr(proxyptr, '@');
+  if(atsign) {
+    char proxyuser[MAX_CURL_USER_LENGTH];
+    char proxypasswd[MAX_CURL_PASSWORD_LENGTH];
+    proxypasswd[0] = 0;
+
+    if(1 <= sscanf(proxyptr,
+                   "%" MAX_CURL_USER_LENGTH_TXT"[^:@]:"
+                   "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]",
+                   proxyuser, proxypasswd)) {
+      CURLcode res = CURLE_OK;
+
+      /* found user and password, rip them out.  note that we are
+         unescaping them, as there is otherwise no way to have a
+         username or password with reserved characters like ':' in
+         them. */
+      Curl_safefree(conn->proxyuser);
+      conn->proxyuser = curl_easy_unescape(data, proxyuser, 0, NULL);
+
+      if(!conn->proxyuser)
+        res = CURLE_OUT_OF_MEMORY;
+      else {
+        Curl_safefree(conn->proxypasswd);
+        conn->proxypasswd = curl_easy_unescape(data, proxypasswd, 0, NULL);
+
+        if(!conn->proxypasswd)
+          res = CURLE_OUT_OF_MEMORY;
+      }
+
+      if(CURLE_OK == res) {
+        conn->bits.proxy_user_passwd = TRUE; /* enable it */
+        atsign++; /* the right side of the @-letter */
+
+        if(atsign)
+          proxyptr = atsign; /* now use this instead */
+        else
+          res = CURLE_OUT_OF_MEMORY;
+      }
+
+      if(res)
+        return res;
+    }
+  }
+
+  /* start scanning for port number at this point */
+  portptr = proxyptr;
+
+  /* detect and extract RFC2732-style IPv6-addresses */
+  if(*proxyptr == '[') {
+    char *ptr = ++proxyptr; /* advance beyond the initial bracket */
+    while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '%') ||
+                   (*ptr == '.')))
+      ptr++;
+    if(*ptr == ']')
+      /* yeps, it ended nicely with a bracket as well */
+      *ptr++ = 0;
+    else
+      infof(data, "Invalid IPv6 address format\n");
+    portptr = ptr;
+    /* Note that if this didn't end with a bracket, we still advanced the
+     * proxyptr first, but I can't see anything wrong with that as no host
+     * name nor a numeric can legally start with a bracket.
+     */
+  }
+
+  /* Get port number off proxy.server.com:1080 */
+  prox_portno = strchr(portptr, ':');
+  if(prox_portno) {
+    *prox_portno = 0x0; /* cut off number from host name */
+    prox_portno ++;
+    /* now set the local port number */
+    conn->port = strtol(prox_portno, NULL, 10);
+  }
+  else {
+    if(proxyptr[0]=='/')
+      /* If the first character in the proxy string is a slash, fail
+         immediately. The following code will otherwise clear the string which
+         will lead to code running as if no proxy was set! */
+      return CURLE_COULDNT_RESOLVE_PROXY;
+
+    /* without a port number after the host name, some people seem to use
+       a slash so we strip everything from the first slash */
+    atsign = strchr(proxyptr, '/');
+    if(atsign)
+      *atsign = 0x0; /* cut off path part from host name */
+
+    if(data->set.proxyport)
+      /* None given in the proxy string, then get the default one if it is
+         given */
+      conn->port = data->set.proxyport;
+  }
+
+  /* now, clone the cleaned proxy host name */
+  conn->proxy.rawalloc = strdup(proxyptr);
+  conn->proxy.name = conn->proxy.rawalloc;
+
+  if(!conn->proxy.rawalloc)
+    return CURLE_OUT_OF_MEMORY;
+
+  return CURLE_OK;
+}
+
+/*
+ * Extract the user and password from the authentication string
+ */
+static CURLcode parse_proxy_auth(struct SessionHandle *data,
+                                 struct connectdata *conn)
+{
+  char proxyuser[MAX_CURL_USER_LENGTH]="";
+  char proxypasswd[MAX_CURL_PASSWORD_LENGTH]="";
+
+  if(data->set.str[STRING_PROXYUSERNAME] != NULL) {
+    strncpy(proxyuser, data->set.str[STRING_PROXYUSERNAME],
+            MAX_CURL_USER_LENGTH);
+    proxyuser[MAX_CURL_USER_LENGTH-1] = '\0';   /*To be on safe side*/
+  }
+  if(data->set.str[STRING_PROXYPASSWORD] != NULL) {
+    strncpy(proxypasswd, data->set.str[STRING_PROXYPASSWORD],
+            MAX_CURL_PASSWORD_LENGTH);
+    proxypasswd[MAX_CURL_PASSWORD_LENGTH-1] = '\0'; /*To be on safe side*/
+  }
+
+  conn->proxyuser = curl_easy_unescape(data, proxyuser, 0, NULL);
+  if(!conn->proxyuser)
+    return CURLE_OUT_OF_MEMORY;
+
+  conn->proxypasswd = curl_easy_unescape(data, proxypasswd, 0, NULL);
+  if(!conn->proxypasswd)
+    return CURLE_OUT_OF_MEMORY;
+
+  return CURLE_OK;
+}
+#endif /* CURL_DISABLE_PROXY */
+
+/*
+ *
+ * Parse a user name and password in the URL and strip it out of the host name
+ *
+ * Inputs: data->set.use_netrc (CURLOPT_NETRC)
+ *         conn->host.name
+ *
+ * Outputs: (almost :- all currently undefined)
+ *          conn->bits.user_passwd  - non-zero if non-default passwords exist
+ *          user                    - non-zero length if defined
+ *          passwd                  -   ditto
+ *          conn->host.name         - remove user name and password
+ */
+static CURLcode parse_url_userpass(struct SessionHandle *data,
+                                   struct connectdata *conn,
+                                   char *user, char *passwd)
+{
+  /* At this point, we're hoping all the other special cases have
+   * been taken care of, so conn->host.name is at most
+   *    [user[:password]]@]hostname
+   *
+   * We need somewhere to put the embedded details, so do that first.
+   */
+
+  char *ptr=strchr(conn->host.name, '@');
+  char *userpass = conn->host.name;
+
+  user[0] =0;   /* to make everything well-defined */
+  passwd[0]=0;
+
+  /* We will now try to extract the
+   * possible user+password pair in a string like:
+   * ftp://user:password@ftp.my.site:8021/README */
+  if(ptr != NULL) {
+    /* there's a user+password given here, to the left of the @ */
+
+    conn->host.name = ++ptr;
+
+    /* So the hostname is sane.  Only bother interpreting the
+     * results if we could care.  It could still be wasted
+     * work because it might be overtaken by the programmatically
+     * set user/passwd, but doing that first adds more cases here :-(
+     */
+
+    conn->bits.userpwd_in_url = TRUE;
+    if(data->set.use_netrc != CURL_NETRC_REQUIRED) {
+      /* We could use the one in the URL */
+
+      conn->bits.user_passwd = TRUE; /* enable user+password */
+
+      if(*userpass != ':') {
+        /* the name is given, get user+password */
+        sscanf(userpass, "%" MAX_CURL_USER_LENGTH_TXT "[^:@]:"
+               "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]",
+               user, passwd);
+      }
+      else
+        /* no name given, get the password only */
+        sscanf(userpass, ":%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]", passwd);
+
+      if(user[0]) {
+        char *newname=curl_easy_unescape(data, user, 0, NULL);
+        if(!newname)
+          return CURLE_OUT_OF_MEMORY;
+        if(strlen(newname) < MAX_CURL_USER_LENGTH)
+          strcpy(user, newname);
+
+        /* if the new name is longer than accepted, then just use
+           the unconverted name, it'll be wrong but what the heck */
+        free(newname);
+      }
+      if(passwd[0]) {
+        /* we have a password found in the URL, decode it! */
+        char *newpasswd=curl_easy_unescape(data, passwd, 0, NULL);
+        if(!newpasswd)
+          return CURLE_OUT_OF_MEMORY;
+        if(strlen(newpasswd) < MAX_CURL_PASSWORD_LENGTH)
+          strcpy(passwd, newpasswd);
+
+        free(newpasswd);
+      }
+    }
+  }
+  return CURLE_OK;
+}
+
+/*************************************************************
+ * Figure out the remote port number and fix it in the URL
+ *
+ * No matter if we use a proxy or not, we have to figure out the remote
+ * port number of various reasons.
+ *
+ * To be able to detect port number flawlessly, we must not confuse them
+ * IPv6-specified addresses in the [0::1] style. (RFC2732)
+ *
+ * The conn->host.name is currently [user:passwd@]host[:port] where host
+ * could be a hostname, IPv4 address or IPv6 address.
+ *
+ * The port number embedded in the URL is replaced, if necessary.
+ *************************************************************/
+static CURLcode parse_remote_port(struct SessionHandle *data,
+                                  struct connectdata *conn)
+{
+  char *portptr;
+  char endbracket;
+
+  /* Note that at this point, the IPv6 address cannot contain any scope
+     suffix as that has already been removed in the parseurlandfillconn()
+     function */
+  if((1 == sscanf(conn->host.name, "[%*45[0123456789abcdefABCDEF:.]%c",
+                  &endbracket)) &&
+     (']' == endbracket)) {
+    /* this is a RFC2732-style specified IP-address */
+    conn->bits.ipv6_ip = TRUE;
+
+    conn->host.name++; /* skip over the starting bracket */
+    portptr = strchr(conn->host.name, ']');
+    if(portptr) {
+      *portptr++ = '\0'; /* zero terminate, killing the bracket */
+      if(':' != *portptr)
+        portptr = NULL; /* no port number available */
+    }
+  }
+  else {
+#ifdef ENABLE_IPV6
+    struct in6_addr in6;
+    if(Curl_inet_pton(AF_INET6, conn->host.name, &in6) > 0) {
+      /* This is a numerical IPv6 address, meaning this is a wrongly formatted
+         URL */
+      failf(data, "IPv6 numerical address used in URL without brackets");
+      return CURLE_URL_MALFORMAT;
+    }
+#endif
+
+    portptr = strrchr(conn->host.name, ':');
+  }
+
+  if(data->set.use_port && data->state.allow_port) {
+    /* if set, we use this and ignore the port possibly given in the URL */
+    conn->remote_port = (unsigned short)data->set.use_port;
+    if(portptr)
+      *portptr = '\0'; /* cut off the name there anyway - if there was a port
+                      number - since the port number is to be ignored! */
+    if(conn->bits.httpproxy) {
+      /* we need to create new URL with the new port number */
+      char *url;
+      char type[12]="";
+
+      if(conn->bits.type_set)
+        snprintf(type, sizeof(type), ";type=%c",
+                 data->set.prefer_ascii?'A':
+                 (data->set.ftp_list_only?'D':'I'));
+
+      /*
+       * This synthesized URL isn't always right--suffixes like ;type=A are
+       * stripped off. It would be better to work directly from the original
+       * URL and simply replace the port part of it.
+       */
+      url = aprintf("%s://%s%s%s:%hu%s%s%s", conn->given->scheme,
+                    conn->bits.ipv6_ip?"[":"", conn->host.name,
+                    conn->bits.ipv6_ip?"]":"", conn->remote_port,
+                    data->state.slash_removed?"/":"", data->state.path,
+                    type);
+      if(!url)
+        return CURLE_OUT_OF_MEMORY;
+
+      if(data->change.url_alloc) {
+        Curl_safefree(data->change.url);
+        data->change.url_alloc = FALSE;
+      }
+
+      data->change.url = url;
+      data->change.url_alloc = TRUE;
+    }
+  }
+  else if(portptr) {
+    /* no CURLOPT_PORT given, extract the one from the URL */
+
+    char *rest;
+    unsigned long port;
+
+    port=strtoul(portptr+1, &rest, 10);  /* Port number must be decimal */
+
+    if(rest != (portptr+1) && *rest == '\0') {
+      /* The colon really did have only digits after it,
+       * so it is either a port number or a mistake */
+
+      if(port > 0xffff) {   /* Single unix standard says port numbers are
+                              * 16 bits long */
+        failf(data, "Port number too large: %lu", port);
+        return CURLE_URL_MALFORMAT;
+      }
+
+      *portptr = '\0'; /* cut off the name there */
+      conn->remote_port = curlx_ultous(port);
+    }
+    else if(!port)
+      /* Browser behavior adaptation. If there's a colon with no digits after,
+         just cut off the name there which makes us ignore the colon and just
+         use the default port. Firefox and Chrome both do that. */
+      *portptr = '\0';
+  }
+  return CURLE_OK;
+}
+
+/*
+ * Override a user name and password from the URL with that in the
+ * CURLOPT_USERPWD option or a .netrc file, if applicable.
+ */
+static void override_userpass(struct SessionHandle *data,
+                              struct connectdata *conn,
+                              char *user, char *passwd)
+{
+  if(data->set.str[STRING_USERNAME] != NULL) {
+    strncpy(user, data->set.str[STRING_USERNAME], MAX_CURL_USER_LENGTH);
+    user[MAX_CURL_USER_LENGTH-1] = '\0';   /*To be on safe side*/
+  }
+  if(data->set.str[STRING_PASSWORD] != NULL) {
+    strncpy(passwd, data->set.str[STRING_PASSWORD], MAX_CURL_PASSWORD_LENGTH);
+    passwd[MAX_CURL_PASSWORD_LENGTH-1] = '\0'; /*To be on safe side*/
+  }
+
+  conn->bits.netrc = FALSE;
+  if(data->set.use_netrc != CURL_NETRC_IGNORED) {
+    if(Curl_parsenetrc(conn->host.name,
+                       user, passwd,
+                       data->set.str[STRING_NETRC_FILE])) {
+      infof(data, "Couldn't find host %s in the "
+            DOT_CHAR "netrc file; using defaults\n",
+            conn->host.name);
+    }
+    else {
+      /* set bits.netrc TRUE to remember that we got the name from a .netrc
+         file, so that it is safe to use even if we followed a Location: to a
+         different host or similar. */
+      conn->bits.netrc = TRUE;
+
+      conn->bits.user_passwd = TRUE; /* enable user+password */
+    }
+  }
+}
+
+/*
+ * Set password so it's available in the connection.
+ */
+static CURLcode set_userpass(struct connectdata *conn,
+                             const char *user, const char *passwd)
+{
+  /* If our protocol needs a password and we have none, use the defaults */
+  if((conn->handler->flags & PROTOPT_NEEDSPWD) &&
+     !conn->bits.user_passwd) {
+
+    conn->user = strdup(CURL_DEFAULT_USER);
+    if(conn->user)
+      conn->passwd = strdup(CURL_DEFAULT_PASSWORD);
+    else
+      conn->passwd = NULL;
+    /* This is the default password, so DON'T set conn->bits.user_passwd */
+  }
+  else {
+    /* store user + password, zero-length if not set */
+    conn->user = strdup(user);
+    if(conn->user)
+      conn->passwd = strdup(passwd);
+    else
+      conn->passwd = NULL;
+  }
+  if(!conn->user || !conn->passwd)
+    return CURLE_OUT_OF_MEMORY;
+
+  return CURLE_OK;
+}
+
+/*************************************************************
+ * Resolve the address of the server or proxy
+ *************************************************************/
+static CURLcode resolve_server(struct SessionHandle *data,
+                               struct connectdata *conn,
+                               bool *async)
+{
+  CURLcode result=CURLE_OK;
+  long timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+  /*************************************************************
+   * Resolve the name of the server or proxy
+   *************************************************************/
+  if(conn->bits.reuse)
+    /* We're reusing the connection - no need to resolve anything, and
+       fix_hostname() was called already in create_conn() for the re-use
+       case. */
+    *async = FALSE;
+
+  else {
+    /* this is a fresh connect */
+    int rc;
+    struct Curl_dns_entry *hostaddr;
+
+    /* set a pointer to the hostname we display */
+    fix_hostname(data, conn, &conn->host);
+
+    if(!conn->proxy.name || !*conn->proxy.name) {
+      /* If not connecting via a proxy, extract the port from the URL, if it is
+       * there, thus overriding any defaults that might have been set above. */
+      conn->port =  conn->remote_port; /* it is the same port */
+
+      /* Resolve target host right on */
+      rc = Curl_resolv_timeout(conn, conn->host.name, (int)conn->port,
+                               &hostaddr, timeout_ms);
+      if(rc == CURLRESOLV_PENDING)
+        *async = TRUE;
+
+      else if(rc == CURLRESOLV_TIMEDOUT)
+        result = CURLE_OPERATION_TIMEDOUT;
+
+      else if(!hostaddr) {
+        failf(data, "Couldn't resolve host '%s'", conn->host.dispname);
+        result =  CURLE_COULDNT_RESOLVE_HOST;
+        /* don't return yet, we need to clean up the timeout first */
+      }
+    }
+    else {
+      /* This is a proxy that hasn't been resolved yet. */
+
+      /* IDN-fix the proxy name */
+      fix_hostname(data, conn, &conn->proxy);
+
+      /* resolve proxy */
+      rc = Curl_resolv_timeout(conn, conn->proxy.name, (int)conn->port,
+                               &hostaddr, timeout_ms);
+
+      if(rc == CURLRESOLV_PENDING)
+        *async = TRUE;
+
+      else if(rc == CURLRESOLV_TIMEDOUT)
+        result = CURLE_OPERATION_TIMEDOUT;
+
+      else if(!hostaddr) {
+        failf(data, "Couldn't resolve proxy '%s'", conn->proxy.dispname);
+        result = CURLE_COULDNT_RESOLVE_PROXY;
+        /* don't return yet, we need to clean up the timeout first */
+      }
+    }
+    DEBUGASSERT(conn->dns_entry == NULL);
+    conn->dns_entry = hostaddr;
+  }
+
+  return result;
+}
+
+/*
+ * Cleanup the connection just allocated before we can move along and use the
+ * previously existing one.  All relevant data is copied over and old_conn is
+ * ready for freeing once this function returns.
+ */
+static void reuse_conn(struct connectdata *old_conn,
+                       struct connectdata *conn)
+{
+  if(old_conn->proxy.rawalloc)
+    free(old_conn->proxy.rawalloc);
+
+  /* free the SSL config struct from this connection struct as this was
+     allocated in vain and is targeted for destruction */
+  Curl_free_ssl_config(&old_conn->ssl_config);
+
+  conn->data = old_conn->data;
+
+  /* get the user+password information from the old_conn struct since it may
+   * be new for this request even when we re-use an existing connection */
+  conn->bits.user_passwd = old_conn->bits.user_passwd;
+  if(conn->bits.user_passwd) {
+    /* use the new user name and password though */
+    Curl_safefree(conn->user);
+    Curl_safefree(conn->passwd);
+    conn->user = old_conn->user;
+    conn->passwd = old_conn->passwd;
+    old_conn->user = NULL;
+    old_conn->passwd = NULL;
+  }
+
+  conn->bits.proxy_user_passwd = old_conn->bits.proxy_user_passwd;
+  if(conn->bits.proxy_user_passwd) {
+    /* use the new proxy user name and proxy password though */
+    Curl_safefree(conn->proxyuser);
+    Curl_safefree(conn->proxypasswd);
+    conn->proxyuser = old_conn->proxyuser;
+    conn->proxypasswd = old_conn->proxypasswd;
+    old_conn->proxyuser = NULL;
+    old_conn->proxypasswd = NULL;
+  }
+
+  /* host can change, when doing keepalive with a proxy or if the case is
+     different this time etc */
+  Curl_safefree(conn->host.rawalloc);
+  conn->host=old_conn->host;
+
+  /* persist connection info in session handle */
+  Curl_persistconninfo(conn);
+
+  /* re-use init */
+  conn->bits.reuse = TRUE; /* yes, we're re-using here */
+
+  Curl_safefree(old_conn->user);
+  Curl_safefree(old_conn->passwd);
+  Curl_safefree(old_conn->proxyuser);
+  Curl_safefree(old_conn->proxypasswd);
+  Curl_safefree(old_conn->localdev);
+
+  Curl_llist_destroy(old_conn->send_pipe, NULL);
+  Curl_llist_destroy(old_conn->recv_pipe, NULL);
+  Curl_llist_destroy(old_conn->pend_pipe, NULL);
+  Curl_llist_destroy(old_conn->done_pipe, NULL);
+
+  old_conn->send_pipe = NULL;
+  old_conn->recv_pipe = NULL;
+  old_conn->pend_pipe = NULL;
+  old_conn->done_pipe = NULL;
+
+  Curl_safefree(old_conn->master_buffer);
+}
+
+/**
+ * create_conn() sets up a new connectdata struct, or re-uses an already
+ * existing one, and resolves host name.
+ *
+ * if this function returns CURLE_OK and *async is set to TRUE, the resolve
+ * response will be coming asynchronously. If *async is FALSE, the name is
+ * already resolved.
+ *
+ * @param data The sessionhandle pointer
+ * @param in_connect is set to the next connection data pointer
+ * @param async is set TRUE when an async DNS resolution is pending
+ * @see Curl_setup_conn()
+ *
+ * *NOTE* this function assigns the conn->data pointer!
+ */
+
+static CURLcode create_conn(struct SessionHandle *data,
+                            struct connectdata **in_connect,
+                            bool *async)
+{
+  CURLcode result=CURLE_OK;
+  struct connectdata *conn;
+  struct connectdata *conn_temp = NULL;
+  size_t urllen;
+  char user[MAX_CURL_USER_LENGTH];
+  char passwd[MAX_CURL_PASSWORD_LENGTH];
+  bool reuse;
+  char *proxy = NULL;
+  bool prot_missing = FALSE;
+
+  *async = FALSE;
+
+  /*************************************************************
+   * Check input data
+   *************************************************************/
+
+  if(!data->change.url)
+    return CURLE_URL_MALFORMAT;
+
+  /* First, split up the current URL in parts so that we can use the
+     parts for checking against the already present connections. In order
+     to not have to modify everything at once, we allocate a temporary
+     connection data struct and fill in for comparison purposes. */
+  conn = allocate_conn(data);
+
+  if(!conn)
+    return CURLE_OUT_OF_MEMORY;
+
+  /* We must set the return variable as soon as possible, so that our
+     parent can cleanup any possible allocs we may have done before
+     any failure */
+  *in_connect = conn;
+
+  /* This initing continues below, see the comment "Continue connectdata
+   * initialization here" */
+
+  /***********************************************************
+   * We need to allocate memory to store the path in. We get the size of the
+   * full URL to be sure, and we need to make it at least 256 bytes since
+   * other parts of the code will rely on this fact
+   ***********************************************************/
+#define LEAST_PATH_ALLOC 256
+  urllen=strlen(data->change.url);
+  if(urllen < LEAST_PATH_ALLOC)
+    urllen=LEAST_PATH_ALLOC;
+
+  /*
+   * We malloc() the buffers below urllen+2 to make room for 2 possibilities:
+   * 1 - an extra terminating zero
+   * 2 - an extra slash (in case a syntax like "www.host.com?moo" is used)
+   */
+
+  Curl_safefree(data->state.pathbuffer);
+  data->state.path = NULL;
+
+  data->state.pathbuffer = malloc(urllen+2);
+  if(NULL == data->state.pathbuffer)
+    return CURLE_OUT_OF_MEMORY; /* really bad error */
+  data->state.path = data->state.pathbuffer;
+
+  conn->host.rawalloc = malloc(urllen+2);
+  if(NULL == conn->host.rawalloc) {
+    Curl_safefree(data->state.pathbuffer);
+    data->state.path = NULL;
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  conn->host.name = conn->host.rawalloc;
+  conn->host.name[0] = 0;
+
+  result = parseurlandfillconn(data, conn, &prot_missing, user, passwd);
+  if(result != CURLE_OK)
+    return result;
+
+  /*************************************************************
+   * No protocol part in URL was used, add it!
+   *************************************************************/
+  if(prot_missing) {
+    /* We're guessing prefixes here and if we're told to use a proxy or if
+       we're gonna follow a Location: later or... then we need the protocol
+       part added so that we have a valid URL. */
+    char *reurl;
+
+    reurl = aprintf("%s://%s", conn->handler->scheme, data->change.url);
+
+    if(!reurl) {
+      Curl_safefree(proxy);
+      return CURLE_OUT_OF_MEMORY;
+    }
+
+    if(data->change.url_alloc) {
+      Curl_safefree(data->change.url);
+      data->change.url_alloc = FALSE;
+    }
+
+    data->change.url = reurl;
+    data->change.url_alloc = TRUE; /* free this later */
+  }
+
+  /*************************************************************
+   * If the protocol can't handle url query strings, then cut
+   * of the unhandable part
+   *************************************************************/
+  if((conn->given->flags&PROTOPT_NOURLQUERY)) {
+    char *path_q_sep = strchr(conn->data->state.path, '?');
+    if(path_q_sep) {
+      /* according to rfc3986, allow the query (?foo=bar)
+         also on protocols that can't handle it.
+
+         cut the string-part after '?'
+      */
+
+      /* terminate the string */
+      path_q_sep[0] = 0;
+    }
+  }
+
+#ifndef CURL_DISABLE_PROXY
+  /*************************************************************
+   * Extract the user and password from the authentication string
+   *************************************************************/
+  if(conn->bits.proxy_user_passwd) {
+    result = parse_proxy_auth(data, conn);
+    if(result != CURLE_OK)
+      return result;
+  }
+
+  /*************************************************************
+   * Detect what (if any) proxy to use
+   *************************************************************/
+  if(data->set.str[STRING_PROXY]) {
+    proxy = strdup(data->set.str[STRING_PROXY]);
+    /* if global proxy is set, this is it */
+    if(NULL == proxy) {
+      failf(data, "memory shortage");
+      return CURLE_OUT_OF_MEMORY;
+    }
+  }
+
+  if(data->set.str[STRING_NOPROXY] &&
+     check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY])) {
+    if(proxy) {
+      free(proxy);  /* proxy is in exception list */
+      proxy = NULL;
+    }
+  }
+  else if(!proxy)
+    proxy = detect_proxy(conn);
+
+  if(proxy && (!*proxy || (conn->handler->flags & PROTOPT_NONETWORK))) {
+    free(proxy);  /* Don't bother with an empty proxy string or if the
+                     protocol doesn't work with network */
+    proxy = NULL;
+  }
+
+  /***********************************************************************
+   * If this is supposed to use a proxy, we need to figure out the proxy host
+   * name, proxy type and port number, so that we can re-use an existing
+   * connection that may exist registered to the same proxy host.
+   ***********************************************************************/
+  if(proxy) {
+    result = parse_proxy(data, conn, proxy);
+
+    free(proxy); /* parse_proxy copies the proxy string */
+
+    if(result)
+      return result;
+
+    if((conn->proxytype == CURLPROXY_HTTP) ||
+       (conn->proxytype == CURLPROXY_HTTP_1_0)) {
+#ifdef CURL_DISABLE_HTTP
+      /* asking for a HTTP proxy is a bit funny when HTTP is disabled... */
+      return CURLE_UNSUPPORTED_PROTOCOL;
+#else
+      /* force this connection's protocol to become HTTP if not already
+         compatible - if it isn't tunneling through */
+      if(!(conn->handler->protocol & CURLPROTO_HTTP) &&
+         !conn->bits.tunnel_proxy)
+        conn->handler = &Curl_handler_http;
+
+      conn->bits.httpproxy = TRUE;
+#endif
+    }
+    else
+      conn->bits.httpproxy = FALSE; /* not a HTTP proxy */
+    conn->bits.proxy = TRUE;
+  }
+  else {
+    /* we aren't using the proxy after all... */
+    conn->bits.proxy = FALSE;
+    conn->bits.httpproxy = FALSE;
+    conn->bits.proxy_user_passwd = FALSE;
+    conn->bits.tunnel_proxy = FALSE;
+  }
+
+#endif /* CURL_DISABLE_PROXY */
+
+  /*************************************************************
+   * Setup internals depending on protocol. Needs to be done after
+   * we figured out what/if proxy to use.
+   *************************************************************/
+  result = setup_connection_internals(conn);
+  if(result != CURLE_OK) {
+    Curl_safefree(proxy);
+    return result;
+  }
+
+  conn->recv[FIRSTSOCKET] = Curl_recv_plain;
+  conn->send[FIRSTSOCKET] = Curl_send_plain;
+  conn->recv[SECONDARYSOCKET] = Curl_recv_plain;
+  conn->send[SECONDARYSOCKET] = Curl_send_plain;
+
+  /***********************************************************************
+   * file: is a special case in that it doesn't need a network connection
+   ***********************************************************************/
+#ifndef CURL_DISABLE_FILE
+  if(conn->handler->flags & PROTOPT_NONETWORK) {
+    bool done;
+    /* this is supposed to be the connect function so we better at least check
+       that the file is present here! */
+    DEBUGASSERT(conn->handler->connect_it);
+    result = conn->handler->connect_it(conn, &done);
+
+    /* Setup a "faked" transfer that'll do nothing */
+    if(CURLE_OK == result) {
+      conn->data = data;
+      conn->bits.tcpconnect[FIRSTSOCKET] = TRUE; /* we are "connected */
+
+      ConnectionStore(data, conn);
+
+      /*
+       * Setup whatever necessary for a resumed transfer
+       */
+      result = setup_range(data);
+      if(result) {
+        DEBUGASSERT(conn->handler->done);
+        /* we ignore the return code for the protocol-specific DONE */
+        (void)conn->handler->done(conn, result, FALSE);
+        return result;
+      }
+
+      Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
+                          -1, NULL); /* no upload */
+    }
+
+    return result;
+  }
+#endif
+
+  /*************************************************************
+   * If the protocol is using SSL and HTTP proxy is used, we set
+   * the tunnel_proxy bit.
+   *************************************************************/
+  if((conn->given->flags&PROTOPT_SSL) && conn->bits.httpproxy)
+    conn->bits.tunnel_proxy = TRUE;
+
+  /*************************************************************
+   * Figure out the remote port number and fix it in the URL
+   *************************************************************/
+  result = parse_remote_port(data, conn);
+  if(result != CURLE_OK)
+    return result;
+
+  /*************************************************************
+   * Check for an overridden user name and password, then set it
+   * for use
+   *************************************************************/
+  override_userpass(data, conn, user, passwd);
+  result = set_userpass(conn, user, passwd);
+  if(result != CURLE_OK)
+    return result;
+
+  /* Get a cloned copy of the SSL config situation stored in the
+     connection struct. But to get this going nicely, we must first make
+     sure that the strings in the master copy are pointing to the correct
+     strings in the session handle strings array!
+
+     Keep in mind that the pointers in the master copy are pointing to strings
+     that will be freed as part of the SessionHandle struct, but all cloned
+     copies will be separately allocated.
+  */
+  data->set.ssl.CApath = data->set.str[STRING_SSL_CAPATH];
+  data->set.ssl.CAfile = data->set.str[STRING_SSL_CAFILE];
+  data->set.ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE];
+  data->set.ssl.issuercert = data->set.str[STRING_SSL_ISSUERCERT];
+  data->set.ssl.random_file = data->set.str[STRING_SSL_RANDOM_FILE];
+  data->set.ssl.egdsocket = data->set.str[STRING_SSL_EGDSOCKET];
+  data->set.ssl.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST];
+#ifdef USE_TLS_SRP
+  data->set.ssl.username = data->set.str[STRING_TLSAUTH_USERNAME];
+  data->set.ssl.password = data->set.str[STRING_TLSAUTH_PASSWORD];
+#endif
+
+  if(!Curl_clone_ssl_config(&data->set.ssl, &conn->ssl_config))
+    return CURLE_OUT_OF_MEMORY;
+
+  /*************************************************************
+   * Check the current list of connections to see if we can
+   * re-use an already existing one or if we have to create a
+   * new one.
+   *************************************************************/
+
+  /* reuse_fresh is TRUE if we are told to use a new connection by force, but
+     we only acknowledge this option if this is not a re-used connection
+     already (which happens due to follow-location or during a HTTP
+     authentication phase). */
+  if(data->set.reuse_fresh && !data->state.this_is_a_follow)
+    reuse = FALSE;
+  else
+    reuse = ConnectionExists(data, conn, &conn_temp);
+
+  if(reuse) {
+    /*
+     * We already have a connection for this, we got the former connection
+     * in the conn_temp variable and thus we need to cleanup the one we
+     * just allocated before we can move along and use the previously
+     * existing one.
+     */
+    reuse_conn(conn, conn_temp);
+    free(conn);          /* we don't need this anymore */
+    conn = conn_temp;
+    *in_connect = conn;
+
+    /* set a pointer to the hostname we display */
+    fix_hostname(data, conn, &conn->host);
+
+    infof(data, "Re-using existing connection! (#%ld) with host %s\n",
+          conn->connection_id,
+          conn->proxy.name?conn->proxy.dispname:conn->host.dispname);
+  }
+  else {
+    /*
+     * This is a brand new connection, so let's store it in the connection
+     * cache of ours!
+     */
+    ConnectionStore(data, conn);
+  }
+
+  /* Setup and init stuff before DO starts, in preparing for the transfer. */
+  do_init(conn);
+
+  /*
+   * Setup whatever necessary for a resumed transfer
+   */
+  result = setup_range(data);
+  if(result)
+    return result;
+
+  /* Continue connectdata initialization here. */
+
+  /*
+   * Inherit the proper values from the urldata struct AFTER we have arranged
+   * the persistent connection stuff
+   */
+  conn->fread_func = data->set.fread_func;
+  conn->fread_in = data->set.in;
+  conn->seek_func = data->set.seek_func;
+  conn->seek_client = data->set.seek_client;
+
+  /*************************************************************
+   * Resolve the address of the server or proxy
+   *************************************************************/
+  result = resolve_server(data, conn, async);
+
+  return result;
+}
+
+/* Curl_setup_conn() is called after the name resolve initiated in
+ * create_conn() is all done.
+ *
+ * Curl_setup_conn() also handles reused connections
+ *
+ * conn->data MUST already have been setup fine (in create_conn)
+ */
+
+CURLcode Curl_setup_conn(struct connectdata *conn,
+                         bool *protocol_done)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+
+  Curl_pgrsTime(data, TIMER_NAMELOOKUP);
+
+  if(conn->handler->flags & PROTOPT_NONETWORK) {
+    /* nothing to setup when not using a network */
+    *protocol_done = TRUE;
+    return result;
+  }
+  *protocol_done = FALSE; /* default to not done */
+
+  /* set proxy_connect_closed to false unconditionally already here since it
+     is used strictly to provide extra information to a parent function in the
+     case of proxy CONNECT failures and we must make sure we don't have it
+     lingering set from a previous invoke */
+  conn->bits.proxy_connect_closed = FALSE;
+
+  /*
+   * Set user-agent. Used for HTTP, but since we can attempt to tunnel
+   * basically anything through a http proxy we can't limit this based on
+   * protocol.
+   */
+  if(data->set.str[STRING_USERAGENT]) {
+    Curl_safefree(conn->allocptr.uagent);
+    conn->allocptr.uagent =
+      aprintf("User-Agent: %s\r\n", data->set.str[STRING_USERAGENT]);
+    if(!conn->allocptr.uagent)
+      return CURLE_OUT_OF_MEMORY;
+  }
+
+  data->req.headerbytecount = 0;
+
+#ifdef CURL_DO_LINEEND_CONV
+  data->state.crlf_conversions = 0; /* reset CRLF conversion counter */
+#endif /* CURL_DO_LINEEND_CONV */
+
+  for(;;) {
+    /* loop for CURL_SERVER_CLOSED_CONNECTION */
+
+    if(CURL_SOCKET_BAD == conn->sock[FIRSTSOCKET]) {
+      /* Try to connect only if not already connected */
+      bool connected = FALSE;
+
+      result = ConnectPlease(data, conn, &connected);
+
+      if(result && !conn->ip_addr) {
+        /* transport connection failure not related with authentication */
+        conn->bits.tcpconnect[FIRSTSOCKET] = FALSE;
+        return result;
+      }
+
+      if(connected) {
+        result = Curl_protocol_connect(conn, protocol_done);
+        if(CURLE_OK == result)
+          conn->bits.tcpconnect[FIRSTSOCKET] = TRUE;
+      }
+      else
+        conn->bits.tcpconnect[FIRSTSOCKET] = FALSE;
+
+      /* if the connection was closed by the server while exchanging
+         authentication informations, retry with the new set
+         authentication information */
+      if(conn->bits.proxy_connect_closed) {
+        /* reset the error buffer */
+        if(data->set.errorbuffer)
+          data->set.errorbuffer[0] = '\0';
+        data->state.errorbuf = FALSE;
+        continue;
+      }
+
+      if(CURLE_OK != result)
+        return result;
+    }
+    else {
+      Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */
+      Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */
+      conn->bits.tcpconnect[FIRSTSOCKET] = TRUE;
+      *protocol_done = TRUE;
+      Curl_verboseconnect(conn);
+      Curl_updateconninfo(conn, conn->sock[FIRSTSOCKET]);
+    }
+    /* Stop the loop now */
+    break;
+  }
+
+  conn->now = Curl_tvnow(); /* time this *after* the connect is done, we
+                               set this here perhaps a second time */
+
+#ifdef __EMX__
+  /*
+   * This check is quite a hack. We're calling _fsetmode to fix the problem
+   * with fwrite converting newline characters (you get mangled text files,
+   * and corrupted binary files when you download to stdout and redirect it to
+   * a file).
+   */
+
+  if((data->set.out)->_handle == NULL) {
+    _fsetmode(stdout, "b");
+  }
+#endif
+
+  return result;
+}
+
+CURLcode Curl_connect(struct SessionHandle *data,
+                      struct connectdata **in_connect,
+                      bool *asyncp,
+                      bool *protocol_done)
+{
+  CURLcode code;
+
+  *asyncp = FALSE; /* assume synchronous resolves by default */
+
+  /* call the stuff that needs to be called */
+  code = create_conn(data, in_connect, asyncp);
+
+  if(CURLE_OK == code) {
+    /* no error */
+    if((*in_connect)->send_pipe->size || (*in_connect)->recv_pipe->size)
+      /* pipelining */
+      *protocol_done = TRUE;
+    else if(!*asyncp) {
+      /* DNS resolution is done: that's either because this is a reused
+         connection, in which case DNS was unnecessary, or because DNS
+         really did finish already (synch resolver/fast async resolve) */
+      code = Curl_setup_conn(*in_connect, protocol_done);
+    }
+  }
+
+  if(code && *in_connect) {
+    /* We're not allowed to return failure with memory left allocated
+       in the connectdata struct, free those here */
+    Curl_disconnect(*in_connect, FALSE); /* close the connection */
+    *in_connect = NULL;           /* return a NULL */
+  }
+
+  return code;
+}
+
+CURLcode Curl_done(struct connectdata **connp,
+                   CURLcode status,  /* an error if this is called after an
+                                        error was detected */
+                   bool premature)
+{
+  CURLcode result;
+  struct connectdata *conn;
+  struct SessionHandle *data;
+
+  DEBUGASSERT(*connp);
+
+  conn = *connp;
+  data = conn->data;
+
+  if(conn->bits.done)
+    /* Stop if Curl_done() has already been called */
+    return CURLE_OK;
+
+  Curl_getoff_all_pipelines(data, conn);
+
+  if((conn->send_pipe->size + conn->recv_pipe->size != 0 &&
+      !data->set.reuse_forbid &&
+      !conn->bits.close))
+    /* Stop if pipeline is not empty and we do not have to close
+       connection. */
+    return CURLE_OK;
+
+  conn->bits.done = TRUE; /* called just now! */
+
+  /* Cleanup possible redirect junk */
+  if(data->req.newurl) {
+    free(data->req.newurl);
+    data->req.newurl = NULL;
+  }
+  if(data->req.location) {
+    free(data->req.location);
+    data->req.location = NULL;
+  }
+
+  Curl_resolver_cancel(conn);
+
+  if(conn->dns_entry) {
+    Curl_resolv_unlock(data, conn->dns_entry); /* done with this */
+    conn->dns_entry = NULL;
+  }
+
+  /* this calls the protocol-specific function pointer previously set */
+  if(conn->handler->done)
+    result = conn->handler->done(conn, status, premature);
+  else
+    result = CURLE_OK;
+
+  if(Curl_pgrsDone(conn) && !result)
+    result = CURLE_ABORTED_BY_CALLBACK;
+
+  /* if the transfer was completed in a paused state there can be buffered
+     data left to write and then kill */
+  if(data->state.tempwrite) {
+    free(data->state.tempwrite);
+    data->state.tempwrite = NULL;
+  }
+
+  /* if data->set.reuse_forbid is TRUE, it means the libcurl client has
+     forced us to close this no matter what we think.
+
+     if conn->bits.close is TRUE, it means that the connection should be
+     closed in spite of all our efforts to be nice, due to protocol
+     restrictions in our or the server's end
+
+     if premature is TRUE, it means this connection was said to be DONE before
+     the entire request operation is complete and thus we can't know in what
+     state it is for re-using, so we're forced to close it. In a perfect world
+     we can add code that keep track of if we really must close it here or not,
+     but currently we have no such detail knowledge.
+
+     connection_id == -1 here means that the connection has not been added
+     to the connection cache (OOM) and thus we must disconnect it here.
+  */
+  if(data->set.reuse_forbid || conn->bits.close || premature ||
+     (-1 == conn->connection_id)) {
+    CURLcode res2 = Curl_disconnect(conn, premature); /* close connection */
+
+    /* If we had an error already, make sure we return that one. But
+       if we got a new error, return that. */
+    if(!result && res2)
+      result = res2;
+  }
+  else {
+    ConnectionDone(conn); /* the connection is no longer in use */
+
+    /* remember the most recently used connection */
+    data->state.lastconnect = conn;
+
+    infof(data, "Connection #%ld to host %s left intact\n",
+          conn->connection_id,
+          conn->bits.httpproxy?conn->proxy.dispname:conn->host.dispname);
+  }
+
+  *connp = NULL; /* to make the caller of this function better detect that
+                    this was either closed or handed over to the connection
+                    cache here, and therefore cannot be used from this point on
+                 */
+
+  return result;
+}
+
+/*
+ * do_init() inits the readwrite session. This is inited each time (in the DO
+ * function before the protocol-specific DO functions are invoked) for a
+ * transfer, sometimes multiple times on the same SessionHandle. Make sure
+ * nothing in here depends on stuff that are setup dynamically for the
+ * transfer.
+ */
+
+static CURLcode do_init(struct connectdata *conn)
+{
+  struct SessionHandle *data = conn->data;
+  struct SingleRequest *k = &data->req;
+
+  conn->bits.done = FALSE; /* Curl_done() is not called yet */
+  conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to use */
+  data->state.expect100header = FALSE;
+
+  if(data->set.opt_no_body)
+    /* in HTTP lingo, no body means using the HEAD request... */
+    data->set.httpreq = HTTPREQ_HEAD;
+  else if(HTTPREQ_HEAD == data->set.httpreq)
+    /* ... but if unset there really is no perfect method that is the
+       "opposite" of HEAD but in reality most people probably think GET
+       then. The important thing is that we can't let it remain HEAD if the
+       opt_no_body is set FALSE since then we'll behave wrong when getting
+       HTTP. */
+    data->set.httpreq = HTTPREQ_GET;
+
+  /* NB: the content encoding software depends on this initialization */
+  Curl_easy_initHandleData(data);
+
+  k->start = Curl_tvnow(); /* start time */
+  k->now = k->start;   /* current time is now */
+  k->header = TRUE; /* assume header */
+
+  k->bytecount = 0;
+
+  k->buf = data->state.buffer;
+  k->uploadbuf = data->state.uploadbuffer;
+  k->hbufp = data->state.headerbuff;
+  k->ignorebody=FALSE;
+
+  Curl_speedinit(data);
+
+  Curl_pgrsSetUploadCounter(data, 0);
+  Curl_pgrsSetDownloadCounter(data, 0);
+
+  return CURLE_OK;
+}
+
+/*
+ * do_complete is called when the DO actions are complete.
+ *
+ * We init chunking and trailer bits to their default values here immediately
+ * before receiving any header data for the current request in the pipeline.
+ */
+static void do_complete(struct connectdata *conn)
+{
+  conn->data->req.chunk=FALSE;
+  conn->data->req.maxfd = (conn->sockfd>conn->writesockfd?
+                           conn->sockfd:conn->writesockfd)+1;
+  Curl_pgrsTime(conn->data, TIMER_PRETRANSFER);
+}
+
+CURLcode Curl_do(struct connectdata **connp, bool *done)
+{
+  CURLcode result=CURLE_OK;
+  struct connectdata *conn = *connp;
+  struct SessionHandle *data = conn->data;
+
+  if(conn->handler->do_it) {
+    /* generic protocol-specific function pointer set in curl_connect() */
+    result = conn->handler->do_it(conn, done);
+
+    /* This was formerly done in curl_transfer.c, but we better do it here */
+    if((CURLE_SEND_ERROR == result) && conn->bits.reuse) {
+      /*
+       * If the connection is using an easy handle, call reconnect
+       * to re-establish the connection.  Otherwise, let the multi logic
+       * figure out how to re-establish the connection.
+       */
+      if(!data->multi) {
+        result = Curl_reconnect_request(connp);
+
+        if(result == CURLE_OK) {
+          /* ... finally back to actually retry the DO phase */
+          conn = *connp; /* re-assign conn since Curl_reconnect_request
+                            creates a new connection */
+          result = conn->handler->do_it(conn, done);
+        }
+      }
+      else
+        return result;
+    }
+
+    if((result == CURLE_OK) && *done)
+      /* do_complete must be called after the protocol-specific DO function */
+      do_complete(conn);
+  }
+  return result;
+}
+
+/*
+ * Curl_do_more() is called during the DO_MORE multi state. It is basically a
+ * second stage DO state which (wrongly) was introduced to support FTP's
+ * second connection.
+ *
+ * TODO: A future libcurl should be able to work away this state.
+ *
+ */
+
+CURLcode Curl_do_more(struct connectdata *conn, bool *completed)
+{
+  CURLcode result=CURLE_OK;
+
+  *completed = FALSE;
+
+  if(conn->handler->do_more)
+    result = conn->handler->do_more(conn, completed);
+
+  if(!result && *completed)
+    /* do_complete must be called after the protocol-specific DO function */
+    do_complete(conn);
+
+  return result;
+}
+
+/* Called on connect, and if there's already a protocol-specific struct
+   allocated for a different connection, this frees it that it can be setup
+   properly later on. */
+void Curl_reset_reqproto(struct connectdata *conn)
+{
+  struct SessionHandle *data = conn->data;
+  if(data->state.proto.generic && data->state.current_conn != conn) {
+    free(data->state.proto.generic);
+    data->state.proto.generic = NULL;
+  }
+  data->state.current_conn = conn;
+}
diff --git a/lib/curl_version.c b/lib/curl_version.c
new file mode 100644 (file)
index 0000000..fe1f736
--- /dev/null
@@ -0,0 +1,343 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+#include "curl_urldata.h"
+#include "curl_sslgen.h"
+
+#define _MPRINTF_REPLACE /* use the internal *printf() functions */
+#include <curl/mprintf.h>
+
+#ifdef USE_ARES
+#  if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
+     (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__))
+#    define CARES_STATICLIB
+#  endif
+#  include <ares.h>
+#endif
+
+#ifdef USE_LIBIDN
+#include <stringprep.h>
+#endif
+
+#if defined(HAVE_ICONV) && defined(CURL_DOES_CONVERSIONS)
+#include <iconv.h>
+#endif
+
+#ifdef USE_LIBRTMP
+#include <librtmp/rtmp.h>
+#endif
+
+#ifdef USE_LIBSSH2
+#include <libssh2.h>
+#endif
+
+#ifdef HAVE_LIBSSH2_VERSION
+/* get it run-time if possible */
+#define CURL_LIBSSH2_VERSION libssh2_version(0)
+#else
+/* use build-time if run-time not possible */
+#define CURL_LIBSSH2_VERSION LIBSSH2_VERSION
+#endif
+
+char *curl_version(void)
+{
+  static char version[200];
+  char *ptr = version;
+  size_t len;
+  size_t left = sizeof(version);
+
+  strcpy(ptr, LIBCURL_NAME "/" LIBCURL_VERSION);
+  len = strlen(ptr);
+  left -= len;
+  ptr += len;
+
+  if(left > 1) {
+    len = Curl_ssl_version(ptr + 1, left - 1);
+
+    if(len > 0) {
+      *ptr = ' ';
+      left -= ++len;
+      ptr += len;
+    }
+  }
+
+#ifdef HAVE_LIBZ
+  len = snprintf(ptr, left, " zlib/%s", zlibVersion());
+  left -= len;
+  ptr += len;
+#endif
+#ifdef USE_ARES
+  /* this function is only present in c-ares, not in the original ares */
+  len = snprintf(ptr, left, " c-ares/%s", ares_version(NULL));
+  left -= len;
+  ptr += len;
+#endif
+#ifdef USE_LIBIDN
+  if(stringprep_check_version(LIBIDN_REQUIRED_VERSION)) {
+    len = snprintf(ptr, left, " libidn/%s", stringprep_check_version(NULL));
+    left -= len;
+    ptr += len;
+  }
+#endif
+#ifdef USE_WIN32_IDN
+  len = snprintf(ptr, left, " WinIDN");
+  left -= len;
+  ptr += len;
+#endif
+#if defined(HAVE_ICONV) && defined(CURL_DOES_CONVERSIONS)
+#ifdef _LIBICONV_VERSION
+  len = snprintf(ptr, left, " iconv/%d.%d",
+                 _LIBICONV_VERSION >> 8, _LIBICONV_VERSION & 255);
+#else
+  /* version unknown */
+  len = snprintf(ptr, left, " iconv");
+#endif /* _LIBICONV_VERSION */
+  left -= len;
+  ptr += len;
+#endif
+#ifdef USE_LIBSSH2
+  len = snprintf(ptr, left, " libssh2/%s", CURL_LIBSSH2_VERSION);
+  left -= len;
+  ptr += len;
+#endif
+#ifdef USE_LIBRTMP
+  {
+    char suff[2];
+    if(RTMP_LIB_VERSION & 0xff) {
+      suff[0] = (RTMP_LIB_VERSION & 0xff) + 'a' - 1;
+      suff[1] = '\0';
+    }
+    else
+      suff[0] = '\0';
+
+    snprintf(ptr, left, " librtmp/%d.%d%s",
+             RTMP_LIB_VERSION >> 16, (RTMP_LIB_VERSION >> 8) & 0xff,
+             suff);
+/*
+  If another lib version is added below this one, this code would
+  also have to do:
+
+    len = what snprintf() returned
+
+    left -= len;
+    ptr += len;
+*/
+  }
+#endif
+
+  return version;
+}
+
+/* data for curl_version_info
+
+   Keep the list sorted alphabetically. It is also written so that each
+   protocol line has its own #if line to make things easier on the eye.
+ */
+
+static const char * const protocols[] = {
+#ifndef CURL_DISABLE_DICT
+  "dict",
+#endif
+#ifndef CURL_DISABLE_FILE
+  "file",
+#endif
+#ifndef CURL_DISABLE_FTP
+  "ftp",
+#endif
+#if defined(USE_SSL) && !defined(CURL_DISABLE_FTP)
+  "ftps",
+#endif
+#ifndef CURL_DISABLE_GOPHER
+  "gopher",
+#endif
+#ifndef CURL_DISABLE_HTTP
+  "http",
+#endif
+#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP)
+  "https",
+#endif
+#ifndef CURL_DISABLE_IMAP
+  "imap",
+#endif
+#if defined(USE_SSL) && !defined(CURL_DISABLE_IMAP)
+  "imaps",
+#endif
+#ifndef CURL_DISABLE_LDAP
+  "ldap",
+#if !defined(CURL_DISABLE_LDAPS) && \
+    ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \
+     (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL)))
+  "ldaps",
+#endif
+#endif
+#ifndef CURL_DISABLE_POP3
+  "pop3",
+#endif
+#if defined(USE_SSL) && !defined(CURL_DISABLE_POP3)
+  "pop3s",
+#endif
+#ifdef USE_LIBRTMP
+  "rtmp",
+#endif
+#ifndef CURL_DISABLE_RTSP
+  "rtsp",
+#endif
+#ifdef USE_LIBSSH2
+  "scp",
+#endif
+#ifdef USE_LIBSSH2
+  "sftp",
+#endif
+#ifndef CURL_DISABLE_SMTP
+  "smtp",
+#endif
+#if defined(USE_SSL) && !defined(CURL_DISABLE_SMTP)
+  "smtps",
+#endif
+#ifndef CURL_DISABLE_TELNET
+  "telnet",
+#endif
+#ifndef CURL_DISABLE_TFTP
+  "tftp",
+#endif
+
+  NULL
+};
+
+static curl_version_info_data version_info = {
+  CURLVERSION_NOW,
+  LIBCURL_VERSION,
+  LIBCURL_VERSION_NUM,
+  OS, /* as found by configure or set by hand at build-time */
+  0 /* features is 0 by default */
+#ifdef ENABLE_IPV6
+  | CURL_VERSION_IPV6
+#endif
+#ifdef HAVE_KRB4
+  | CURL_VERSION_KERBEROS4
+#endif
+#ifdef USE_SSL
+  | CURL_VERSION_SSL
+#endif
+#ifdef USE_NTLM
+  | CURL_VERSION_NTLM
+#endif
+#if defined(USE_NTLM) && defined(NTLM_WB_ENABLED)
+  | CURL_VERSION_NTLM_WB
+#endif
+#ifdef USE_WINDOWS_SSPI
+  | CURL_VERSION_SSPI
+#endif
+#ifdef HAVE_LIBZ
+  | CURL_VERSION_LIBZ
+#endif
+#ifdef USE_HTTP_NEGOTIATE
+  | CURL_VERSION_GSSNEGOTIATE
+#endif
+#ifdef DEBUGBUILD
+  | CURL_VERSION_DEBUG
+#endif
+#ifdef CURLDEBUG
+  | CURL_VERSION_CURLDEBUG
+#endif
+#ifdef CURLRES_ASYNCH
+  | CURL_VERSION_ASYNCHDNS
+#endif
+#ifdef HAVE_SPNEGO
+  | CURL_VERSION_SPNEGO
+#endif
+#if (CURL_SIZEOF_CURL_OFF_T > 4) && \
+    ( (SIZEOF_OFF_T > 4) || defined(USE_WIN32_LARGE_FILES) )
+  | CURL_VERSION_LARGEFILE
+#endif
+#if defined(CURL_DOES_CONVERSIONS)
+  | CURL_VERSION_CONV
+#endif
+#if defined(USE_TLS_SRP)
+  | CURL_VERSION_TLSAUTH_SRP
+#endif
+  ,
+  NULL, /* ssl_version */
+  0,    /* ssl_version_num, this is kept at zero */
+  NULL, /* zlib_version */
+  protocols,
+  NULL, /* c-ares version */
+  0,    /* c-ares version numerical */
+  NULL, /* libidn version */
+  0,    /* iconv version */
+  NULL, /* ssh lib version */
+};
+
+curl_version_info_data *curl_version_info(CURLversion stamp)
+{
+#ifdef USE_LIBSSH2
+  static char ssh_buffer[80];
+#endif
+
+#ifdef USE_SSL
+  static char ssl_buffer[80];
+  Curl_ssl_version(ssl_buffer, sizeof(ssl_buffer));
+  version_info.ssl_version = ssl_buffer;
+#endif
+
+#ifdef HAVE_LIBZ
+  version_info.libz_version = zlibVersion();
+  /* libz left NULL if non-existing */
+#endif
+#ifdef USE_ARES
+  {
+    int aresnum;
+    version_info.ares = ares_version(&aresnum);
+    version_info.ares_num = aresnum;
+  }
+#endif
+#ifdef USE_LIBIDN
+  /* This returns a version string if we use the given version or later,
+     otherwise it returns NULL */
+  version_info.libidn = stringprep_check_version(LIBIDN_REQUIRED_VERSION);
+  if(version_info.libidn)
+    version_info.features |= CURL_VERSION_IDN;
+#elif defined(USE_WIN32_IDN)
+  version_info.features |= CURL_VERSION_IDN;
+#endif
+
+#if defined(HAVE_ICONV) && defined(CURL_DOES_CONVERSIONS)
+#ifdef _LIBICONV_VERSION
+  version_info.iconv_ver_num = _LIBICONV_VERSION;
+#else
+  /* version unknown */
+  version_info.iconv_ver_num = -1;
+#endif /* _LIBICONV_VERSION */
+#endif
+
+#ifdef USE_LIBSSH2
+  snprintf(ssh_buffer, sizeof(ssh_buffer), "libssh2/%s", LIBSSH2_VERSION);
+  version_info.libssh_version = ssh_buffer;
+#endif
+
+  (void)stamp; /* avoid compiler warnings, we don't use this */
+
+  return &version_info;
+}
diff --git a/lib/curl_warnless.c b/lib/curl_warnless.c
new file mode 100644 (file)
index 0000000..30cdbe6
--- /dev/null
@@ -0,0 +1,431 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if defined(__INTEL_COMPILER) && defined(__unix__)
+
+#ifdef HAVE_NETINET_IN_H
+#  include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#  include <arpa/inet.h>
+#endif
+
+#endif /* __INTEL_COMPILER && __unix__ */
+
+#define BUILDING_WARNLESS_C 1
+
+#include "curl_warnless.h"
+
+#define CURL_MASK_SCHAR  0x7F
+#define CURL_MASK_UCHAR  0xFF
+
+#if (SIZEOF_SHORT == 2)
+#  define CURL_MASK_SSHORT  0x7FFF
+#  define CURL_MASK_USHORT  0xFFFF
+#elif (SIZEOF_SHORT == 4)
+#  define CURL_MASK_SSHORT  0x7FFFFFFF
+#  define CURL_MASK_USHORT  0xFFFFFFFF
+#elif (SIZEOF_SHORT == 8)
+#  define CURL_MASK_SSHORT  0x7FFFFFFFFFFFFFFF
+#  define CURL_MASK_USHORT  0xFFFFFFFFFFFFFFFF
+#else
+#  error "SIZEOF_SHORT not defined"
+#endif
+
+#if (SIZEOF_INT == 2)
+#  define CURL_MASK_SINT  0x7FFF
+#  define CURL_MASK_UINT  0xFFFF
+#elif (SIZEOF_INT == 4)
+#  define CURL_MASK_SINT  0x7FFFFFFF
+#  define CURL_MASK_UINT  0xFFFFFFFF
+#elif (SIZEOF_INT == 8)
+#  define CURL_MASK_SINT  0x7FFFFFFFFFFFFFFF
+#  define CURL_MASK_UINT  0xFFFFFFFFFFFFFFFF
+#elif (SIZEOF_INT == 16)
+#  define CURL_MASK_SINT  0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+#  define CURL_MASK_UINT  0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+#else
+#  error "SIZEOF_INT not defined"
+#endif
+
+#if (CURL_SIZEOF_LONG == 2)
+#  define CURL_MASK_SLONG  0x7FFFL
+#  define CURL_MASK_ULONG  0xFFFFUL
+#elif (CURL_SIZEOF_LONG == 4)
+#  define CURL_MASK_SLONG  0x7FFFFFFFL
+#  define CURL_MASK_ULONG  0xFFFFFFFFUL
+#elif (CURL_SIZEOF_LONG == 8)
+#  define CURL_MASK_SLONG  0x7FFFFFFFFFFFFFFFL
+#  define CURL_MASK_ULONG  0xFFFFFFFFFFFFFFFFUL
+#elif (CURL_SIZEOF_LONG == 16)
+#  define CURL_MASK_SLONG  0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFL
+#  define CURL_MASK_ULONG  0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFUL
+#else
+#  error "CURL_SIZEOF_LONG not defined"
+#endif
+
+#if (CURL_SIZEOF_CURL_OFF_T == 2)
+#  define CURL_MASK_SCOFFT  CURL_OFF_T_C(0x7FFF)
+#  define CURL_MASK_UCOFFT  CURL_OFF_TU_C(0xFFFF)
+#elif (CURL_SIZEOF_CURL_OFF_T == 4)
+#  define CURL_MASK_SCOFFT  CURL_OFF_T_C(0x7FFFFFFF)
+#  define CURL_MASK_UCOFFT  CURL_OFF_TU_C(0xFFFFFFFF)
+#elif (CURL_SIZEOF_CURL_OFF_T == 8)
+#  define CURL_MASK_SCOFFT  CURL_OFF_T_C(0x7FFFFFFFFFFFFFFF)
+#  define CURL_MASK_UCOFFT  CURL_OFF_TU_C(0xFFFFFFFFFFFFFFFF)
+#elif (CURL_SIZEOF_CURL_OFF_T == 16)
+#  define CURL_MASK_SCOFFT  CURL_OFF_T_C(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
+#  define CURL_MASK_UCOFFT  CURL_OFF_TU_C(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
+#else
+#  error "CURL_SIZEOF_CURL_OFF_T not defined"
+#endif
+
+#if (SIZEOF_SIZE_T == SIZEOF_SHORT)
+#  define CURL_MASK_SSIZE_T  CURL_MASK_SSHORT
+#  define CURL_MASK_USIZE_T  CURL_MASK_USHORT
+#elif (SIZEOF_SIZE_T == SIZEOF_INT)
+#  define CURL_MASK_SSIZE_T  CURL_MASK_SINT
+#  define CURL_MASK_USIZE_T  CURL_MASK_UINT
+#elif (SIZEOF_SIZE_T == CURL_SIZEOF_LONG)
+#  define CURL_MASK_SSIZE_T  CURL_MASK_SLONG
+#  define CURL_MASK_USIZE_T  CURL_MASK_ULONG
+#elif (SIZEOF_SIZE_T == CURL_SIZEOF_CURL_OFF_T)
+#  define CURL_MASK_SSIZE_T  CURL_MASK_SCOFFT
+#  define CURL_MASK_USIZE_T  CURL_MASK_UCOFFT
+#else
+#  error "SIZEOF_SIZE_T not defined"
+#endif
+
+/*
+** unsigned long to unsigned short
+*/
+
+unsigned short curlx_ultous(unsigned long ulnum)
+{
+#ifdef __INTEL_COMPILER
+#  pragma warning(push)
+#  pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+  DEBUGASSERT(ulnum <= (unsigned long) CURL_MASK_USHORT);
+  return (unsigned short)(ulnum & (unsigned long) CURL_MASK_USHORT);
+
+#ifdef __INTEL_COMPILER
+#  pragma warning(pop)
+#endif
+}
+
+/*
+** unsigned long to unsigned char
+*/
+
+unsigned char curlx_ultouc(unsigned long ulnum)
+{
+#ifdef __INTEL_COMPILER
+#  pragma warning(push)
+#  pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+  DEBUGASSERT(ulnum <= (unsigned long) CURL_MASK_UCHAR);
+  return (unsigned char)(ulnum & (unsigned long) CURL_MASK_UCHAR);
+
+#ifdef __INTEL_COMPILER
+#  pragma warning(pop)
+#endif
+}
+
+/*
+** unsigned long to signed int
+*/
+
+int curlx_ultosi(unsigned long ulnum)
+{
+#ifdef __INTEL_COMPILER
+#  pragma warning(push)
+#  pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+  DEBUGASSERT(ulnum <= (unsigned long) CURL_MASK_SINT);
+  return (int)(ulnum & (unsigned long) CURL_MASK_SINT);
+
+#ifdef __INTEL_COMPILER
+#  pragma warning(pop)
+#endif
+}
+
+/*
+** unsigned size_t to signed int
+*/
+
+int curlx_uztosi(size_t uznum)
+{
+#ifdef __INTEL_COMPILER
+#  pragma warning(push)
+#  pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+  DEBUGASSERT(uznum <= (size_t) CURL_MASK_SINT);
+  return (int)(uznum & (size_t) CURL_MASK_SINT);
+
+#ifdef __INTEL_COMPILER
+#  pragma warning(pop)
+#endif
+}
+
+/*
+** unsigned size_t to unsigned long
+*/
+
+unsigned long curlx_uztoul(size_t uznum)
+{
+#ifdef __INTEL_COMPILER
+# pragma warning(push)
+# pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+#if (CURL_SIZEOF_LONG < SIZEOF_SIZE_T)
+  DEBUGASSERT(uznum <= (size_t) CURL_MASK_ULONG);
+#endif
+  return (unsigned long)(uznum & (size_t) CURL_MASK_ULONG);
+
+#ifdef __INTEL_COMPILER
+# pragma warning(pop)
+#endif
+}
+
+/*
+** unsigned size_t to unsigned int
+*/
+
+unsigned int curlx_uztoui(size_t uznum)
+{
+#ifdef __INTEL_COMPILER
+# pragma warning(push)
+# pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+#if (SIZEOF_INT < SIZEOF_SIZE_T)
+  DEBUGASSERT(uznum <= (size_t) CURL_MASK_UINT);
+#endif
+  return (unsigned int)(uznum & (size_t) CURL_MASK_UINT);
+
+#ifdef __INTEL_COMPILER
+# pragma warning(pop)
+#endif
+}
+
+/*
+** signed long to signed int
+*/
+
+int curlx_sltosi(long slnum)
+{
+#ifdef __INTEL_COMPILER
+#  pragma warning(push)
+#  pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+  DEBUGASSERT(slnum >= 0);
+#if (SIZEOF_INT < CURL_SIZEOF_LONG)
+  DEBUGASSERT((unsigned long) slnum <= (unsigned long) CURL_MASK_SINT);
+#endif
+  return (int)(slnum & (long) CURL_MASK_SINT);
+
+#ifdef __INTEL_COMPILER
+#  pragma warning(pop)
+#endif
+}
+
+/*
+** signed long to unsigned int
+*/
+
+unsigned int curlx_sltoui(long slnum)
+{
+#ifdef __INTEL_COMPILER
+#  pragma warning(push)
+#  pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+  DEBUGASSERT(slnum >= 0);
+#if (SIZEOF_INT < CURL_SIZEOF_LONG)
+  DEBUGASSERT((unsigned long) slnum <= (unsigned long) CURL_MASK_UINT);
+#endif
+  return (unsigned int)(slnum & (long) CURL_MASK_UINT);
+
+#ifdef __INTEL_COMPILER
+#  pragma warning(pop)
+#endif
+}
+
+/*
+** signed long to unsigned short
+*/
+
+unsigned short curlx_sltous(long slnum)
+{
+#ifdef __INTEL_COMPILER
+#  pragma warning(push)
+#  pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+  DEBUGASSERT(slnum >= 0);
+  DEBUGASSERT((unsigned long) slnum <= (unsigned long) CURL_MASK_USHORT);
+  return (unsigned short)(slnum & (long) CURL_MASK_USHORT);
+
+#ifdef __INTEL_COMPILER
+#  pragma warning(pop)
+#endif
+}
+
+/*
+** unsigned size_t to signed ssize_t
+*/
+
+ssize_t curlx_uztosz(size_t uznum)
+{
+#ifdef __INTEL_COMPILER
+#  pragma warning(push)
+#  pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+  DEBUGASSERT(uznum <= (size_t) CURL_MASK_SSIZE_T);
+  return (ssize_t)(uznum & (size_t) CURL_MASK_SSIZE_T);
+
+#ifdef __INTEL_COMPILER
+#  pragma warning(pop)
+#endif
+}
+
+/*
+** signed curl_off_t to unsigned size_t
+*/
+
+size_t curlx_sotouz(curl_off_t sonum)
+{
+#ifdef __INTEL_COMPILER
+#  pragma warning(push)
+#  pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+  DEBUGASSERT(sonum >= 0);
+  return (size_t)(sonum & (curl_off_t) CURL_MASK_USIZE_T);
+
+#ifdef __INTEL_COMPILER
+#  pragma warning(pop)
+#endif
+}
+
+/*
+** signed ssize_t to signed int
+*/
+
+int curlx_sztosi(ssize_t sznum)
+{
+#ifdef __INTEL_COMPILER
+#  pragma warning(push)
+#  pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+  DEBUGASSERT(sznum >= 0);
+#if (SIZEOF_INT < SIZEOF_SIZE_T)
+  DEBUGASSERT((size_t) sznum <= (size_t) CURL_MASK_SINT);
+#endif
+  return (int)(sznum & (ssize_t) CURL_MASK_SINT);
+
+#ifdef __INTEL_COMPILER
+#  pragma warning(pop)
+#endif
+}
+
+/*
+** signed int to unsigned size_t
+*/
+
+size_t curlx_sitouz(int sinum)
+{
+#ifdef __INTEL_COMPILER
+#  pragma warning(push)
+#  pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+  DEBUGASSERT(sinum >= 0);
+  return (size_t) sinum;
+
+#ifdef __INTEL_COMPILER
+#  pragma warning(pop)
+#endif
+}
+
+#if defined(__INTEL_COMPILER) && defined(__unix__)
+
+int curlx_FD_ISSET(int fd, fd_set *fdset)
+{
+  #pragma warning(push)
+  #pragma warning(disable:1469) /* clobber ignored */
+  return FD_ISSET(fd, fdset);
+  #pragma warning(pop)
+}
+
+void curlx_FD_SET(int fd, fd_set *fdset)
+{
+  #pragma warning(push)
+  #pragma warning(disable:1469) /* clobber ignored */
+  FD_SET(fd, fdset);
+  #pragma warning(pop)
+}
+
+void curlx_FD_ZERO(fd_set *fdset)
+{
+  #pragma warning(push)
+  #pragma warning(disable:593) /* variable was set but never used */
+  FD_ZERO(fdset);
+  #pragma warning(pop)
+}
+
+unsigned short curlx_htons(unsigned short usnum)
+{
+#if (__INTEL_COMPILER == 910) && defined(__i386__)
+  return (unsigned short)(((usnum << 8) & 0xFF00) | ((usnum >> 8) & 0x00FF));
+#else
+  #pragma warning(push)
+  #pragma warning(disable:810) /* conversion may lose significant bits */
+  return htons(usnum);
+  #pragma warning(pop)
+#endif
+}
+
+unsigned short curlx_ntohs(unsigned short usnum)
+{
+#if (__INTEL_COMPILER == 910) && defined(__i386__)
+  return (unsigned short)(((usnum << 8) & 0xFF00) | ((usnum >> 8) & 0x00FF));
+#else
+  #pragma warning(push)
+  #pragma warning(disable:810) /* conversion may lose significant bits */
+  return ntohs(usnum);
+  #pragma warning(pop)
+#endif
+}
+
+#endif /* __INTEL_COMPILER && __unix__ */
diff --git a/lib/curl_wildcard.c b/lib/curl_wildcard.c
new file mode 100644 (file)
index 0000000..d6ba2b2
--- /dev/null
@@ -0,0 +1,77 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include "curl_wildcard.h"
+#include "curl_llist.h"
+#include "curl_fileinfo.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "curl_memdebug.h"
+
+CURLcode Curl_wildcard_init(struct WildcardData *wc)
+{
+  DEBUGASSERT(wc->filelist == NULL);
+  /* now allocate only wc->filelist, everything else
+     will be allocated if it is needed. */
+  wc->filelist = Curl_llist_alloc(Curl_fileinfo_dtor);
+  if(!wc->filelist) {;
+    return CURLE_OUT_OF_MEMORY;
+  }
+  return CURLE_OK;
+}
+
+void Curl_wildcard_dtor(struct WildcardData *wc)
+{
+  if(!wc)
+    return;
+
+  if(wc->tmp_dtor) {
+    wc->tmp_dtor(wc->tmp);
+    wc->tmp_dtor = ZERO_NULL;
+    wc->tmp = NULL;
+  }
+  DEBUGASSERT(wc->tmp == NULL);
+
+  if(wc->filelist) {
+    Curl_llist_destroy(wc->filelist, NULL);
+    wc->filelist = NULL;
+  }
+
+  if(wc->path) {
+    free(wc->path);
+    wc->path = NULL;
+  }
+
+  if(wc->pattern) {
+    free(wc->pattern);
+    wc->pattern = NULL;
+  }
+
+  wc->customptr = NULL;
+  wc->state = CURLWC_INIT;
+}
diff --git a/lib/cyassl.c b/lib/cyassl.c
deleted file mode 100644 (file)
index 32f1cfe..0000000
+++ /dev/null
@@ -1,611 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-/*
- * Source file for all CyaSSL-specific code for the TLS/SSL layer. No code
- * but curl_sslgen.c should ever call or use these functions.
- *
- */
-
-#include "curl_setup.h"
-
-#ifdef USE_CYASSL
-
-#ifdef HAVE_LIMITS_H
-#include <limits.h>
-#endif
-
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_inet_pton.h"
-#include "curl_cyassl.h"
-#include "curl_sslgen.h"
-#include "curl_parsedate.h"
-#include "curl_connect.h" /* for the connect timeout */
-#include "curl_select.h"
-#include "curl_rawstr.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-#include <cyassl/ssl.h>
-#include <cyassl/error.h>
-
-
-static Curl_recv cyassl_recv;
-static Curl_send cyassl_send;
-
-
-static int do_file_type(const char *type)
-{
-  if(!type || !type[0])
-    return SSL_FILETYPE_PEM;
-  if(Curl_raw_equal(type, "PEM"))
-    return SSL_FILETYPE_PEM;
-  if(Curl_raw_equal(type, "DER"))
-    return SSL_FILETYPE_ASN1;
-  return -1;
-}
-
-/*
- * This function loads all the client/CA certificates and CRLs. Setup the TLS
- * layer and do all necessary magic.
- */
-static CURLcode
-cyassl_connect_step1(struct connectdata *conn,
-                     int sockindex)
-{
-  struct SessionHandle *data = conn->data;
-  struct ssl_connect_data* conssl = &conn->ssl[sockindex];
-  SSL_METHOD* req_method = NULL;
-  void* ssl_sessionid = NULL;
-  curl_socket_t sockfd = conn->sock[sockindex];
-
-  if(conssl->state == ssl_connection_complete)
-    return CURLE_OK;
-
-  /* CyaSSL doesn't support SSLv2 */
-  if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) {
-    failf(data, "CyaSSL does not support SSLv2");
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-
-  /* check to see if we've been told to use an explicit SSL/TLS version */
-  switch(data->set.ssl.version) {
-  case CURL_SSLVERSION_DEFAULT:
-    /* we try to figure out version */
-    req_method = SSLv23_client_method();
-    break;
-  case CURL_SSLVERSION_TLSv1:
-    req_method = TLSv1_client_method();
-    break;
-  case CURL_SSLVERSION_SSLv3:
-    req_method = SSLv3_client_method();
-    break;
-  default:
-    req_method = TLSv1_client_method();
-  }
-
-  if(!req_method) {
-    failf(data, "SSL: couldn't create a method!");
-    return CURLE_OUT_OF_MEMORY;
-  }
-
-  if(conssl->ctx)
-    SSL_CTX_free(conssl->ctx);
-  conssl->ctx = SSL_CTX_new(req_method);
-
-  if(!conssl->ctx) {
-    failf(data, "SSL: couldn't create a context!");
-    return CURLE_OUT_OF_MEMORY;
-  }
-
-#ifndef NO_FILESYSTEM
-  /* load trusted cacert */
-  if(data->set.str[STRING_SSL_CAFILE]) {
-    if(!SSL_CTX_load_verify_locations(conssl->ctx,
-                                      data->set.str[STRING_SSL_CAFILE],
-                                      data->set.str[STRING_SSL_CAPATH])) {
-      if(data->set.ssl.verifypeer) {
-        /* Fail if we insiste on successfully verifying the server. */
-        failf(data,"error setting certificate verify locations:\n"
-              "  CAfile: %s\n  CApath: %s",
-              data->set.str[STRING_SSL_CAFILE]?
-              data->set.str[STRING_SSL_CAFILE]: "none",
-              data->set.str[STRING_SSL_CAPATH]?
-              data->set.str[STRING_SSL_CAPATH] : "none");
-        return CURLE_SSL_CACERT_BADFILE;
-      }
-      else {
-        /* Just continue with a warning if no strict  certificate
-           verification is required. */
-        infof(data, "error setting certificate verify locations,"
-              " continuing anyway:\n");
-      }
-    }
-    else {
-      /* Everything is fine. */
-      infof(data, "successfully set certificate verify locations:\n");
-    }
-    infof(data,
-          "  CAfile: %s\n"
-          "  CApath: %s\n",
-          data->set.str[STRING_SSL_CAFILE] ? data->set.str[STRING_SSL_CAFILE]:
-          "none",
-          data->set.str[STRING_SSL_CAPATH] ? data->set.str[STRING_SSL_CAPATH]:
-          "none");
-  }
-
-  /* Load the client certificate, and private key */
-  if(data->set.str[STRING_CERT] && data->set.str[STRING_KEY]) {
-    int file_type = do_file_type(data->set.str[STRING_CERT_TYPE]);
-
-    if(SSL_CTX_use_certificate_file(conssl->ctx, data->set.str[STRING_CERT],
-                                     file_type) != 1) {
-      failf(data, "unable to use client certificate (no key or wrong pass"
-            " phrase?)");
-      return CURLE_SSL_CONNECT_ERROR;
-    }
-
-    file_type = do_file_type(data->set.str[STRING_KEY_TYPE]);
-    if(SSL_CTX_use_PrivateKey_file(conssl->ctx, data->set.str[STRING_KEY],
-                                    file_type) != 1) {
-      failf(data, "unable to set private key");
-      return CURLE_SSL_CONNECT_ERROR;
-    }
-  }
-#else
-  if(CyaSSL_no_filesystem_verify(conssl->ctx)!= SSL_SUCCESS) {
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-#endif /* NO_FILESYSTEM */
-
-  /* SSL always tries to verify the peer, this only says whether it should
-   * fail to connect if the verification fails, or if it should continue
-   * anyway. In the latter case the result of the verification is checked with
-   * SSL_get_verify_result() below. */
-  SSL_CTX_set_verify(conssl->ctx,
-                     data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE,
-                     NULL);
-
-  /* Let's make an SSL structure */
-  if(conssl->handle)
-    SSL_free(conssl->handle);
-  conssl->handle = SSL_new(conssl->ctx);
-  if(!conssl->handle) {
-    failf(data, "SSL: couldn't create a context (handle)!");
-    return CURLE_OUT_OF_MEMORY;
-  }
-
-  /* Check if there's a cached ID we can/should use here! */
-  if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) {
-    /* we got a session id, use it! */
-    if(!SSL_set_session(conssl->handle, ssl_sessionid)) {
-      failf(data, "SSL: SSL_set_session failed: %s",
-            ERR_error_string(SSL_get_error(conssl->handle, 0),NULL));
-      return CURLE_SSL_CONNECT_ERROR;
-    }
-    /* Informational message */
-    infof (data, "SSL re-using session ID\n");
-  }
-
-  /* pass the raw socket into the SSL layer */
-  if(!SSL_set_fd(conssl->handle, (int)sockfd)) {
-    failf(data, "SSL: SSL_set_fd failed");
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-
-  conssl->connecting_state = ssl_connect_2;
-  return CURLE_OK;
-}
-
-
-static CURLcode
-cyassl_connect_step2(struct connectdata *conn,
-                     int sockindex)
-{
-  int ret = -1;
-  struct SessionHandle *data = conn->data;
-  struct ssl_connect_data* conssl = &conn->ssl[sockindex];
-
-  infof(data, "CyaSSL: Connecting to %s:%d\n",
-        conn->host.name, conn->remote_port);
-
-  conn->recv[sockindex] = cyassl_recv;
-  conn->send[sockindex] = cyassl_send;
-
-  /* Enable RFC2818 checks */
-  if(data->set.ssl.verifyhost) {
-    ret = CyaSSL_check_domain_name(conssl->handle, conn->host.name);
-    if(ret == SSL_FAILURE)
-      return CURLE_OUT_OF_MEMORY;
-  }
-
-  ret = SSL_connect(conssl->handle);
-  if(ret != 1) {
-    char error_buffer[80];
-    int  detail = SSL_get_error(conssl->handle, ret);
-
-    if(SSL_ERROR_WANT_READ == detail) {
-      conssl->connecting_state = ssl_connect_2_reading;
-      return CURLE_OK;
-    }
-    else if(SSL_ERROR_WANT_WRITE == detail) {
-      conssl->connecting_state = ssl_connect_2_writing;
-      return CURLE_OK;
-    }
-    /* There is no easy way to override only the CN matching.
-     * This will enable the override of both mismatching SubjectAltNames
-     * as also mismatching CN fields */
-    else if(DOMAIN_NAME_MISMATCH == detail) {
-#if 1
-      failf(data, "\tsubject alt name(s) or common name do not match \"%s\"\n",
-            conn->host.dispname);
-      return CURLE_PEER_FAILED_VERIFICATION;
-#else
-      /* When the CyaSSL_check_domain_name() is used and you desire to continue
-       * on a DOMAIN_NAME_MISMATCH, i.e. 'data->set.ssl.verifyhost == 0',
-       * CyaSSL version 2.4.0 will fail with an INCOMPLETE_DATA error. The only
-       * way to do this is currently to switch the CyaSSL_check_domain_name()
-       * in and out based on the 'data->set.ssl.verifyhost' value. */
-      if(data->set.ssl.verifyhost) {
-        failf(data,
-              "\tsubject alt name(s) or common name do not match \"%s\"\n",
-              conn->host.dispname);
-        return CURLE_PEER_FAILED_VERIFICATION;
-      }
-      else {
-        infof(data,
-              "\tsubject alt name(s) and/or common name do not match \"%s\"\n",
-              conn->host.dispname);
-        return CURLE_OK;
-      }
-#endif
-    }
-    else {
-      failf(data, "SSL_connect failed with error %d: %s", detail,
-          ERR_error_string(detail, error_buffer));
-      return CURLE_SSL_CONNECT_ERROR;
-    }
-  }
-
-  conssl->connecting_state = ssl_connect_3;
-  infof(data, "SSL connected\n");
-
-  return CURLE_OK;
-}
-
-
-static CURLcode
-cyassl_connect_step3(struct connectdata *conn,
-                     int sockindex)
-{
-  CURLcode retcode = CURLE_OK;
-  void *old_ssl_sessionid=NULL;
-  struct SessionHandle *data = conn->data;
-  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-  int incache;
-  SSL_SESSION *our_ssl_sessionid;
-
-  DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
-
-  our_ssl_sessionid = SSL_get_session(connssl->handle);
-
-  incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL));
-  if(incache) {
-    if(old_ssl_sessionid != our_ssl_sessionid) {
-      infof(data, "old SSL session ID is stale, removing\n");
-      Curl_ssl_delsessionid(conn, old_ssl_sessionid);
-      incache = FALSE;
-    }
-  }
-  if(!incache) {
-    retcode = Curl_ssl_addsessionid(conn, our_ssl_sessionid,
-                                    0 /* unknown size */);
-    if(retcode) {
-      failf(data, "failed to store ssl session");
-      return retcode;
-    }
-  }
-
-  connssl->connecting_state = ssl_connect_done;
-
-  return retcode;
-}
-
-
-static ssize_t cyassl_send(struct connectdata *conn,
-                           int sockindex,
-                           const void *mem,
-                           size_t len,
-                           CURLcode *curlcode)
-{
-  char error_buffer[80];
-  int  memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
-  int  rc     = SSL_write(conn->ssl[sockindex].handle, mem, memlen);
-
-  if(rc < 0) {
-    int err = SSL_get_error(conn->ssl[sockindex].handle, rc);
-
-    switch(err) {
-    case SSL_ERROR_WANT_READ:
-    case SSL_ERROR_WANT_WRITE:
-      /* there's data pending, re-invoke SSL_write() */
-      *curlcode = CURLE_AGAIN;
-      return -1;
-    default:
-      failf(conn->data, "SSL write: %s, errno %d",
-            ERR_error_string(err, error_buffer),
-            SOCKERRNO);
-      *curlcode = CURLE_SEND_ERROR;
-      return -1;
-    }
-  }
-  return rc;
-}
-
-void Curl_cyassl_close_all(struct SessionHandle *data)
-{
-  (void)data;
-}
-
-void Curl_cyassl_close(struct connectdata *conn, int sockindex)
-{
-  struct ssl_connect_data *conssl = &conn->ssl[sockindex];
-
-  if(conssl->handle) {
-    (void)SSL_shutdown(conssl->handle);
-    SSL_free (conssl->handle);
-    conssl->handle = NULL;
-  }
-  if(conssl->ctx) {
-    SSL_CTX_free (conssl->ctx);
-    conssl->ctx = NULL;
-  }
-}
-
-static ssize_t cyassl_recv(struct connectdata *conn,
-                           int num,
-                           char *buf,
-                           size_t buffersize,
-                           CURLcode *curlcode)
-{
-  char error_buffer[80];
-  int  buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
-  int  nread    = SSL_read(conn->ssl[num].handle, buf, buffsize);
-
-  if(nread < 0) {
-    int err = SSL_get_error(conn->ssl[num].handle, nread);
-
-    switch(err) {
-    case SSL_ERROR_ZERO_RETURN: /* no more data */
-      break;
-    case SSL_ERROR_WANT_READ:
-    case SSL_ERROR_WANT_WRITE:
-      /* there's data pending, re-invoke SSL_read() */
-      *curlcode = CURLE_AGAIN;
-      return -1;
-    default:
-      failf(conn->data, "SSL read: %s, errno %d",
-            ERR_error_string(err, error_buffer),
-            SOCKERRNO);
-      *curlcode = CURLE_RECV_ERROR;
-      return -1;
-    }
-  }
-  return nread;
-}
-
-
-void Curl_cyassl_session_free(void *ptr)
-{
-  (void)ptr;
-  /* CyaSSL reuses sessions on own, no free */
-}
-
-
-size_t Curl_cyassl_version(char *buffer, size_t size)
-{
-#ifdef CYASSL_VERSION
-  return snprintf(buffer, size, "CyaSSL/%s", CYASSL_VERSION);
-#else
-  return snprintf(buffer, size, "CyaSSL/%s", "<1.8.8");
-#endif
-}
-
-
-int Curl_cyassl_init(void)
-{
-  if(CyaSSL_Init() == 0)
-    return 1;
-
-  return -1;
-}
-
-
-bool Curl_cyassl_data_pending(const struct connectdata* conn, int connindex)
-{
-  if(conn->ssl[connindex].handle)   /* SSL is in use */
-    return (0 != SSL_pending(conn->ssl[connindex].handle)) ? TRUE : FALSE;
-  else
-    return FALSE;
-}
-
-
-/*
- * This function is called to shut down the SSL layer but keep the
- * socket open (CCC - Clear Command Channel)
- */
-int Curl_cyassl_shutdown(struct connectdata *conn, int sockindex)
-{
-  int retval = 0;
-  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-
-  if(connssl->handle) {
-    SSL_free (connssl->handle);
-    connssl->handle = NULL;
-  }
-  return retval;
-}
-
-
-static CURLcode
-cyassl_connect_common(struct connectdata *conn,
-                      int sockindex,
-                      bool nonblocking,
-                      bool *done)
-{
-  CURLcode retcode;
-  struct SessionHandle *data = conn->data;
-  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-  curl_socket_t sockfd = conn->sock[sockindex];
-  long timeout_ms;
-  int what;
-
-  /* check if the connection has already been established */
-  if(ssl_connection_complete == connssl->state) {
-    *done = TRUE;
-    return CURLE_OK;
-  }
-
-  if(ssl_connect_1==connssl->connecting_state) {
-    /* Find out how much more time we're allowed */
-    timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
-    if(timeout_ms < 0) {
-      /* no need to continue if time already is up */
-      failf(data, "SSL connection timeout");
-      return CURLE_OPERATION_TIMEDOUT;
-    }
-    retcode = cyassl_connect_step1(conn, sockindex);
-    if(retcode)
-      return retcode;
-  }
-
-  while(ssl_connect_2 == connssl->connecting_state ||
-        ssl_connect_2_reading == connssl->connecting_state ||
-        ssl_connect_2_writing == connssl->connecting_state) {
-
-    /* check allowed time left */
-    timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
-    if(timeout_ms < 0) {
-      /* no need to continue if time already is up */
-      failf(data, "SSL connection timeout");
-      return CURLE_OPERATION_TIMEDOUT;
-    }
-
-    /* if ssl is expecting something, check if it's available. */
-    if(connssl->connecting_state == ssl_connect_2_reading
-       || connssl->connecting_state == ssl_connect_2_writing) {
-
-      curl_socket_t writefd = ssl_connect_2_writing==
-        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
-      curl_socket_t readfd = ssl_connect_2_reading==
-        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
-
-      what = Curl_socket_ready(readfd, writefd, nonblocking?0:timeout_ms);
-      if(what < 0) {
-        /* fatal error */
-        failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
-        return CURLE_SSL_CONNECT_ERROR;
-      }
-      else if(0 == what) {
-        if(nonblocking) {
-          *done = FALSE;
-          return CURLE_OK;
-        }
-        else {
-          /* timeout */
-          failf(data, "SSL connection timeout");
-          return CURLE_OPERATION_TIMEDOUT;
-        }
-      }
-      /* socket is readable or writable */
-    }
-
-    /* Run transaction, and return to the caller if it failed or if
-     * this connection is part of a multi handle and this loop would
-     * execute again. This permits the owner of a multi handle to
-     * abort a connection attempt before step2 has completed while
-     * ensuring that a client using select() or epoll() will always
-     * have a valid fdset to wait on.
-     */
-    retcode = cyassl_connect_step2(conn, sockindex);
-    if(retcode || (nonblocking &&
-                   (ssl_connect_2 == connssl->connecting_state ||
-                    ssl_connect_2_reading == connssl->connecting_state ||
-                    ssl_connect_2_writing == connssl->connecting_state)))
-      return retcode;
-
-  } /* repeat step2 until all transactions are done. */
-
-  if(ssl_connect_3==connssl->connecting_state) {
-    retcode = cyassl_connect_step3(conn, sockindex);
-    if(retcode)
-      return retcode;
-  }
-
-  if(ssl_connect_done==connssl->connecting_state) {
-    connssl->state = ssl_connection_complete;
-    conn->recv[sockindex] = cyassl_recv;
-    conn->send[sockindex] = cyassl_send;
-    *done = TRUE;
-  }
-  else
-    *done = FALSE;
-
-  /* Reset our connect state machine */
-  connssl->connecting_state = ssl_connect_1;
-
-  return CURLE_OK;
-}
-
-
-CURLcode
-Curl_cyassl_connect_nonblocking(struct connectdata *conn,
-                                int sockindex,
-                                bool *done)
-{
-  return cyassl_connect_common(conn, sockindex, TRUE, done);
-}
-
-
-CURLcode
-Curl_cyassl_connect(struct connectdata *conn,
-                    int sockindex)
-{
-  CURLcode retcode;
-  bool done = FALSE;
-
-  retcode = cyassl_connect_common(conn, sockindex, FALSE, &done);
-  if(retcode)
-    return retcode;
-
-  DEBUGASSERT(done);
-
-  return CURLE_OK;
-}
-
-#endif
diff --git a/lib/dict.c b/lib/dict.c
deleted file mode 100644 (file)
index 114ef7c..0000000
+++ /dev/null
@@ -1,284 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifndef CURL_DISABLE_DICT
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef HAVE_NET_IF_H
-#include <net/if.h>
-#endif
-#ifdef HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-
-#ifdef HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-
-#ifdef HAVE_SYS_SELECT_H
-#include <sys/select.h>
-#endif
-
-#include "curl_urldata.h"
-#include <curl/curl.h>
-#include "curl_transfer.h"
-#include "curl_sendf.h"
-
-#include "curl_progress.h"
-#include "curl_strequal.h"
-#include "curl_dict.h"
-#include "curl_rawstr.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/*
- * Forward declarations.
- */
-
-static CURLcode dict_do(struct connectdata *conn, bool *done);
-
-/*
- * DICT protocol handler.
- */
-
-const struct Curl_handler Curl_handler_dict = {
-  "DICT",                               /* scheme */
-  ZERO_NULL,                            /* setup_connection */
-  dict_do,                              /* do_it */
-  ZERO_NULL,                            /* done */
-  ZERO_NULL,                            /* do_more */
-  ZERO_NULL,                            /* connect_it */
-  ZERO_NULL,                            /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
-  ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* readwrite */
-  PORT_DICT,                            /* defport */
-  CURLPROTO_DICT,                       /* protocol */
-  PROTOPT_NONE | PROTOPT_NOURLQUERY      /* flags */
-};
-
-static char *unescape_word(struct SessionHandle *data, const char *inputbuff)
-{
-  char *newp;
-  char *dictp;
-  char *ptr;
-  int len;
-  char byte;
-  int olen=0;
-
-  newp = curl_easy_unescape(data, inputbuff, 0, &len);
-  if(!newp)
-    return NULL;
-
-  dictp = malloc(((size_t)len)*2 + 1); /* add one for terminating zero */
-  if(dictp) {
-    /* According to RFC2229 section 2.2, these letters need to be escaped with
-       \[letter] */
-    for(ptr = newp;
-        (byte = *ptr) != 0;
-        ptr++) {
-      if((byte <= 32) || (byte == 127) ||
-          (byte == '\'') || (byte == '\"') || (byte == '\\')) {
-        dictp[olen++] = '\\';
-      }
-      dictp[olen++] = byte;
-    }
-    dictp[olen]=0;
-
-    free(newp);
-  }
-  return dictp;
-}
-
-static CURLcode dict_do(struct connectdata *conn, bool *done)
-{
-  char *word;
-  char *eword;
-  char *ppath;
-  char *database = NULL;
-  char *strategy = NULL;
-  char *nthdef = NULL; /* This is not part of the protocol, but required
-                          by RFC 2229 */
-  CURLcode result=CURLE_OK;
-  struct SessionHandle *data=conn->data;
-  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
-
-  char *path = data->state.path;
-  curl_off_t *bytecount = &data->req.bytecount;
-
-  *done = TRUE; /* unconditionally */
-
-  if(conn->bits.user_passwd) {
-    /* AUTH is missing */
-  }
-
-  if(Curl_raw_nequal(path, DICT_MATCH, sizeof(DICT_MATCH)-1) ||
-      Curl_raw_nequal(path, DICT_MATCH2, sizeof(DICT_MATCH2)-1) ||
-      Curl_raw_nequal(path, DICT_MATCH3, sizeof(DICT_MATCH3)-1)) {
-
-    word = strchr(path, ':');
-    if(word) {
-      word++;
-      database = strchr(word, ':');
-      if(database) {
-        *database++ = (char)0;
-        strategy = strchr(database, ':');
-        if(strategy) {
-          *strategy++ = (char)0;
-          nthdef = strchr(strategy, ':');
-          if(nthdef) {
-            *nthdef = (char)0;
-          }
-        }
-      }
-    }
-
-    if((word == NULL) || (*word == (char)0)) {
-      infof(data, "lookup word is missing\n");
-      word=(char *)"default";
-    }
-    if((database == NULL) || (*database == (char)0)) {
-      database = (char *)"!";
-    }
-    if((strategy == NULL) || (*strategy == (char)0)) {
-      strategy = (char *)".";
-    }
-
-    eword = unescape_word(data, word);
-    if(!eword)
-      return CURLE_OUT_OF_MEMORY;
-
-    result = Curl_sendf(sockfd, conn,
-                        "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
-                        "MATCH "
-                        "%s "    /* database */
-                        "%s "    /* strategy */
-                        "%s\r\n" /* word */
-                        "QUIT\r\n",
-
-                        database,
-                        strategy,
-                        eword
-                        );
-
-    free(eword);
-
-    if(result) {
-      failf(data, "Failed sending DICT request");
-      return result;
-    }
-    Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount,
-                        -1, NULL); /* no upload */
-  }
-  else if(Curl_raw_nequal(path, DICT_DEFINE, sizeof(DICT_DEFINE)-1) ||
-           Curl_raw_nequal(path, DICT_DEFINE2, sizeof(DICT_DEFINE2)-1) ||
-           Curl_raw_nequal(path, DICT_DEFINE3, sizeof(DICT_DEFINE3)-1)) {
-
-    word = strchr(path, ':');
-    if(word) {
-      word++;
-      database = strchr(word, ':');
-      if(database) {
-        *database++ = (char)0;
-        nthdef = strchr(database, ':');
-        if(nthdef) {
-          *nthdef = (char)0;
-        }
-      }
-    }
-
-    if((word == NULL) || (*word == (char)0)) {
-      infof(data, "lookup word is missing\n");
-      word=(char *)"default";
-    }
-    if((database == NULL) || (*database == (char)0)) {
-      database = (char *)"!";
-    }
-
-    eword = unescape_word(data, word);
-    if(!eword)
-      return CURLE_OUT_OF_MEMORY;
-
-    result = Curl_sendf(sockfd, conn,
-                        "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
-                        "DEFINE "
-                        "%s "     /* database */
-                        "%s\r\n"  /* word */
-                        "QUIT\r\n",
-                        database,
-                        eword);
-
-    free(eword);
-
-    if(result) {
-      failf(data, "Failed sending DICT request");
-      return result;
-    }
-    Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount,
-                        -1, NULL); /* no upload */
-  }
-  else {
-
-    ppath = strchr(path, '/');
-    if(ppath) {
-      int i;
-
-      ppath++;
-      for(i = 0; ppath[i]; i++) {
-        if(ppath[i] == ':')
-          ppath[i] = ' ';
-      }
-      result = Curl_sendf(sockfd, conn,
-                          "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
-                          "%s\r\n"
-                          "QUIT\r\n", ppath);
-      if(result) {
-        failf(data, "Failed sending DICT request");
-        return result;
-      }
-
-      Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount, -1, NULL);
-    }
-  }
-
-  return CURLE_OK;
-}
-#endif /*CURL_DISABLE_DICT*/
diff --git a/lib/easy.c b/lib/easy.c
deleted file mode 100644 (file)
index a2181cc..0000000
+++ /dev/null
@@ -1,897 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef HAVE_NET_IF_H
-#include <net/if.h>
-#endif
-#ifdef HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-
-#ifdef HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-
-#include "curl_strequal.h"
-#include "curl_urldata.h"
-#include <curl/curl.h>
-#include "curl_transfer.h"
-#include "curl_sslgen.h"
-#include "curl_url.h"
-#include "curl_getinfo.h"
-#include "curl_hostip.h"
-#include "curl_share.h"
-#include "curl_strdup.h"
-#include "curl_memory.h"
-#include "curl_progress.h"
-#include "curl_easyif.h"
-#include "curl_select.h"
-#include "curl_sendf.h" /* for failf function prototype */
-#include "curl_ntlm.h"
-#include "curl_connect.h" /* for Curl_getconnectinfo */
-#include "curl_slist.h"
-#include "curl_amigaos.h"
-#include "curl_rand.h"
-#include "curl_non_ascii.h"
-#include "curl_warnless.h"
-#include "curl_conncache.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/* win32_cleanup() is for win32 socket cleanup functionality, the opposite
-   of win32_init() */
-static void win32_cleanup(void)
-{
-#ifdef USE_WINSOCK
-  WSACleanup();
-#endif
-#ifdef USE_WINDOWS_SSPI
-  Curl_sspi_global_cleanup();
-#endif
-}
-
-/* win32_init() performs win32 socket initialization to properly setup the
-   stack to allow networking */
-static CURLcode win32_init(void)
-{
-#ifdef USE_WINSOCK
-  WORD wVersionRequested;
-  WSADATA wsaData;
-  int res;
-
-#if defined(ENABLE_IPV6) && (USE_WINSOCK < 2)
-  Error IPV6_requires_winsock2
-#endif
-
-  wVersionRequested = MAKEWORD(USE_WINSOCK, USE_WINSOCK);
-
-  res = WSAStartup(wVersionRequested, &wsaData);
-
-  if(res != 0)
-    /* Tell the user that we couldn't find a useable */
-    /* winsock.dll.     */
-    return CURLE_FAILED_INIT;
-
-  /* Confirm that the Windows Sockets DLL supports what we need.*/
-  /* Note that if the DLL supports versions greater */
-  /* than wVersionRequested, it will still return */
-  /* wVersionRequested in wVersion. wHighVersion contains the */
-  /* highest supported version. */
-
-  if(LOBYTE( wsaData.wVersion ) != LOBYTE(wVersionRequested) ||
-     HIBYTE( wsaData.wVersion ) != HIBYTE(wVersionRequested) ) {
-    /* Tell the user that we couldn't find a useable */
-
-    /* winsock.dll. */
-    WSACleanup();
-    return CURLE_FAILED_INIT;
-  }
-  /* The Windows Sockets DLL is acceptable. Proceed. */
-#elif defined(USE_LWIPSOCK)
-  lwip_init();
-#endif
-
-#ifdef USE_WINDOWS_SSPI
-  {
-    CURLcode err = Curl_sspi_global_init();
-    if(err != CURLE_OK)
-      return err;
-  }
-#endif
-
-  return CURLE_OK;
-}
-
-#ifdef USE_LIBIDN
-/*
- * Initialise use of IDNA library.
- * It falls back to ASCII if $CHARSET isn't defined. This doesn't work for
- * idna_to_ascii_lz().
- */
-static void idna_init (void)
-{
-#ifdef WIN32
-  char buf[60];
-  UINT cp = GetACP();
-
-  if(!getenv("CHARSET") && cp > 0) {
-    snprintf(buf, sizeof(buf), "CHARSET=cp%u", cp);
-    putenv(buf);
-  }
-#else
-  /* to do? */
-#endif
-}
-#endif  /* USE_LIBIDN */
-
-/* true globals -- for curl_global_init() and curl_global_cleanup() */
-static unsigned int  initialized;
-static long          init_flags;
-
-/*
- * strdup (and other memory functions) is redefined in complicated
- * ways, but at this point it must be defined as the system-supplied strdup
- * so the callback pointer is initialized correctly.
- */
-#if defined(_WIN32_WCE)
-#define system_strdup _strdup
-#elif !defined(HAVE_STRDUP)
-#define system_strdup curlx_strdup
-#else
-#define system_strdup strdup
-#endif
-
-#if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__)
-#  pragma warning(disable:4232) /* MSVC extension, dllimport identity */
-#endif
-
-#ifndef __SYMBIAN32__
-/*
- * If a memory-using function (like curl_getenv) is used before
- * curl_global_init() is called, we need to have these pointers set already.
- */
-curl_malloc_callback Curl_cmalloc = (curl_malloc_callback)malloc;
-curl_free_callback Curl_cfree = (curl_free_callback)free;
-curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc;
-curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup;
-curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc;
-#else
-/*
- * Symbian OS doesn't support initialization to code in writeable static data.
- * Initialization will occur in the curl_global_init() call.
- */
-curl_malloc_callback Curl_cmalloc;
-curl_free_callback Curl_cfree;
-curl_realloc_callback Curl_crealloc;
-curl_strdup_callback Curl_cstrdup;
-curl_calloc_callback Curl_ccalloc;
-#endif
-
-#if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__)
-#  pragma warning(default:4232) /* MSVC extension, dllimport identity */
-#endif
-
-/**
- * curl_global_init() globally initializes cURL given a bitwise set of the
- * different features of what to initialize.
- */
-CURLcode curl_global_init(long flags)
-{
-  if(initialized++)
-    return CURLE_OK;
-
-  /* Setup the default memory functions here (again) */
-  Curl_cmalloc = (curl_malloc_callback)malloc;
-  Curl_cfree = (curl_free_callback)free;
-  Curl_crealloc = (curl_realloc_callback)realloc;
-  Curl_cstrdup = (curl_strdup_callback)system_strdup;
-  Curl_ccalloc = (curl_calloc_callback)calloc;
-
-  if(flags & CURL_GLOBAL_SSL)
-    if(!Curl_ssl_init()) {
-      DEBUGF(fprintf(stderr, "Error: Curl_ssl_init failed\n"));
-      return CURLE_FAILED_INIT;
-    }
-
-  if(flags & CURL_GLOBAL_WIN32)
-    if(win32_init() != CURLE_OK) {
-      DEBUGF(fprintf(stderr, "Error: win32_init failed\n"));
-      return CURLE_FAILED_INIT;
-    }
-
-#ifdef __AMIGA__
-  if(!Curl_amiga_init()) {
-    DEBUGF(fprintf(stderr, "Error: Curl_amiga_init failed\n"));
-    return CURLE_FAILED_INIT;
-  }
-#endif
-
-#ifdef NETWARE
-  if(netware_init()) {
-    DEBUGF(fprintf(stderr, "Warning: LONG namespace not available\n"));
-  }
-#endif
-
-#ifdef USE_LIBIDN
-  idna_init();
-#endif
-
-  if(Curl_resolver_global_init() != CURLE_OK) {
-    DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n"));
-    return CURLE_FAILED_INIT;
-  }
-
-#if defined(USE_LIBSSH2) && defined(HAVE_LIBSSH2_INIT)
-  if(libssh2_init(0)) {
-    DEBUGF(fprintf(stderr, "Error: libssh2_init failed\n"));
-    return CURLE_FAILED_INIT;
-  }
-#endif
-
-  init_flags  = flags;
-
-  /* Preset pseudo-random number sequence. */
-
-  Curl_srand();
-
-  return CURLE_OK;
-}
-
-/*
- * curl_global_init_mem() globally initializes cURL and also registers the
- * user provided callback routines.
- */
-CURLcode curl_global_init_mem(long flags, curl_malloc_callback m,
-                              curl_free_callback f, curl_realloc_callback r,
-                              curl_strdup_callback s, curl_calloc_callback c)
-{
-  CURLcode code = CURLE_OK;
-
-  /* Invalid input, return immediately */
-  if(!m || !f || !r || !s || !c)
-    return CURLE_FAILED_INIT;
-
-  /* Already initialized, don't do it again */
-  if(initialized)
-    return CURLE_OK;
-
-  /* Call the actual init function first */
-  code = curl_global_init(flags);
-  if(code == CURLE_OK) {
-    Curl_cmalloc = m;
-    Curl_cfree = f;
-    Curl_cstrdup = s;
-    Curl_crealloc = r;
-    Curl_ccalloc = c;
-  }
-
-  return code;
-}
-
-/**
- * curl_global_cleanup() globally cleanups cURL, uses the value of
- * "init_flags" to determine what needs to be cleaned up and what doesn't.
- */
-void curl_global_cleanup(void)
-{
-  if(!initialized)
-    return;
-
-  if(--initialized)
-    return;
-
-  Curl_global_host_cache_dtor();
-
-  if(init_flags & CURL_GLOBAL_SSL)
-    Curl_ssl_cleanup();
-
-  Curl_resolver_global_cleanup();
-
-  if(init_flags & CURL_GLOBAL_WIN32)
-    win32_cleanup();
-
-  Curl_amiga_cleanup();
-
-#if defined(USE_LIBSSH2) && defined(HAVE_LIBSSH2_EXIT)
-  (void)libssh2_exit();
-#endif
-
-  init_flags  = 0;
-}
-
-/*
- * curl_easy_init() is the external interface to alloc, setup and init an
- * easy handle that is returned. If anything goes wrong, NULL is returned.
- */
-CURL *curl_easy_init(void)
-{
-  CURLcode res;
-  struct SessionHandle *data;
-
-  /* Make sure we inited the global SSL stuff */
-  if(!initialized) {
-    res = curl_global_init(CURL_GLOBAL_DEFAULT);
-    if(res) {
-      /* something in the global init failed, return nothing */
-      DEBUGF(fprintf(stderr, "Error: curl_global_init failed\n"));
-      return NULL;
-    }
-  }
-
-  /* We use curl_open() with undefined URL so far */
-  res = Curl_open(&data);
-  if(res != CURLE_OK) {
-    DEBUGF(fprintf(stderr, "Error: Curl_open failed\n"));
-    return NULL;
-  }
-
-  return data;
-}
-
-/*
- * curl_easy_setopt() is the external interface for setting options on an
- * easy handle.
- */
-
-#undef curl_easy_setopt
-CURLcode curl_easy_setopt(CURL *curl, CURLoption tag, ...)
-{
-  va_list arg;
-  struct SessionHandle *data = curl;
-  CURLcode ret;
-
-  if(!curl)
-    return CURLE_BAD_FUNCTION_ARGUMENT;
-
-  va_start(arg, tag);
-
-  ret = Curl_setopt(data, tag, arg);
-
-  va_end(arg);
-  return ret;
-}
-
-#ifdef CURL_MULTIEASY
-/***************************************************************************
- * This function is still only for testing purposes. It makes a great way
- * to run the full test suite on the multi interface instead of the easy one.
- ***************************************************************************
- *
- * The *new* curl_easy_perform() is the external interface that performs a
- * transfer previously setup.
- *
- * Wrapper-function that: creates a multi handle, adds the easy handle to it,
- * runs curl_multi_perform() until the transfer is done, then detaches the
- * easy handle, destroys the multi handle and returns the easy handle's return
- * code. This will make everything internally use and assume multi interface.
- */
-CURLcode curl_easy_perform(CURL *easy)
-{
-  CURLM *multi;
-  CURLMcode mcode;
-  CURLcode code = CURLE_OK;
-  int still_running;
-  struct timeval timeout;
-  int rc;
-  CURLMsg *msg;
-  fd_set fdread;
-  fd_set fdwrite;
-  fd_set fdexcep;
-  int maxfd;
-
-  if(!easy)
-    return CURLE_BAD_FUNCTION_ARGUMENT;
-
-  multi = curl_multi_init();
-  if(!multi)
-    return CURLE_OUT_OF_MEMORY;
-
-  mcode = curl_multi_add_handle(multi, easy);
-  if(mcode) {
-    curl_multi_cleanup(multi);
-    if(mcode == CURLM_OUT_OF_MEMORY)
-      return CURLE_OUT_OF_MEMORY;
-    else
-      return CURLE_FAILED_INIT;
-  }
-
-  /* we start some action by calling perform right away */
-
-  do {
-    while(CURLM_CALL_MULTI_PERFORM ==
-          curl_multi_perform(multi, &still_running));
-
-    if(!still_running)
-      break;
-
-    FD_ZERO(&fdread);
-    FD_ZERO(&fdwrite);
-    FD_ZERO(&fdexcep);
-
-    /* timeout once per second */
-    timeout.tv_sec = 1;
-    timeout.tv_usec = 0;
-
-    /* Old deprecated style: get file descriptors from the transfers */
-    curl_multi_fdset(multi, &fdread, &fdwrite, &fdexcep, &maxfd);
-    rc = Curl_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
-
-    /* The way is to extract the sockets and wait for them without using
-       select. This whole alternative version should probably rather use the
-       curl_multi_socket() approach. */
-
-    if(rc == -1)
-      /* select error */
-      break;
-
-    /* timeout or data to send/receive => loop! */
-  } while(still_running);
-
-  msg = curl_multi_info_read(multi, &rc);
-  if(msg)
-    code = msg->data.result;
-
-  mcode = curl_multi_remove_handle(multi, easy);
-  /* what to do if it fails? */
-
-  mcode = curl_multi_cleanup(multi);
-  /* what to do if it fails? */
-
-  return code;
-}
-#else
-/*
- * curl_easy_perform() is the external interface that performs a transfer
- * previously setup.
- */
-CURLcode curl_easy_perform(CURL *curl)
-{
-  struct SessionHandle *data = (struct SessionHandle *)curl;
-
-  if(!data)
-    return CURLE_BAD_FUNCTION_ARGUMENT;
-
-  if(! (data->share && data->share->hostcache)) {
-    /* this handle is not using a shared dns cache */
-
-    if(data->set.global_dns_cache &&
-       (data->dns.hostcachetype != HCACHE_GLOBAL)) {
-      /* global dns cache was requested but still isn't */
-      struct curl_hash *ptr;
-
-      if(data->dns.hostcachetype == HCACHE_PRIVATE) {
-        /* if the current cache is private, kill it first */
-        Curl_hash_destroy(data->dns.hostcache);
-        data->dns.hostcachetype = HCACHE_NONE;
-        data->dns.hostcache = NULL;
-      }
-
-      ptr = Curl_global_host_cache_init();
-      if(ptr) {
-        /* only do this if the global cache init works */
-        data->dns.hostcache = ptr;
-        data->dns.hostcachetype = HCACHE_GLOBAL;
-      }
-    }
-
-    if(!data->dns.hostcache) {
-      data->dns.hostcachetype = HCACHE_PRIVATE;
-      data->dns.hostcache = Curl_mk_dnscache();
-
-      if(!data->dns.hostcache)
-        /* While we possibly could survive and do good without a host cache,
-           the fact that creating it failed indicates that things are truly
-           screwed up and we should bail out! */
-        return CURLE_OUT_OF_MEMORY;
-    }
-
-  }
-
-  if(!data->state.conn_cache) {
-    /* Oops, no connection cache, create one */
-    data->state.conn_cache = Curl_conncache_init(CONNCACHE_PRIVATE);
-    if(!data->state.conn_cache)
-      return CURLE_OUT_OF_MEMORY;
-  }
-
-  return Curl_perform(data);
-}
-#endif
-
-/*
- * curl_easy_cleanup() is the external interface to cleaning/freeing the given
- * easy handle.
- */
-void curl_easy_cleanup(CURL *curl)
-{
-  struct SessionHandle *data = (struct SessionHandle *)curl;
-
-  if(!data)
-    return;
-
-  Curl_close(data);
-}
-
-/*
- * Store a pointed to the multi handle within the easy handle's data struct.
- */
-void Curl_easy_addmulti(struct SessionHandle *data,
-                        void *multi)
-{
-  data->multi = multi;
-  if(multi == NULL)
-    /* the association is cleared, mark the easy handle as not used by an
-       interface */
-    data->state.used_interface = Curl_if_none;
-}
-
-void Curl_easy_initHandleData(struct SessionHandle *data)
-{
-    memset(&data->req, 0, sizeof(struct SingleRequest));
-
-    data->req.maxdownload = -1;
-}
-
-/*
- * curl_easy_getinfo() is an external interface that allows an app to retrieve
- * information from a performed transfer and similar.
- */
-#undef curl_easy_getinfo
-CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...)
-{
-  va_list arg;
-  void *paramp;
-  struct SessionHandle *data = (struct SessionHandle *)curl;
-
-  va_start(arg, info);
-  paramp = va_arg(arg, void *);
-
-  return Curl_getinfo(data, info, paramp);
-}
-
-/*
- * curl_easy_duphandle() is an external interface to allow duplication of a
- * given input easy handle. The returned handle will be a new working handle
- * with all options set exactly as the input source handle.
- */
-CURL *curl_easy_duphandle(CURL *incurl)
-{
-  struct SessionHandle *data=(struct SessionHandle *)incurl;
-
-  struct SessionHandle *outcurl = calloc(1, sizeof(struct SessionHandle));
-  if(NULL == outcurl)
-    goto fail;
-
-  /*
-   * We setup a few buffers we need. We should probably make them
-   * get setup on-demand in the code, as that would probably decrease
-   * the likeliness of us forgetting to init a buffer here in the future.
-   */
-  outcurl->state.headerbuff = malloc(HEADERSIZE);
-  if(!outcurl->state.headerbuff)
-    goto fail;
-  outcurl->state.headersize = HEADERSIZE;
-
-  /* copy all userdefined values */
-  if(Curl_dupset(outcurl, data) != CURLE_OK)
-    goto fail;
-
-  /* the connection cache is setup on demand */
-  outcurl->state.conn_cache = NULL;
-
-  outcurl->state.lastconnect = NULL;
-
-  outcurl->progress.flags    = data->progress.flags;
-  outcurl->progress.callback = data->progress.callback;
-
-  if(data->cookies) {
-    /* If cookies are enabled in the parent handle, we enable them
-       in the clone as well! */
-    outcurl->cookies = Curl_cookie_init(data,
-                                        data->cookies->filename,
-                                        outcurl->cookies,
-                                        data->set.cookiesession);
-    if(!outcurl->cookies)
-      goto fail;
-  }
-
-  /* duplicate all values in 'change' */
-  if(data->change.cookielist) {
-    outcurl->change.cookielist =
-      Curl_slist_duplicate(data->change.cookielist);
-    if(!outcurl->change.cookielist)
-      goto fail;
-  }
-
-  if(data->change.url) {
-    outcurl->change.url = strdup(data->change.url);
-    if(!outcurl->change.url)
-      goto fail;
-    outcurl->change.url_alloc = TRUE;
-  }
-
-  if(data->change.referer) {
-    outcurl->change.referer = strdup(data->change.referer);
-    if(!outcurl->change.referer)
-      goto fail;
-    outcurl->change.referer_alloc = TRUE;
-  }
-
-  /* Clone the resolver handle, if present, for the new handle */
-  if(Curl_resolver_duphandle(&outcurl->state.resolver,
-                             data->state.resolver) != CURLE_OK)
-    goto fail;
-
-  Curl_convert_setup(outcurl);
-
-  Curl_easy_initHandleData(outcurl);
-
-  outcurl->magic = CURLEASY_MAGIC_NUMBER;
-
-  /* we reach this point and thus we are OK */
-
-  return outcurl;
-
-  fail:
-
-  if(outcurl) {
-    curl_slist_free_all(outcurl->change.cookielist);
-    outcurl->change.cookielist = NULL;
-    Curl_safefree(outcurl->state.headerbuff);
-    Curl_safefree(outcurl->change.url);
-    Curl_safefree(outcurl->change.referer);
-    Curl_freeset(outcurl);
-    free(outcurl);
-  }
-
-  return NULL;
-}
-
-/*
- * curl_easy_reset() is an external interface that allows an app to re-
- * initialize a session handle to the default values.
- */
-void curl_easy_reset(CURL *curl)
-{
-  struct SessionHandle *data = (struct SessionHandle *)curl;
-
-  Curl_safefree(data->state.pathbuffer);
-
-  data->state.path = NULL;
-
-  Curl_safefree(data->state.proto.generic);
-
-  /* zero out UserDefined data: */
-  Curl_freeset(data);
-  memset(&data->set, 0, sizeof(struct UserDefined));
-  (void)Curl_init_userdefined(&data->set);
-
-  /* zero out Progress data: */
-  memset(&data->progress, 0, sizeof(struct Progress));
-
-  /* init Handle data */
-  Curl_easy_initHandleData(data);
-
-  data->progress.flags |= PGRS_HIDE;
-  data->state.current_speed = -1; /* init to negative == impossible */
-}
-
-/*
- * curl_easy_pause() allows an application to pause or unpause a specific
- * transfer and direction. This function sets the full new state for the
- * current connection this easy handle operates on.
- *
- * NOTE: if you have the receiving paused and you call this function to remove
- * the pausing, you may get your write callback called at this point.
- *
- * Action is a bitmask consisting of CURLPAUSE_* bits in curl/curl.h
- */
-CURLcode curl_easy_pause(CURL *curl, int action)
-{
-  struct SessionHandle *data = (struct SessionHandle *)curl;
-  struct SingleRequest *k = &data->req;
-  CURLcode result = CURLE_OK;
-
-  /* first switch off both pause bits */
-  int newstate = k->keepon &~ (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE);
-
-  /* set the new desired pause bits */
-  newstate |= ((action & CURLPAUSE_RECV)?KEEP_RECV_PAUSE:0) |
-    ((action & CURLPAUSE_SEND)?KEEP_SEND_PAUSE:0);
-
-  /* put it back in the keepon */
-  k->keepon = newstate;
-
-  if(!(newstate & KEEP_RECV_PAUSE) && data->state.tempwrite) {
-    /* we have a buffer for sending that we now seem to be able to deliver
-       since the receive pausing is lifted! */
-
-    /* get the pointer, type and length in local copies since the function may
-       return PAUSE again and then we'll get a new copy allocted and stored in
-       the tempwrite variables */
-    char *tempwrite = data->state.tempwrite;
-    char *freewrite = tempwrite; /* store this pointer to free it later */
-    size_t tempsize = data->state.tempwritesize;
-    int temptype = data->state.tempwritetype;
-    size_t chunklen;
-
-    /* clear tempwrite here just to make sure it gets cleared if there's no
-       further use of it, and make sure we don't clear it after the function
-       invoke as it may have been set to a new value by then */
-    data->state.tempwrite = NULL;
-
-    /* since the write callback API is define to never exceed
-       CURL_MAX_WRITE_SIZE bytes in a single call, and since we may in fact
-       have more data than that in our buffer here, we must loop sending the
-       data in multiple calls until there's no data left or we get another
-       pause returned.
-
-       A tricky part is that the function we call will "buffer" the data
-       itself when it pauses on a particular buffer, so we may need to do some
-       extra trickery if we get a pause return here.
-    */
-    do {
-      chunklen = (tempsize > CURL_MAX_WRITE_SIZE)?CURL_MAX_WRITE_SIZE:tempsize;
-
-      result = Curl_client_write(data->state.current_conn,
-                                 temptype, tempwrite, chunklen);
-      if(result)
-        /* failures abort the loop at once */
-        break;
-
-      if(data->state.tempwrite && (tempsize - chunklen)) {
-        /* Ouch, the reading is again paused and the block we send is now
-           "cached". If this is the final chunk we can leave it like this, but
-           if we have more chunks that are cached after this, we need to free
-           the newly cached one and put back a version that is truly the entire
-           contents that is saved for later
-        */
-        char *newptr;
-
-        /* note that tempsize is still the size as before the callback was
-           used, and thus the whole piece of data to keep */
-        newptr = realloc(data->state.tempwrite, tempsize);
-
-        if(!newptr) {
-          free(data->state.tempwrite); /* free old area */
-          data->state.tempwrite = NULL;
-          result = CURLE_OUT_OF_MEMORY;
-          /* tempwrite will be freed further down */
-          break;
-        }
-        data->state.tempwrite = newptr; /* store new pointer */
-        memcpy(newptr, tempwrite, tempsize);
-        data->state.tempwritesize = tempsize; /* store new size */
-        /* tempwrite will be freed further down */
-        break; /* go back to pausing until further notice */
-      }
-      else {
-        tempsize -= chunklen;  /* left after the call above */
-        tempwrite += chunklen; /* advance the pointer */
-      }
-
-    } while((result == CURLE_OK) && tempsize);
-
-    free(freewrite); /* this is unconditionally no longer used */
-  }
-
-  return result;
-}
-
-
-static CURLcode easy_connection(struct SessionHandle *data,
-                                curl_socket_t *sfd,
-                                struct connectdata **connp)
-{
-  if(data == NULL)
-    return CURLE_BAD_FUNCTION_ARGUMENT;
-
-  /* only allow these to be called on handles with CURLOPT_CONNECT_ONLY */
-  if(!data->set.connect_only) {
-    failf(data, "CONNECT_ONLY is required!");
-    return CURLE_UNSUPPORTED_PROTOCOL;
-  }
-
-  *sfd = Curl_getconnectinfo(data, connp);
-
-  if(*sfd == CURL_SOCKET_BAD) {
-    failf(data, "Failed to get recent socket");
-    return CURLE_UNSUPPORTED_PROTOCOL;
-  }
-
-  return CURLE_OK;
-}
-
-/*
- * Receives data from the connected socket. Use after successful
- * curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
- * Returns CURLE_OK on success, error code on error.
- */
-CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, size_t *n)
-{
-  curl_socket_t sfd;
-  CURLcode ret;
-  ssize_t n1;
-  struct connectdata *c;
-  struct SessionHandle *data = (struct SessionHandle *)curl;
-
-  ret = easy_connection(data, &sfd, &c);
-  if(ret)
-    return ret;
-
-  *n = 0;
-  ret = Curl_read(c, sfd, buffer, buflen, &n1);
-
-  if(ret != CURLE_OK)
-    return ret;
-
-  *n = (size_t)n1;
-
-  return CURLE_OK;
-}
-
-/*
- * Sends data over the connected socket. Use after successful
- * curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
- */
-CURLcode curl_easy_send(CURL *curl, const void *buffer, size_t buflen,
-                        size_t *n)
-{
-  curl_socket_t sfd;
-  CURLcode ret;
-  ssize_t n1;
-  struct connectdata *c = NULL;
-  struct SessionHandle *data = (struct SessionHandle *)curl;
-
-  ret = easy_connection(data, &sfd, &c);
-  if(ret)
-    return ret;
-
-  *n = 0;
-  ret = Curl_write(c, sfd, buffer, buflen, &n1);
-
-  if(n1 == -1)
-    return CURLE_SEND_ERROR;
-
-  /* detect EAGAIN */
-  if((CURLE_OK == ret) && (0 == n1))
-    return CURLE_AGAIN;
-
-  *n = (size_t)n1;
-
-  return ret;
-}
diff --git a/lib/escape.c b/lib/escape.c
deleted file mode 100644 (file)
index c0ed571..0000000
+++ /dev/null
@@ -1,233 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-/* Escape and unescape URL encoding in strings. The functions return a new
- * allocated string or NULL if an error occurred.  */
-
-#include "curl_setup.h"
-
-#include <curl/curl.h>
-
-#include "curl_memory.h"
-#include "curl_urldata.h"
-#include "curl_warnless.h"
-#include "curl_non_ascii.h"
-#include "curl_escape.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/* Portable character check (remember EBCDIC). Do not use isalnum() because
-   its behavior is altered by the current locale.
-   See http://tools.ietf.org/html/rfc3986#section-2.3
-*/
-static bool Curl_isunreserved(unsigned char in)
-{
-  switch (in) {
-    case '0': case '1': case '2': case '3': case '4':
-    case '5': case '6': case '7': case '8': case '9':
-    case 'a': case 'b': case 'c': case 'd': case 'e':
-    case 'f': case 'g': case 'h': case 'i': case 'j':
-    case 'k': case 'l': case 'm': case 'n': case 'o':
-    case 'p': case 'q': case 'r': case 's': case 't':
-    case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
-    case 'A': case 'B': case 'C': case 'D': case 'E':
-    case 'F': case 'G': case 'H': case 'I': case 'J':
-    case 'K': case 'L': case 'M': case 'N': case 'O':
-    case 'P': case 'Q': case 'R': case 'S': case 'T':
-    case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
-    case '-': case '.': case '_': case '~':
-      return TRUE;
-    default:
-      break;
-  }
-  return FALSE;
-}
-
-/* for ABI-compatibility with previous versions */
-char *curl_escape(const char *string, int inlength)
-{
-  return curl_easy_escape(NULL, string, inlength);
-}
-
-/* for ABI-compatibility with previous versions */
-char *curl_unescape(const char *string, int length)
-{
-  return curl_easy_unescape(NULL, string, length, NULL);
-}
-
-char *curl_easy_escape(CURL *handle, const char *string, int inlength)
-{
-  size_t alloc = (inlength?(size_t)inlength:strlen(string))+1;
-  char *ns;
-  char *testing_ptr = NULL;
-  unsigned char in; /* we need to treat the characters unsigned */
-  size_t newlen = alloc;
-  size_t strindex=0;
-  size_t length;
-  CURLcode res;
-
-  ns = malloc(alloc);
-  if(!ns)
-    return NULL;
-
-  length = alloc-1;
-  while(length--) {
-    in = *string;
-
-    if(Curl_isunreserved(in))
-      /* just copy this */
-      ns[strindex++]=in;
-    else {
-      /* encode it */
-      newlen += 2; /* the size grows with two, since this'll become a %XX */
-      if(newlen > alloc) {
-        alloc *= 2;
-        testing_ptr = realloc(ns, alloc);
-        if(!testing_ptr) {
-          free( ns );
-          return NULL;
-        }
-        else {
-          ns = testing_ptr;
-        }
-      }
-
-      res = Curl_convert_to_network(handle, &in, 1);
-      if(res) {
-        /* Curl_convert_to_network calls failf if unsuccessful */
-        free(ns);
-        return NULL;
-      }
-
-      snprintf(&ns[strindex], 4, "%%%02X", in);
-
-      strindex+=3;
-    }
-    string++;
-  }
-  ns[strindex]=0; /* terminate it */
-  return ns;
-}
-
-/*
- * Curl_urldecode() URL decodes the given string.
- *
- * Optionally detects control characters (byte codes lower than 32) in the
- * data and rejects such data.
- *
- * Returns a pointer to a malloced string in *ostring with length given in
- * *olen. If length == 0, the length is assumed to be strlen(string).
- *
- */
-CURLcode Curl_urldecode(struct SessionHandle *data,
-                        const char *string, size_t length,
-                        char **ostring, size_t *olen,
-                        bool reject_ctrl)
-{
-  size_t alloc = (length?length:strlen(string))+1;
-  char *ns = malloc(alloc);
-  unsigned char in;
-  size_t strindex=0;
-  unsigned long hex;
-  CURLcode res;
-
-  if(!ns)
-    return CURLE_OUT_OF_MEMORY;
-
-  while(--alloc > 0) {
-    in = *string;
-    if(('%' == in) && ISXDIGIT(string[1]) && ISXDIGIT(string[2])) {
-      /* this is two hexadecimal digits following a '%' */
-      char hexstr[3];
-      char *ptr;
-      hexstr[0] = string[1];
-      hexstr[1] = string[2];
-      hexstr[2] = 0;
-
-      hex = strtoul(hexstr, &ptr, 16);
-
-      in = curlx_ultouc(hex); /* this long is never bigger than 255 anyway */
-
-      res = Curl_convert_from_network(data, &in, 1);
-      if(res) {
-        /* Curl_convert_from_network calls failf if unsuccessful */
-        free(ns);
-        return res;
-      }
-
-      string+=2;
-      alloc-=2;
-    }
-    if(reject_ctrl && (in < 0x20)) {
-      free(ns);
-      return CURLE_URL_MALFORMAT;
-    }
-
-    ns[strindex++] = in;
-    string++;
-  }
-  ns[strindex]=0; /* terminate it */
-
-  if(olen)
-    /* store output size */
-    *olen = strindex;
-
-  if(ostring)
-    /* store output string */
-    *ostring = ns;
-
-  return CURLE_OK;
-}
-
-/*
- * Unescapes the given URL escaped string of given length. Returns a
- * pointer to a malloced string with length given in *olen.
- * If length == 0, the length is assumed to be strlen(string).
- * If olen == NULL, no output length is stored.
- */
-char *curl_easy_unescape(CURL *handle, const char *string, int length,
-                         int *olen)
-{
-  char *str = NULL;
-  size_t inputlen = length;
-  size_t outputlen;
-  CURLcode res = Curl_urldecode(handle, string, inputlen, &str, &outputlen,
-                                FALSE);
-  if(res)
-    return NULL;
-  if(olen)
-    *olen = curlx_uztosi(outputlen);
-  return str;
-}
-
-/* For operating systems/environments that use different malloc/free
-   systems for the app and for this library, we provide a free that uses
-   the library's memory system */
-void curl_free(void *p)
-{
-  if(p)
-    free(p);
-}
diff --git a/lib/file.c b/lib/file.c
deleted file mode 100644 (file)
index 6ea2bd7..0000000
+++ /dev/null
@@ -1,591 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifndef CURL_DISABLE_FILE
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef HAVE_NET_IF_H
-#include <net/if.h>
-#endif
-#ifdef HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-
-#ifdef HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
-
-#include "curl_strtoofft.h"
-#include "curl_urldata.h"
-#include <curl/curl.h>
-#include "curl_progress.h"
-#include "curl_sendf.h"
-#include "curl_escape.h"
-#include "curl_file.h"
-#include "curl_speedcheck.h"
-#include "curl_getinfo.h"
-#include "curl_transfer.h"
-#include "curl_url.h"
-#include "curl_memory.h"
-#include "curl_parsedate.h" /* for the week day and month names */
-#include "curl_warnless.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-#if defined(WIN32) || defined(MSDOS) || defined(__EMX__) || \
-  defined(__SYMBIAN32__)
-#define DOS_FILESYSTEM 1
-#endif
-
-#ifdef OPEN_NEEDS_ARG3
-#  define open_readonly(p,f) open((p),(f),(0))
-#else
-#  define open_readonly(p,f) open((p),(f))
-#endif
-
-/*
- * Forward declarations.
- */
-
-static CURLcode file_do(struct connectdata *, bool *done);
-static CURLcode file_done(struct connectdata *conn,
-                          CURLcode status, bool premature);
-static CURLcode file_connect(struct connectdata *conn, bool *done);
-static CURLcode file_disconnect(struct connectdata *conn,
-                                bool dead_connection);
-
-
-/*
- * FILE scheme handler.
- */
-
-const struct Curl_handler Curl_handler_file = {
-  "FILE",                               /* scheme */
-  ZERO_NULL,                            /* setup_connection */
-  file_do,                              /* do_it */
-  file_done,                            /* done */
-  ZERO_NULL,                            /* do_more */
-  file_connect,                         /* connect_it */
-  ZERO_NULL,                            /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
-  file_disconnect,                      /* disconnect */
-  ZERO_NULL,                            /* readwrite */
-  0,                                    /* defport */
-  CURLPROTO_FILE,                       /* protocol */
-  PROTOPT_NONETWORK | PROTOPT_NOURLQUERY /* flags */
-};
-
-
- /*
-  Check if this is a range download, and if so, set the internal variables
-  properly. This code is copied from the FTP implementation and might as
-  well be factored out.
- */
-static CURLcode file_range(struct connectdata *conn)
-{
-  curl_off_t from, to;
-  curl_off_t totalsize=-1;
-  char *ptr;
-  char *ptr2;
-  struct SessionHandle *data = conn->data;
-
-  if(data->state.use_range && data->state.range) {
-    from=curlx_strtoofft(data->state.range, &ptr, 0);
-    while(*ptr && (ISSPACE(*ptr) || (*ptr=='-')))
-      ptr++;
-    to=curlx_strtoofft(ptr, &ptr2, 0);
-    if(ptr == ptr2) {
-      /* we didn't get any digit */
-      to=-1;
-    }
-    if((-1 == to) && (from>=0)) {
-      /* X - */
-      data->state.resume_from = from;
-      DEBUGF(infof(data, "RANGE %" FORMAT_OFF_T " to end of file\n",
-                   from));
-    }
-    else if(from < 0) {
-      /* -Y */
-      data->req.maxdownload = -from;
-      data->state.resume_from = from;
-      DEBUGF(infof(data, "RANGE the last %" FORMAT_OFF_T " bytes\n",
-                   -from));
-    }
-    else {
-      /* X-Y */
-      totalsize = to-from;
-      data->req.maxdownload = totalsize+1; /* include last byte */
-      data->state.resume_from = from;
-      DEBUGF(infof(data, "RANGE from %" FORMAT_OFF_T
-                   " getting %" FORMAT_OFF_T " bytes\n",
-                   from, data->req.maxdownload));
-    }
-    DEBUGF(infof(data, "range-download from %" FORMAT_OFF_T
-                 " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
-                 from, to, data->req.maxdownload));
-  }
-  else
-    data->req.maxdownload = -1;
-  return CURLE_OK;
-}
-
-/*
- * file_connect() gets called from Curl_protocol_connect() to allow us to
- * do protocol-specific actions at connect-time.  We emulate a
- * connect-then-transfer protocol and "connect" to the file here
- */
-static CURLcode file_connect(struct connectdata *conn, bool *done)
-{
-  struct SessionHandle *data = conn->data;
-  char *real_path;
-  struct FILEPROTO *file;
-  int fd;
-#ifdef DOS_FILESYSTEM
-  int i;
-  char *actual_path;
-#endif
-
-  /* If there already is a protocol-specific struct allocated for this
-     sessionhandle, deal with it */
-  Curl_reset_reqproto(conn);
-
-  real_path = curl_easy_unescape(data, data->state.path, 0, NULL);
-  if(!real_path)
-    return CURLE_OUT_OF_MEMORY;
-
-  if(!data->state.proto.file) {
-    file = calloc(1, sizeof(struct FILEPROTO));
-    if(!file) {
-      free(real_path);
-      return CURLE_OUT_OF_MEMORY;
-    }
-    data->state.proto.file = file;
-  }
-  else {
-    /* file is not a protocol that can deal with "persistancy" */
-    file = data->state.proto.file;
-    Curl_safefree(file->freepath);
-    file->path = NULL;
-    if(file->fd != -1)
-      close(file->fd);
-    file->fd = -1;
-  }
-
-#ifdef DOS_FILESYSTEM
-  /* If the first character is a slash, and there's
-     something that looks like a drive at the beginning of
-     the path, skip the slash.  If we remove the initial
-     slash in all cases, paths without drive letters end up
-     relative to the current directory which isn't how
-     browsers work.
-
-     Some browsers accept | instead of : as the drive letter
-     separator, so we do too.
-
-     On other platforms, we need the slash to indicate an
-     absolute pathname.  On Windows, absolute paths start
-     with a drive letter.
-  */
-  actual_path = real_path;
-  if((actual_path[0] == '/') &&
-      actual_path[1] &&
-     (actual_path[2] == ':' || actual_path[2] == '|')) {
-    actual_path[2] = ':';
-    actual_path++;
-  }
-
-  /* change path separators from '/' to '\\' for DOS, Windows and OS/2 */
-  for(i=0; actual_path[i] != '\0'; ++i)
-    if(actual_path[i] == '/')
-      actual_path[i] = '\\';
-
-  fd = open_readonly(actual_path, O_RDONLY|O_BINARY);
-  file->path = actual_path;
-#else
-  fd = open_readonly(real_path, O_RDONLY);
-  file->path = real_path;
-#endif
-  file->freepath = real_path; /* free this when done */
-
-  file->fd = fd;
-  if(!data->set.upload && (fd == -1)) {
-    failf(data, "Couldn't open file %s", data->state.path);
-    file_done(conn, CURLE_FILE_COULDNT_READ_FILE, FALSE);
-    return CURLE_FILE_COULDNT_READ_FILE;
-  }
-  *done = TRUE;
-
-  return CURLE_OK;
-}
-
-static CURLcode file_done(struct connectdata *conn,
-                               CURLcode status, bool premature)
-{
-  struct FILEPROTO *file = conn->data->state.proto.file;
-  (void)status; /* not used */
-  (void)premature; /* not used */
-
-  if(file) {
-    Curl_safefree(file->freepath);
-    file->path = NULL;
-    if(file->fd != -1)
-      close(file->fd);
-    file->fd = -1;
-  }
-
-  return CURLE_OK;
-}
-
-static CURLcode file_disconnect(struct connectdata *conn,
-                                bool dead_connection)
-{
-  struct FILEPROTO *file = conn->data->state.proto.file;
-  (void)dead_connection; /* not used */
-
-  if(file) {
-    Curl_safefree(file->freepath);
-    file->path = NULL;
-    if(file->fd != -1)
-      close(file->fd);
-    file->fd = -1;
-  }
-
-  return CURLE_OK;
-}
-
-#ifdef DOS_FILESYSTEM
-#define DIRSEP '\\'
-#else
-#define DIRSEP '/'
-#endif
-
-static CURLcode file_upload(struct connectdata *conn)
-{
-  struct FILEPROTO *file = conn->data->state.proto.file;
-  const char *dir = strchr(file->path, DIRSEP);
-  int fd;
-  int mode;
-  CURLcode res=CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  char *buf = data->state.buffer;
-  size_t nread;
-  size_t nwrite;
-  curl_off_t bytecount = 0;
-  struct timeval now = Curl_tvnow();
-  struct_stat file_stat;
-  const char* buf2;
-
-  /*
-   * Since FILE: doesn't do the full init, we need to provide some extra
-   * assignments here.
-   */
-  conn->fread_func = data->set.fread_func;
-  conn->fread_in = data->set.in;
-  conn->data->req.upload_fromhere = buf;
-
-  if(!dir)
-    return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
-
-  if(!dir[1])
-    return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
-
-#ifdef O_BINARY
-#define MODE_DEFAULT O_WRONLY|O_CREAT|O_BINARY
-#else
-#define MODE_DEFAULT O_WRONLY|O_CREAT
-#endif
-
-  if(data->state.resume_from)
-    mode = MODE_DEFAULT|O_APPEND;
-  else
-    mode = MODE_DEFAULT|O_TRUNC;
-
-  fd = open(file->path, mode, conn->data->set.new_file_perms);
-  if(fd < 0) {
-    failf(data, "Can't open %s for writing", file->path);
-    return CURLE_WRITE_ERROR;
-  }
-
-  if(-1 != data->set.infilesize)
-    /* known size of data to "upload" */
-    Curl_pgrsSetUploadSize(data, data->set.infilesize);
-
-  /* treat the negative resume offset value as the case of "-" */
-  if(data->state.resume_from < 0) {
-    if(fstat(fd, &file_stat)) {
-      close(fd);
-      failf(data, "Can't get the size of %s", file->path);
-      return CURLE_WRITE_ERROR;
-    }
-    else
-      data->state.resume_from = (curl_off_t)file_stat.st_size;
-  }
-
-  while(res == CURLE_OK) {
-    int readcount;
-    res = Curl_fillreadbuffer(conn, BUFSIZE, &readcount);
-    if(res)
-      break;
-
-    if(readcount <= 0)  /* fix questionable compare error. curlvms */
-      break;
-
-    nread = (size_t)readcount;
-
-    /*skip bytes before resume point*/
-    if(data->state.resume_from) {
-      if((curl_off_t)nread <= data->state.resume_from ) {
-        data->state.resume_from -= nread;
-        nread = 0;
-        buf2 = buf;
-      }
-      else {
-        buf2 = buf + data->state.resume_from;
-        nread -= (size_t)data->state.resume_from;
-        data->state.resume_from = 0;
-      }
-    }
-    else
-      buf2 = buf;
-
-    /* write the data to the target */
-    nwrite = write(fd, buf2, nread);
-    if(nwrite != nread) {
-      res = CURLE_SEND_ERROR;
-      break;
-    }
-
-    bytecount += nread;
-
-    Curl_pgrsSetUploadCounter(data, bytecount);
-
-    if(Curl_pgrsUpdate(conn))
-      res = CURLE_ABORTED_BY_CALLBACK;
-    else
-      res = Curl_speedcheck(data, now);
-  }
-  if(!res && Curl_pgrsUpdate(conn))
-    res = CURLE_ABORTED_BY_CALLBACK;
-
-  close(fd);
-
-  return res;
-}
-
-/*
- * file_do() is the protocol-specific function for the do-phase, separated
- * from the connect-phase above. Other protocols merely setup the transfer in
- * the do-phase, to have it done in the main transfer loop but since some
- * platforms we support don't allow select()ing etc on file handles (as
- * opposed to sockets) we instead perform the whole do-operation in this
- * function.
- */
-static CURLcode file_do(struct connectdata *conn, bool *done)
-{
-  /* This implementation ignores the host name in conformance with
-     RFC 1738. Only local files (reachable via the standard file system)
-     are supported. This means that files on remotely mounted directories
-     (via NFS, Samba, NT sharing) can be accessed through a file:// URL
-  */
-  CURLcode res = CURLE_OK;
-  struct_stat statbuf; /* struct_stat instead of struct stat just to allow the
-                          Windows version to have a different struct without
-                          having to redefine the simple word 'stat' */
-  curl_off_t expected_size=0;
-  bool fstated=FALSE;
-  ssize_t nread;
-  struct SessionHandle *data = conn->data;
-  char *buf = data->state.buffer;
-  curl_off_t bytecount = 0;
-  int fd;
-  struct timeval now = Curl_tvnow();
-
-  *done = TRUE; /* unconditionally */
-
-  Curl_initinfo(data);
-  Curl_pgrsStartNow(data);
-
-  if(data->set.upload)
-    return file_upload(conn);
-
-  /* get the fd from the connection phase */
-  fd = conn->data->state.proto.file->fd;
-
-  /* VMS: This only works reliable for STREAMLF files */
-  if(-1 != fstat(fd, &statbuf)) {
-    /* we could stat it, then read out the size */
-    expected_size = statbuf.st_size;
-    /* and store the modification time */
-    data->info.filetime = (long)statbuf.st_mtime;
-    fstated = TRUE;
-  }
-
-  if(fstated && !data->state.range && data->set.timecondition) {
-    if(!Curl_meets_timecondition(data, (time_t)data->info.filetime)) {
-      *done = TRUE;
-      return CURLE_OK;
-    }
-  }
-
-  /* If we have selected NOBODY and HEADER, it means that we only want file
-     information. Which for FILE can't be much more than the file size and
-     date. */
-  if(data->set.opt_no_body && data->set.include_header && fstated) {
-    CURLcode result;
-    snprintf(buf, sizeof(data->state.buffer),
-             "Content-Length: %" FORMAT_OFF_T "\r\n", expected_size);
-    result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
-    if(result)
-      return result;
-
-    result = Curl_client_write(conn, CLIENTWRITE_BOTH,
-                               (char *)"Accept-ranges: bytes\r\n", 0);
-    if(result)
-      return result;
-
-    if(fstated) {
-      time_t filetime = (time_t)statbuf.st_mtime;
-      struct tm buffer;
-      const struct tm *tm = &buffer;
-      result = Curl_gmtime(filetime, &buffer);
-      if(result)
-        return result;
-
-      /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
-      snprintf(buf, BUFSIZE-1,
-               "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
-               Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
-               tm->tm_mday,
-               Curl_month[tm->tm_mon],
-               tm->tm_year + 1900,
-               tm->tm_hour,
-               tm->tm_min,
-               tm->tm_sec);
-      result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
-    }
-    /* if we fstat()ed the file, set the file size to make it available post-
-       transfer */
-    if(fstated)
-      Curl_pgrsSetDownloadSize(data, expected_size);
-    return result;
-  }
-
-  /* Check whether file range has been specified */
-  file_range(conn);
-
-  /* Adjust the start offset in case we want to get the N last bytes
-   * of the stream iff the filesize could be determined */
-  if(data->state.resume_from < 0) {
-    if(!fstated) {
-      failf(data, "Can't get the size of file.");
-      return CURLE_READ_ERROR;
-    }
-    else
-      data->state.resume_from += (curl_off_t)statbuf.st_size;
-  }
-
-  if(data->state.resume_from <= expected_size)
-    expected_size -= data->state.resume_from;
-  else {
-    failf(data, "failed to resume file:// transfer");
-    return CURLE_BAD_DOWNLOAD_RESUME;
-  }
-
-  /* A high water mark has been specified so we obey... */
-  if(data->req.maxdownload > 0)
-    expected_size = data->req.maxdownload;
-
-  if(fstated && (expected_size == 0))
-    return CURLE_OK;
-
-  /* The following is a shortcut implementation of file reading
-     this is both more efficient than the former call to download() and
-     it avoids problems with select() and recv() on file descriptors
-     in Winsock */
-  if(fstated)
-    Curl_pgrsSetDownloadSize(data, expected_size);
-
-  if(data->state.resume_from) {
-    if(data->state.resume_from !=
-       lseek(fd, data->state.resume_from, SEEK_SET))
-      return CURLE_BAD_DOWNLOAD_RESUME;
-  }
-
-  Curl_pgrsTime(data, TIMER_STARTTRANSFER);
-
-  while(res == CURLE_OK) {
-    /* Don't fill a whole buffer if we want less than all data */
-    size_t bytestoread =
-      (expected_size < CURL_OFF_T_C(BUFSIZE) - CURL_OFF_T_C(1)) ?
-      curlx_sotouz(expected_size) : BUFSIZE - 1;
-
-    nread = read(fd, buf, bytestoread);
-
-    if(nread > 0)
-      buf[nread] = 0;
-
-    if(nread <= 0 || expected_size == 0)
-      break;
-
-    bytecount += nread;
-    expected_size -= nread;
-
-    res = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread);
-    if(res)
-      return res;
-
-    Curl_pgrsSetDownloadCounter(data, bytecount);
-
-    if(Curl_pgrsUpdate(conn))
-      res = CURLE_ABORTED_BY_CALLBACK;
-    else
-      res = Curl_speedcheck(data, now);
-  }
-  if(Curl_pgrsUpdate(conn))
-    res = CURLE_ABORTED_BY_CALLBACK;
-
-  return res;
-}
-
-#endif
diff --git a/lib/fileinfo.c b/lib/fileinfo.c
deleted file mode 100644 (file)
index 433c709..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 2010-2011, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#include "curl_strdup.h"
-#include "curl_fileinfo.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-struct curl_fileinfo *Curl_fileinfo_alloc(void)
-{
-  struct curl_fileinfo *tmp = malloc(sizeof(struct curl_fileinfo));
-  if(!tmp)
-    return NULL;
-  memset(tmp, 0, sizeof(struct curl_fileinfo));
-  return tmp;
-}
-
-void Curl_fileinfo_dtor(void *user, void *element)
-{
-  struct curl_fileinfo *finfo = element;
-  (void) user;
-  if(!finfo)
-    return;
-
-  Curl_safefree(finfo->b_data);
-
-  free(finfo);
-}
diff --git a/lib/formdata.c b/lib/formdata.c
deleted file mode 100644 (file)
index c7d85c4..0000000
+++ /dev/null
@@ -1,1494 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#include <curl/curl.h>
-
-/* Length of the random boundary string. */
-#define BOUNDARY_LENGTH 40
-
-#if !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY)
-
-#if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
-#include <libgen.h>
-#endif
-
-#include "curl_urldata.h" /* for struct SessionHandle */
-#include "curl_formdata.h"
-#include "curl_rand.h"
-#include "curl_strequal.h"
-#include "curl_memory.h"
-#include "curl_sendf.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-#endif  /* !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY) */
-
-#ifndef CURL_DISABLE_HTTP
-
-#ifndef HAVE_BASENAME
-static char *Curl_basename(char *path);
-#define basename(x)  Curl_basename((x))
-#endif
-
-static size_t readfromfile(struct Form *form, char *buffer, size_t size);
-
-/* What kind of Content-Type to use on un-specified files with unrecognized
-   extensions. */
-#define HTTPPOST_CONTENTTYPE_DEFAULT "application/octet-stream"
-
-#define FORM_FILE_SEPARATOR ','
-#define FORM_TYPE_SEPARATOR ';'
-
-/***************************************************************************
- *
- * AddHttpPost()
- *
- * Adds a HttpPost structure to the list, if parent_post is given becomes
- * a subpost of parent_post instead of a direct list element.
- *
- * Returns newly allocated HttpPost on success and NULL if malloc failed.
- *
- ***************************************************************************/
-static struct curl_httppost *
-AddHttpPost(char *name, size_t namelength,
-            char *value, size_t contentslength,
-            char *buffer, size_t bufferlength,
-            char *contenttype,
-            long flags,
-            struct curl_slist* contentHeader,
-            char *showfilename, char *userp,
-            struct curl_httppost *parent_post,
-            struct curl_httppost **httppost,
-            struct curl_httppost **last_post)
-{
-  struct curl_httppost *post;
-  post = calloc(1, sizeof(struct curl_httppost));
-  if(post) {
-    post->name = name;
-    post->namelength = (long)(name?(namelength?namelength:strlen(name)):0);
-    post->contents = value;
-    post->contentslength = (long)contentslength;
-    post->buffer = buffer;
-    post->bufferlength = (long)bufferlength;
-    post->contenttype = contenttype;
-    post->contentheader = contentHeader;
-    post->showfilename = showfilename;
-    post->userp = userp,
-    post->flags = flags;
-  }
-  else
-    return NULL;
-
-  if(parent_post) {
-    /* now, point our 'more' to the original 'more' */
-    post->more = parent_post->more;
-
-    /* then move the original 'more' to point to ourselves */
-    parent_post->more = post;
-  }
-  else {
-    /* make the previous point to this */
-    if(*last_post)
-      (*last_post)->next = post;
-    else
-      (*httppost) = post;
-
-    (*last_post) = post;
-  }
-  return post;
-}
-
-/***************************************************************************
- *
- * AddFormInfo()
- *
- * Adds a FormInfo structure to the list presented by parent_form_info.
- *
- * Returns newly allocated FormInfo on success and NULL if malloc failed/
- * parent_form_info is NULL.
- *
- ***************************************************************************/
-static FormInfo * AddFormInfo(char *value,
-                              char *contenttype,
-                              FormInfo *parent_form_info)
-{
-  FormInfo *form_info;
-  form_info = calloc(1, sizeof(struct FormInfo));
-  if(form_info) {
-    if(value)
-      form_info->value = value;
-    if(contenttype)
-      form_info->contenttype = contenttype;
-    form_info->flags = HTTPPOST_FILENAME;
-  }
-  else
-    return NULL;
-
-  if(parent_form_info) {
-    /* now, point our 'more' to the original 'more' */
-    form_info->more = parent_form_info->more;
-
-    /* then move the original 'more' to point to ourselves */
-    parent_form_info->more = form_info;
-  }
-
-  return form_info;
-}
-
-/***************************************************************************
- *
- * ContentTypeForFilename()
- *
- * Provides content type for filename if one of the known types (else
- * (either the prevtype or the default is returned).
- *
- * Returns some valid contenttype for filename.
- *
- ***************************************************************************/
-static const char * ContentTypeForFilename (const char *filename,
-                                            const char *prevtype)
-{
-  const char *contenttype = NULL;
-  unsigned int i;
-  /*
-   * No type was specified, we scan through a few well-known
-   * extensions and pick the first we match!
-   */
-  struct ContentType {
-    char extension[6];
-    const char *type;
-  };
-  static const struct ContentType ctts[]={
-    {".gif",  "image/gif"},
-    {".jpg",  "image/jpeg"},
-    {".jpeg", "image/jpeg"},
-    {".txt",  "text/plain"},
-    {".html", "text/html"},
-    {".xml", "application/xml"}
-  };
-
-  if(prevtype)
-    /* default to the previously set/used! */
-    contenttype = prevtype;
-  else
-    contenttype = HTTPPOST_CONTENTTYPE_DEFAULT;
-
-  if(filename) { /* in case a NULL was passed in */
-    for(i=0; i<sizeof(ctts)/sizeof(ctts[0]); i++) {
-      if(strlen(filename) >= strlen(ctts[i].extension)) {
-        if(strequal(filename +
-                    strlen(filename) - strlen(ctts[i].extension),
-                    ctts[i].extension)) {
-          contenttype = ctts[i].type;
-          break;
-        }
-      }
-    }
-  }
-  /* we have a contenttype by now */
-  return contenttype;
-}
-
-/***************************************************************************
- *
- * memdup()
- *
- * Copies the 'source' data to a newly allocated buffer buffer (that is
- * returned). Uses buffer_length if not null, else uses strlen to determine
- * the length of the buffer to be copied
- *
- * Returns the new pointer or NULL on failure.
- *
- ***************************************************************************/
-static char *memdup(const char *src, size_t buffer_length)
-{
-  size_t length;
-  bool add = FALSE;
-  char *buffer;
-
-  if(buffer_length)
-    length = buffer_length;
-  else if(src) {
-    length = strlen(src);
-    add = TRUE;
-  }
-  else
-    /* no length and a NULL src pointer! */
-    return strdup("");
-
-  buffer = malloc(length+add);
-  if(!buffer)
-    return NULL; /* fail */
-
-  memcpy(buffer, src, length);
-
-  /* if length unknown do null termination */
-  if(add)
-    buffer[length] = '\0';
-
-  return buffer;
-}
-
-/***************************************************************************
- *
- * FormAdd()
- *
- * Stores a formpost parameter and builds the appropriate linked list.
- *
- * Has two principal functionalities: using files and byte arrays as
- * post parts. Byte arrays are either copied or just the pointer is stored
- * (as the user requests) while for files only the filename and not the
- * content is stored.
- *
- * While you may have only one byte array for each name, multiple filenames
- * are allowed (and because of this feature CURLFORM_END is needed after
- * using CURLFORM_FILE).
- *
- * Examples:
- *
- * Simple name/value pair with copied contents:
- * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
- * CURLFORM_COPYCONTENTS, "value", CURLFORM_END);
- *
- * name/value pair where only the content pointer is remembered:
- * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
- * CURLFORM_PTRCONTENTS, ptr, CURLFORM_CONTENTSLENGTH, 10, CURLFORM_END);
- * (if CURLFORM_CONTENTSLENGTH is missing strlen () is used)
- *
- * storing a filename (CONTENTTYPE is optional!):
- * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
- * CURLFORM_FILE, "filename1", CURLFORM_CONTENTTYPE, "plain/text",
- * CURLFORM_END);
- *
- * storing multiple filenames:
- * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
- * CURLFORM_FILE, "filename1", CURLFORM_FILE, "filename2", CURLFORM_END);
- *
- * Returns:
- * CURL_FORMADD_OK             on success
- * CURL_FORMADD_MEMORY         if the FormInfo allocation fails
- * CURL_FORMADD_OPTION_TWICE   if one option is given twice for one Form
- * CURL_FORMADD_NULL           if a null pointer was given for a char
- * CURL_FORMADD_MEMORY         if the allocation of a FormInfo struct failed
- * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used
- * CURL_FORMADD_INCOMPLETE     if the some FormInfo is not complete (or error)
- * CURL_FORMADD_MEMORY         if a HttpPost struct cannot be allocated
- * CURL_FORMADD_MEMORY         if some allocation for string copying failed.
- * CURL_FORMADD_ILLEGAL_ARRAY  if an illegal option is used in an array
- *
- ***************************************************************************/
-
-static
-CURLFORMcode FormAdd(struct curl_httppost **httppost,
-                     struct curl_httppost **last_post,
-                     va_list params)
-{
-  FormInfo *first_form, *current_form, *form = NULL;
-  CURLFORMcode return_value = CURL_FORMADD_OK;
-  const char *prevtype = NULL;
-  struct curl_httppost *post = NULL;
-  CURLformoption option;
-  struct curl_forms *forms = NULL;
-  char *array_value=NULL; /* value read from an array */
-
-  /* This is a state variable, that if TRUE means that we're parsing an
-     array that we got passed to us. If FALSE we're parsing the input
-     va_list arguments. */
-  bool array_state = FALSE;
-
-  /*
-   * We need to allocate the first struct to fill in.
-   */
-  first_form = calloc(1, sizeof(struct FormInfo));
-  if(!first_form)
-    return CURL_FORMADD_MEMORY;
-
-  current_form = first_form;
-
-  /*
-   * Loop through all the options set. Break if we have an error to report.
-   */
-  while(return_value == CURL_FORMADD_OK) {
-
-    /* first see if we have more parts of the array param */
-    if(array_state && forms) {
-      /* get the upcoming option from the given array */
-      option = forms->option;
-      array_value = (char *)forms->value;
-
-      forms++; /* advance this to next entry */
-      if(CURLFORM_END == option) {
-        /* end of array state */
-        array_state = FALSE;
-        continue;
-      }
-    }
-    else {
-      /* This is not array-state, get next option */
-      option = va_arg(params, CURLformoption);
-      if(CURLFORM_END == option)
-        break;
-    }
-
-    switch (option) {
-    case CURLFORM_ARRAY:
-      if(array_state)
-        /* we don't support an array from within an array */
-        return_value = CURL_FORMADD_ILLEGAL_ARRAY;
-      else {
-        forms = va_arg(params, struct curl_forms *);
-        if(forms)
-          array_state = TRUE;
-        else
-          return_value = CURL_FORMADD_NULL;
-      }
-      break;
-
-      /*
-       * Set the Name property.
-       */
-    case CURLFORM_PTRNAME:
-#ifdef CURL_DOES_CONVERSIONS
-      /* Treat CURLFORM_PTR like CURLFORM_COPYNAME so that libcurl will copy
-       * the data in all cases so that we'll have safe memory for the eventual
-       * conversion.
-       */
-#else
-      current_form->flags |= HTTPPOST_PTRNAME; /* fall through */
-#endif
-    case CURLFORM_COPYNAME:
-      if(current_form->name)
-        return_value = CURL_FORMADD_OPTION_TWICE;
-      else {
-        char *name = array_state?
-          array_value:va_arg(params, char *);
-        if(name)
-          current_form->name = name; /* store for the moment */
-        else
-          return_value = CURL_FORMADD_NULL;
-      }
-      break;
-    case CURLFORM_NAMELENGTH:
-      if(current_form->namelength)
-        return_value = CURL_FORMADD_OPTION_TWICE;
-      else
-        current_form->namelength =
-          array_state?(size_t)array_value:(size_t)va_arg(params, long);
-      break;
-
-      /*
-       * Set the contents property.
-       */
-    case CURLFORM_PTRCONTENTS:
-      current_form->flags |= HTTPPOST_PTRCONTENTS; /* fall through */
-    case CURLFORM_COPYCONTENTS:
-      if(current_form->value)
-        return_value = CURL_FORMADD_OPTION_TWICE;
-      else {
-        char *value =
-          array_state?array_value:va_arg(params, char *);
-        if(value)
-          current_form->value = value; /* store for the moment */
-        else
-          return_value = CURL_FORMADD_NULL;
-      }
-      break;
-    case CURLFORM_CONTENTSLENGTH:
-      if(current_form->contentslength)
-        return_value = CURL_FORMADD_OPTION_TWICE;
-      else
-        current_form->contentslength =
-          array_state?(size_t)array_value:(size_t)va_arg(params, long);
-      break;
-
-      /* Get contents from a given file name */
-    case CURLFORM_FILECONTENT:
-      if(current_form->flags != 0)
-        return_value = CURL_FORMADD_OPTION_TWICE;
-      else {
-        const char *filename = array_state?
-          array_value:va_arg(params, char *);
-        if(filename) {
-          current_form->value = strdup(filename);
-          if(!current_form->value)
-            return_value = CURL_FORMADD_MEMORY;
-          else {
-            current_form->flags |= HTTPPOST_READFILE;
-            current_form->value_alloc = TRUE;
-          }
-        }
-        else
-          return_value = CURL_FORMADD_NULL;
-      }
-      break;
-
-      /* We upload a file */
-    case CURLFORM_FILE:
-      {
-        const char *filename = array_state?array_value:
-          va_arg(params, char *);
-
-        if(current_form->value) {
-          if(current_form->flags & HTTPPOST_FILENAME) {
-            if(filename) {
-              char *fname = strdup(filename);
-              if(!fname)
-                return_value = CURL_FORMADD_MEMORY;
-              else {
-                form = AddFormInfo(fname, NULL, current_form);
-                if(!form) {
-                  Curl_safefree(fname);
-                  return_value = CURL_FORMADD_MEMORY;
-                }
-                else {
-                  form->value_alloc = TRUE;
-                  current_form = form;
-                  form = NULL;
-                }
-              }
-            }
-            else
-              return_value = CURL_FORMADD_NULL;
-          }
-          else
-            return_value = CURL_FORMADD_OPTION_TWICE;
-        }
-        else {
-          if(filename) {
-            current_form->value = strdup(filename);
-            if(!current_form->value)
-              return_value = CURL_FORMADD_MEMORY;
-            else {
-              current_form->flags |= HTTPPOST_FILENAME;
-              current_form->value_alloc = TRUE;
-            }
-          }
-          else
-            return_value = CURL_FORMADD_NULL;
-        }
-        break;
-      }
-
-    case CURLFORM_BUFFERPTR:
-      current_form->flags |= HTTPPOST_PTRBUFFER|HTTPPOST_BUFFER;
-      if(current_form->buffer)
-        return_value = CURL_FORMADD_OPTION_TWICE;
-      else {
-        char *buffer =
-          array_state?array_value:va_arg(params, char *);
-        if(buffer) {
-          current_form->buffer = buffer; /* store for the moment */
-          current_form->value = buffer; /* make it non-NULL to be accepted
-                                           as fine */
-        }
-        else
-          return_value = CURL_FORMADD_NULL;
-      }
-      break;
-
-    case CURLFORM_BUFFERLENGTH:
-      if(current_form->bufferlength)
-        return_value = CURL_FORMADD_OPTION_TWICE;
-      else
-        current_form->bufferlength =
-          array_state?(size_t)array_value:(size_t)va_arg(params, long);
-      break;
-
-    case CURLFORM_STREAM:
-      current_form->flags |= HTTPPOST_CALLBACK;
-      if(current_form->userp)
-        return_value = CURL_FORMADD_OPTION_TWICE;
-      else {
-        char *userp =
-          array_state?array_value:va_arg(params, char *);
-        if(userp) {
-          current_form->userp = userp;
-          current_form->value = userp; /* this isn't strictly true but we
-                                          derive a value from this later on
-                                          and we need this non-NULL to be
-                                          accepted as a fine form part */
-        }
-        else
-          return_value = CURL_FORMADD_NULL;
-      }
-      break;
-
-    case CURLFORM_CONTENTTYPE:
-      {
-        const char *contenttype =
-          array_state?array_value:va_arg(params, char *);
-        if(current_form->contenttype) {
-          if(current_form->flags & HTTPPOST_FILENAME) {
-            if(contenttype) {
-              char *type = strdup(contenttype);
-              if(!type)
-                return_value = CURL_FORMADD_MEMORY;
-              else {
-                form = AddFormInfo(NULL, type, current_form);
-                if(!form) {
-                  Curl_safefree(type);
-                  return_value = CURL_FORMADD_MEMORY;
-                }
-                else {
-                  form->contenttype_alloc = TRUE;
-                  current_form = form;
-                  form = NULL;
-                }
-              }
-            }
-            else
-              return_value = CURL_FORMADD_NULL;
-          }
-          else
-            return_value = CURL_FORMADD_OPTION_TWICE;
-        }
-        else {
-          if(contenttype) {
-            current_form->contenttype = strdup(contenttype);
-            if(!current_form->contenttype)
-              return_value = CURL_FORMADD_MEMORY;
-            else
-              current_form->contenttype_alloc = TRUE;
-          }
-          else
-            return_value = CURL_FORMADD_NULL;
-        }
-        break;
-      }
-    case CURLFORM_CONTENTHEADER:
-      {
-        /* this "cast increases required alignment of target type" but
-           we consider it OK anyway */
-        struct curl_slist* list = array_state?
-          (struct curl_slist*)array_value:
-          va_arg(params, struct curl_slist*);
-
-        if(current_form->contentheader)
-          return_value = CURL_FORMADD_OPTION_TWICE;
-        else
-          current_form->contentheader = list;
-
-        break;
-      }
-    case CURLFORM_FILENAME:
-    case CURLFORM_BUFFER:
-      {
-        const char *filename = array_state?array_value:
-          va_arg(params, char *);
-        if(current_form->showfilename)
-          return_value = CURL_FORMADD_OPTION_TWICE;
-        else {
-          current_form->showfilename = strdup(filename);
-          if(!current_form->showfilename)
-            return_value = CURL_FORMADD_MEMORY;
-          else
-            current_form->showfilename_alloc = TRUE;
-        }
-        break;
-      }
-    default:
-      return_value = CURL_FORMADD_UNKNOWN_OPTION;
-      break;
-    }
-  }
-
-  if(CURL_FORMADD_OK != return_value) {
-    /* On error, free allocated fields for all nodes of the FormInfo linked
-       list without deallocating nodes. List nodes are deallocated later on */
-    FormInfo *ptr;
-    for(ptr = first_form; ptr != NULL; ptr = ptr->more) {
-      if(ptr->name_alloc) {
-        Curl_safefree(ptr->name);
-        ptr->name_alloc = FALSE;
-      }
-      if(ptr->value_alloc) {
-        Curl_safefree(ptr->value);
-        ptr->value_alloc = FALSE;
-      }
-      if(ptr->contenttype_alloc) {
-        Curl_safefree(ptr->contenttype);
-        ptr->contenttype_alloc = FALSE;
-      }
-      if(ptr->showfilename_alloc) {
-        Curl_safefree(ptr->showfilename);
-        ptr->showfilename_alloc = FALSE;
-      }
-    }
-  }
-
-  if(CURL_FORMADD_OK == return_value) {
-    /* go through the list, check for completeness and if everything is
-     * alright add the HttpPost item otherwise set return_value accordingly */
-
-    post = NULL;
-    for(form = first_form;
-        form != NULL;
-        form = form->more) {
-      if(((!form->name || !form->value) && !post) ||
-         ( (form->contentslength) &&
-           (form->flags & HTTPPOST_FILENAME) ) ||
-         ( (form->flags & HTTPPOST_FILENAME) &&
-           (form->flags & HTTPPOST_PTRCONTENTS) ) ||
-
-         ( (!form->buffer) &&
-           (form->flags & HTTPPOST_BUFFER) &&
-           (form->flags & HTTPPOST_PTRBUFFER) ) ||
-
-         ( (form->flags & HTTPPOST_READFILE) &&
-           (form->flags & HTTPPOST_PTRCONTENTS) )
-        ) {
-        return_value = CURL_FORMADD_INCOMPLETE;
-        break;
-      }
-      else {
-        if(((form->flags & HTTPPOST_FILENAME) ||
-            (form->flags & HTTPPOST_BUFFER)) &&
-           !form->contenttype ) {
-          /* our contenttype is missing */
-          form->contenttype
-            = strdup(ContentTypeForFilename(form->value, prevtype));
-          if(!form->contenttype) {
-            return_value = CURL_FORMADD_MEMORY;
-            break;
-          }
-          form->contenttype_alloc = TRUE;
-        }
-        if(!(form->flags & HTTPPOST_PTRNAME) &&
-           (form == first_form) ) {
-          /* Note that there's small risk that form->name is NULL here if the
-             app passed in a bad combo, so we better check for that first. */
-          if(form->name)
-            /* copy name (without strdup; possibly contains null characters) */
-            form->name = memdup(form->name, form->namelength);
-          if(!form->name) {
-            return_value = CURL_FORMADD_MEMORY;
-            break;
-          }
-          form->name_alloc = TRUE;
-        }
-        if(!(form->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE |
-                            HTTPPOST_PTRCONTENTS | HTTPPOST_PTRBUFFER |
-                            HTTPPOST_CALLBACK)) ) {
-          /* copy value (without strdup; possibly contains null characters) */
-          form->value = memdup(form->value, form->contentslength);
-          if(!form->value) {
-            return_value = CURL_FORMADD_MEMORY;
-            break;
-          }
-          form->value_alloc = TRUE;
-        }
-        post = AddHttpPost(form->name, form->namelength,
-                           form->value, form->contentslength,
-                           form->buffer, form->bufferlength,
-                           form->contenttype, form->flags,
-                           form->contentheader, form->showfilename,
-                           form->userp,
-                           post, httppost,
-                           last_post);
-
-        if(!post) {
-          return_value = CURL_FORMADD_MEMORY;
-          break;
-        }
-
-        if(form->contenttype)
-          prevtype = form->contenttype;
-      }
-    }
-    if(CURL_FORMADD_OK != return_value) {
-      /* On error, free allocated fields for nodes of the FormInfo linked
-         list which are not already owned by the httppost linked list
-         without deallocating nodes. List nodes are deallocated later on */
-      FormInfo *ptr;
-      for(ptr = form; ptr != NULL; ptr = ptr->more) {
-        if(ptr->name_alloc) {
-          Curl_safefree(ptr->name);
-          ptr->name_alloc = FALSE;
-        }
-        if(ptr->value_alloc) {
-          Curl_safefree(ptr->value);
-          ptr->value_alloc = FALSE;
-        }
-        if(ptr->contenttype_alloc) {
-          Curl_safefree(ptr->contenttype);
-          ptr->contenttype_alloc = FALSE;
-        }
-        if(ptr->showfilename_alloc) {
-          Curl_safefree(ptr->showfilename);
-          ptr->showfilename_alloc = FALSE;
-        }
-      }
-    }
-  }
-
-  /* Always deallocate FormInfo linked list nodes without touching node
-     fields given that these have either been deallocated or are owned
-     now by the httppost linked list */
-  while(first_form) {
-    FormInfo *ptr = first_form->more;
-    Curl_safefree(first_form);
-    first_form = ptr;
-  }
-
-  return return_value;
-}
-
-/*
- * curl_formadd() is a public API to add a section to the multipart formpost.
- *
- * @unittest: 1308
- */
-
-CURLFORMcode curl_formadd(struct curl_httppost **httppost,
-                          struct curl_httppost **last_post,
-                          ...)
-{
-  va_list arg;
-  CURLFORMcode result;
-  va_start(arg, last_post);
-  result = FormAdd(httppost, last_post, arg);
-  va_end(arg);
-  return result;
-}
-
-/*
- * AddFormData() adds a chunk of data to the FormData linked list.
- *
- * size is incremented by the chunk length, unless it is NULL
- */
-static CURLcode AddFormData(struct FormData **formp,
-                            enum formtype type,
-                            const void *line,
-                            size_t length,
-                            curl_off_t *size)
-{
-  struct FormData *newform = malloc(sizeof(struct FormData));
-  if(!newform)
-    return CURLE_OUT_OF_MEMORY;
-  newform->next = NULL;
-
-  if(type <= FORM_CONTENT) {
-    /* we make it easier for plain strings: */
-    if(!length)
-      length = strlen((char *)line);
-
-    newform->line = malloc(length+1);
-    if(!newform->line) {
-      free(newform);
-      return CURLE_OUT_OF_MEMORY;
-    }
-    memcpy(newform->line, line, length);
-    newform->length = length;
-    newform->line[length]=0; /* zero terminate for easier debugging */
-  }
-  else
-    /* For callbacks and files we don't have any actual data so we just keep a
-       pointer to whatever this points to */
-    newform->line = (char *)line;
-
-  newform->type = type;
-
-  if(*formp) {
-    (*formp)->next = newform;
-    *formp = newform;
-  }
-  else
-    *formp = newform;
-
-  if(size) {
-    if(type != FORM_FILE)
-      /* for static content as well as callback data we add the size given
-         as input argument */
-      *size += length;
-    else {
-      /* Since this is a file to be uploaded here, add the size of the actual
-         file */
-      if(!strequal("-", newform->line)) {
-        struct_stat file;
-        if(!stat(newform->line, &file)) {
-          *size += file.st_size;
-        }
-      }
-    }
-  }
-  return CURLE_OK;
-}
-
-/*
- * AddFormDataf() adds printf()-style formatted data to the formdata chain.
- */
-
-static CURLcode AddFormDataf(struct FormData **formp,
-                             curl_off_t *size,
-                             const char *fmt, ...)
-{
-  char s[4096];
-  va_list ap;
-  va_start(ap, fmt);
-  vsnprintf(s, sizeof(s), fmt, ap);
-  va_end(ap);
-
-  return AddFormData(formp, FORM_DATA, s, 0, size);
-}
-
-/*
- * Curl_formclean() is used from curl_http.c, this cleans a built FormData
- * linked list
- */
-void Curl_formclean(struct FormData **form_ptr)
-{
-  struct FormData *next, *form;
-
-  form = *form_ptr;
-  if(!form)
-    return;
-
-  do {
-    next=form->next;  /* the following form line */
-    if(form->type <= FORM_CONTENT)
-      free(form->line); /* free the line */
-    free(form);       /* free the struct */
-
-  } while((form = next) != NULL); /* continue */
-
-  *form_ptr = NULL;
-}
-
-/*
- * curl_formget()
- * Serialize a curl_httppost struct.
- * Returns 0 on success.
- *
- * @unittest: 1308
- */
-int curl_formget(struct curl_httppost *form, void *arg,
-                 curl_formget_callback append)
-{
-  CURLcode rc;
-  curl_off_t size;
-  struct FormData *data, *ptr;
-
-  rc = Curl_getformdata(NULL, &data, form, NULL, &size);
-  if(rc != CURLE_OK)
-    return (int)rc;
-
-  for(ptr = data; ptr; ptr = ptr->next) {
-    if((ptr->type == FORM_FILE) || (ptr->type == FORM_CALLBACK)) {
-      char buffer[8192];
-      size_t nread;
-      struct Form temp;
-
-      Curl_FormInit(&temp, ptr);
-
-      do {
-        nread = readfromfile(&temp, buffer, sizeof(buffer));
-        if((nread == (size_t) -1) ||
-           (nread > sizeof(buffer)) ||
-           (nread != append(arg, buffer, nread))) {
-          if(temp.fp)
-            fclose(temp.fp);
-          Curl_formclean(&data);
-          return -1;
-        }
-      } while(nread);
-    }
-    else {
-      if(ptr->length != append(arg, ptr->line, ptr->length)) {
-        Curl_formclean(&data);
-        return -1;
-      }
-    }
-  }
-  Curl_formclean(&data);
-  return 0;
-}
-
-/*
- * curl_formfree() is an external function to free up a whole form post
- * chain
- */
-void curl_formfree(struct curl_httppost *form)
-{
-  struct curl_httppost *next;
-
-  if(!form)
-    /* no form to free, just get out of this */
-    return;
-
-  do {
-    next=form->next;  /* the following form line */
-
-    /* recurse to sub-contents */
-    if(form->more)
-      curl_formfree(form->more);
-
-    if(!(form->flags & HTTPPOST_PTRNAME) && form->name)
-      free(form->name); /* free the name */
-    if(!(form->flags &
-         (HTTPPOST_PTRCONTENTS|HTTPPOST_BUFFER|HTTPPOST_CALLBACK)) &&
-       form->contents)
-      free(form->contents); /* free the contents */
-    if(form->contenttype)
-      free(form->contenttype); /* free the content type */
-    if(form->showfilename)
-      free(form->showfilename); /* free the faked file name */
-    free(form);       /* free the struct */
-
-  } while((form = next) != NULL); /* continue */
-}
-
-#ifndef HAVE_BASENAME
-/*
-  (Quote from The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004
-  Edition)
-
-  The basename() function shall take the pathname pointed to by path and
-  return a pointer to the final component of the pathname, deleting any
-  trailing '/' characters.
-
-  If the string pointed to by path consists entirely of the '/' character,
-  basename() shall return a pointer to the string "/". If the string pointed
-  to by path is exactly "//", it is implementation-defined whether '/' or "//"
-  is returned.
-
-  If path is a null pointer or points to an empty string, basename() shall
-  return a pointer to the string ".".
-
-  The basename() function may modify the string pointed to by path, and may
-  return a pointer to static storage that may then be overwritten by a
-  subsequent call to basename().
-
-  The basename() function need not be reentrant. A function that is not
-  required to be reentrant is not required to be thread-safe.
-
-*/
-static char *Curl_basename(char *path)
-{
-  /* Ignore all the details above for now and make a quick and simple
-     implementaion here */
-  char *s1;
-  char *s2;
-
-  s1=strrchr(path, '/');
-  s2=strrchr(path, '\\');
-
-  if(s1 && s2) {
-    path = (s1 > s2? s1 : s2)+1;
-  }
-  else if(s1)
-    path = s1 + 1;
-  else if(s2)
-    path = s2 + 1;
-
-  return path;
-}
-#endif
-
-static char *strippath(const char *fullfile)
-{
-  char *filename;
-  char *base;
-  filename = strdup(fullfile); /* duplicate since basename() may ruin the
-                                  buffer it works on */
-  if(!filename)
-    return NULL;
-  base = strdup(basename(filename));
-
-  free(filename); /* free temporary buffer */
-
-  return base; /* returns an allocated string or NULL ! */
-}
-
-/*
- * Curl_getformdata() converts a linked list of "meta data" into a complete
- * (possibly huge) multipart formdata. The input list is in 'post', while the
- * output resulting linked lists gets stored in '*finalform'. *sizep will get
- * the total size of the whole POST.
- * A multipart/form_data content-type is built, unless a custom content-type
- * is passed in 'custom_content_type'.
- *
- * This function will not do a failf() for the potential memory failures but
- * should for all other errors it spots. Just note that this function MAY get
- * a NULL pointer in the 'data' argument.
- */
-
-CURLcode Curl_getformdata(struct SessionHandle *data,
-                          struct FormData **finalform,
-                          struct curl_httppost *post,
-                          const char *custom_content_type,
-                          curl_off_t *sizep)
-{
-  struct FormData *form = NULL;
-  struct FormData *firstform;
-  struct curl_httppost *file;
-  CURLcode result = CURLE_OK;
-
-  curl_off_t size = 0; /* support potentially ENORMOUS formposts */
-  char *boundary;
-  char *fileboundary = NULL;
-  struct curl_slist* curList;
-
-  *finalform = NULL; /* default form is empty */
-
-  if(!post)
-    return result; /* no input => no output! */
-
-  boundary = Curl_FormBoundary();
-  if(!boundary)
-    return CURLE_OUT_OF_MEMORY;
-
-  /* Make the first line of the output */
-  result = AddFormDataf(&form, NULL,
-                        "%s; boundary=%s\r\n",
-                        custom_content_type?custom_content_type:
-                        "Content-Type: multipart/form-data",
-                        boundary);
-
-  if(result) {
-    Curl_safefree(boundary);
-    return result;
-  }
-  /* we DO NOT include that line in the total size of the POST, since it'll be
-     part of the header! */
-
-  firstform = form;
-
-  do {
-
-    if(size) {
-      result = AddFormDataf(&form, &size, "\r\n");
-      if(result)
-        break;
-    }
-
-    /* boundary */
-    result = AddFormDataf(&form, &size, "--%s\r\n", boundary);
-    if(result)
-      break;
-
-    /* Maybe later this should be disabled when a custom_content_type is
-       passed, since Content-Disposition is not meaningful for all multipart
-       types.
-    */
-    result = AddFormDataf(&form, &size,
-                          "Content-Disposition: form-data; name=\"");
-    if(result)
-      break;
-
-    result = AddFormData(&form, FORM_DATA, post->name, post->namelength,
-                         &size);
-    if(result)
-      break;
-
-    result = AddFormDataf(&form, &size, "\"");
-    if(result)
-      break;
-
-    if(post->more) {
-      /* If used, this is a link to more file names, we must then do
-         the magic to include several files with the same field name */
-
-      Curl_safefree(fileboundary);
-      fileboundary = Curl_FormBoundary();
-      if(!fileboundary) {
-        result = CURLE_OUT_OF_MEMORY;
-        break;
-      }
-
-      result = AddFormDataf(&form, &size,
-                            "\r\nContent-Type: multipart/mixed,"
-                            " boundary=%s\r\n",
-                            fileboundary);
-      if(result)
-        break;
-    }
-
-    file = post;
-
-    do {
-
-      /* If 'showfilename' is set, that is a faked name passed on to us
-         to use to in the formpost. If that is not set, the actually used
-         local file name should be added. */
-
-      if(post->more) {
-        /* if multiple-file */
-        char *filebasename = NULL;
-        if(!file->showfilename) {
-          filebasename = strippath(file->contents);
-          if(!filebasename) {
-            result = CURLE_OUT_OF_MEMORY;
-            break;
-          }
-        }
-
-        result = AddFormDataf(&form, &size,
-                              "\r\n--%s\r\nContent-Disposition: "
-                              "attachment; filename=\"%s\"",
-                              fileboundary,
-                              (file->showfilename?file->showfilename:
-                               filebasename));
-        Curl_safefree(filebasename);
-        if(result)
-          break;
-      }
-      else if(post->flags & (HTTPPOST_FILENAME|HTTPPOST_BUFFER|
-                             HTTPPOST_CALLBACK)) {
-        /* it should be noted that for the HTTPPOST_FILENAME and
-           HTTPPOST_CALLBACK cases the ->showfilename struct member is always
-           assigned at this point */
-        if(post->showfilename || (post->flags & HTTPPOST_FILENAME)) {
-          char *filebasename=
-            (!post->showfilename)?strippath(post->contents):NULL;
-
-          result = AddFormDataf(&form, &size,
-                                "; filename=\"%s\"",
-                                (post->showfilename?post->showfilename:
-                                 filebasename));
-          Curl_safefree(filebasename);
-        }
-
-        if(result)
-          break;
-      }
-
-      if(file->contenttype) {
-        /* we have a specified type */
-        result = AddFormDataf(&form, &size,
-                              "\r\nContent-Type: %s",
-                              file->contenttype);
-        if(result)
-          break;
-      }
-
-      curList = file->contentheader;
-      while(curList) {
-        /* Process the additional headers specified for this form */
-        result = AddFormDataf( &form, &size, "\r\n%s", curList->data );
-        if(result)
-          break;
-        curList = curList->next;
-      }
-      if(result)
-        break;
-
-      result = AddFormDataf(&form, &size, "\r\n\r\n");
-      if(result)
-        break;
-
-      if((post->flags & HTTPPOST_FILENAME) ||
-         (post->flags & HTTPPOST_READFILE)) {
-        /* we should include the contents from the specified file */
-        FILE *fileread;
-
-        fileread = strequal("-", file->contents)?
-          stdin:fopen(file->contents, "rb"); /* binary read for win32  */
-
-        /*
-         * VMS: This only allows for stream files on VMS.  Stream files are
-         * OK, as are FIXED & VAR files WITHOUT implied CC For implied CC,
-         * every record needs to have a \n appended & 1 added to SIZE
-         */
-
-        if(fileread) {
-          if(fileread != stdin) {
-            /* close the file */
-            fclose(fileread);
-            /* add the file name only - for later reading from this */
-            result = AddFormData(&form, FORM_FILE, file->contents, 0, &size);
-          }
-          else {
-            /* When uploading from stdin, we can't know the size of the file,
-             * thus must read the full file as before. We *could* use chunked
-             * transfer-encoding, but that only works for HTTP 1.1 and we
-             * can't be sure we work with such a server.
-             */
-            size_t nread;
-            char buffer[512];
-            while((nread = fread(buffer, 1, sizeof(buffer), fileread)) != 0) {
-              result = AddFormData(&form, FORM_CONTENT, buffer, nread, &size);
-              if(result)
-                break;
-            }
-          }
-        }
-        else {
-          if(data)
-            failf(data, "couldn't open file \"%s\"", file->contents);
-          *finalform = NULL;
-          result = CURLE_READ_ERROR;
-        }
-      }
-      else if(post->flags & HTTPPOST_BUFFER)
-        /* include contents of buffer */
-        result = AddFormData(&form, FORM_CONTENT, post->buffer,
-                             post->bufferlength, &size);
-      else if(post->flags & HTTPPOST_CALLBACK)
-        /* the contents should be read with the callback and the size
-           is set with the contentslength */
-        result = AddFormData(&form, FORM_CALLBACK, post->userp,
-                             post->contentslength, &size);
-      else
-        /* include the contents we got */
-        result = AddFormData(&form, FORM_CONTENT, post->contents,
-                             post->contentslength, &size);
-
-      file = file->more;
-    } while(file && !result); /* for each specified file for this field */
-
-    if(result)
-      break;
-
-    if(post->more) {
-      /* this was a multiple-file inclusion, make a termination file
-         boundary: */
-      result = AddFormDataf(&form, &size,
-                           "\r\n--%s--",
-                           fileboundary);
-      if(result)
-        break;
-    }
-
-  } while((post = post->next) != NULL); /* for each field */
-
-  /* end-boundary for everything */
-  if(CURLE_OK == result)
-    result = AddFormDataf(&form, &size,
-                          "\r\n--%s--\r\n",
-                          boundary);
-
-  if(result) {
-    Curl_formclean(&firstform);
-    Curl_safefree(fileboundary);
-    Curl_safefree(boundary);
-    return result;
-  }
-
-  *sizep = size;
-
-  Curl_safefree(fileboundary);
-  Curl_safefree(boundary);
-
-  *finalform = firstform;
-
-  return result;
-}
-
-/*
- * Curl_FormInit() inits the struct 'form' points to with the 'formdata'
- * and resets the 'sent' counter.
- */
-int Curl_FormInit(struct Form *form, struct FormData *formdata )
-{
-  if(!formdata)
-    return 1; /* error */
-
-  form->data = formdata;
-  form->sent = 0;
-  form->fp = NULL;
-  form->fread_func = ZERO_NULL;
-
-  return 0;
-}
-
-/*
- * readfromfile()
- *
- * The read callback that this function may use can return a value larger than
- * 'size' (which then this function returns) that indicates a problem and it
- * must be properly dealt with
- */
-static size_t readfromfile(struct Form *form, char *buffer,
-                           size_t size)
-{
-  size_t nread;
-  bool callback = (form->data->type == FORM_CALLBACK)?TRUE:FALSE;
-
-  if(callback) {
-    if(form->fread_func == ZERO_NULL)
-      return 0;
-    else
-      nread = form->fread_func(buffer, 1, size, form->data->line);
-  }
-  else {
-    if(!form->fp) {
-      /* this file hasn't yet been opened */
-      form->fp = fopen(form->data->line, "rb"); /* b is for binary */
-      if(!form->fp)
-        return (size_t)-1; /* failure */
-    }
-    nread = fread(buffer, 1, size, form->fp);
-  }
-  if(!nread) {
-    /* this is the last chunk from the file, move on */
-    if(form->fp) {
-      fclose(form->fp);
-      form->fp = NULL;
-    }
-    form->data = form->data->next;
-  }
-
-  return nread;
-}
-
-/*
- * Curl_FormReader() is the fread() emulation function that will be used to
- * deliver the formdata to the transfer loop and then sent away to the peer.
- */
-size_t Curl_FormReader(char *buffer,
-                       size_t size,
-                       size_t nitems,
-                       FILE *mydata)
-{
-  struct Form *form;
-  size_t wantedsize;
-  size_t gotsize = 0;
-
-  form=(struct Form *)mydata;
-
-  wantedsize = size * nitems;
-
-  if(!form->data)
-    return 0; /* nothing, error, empty */
-
-  if((form->data->type == FORM_FILE) ||
-     (form->data->type == FORM_CALLBACK)) {
-    gotsize = readfromfile(form, buffer, wantedsize);
-
-    if(gotsize)
-      /* If positive or -1, return. If zero, continue! */
-      return gotsize;
-  }
-  do {
-
-    if((form->data->length - form->sent ) > wantedsize - gotsize) {
-
-      memcpy(buffer + gotsize , form->data->line + form->sent,
-             wantedsize - gotsize);
-
-      form->sent += wantedsize-gotsize;
-
-      return wantedsize;
-    }
-
-    memcpy(buffer+gotsize,
-           form->data->line + form->sent,
-           (form->data->length - form->sent) );
-    gotsize += form->data->length - form->sent;
-
-    form->sent = 0;
-
-    form->data = form->data->next; /* advance */
-
-  } while(form->data && (form->data->type < FORM_CALLBACK));
-  /* If we got an empty line and we have more data, we proceed to the next
-     line immediately to avoid returning zero before we've reached the end. */
-
-  return gotsize;
-}
-
-/*
- * Curl_formpostheader() returns the first line of the formpost, the
- * request-header part (which is not part of the request-body like the rest of
- * the post).
- */
-char *Curl_formpostheader(void *formp, size_t *len)
-{
-  char *header;
-  struct Form *form=(struct Form *)formp;
-
-  if(!form->data)
-    return 0; /* nothing, ERROR! */
-
-  header = form->data->line;
-  *len = form->data->length;
-
-  form->data = form->data->next; /* advance */
-
-  return header;
-}
-
-#else  /* CURL_DISABLE_HTTP */
-CURLFORMcode curl_formadd(struct curl_httppost **httppost,
-                          struct curl_httppost **last_post,
-                          ...)
-{
-  (void)httppost;
-  (void)last_post;
-  return CURL_FORMADD_DISABLED;
-}
-
-int curl_formget(struct curl_httppost *form, void *arg,
-                 curl_formget_callback append)
-{
-  (void) form;
-  (void) arg;
-  (void) append;
-  return CURL_FORMADD_DISABLED;
-}
-
-void curl_formfree(struct curl_httppost *form)
-{
-  (void)form;
-  /* does nothing HTTP is disabled */
-}
-
-#endif  /* CURL_DISABLE_HTTP */
-
-#if !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY)
-
-/*
- * Curl_FormBoundary() creates a suitable boundary string and returns an
- * allocated one. This is also used by SSL-code so it must be present even
- * if HTTP is disabled!
- */
-char *Curl_FormBoundary(void)
-{
-  char *retstring;
-  size_t i;
-
-  static const char table16[]="0123456789abcdef";
-
-  retstring = malloc(BOUNDARY_LENGTH+1);
-
-  if(!retstring)
-    return NULL; /* failed */
-
-  strcpy(retstring, "----------------------------");
-
-  for(i=strlen(retstring); i<BOUNDARY_LENGTH; i++)
-    retstring[i] = table16[Curl_rand()%16];
-
-  /* 28 dashes and 12 hexadecimal digits makes 12^16 (184884258895036416)
-     combinations */
-  retstring[BOUNDARY_LENGTH]=0; /* zero terminate */
-
-  return retstring;
-}
-
-#endif  /* !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY) */
diff --git a/lib/ftp.c b/lib/ftp.c
deleted file mode 100644 (file)
index 653e30b..0000000
--- a/lib/ftp.c
+++ /dev/null
@@ -1,4596 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifndef CURL_DISABLE_FTP
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef HAVE_UTSNAME_H
-#include <sys/utsname.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef __VMS
-#include <in.h>
-#include <inet.h>
-#endif
-
-#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
-#undef in_addr_t
-#define in_addr_t unsigned long
-#endif
-
-#include <curl/curl.h>
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_if2ip.h"
-#include "curl_hostip.h"
-#include "curl_progress.h"
-#include "curl_transfer.h"
-#include "curl_escape.h"
-#include "curl_http.h" /* for HTTP proxy tunnel stuff */
-#include "curl_socks.h"
-#include "curl_ftp.h"
-#include "curl_fileinfo.h"
-#include "curl_ftplistparser.h"
-
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
-#include "curl_krb4.h"
-#endif
-
-#include "curl_strtoofft.h"
-#include "curl_strequal.h"
-#include "curl_sslgen.h"
-#include "curl_connect.h"
-#include "curl_strerror.h"
-#include "curl_inet_ntop.h"
-#include "curl_inet_pton.h"
-#include "curl_select.h"
-#include "curl_parsedate.h" /* for the week day and month names */
-#include "curl_sockaddr.h" /* required for Curl_sockaddr_storage */
-#include "curl_multiif.h"
-#include "curl_url.h"
-#include "curl_rawstr.h"
-#include "curl_speedcheck.h"
-#include "curl_warnless.h"
-#include "curl_http_proxy.h"
-#include "curl_non_ascii.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-#ifndef NI_MAXHOST
-#define NI_MAXHOST 1025
-#endif
-#ifndef INET_ADDRSTRLEN
-#define INET_ADDRSTRLEN 16
-#endif
-
-#ifdef CURL_DISABLE_VERBOSE_STRINGS
-#define ftp_pasv_verbose(a,b,c,d)  Curl_nop_stmt
-#endif
-
-/* Local API functions */
-static void state(struct connectdata *conn,
-                  ftpstate newstate);
-static CURLcode ftp_sendquote(struct connectdata *conn,
-                              struct curl_slist *quote);
-static CURLcode ftp_quit(struct connectdata *conn);
-static CURLcode ftp_parse_url_path(struct connectdata *conn);
-static CURLcode ftp_regular_transfer(struct connectdata *conn, bool *done);
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-static void ftp_pasv_verbose(struct connectdata *conn,
-                             Curl_addrinfo *ai,
-                             char *newhost, /* ascii version */
-                             int port);
-#endif
-static CURLcode ftp_state_post_rest(struct connectdata *conn);
-static CURLcode ftp_state_post_cwd(struct connectdata *conn);
-static CURLcode ftp_state_quote(struct connectdata *conn,
-                                bool init, ftpstate instate);
-static CURLcode ftp_nb_type(struct connectdata *conn,
-                            bool ascii, ftpstate newstate);
-static int ftp_need_type(struct connectdata *conn,
-                         bool ascii);
-static CURLcode ftp_do(struct connectdata *conn, bool *done);
-static CURLcode ftp_done(struct connectdata *conn,
-                         CURLcode, bool premature);
-static CURLcode ftp_connect(struct connectdata *conn, bool *done);
-static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection);
-static CURLcode ftp_do_more(struct connectdata *conn, bool *completed);
-static CURLcode ftp_multi_statemach(struct connectdata *conn, bool *done);
-static int ftp_getsock(struct connectdata *conn, curl_socket_t *socks,
-                       int numsocks);
-static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks,
-                              int numsocks);
-static CURLcode ftp_doing(struct connectdata *conn,
-                          bool *dophase_done);
-static CURLcode ftp_setup_connection(struct connectdata * conn);
-
-static CURLcode init_wc_data(struct connectdata *conn);
-static CURLcode wc_statemach(struct connectdata *conn);
-
-static void wc_data_dtor(void *ptr);
-
-static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
-                                         curl_off_t filesize);
-
-static CURLcode ftp_readresp(curl_socket_t sockfd,
-                             struct pingpong *pp,
-                             int *ftpcode,
-                             size_t *size);
-
-/* easy-to-use macro: */
-#define FTPSENDF(x,y,z)    if((result = Curl_ftpsendf(x,y,z)) != CURLE_OK) \
-                              return result
-#define PPSENDF(x,y,z)  if((result = Curl_pp_sendf(x,y,z)) != CURLE_OK) \
-                              return result
-
-
-/*
- * FTP protocol handler.
- */
-
-const struct Curl_handler Curl_handler_ftp = {
-  "FTP",                           /* scheme */
-  ftp_setup_connection,            /* setup_connection */
-  ftp_do,                          /* do_it */
-  ftp_done,                        /* done */
-  ftp_do_more,                     /* do_more */
-  ftp_connect,                     /* connect_it */
-  ftp_multi_statemach,             /* connecting */
-  ftp_doing,                       /* doing */
-  ftp_getsock,                     /* proto_getsock */
-  ftp_getsock,                     /* doing_getsock */
-  ftp_domore_getsock,              /* domore_getsock */
-  ZERO_NULL,                       /* perform_getsock */
-  ftp_disconnect,                  /* disconnect */
-  ZERO_NULL,                       /* readwrite */
-  PORT_FTP,                        /* defport */
-  CURLPROTO_FTP,                   /* protocol */
-  PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD
-  | PROTOPT_NOURLQUERY /* flags */
-};
-
-
-#ifdef USE_SSL
-/*
- * FTPS protocol handler.
- */
-
-const struct Curl_handler Curl_handler_ftps = {
-  "FTPS",                          /* scheme */
-  ftp_setup_connection,            /* setup_connection */
-  ftp_do,                          /* do_it */
-  ftp_done,                        /* done */
-  ftp_do_more,                     /* do_more */
-  ftp_connect,                     /* connect_it */
-  ftp_multi_statemach,             /* connecting */
-  ftp_doing,                       /* doing */
-  ftp_getsock,                     /* proto_getsock */
-  ftp_getsock,                     /* doing_getsock */
-  ftp_domore_getsock,              /* domore_getsock */
-  ZERO_NULL,                       /* perform_getsock */
-  ftp_disconnect,                  /* disconnect */
-  ZERO_NULL,                       /* readwrite */
-  PORT_FTPS,                       /* defport */
-  CURLPROTO_FTP | CURLPROTO_FTPS,  /* protocol */
-  PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION |
-  PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY /* flags */
-};
-#endif
-
-#ifndef CURL_DISABLE_HTTP
-/*
- * HTTP-proxyed FTP protocol handler.
- */
-
-static const struct Curl_handler Curl_handler_ftp_proxy = {
-  "FTP",                                /* scheme */
-  ZERO_NULL,                            /* setup_connection */
-  Curl_http,                            /* do_it */
-  Curl_http_done,                       /* done */
-  ZERO_NULL,                            /* do_more */
-  ZERO_NULL,                            /* connect_it */
-  ZERO_NULL,                            /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
-  ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* readwrite */
-  PORT_FTP,                             /* defport */
-  CURLPROTO_HTTP,                       /* protocol */
-  PROTOPT_NONE                          /* flags */
-};
-
-
-#ifdef USE_SSL
-/*
- * HTTP-proxyed FTPS protocol handler.
- */
-
-static const struct Curl_handler Curl_handler_ftps_proxy = {
-  "FTPS",                               /* scheme */
-  ZERO_NULL,                            /* setup_connection */
-  Curl_http,                            /* do_it */
-  Curl_http_done,                       /* done */
-  ZERO_NULL,                            /* do_more */
-  ZERO_NULL,                            /* connect_it */
-  ZERO_NULL,                            /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
-  ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* readwrite */
-  PORT_FTPS,                            /* defport */
-  CURLPROTO_HTTP,                       /* protocol */
-  PROTOPT_NONE                          /* flags */
-};
-#endif
-#endif
-
-
-/*
- * NOTE: back in the old days, we added code in the FTP code that made NOBODY
- * requests on files respond with headers passed to the client/stdout that
- * looked like HTTP ones.
- *
- * This approach is not very elegant, it causes confusion and is error-prone.
- * It is subject for removal at the next (or at least a future) soname bump.
- * Until then you can test the effects of the removal by undefining the
- * following define named CURL_FTP_HTTPSTYLE_HEAD.
- */
-#define CURL_FTP_HTTPSTYLE_HEAD 1
-
-static void freedirs(struct ftp_conn *ftpc)
-{
-  int i;
-  if(ftpc->dirs) {
-    for(i=0; i < ftpc->dirdepth; i++) {
-      if(ftpc->dirs[i]) {
-        free(ftpc->dirs[i]);
-        ftpc->dirs[i]=NULL;
-      }
-    }
-    free(ftpc->dirs);
-    ftpc->dirs = NULL;
-    ftpc->dirdepth = 0;
-  }
-  if(ftpc->file) {
-    free(ftpc->file);
-    ftpc->file = NULL;
-  }
-}
-
-/* Returns non-zero if the given string contains CR (\r) or LF (\n),
-   which are not allowed within RFC 959 <string>.
-   Note: The input string is in the client's encoding which might
-   not be ASCII, so escape sequences \r & \n must be used instead
-   of hex values 0x0d & 0x0a.
-*/
-static bool isBadFtpString(const char *string)
-{
-  return ((NULL != strchr(string, '\r')) ||
-          (NULL != strchr(string, '\n'))) ? TRUE : FALSE;
-}
-
-/***********************************************************************
- *
- * AcceptServerConnect()
- *
- * After connection request is received from the server this function is
- * called to accept the connection and close the listening socket
- *
- */
-static CURLcode AcceptServerConnect(struct connectdata *conn)
-{
-  struct SessionHandle *data = conn->data;
-  curl_socket_t sock = conn->sock[SECONDARYSOCKET];
-  curl_socket_t s = CURL_SOCKET_BAD;
-#ifdef ENABLE_IPV6
-  struct Curl_sockaddr_storage add;
-#else
-  struct sockaddr_in add;
-#endif
-  curl_socklen_t size = (curl_socklen_t) sizeof(add);
-
-  if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) {
-    size = sizeof(add);
-
-    s=accept(sock, (struct sockaddr *) &add, &size);
-  }
-  Curl_closesocket(conn, sock); /* close the first socket */
-
-  if(CURL_SOCKET_BAD == s) {
-    failf(data, "Error accept()ing server connect");
-    return CURLE_FTP_PORT_FAILED;
-  }
-  infof(data, "Connection accepted from server\n");
-
-  conn->sock[SECONDARYSOCKET] = s;
-  curlx_nonblock(s, TRUE); /* enable non-blocking */
-  conn->sock_accepted[SECONDARYSOCKET] = TRUE;
-
-  if(data->set.fsockopt) {
-    int error = 0;
-
-    /* activate callback for setting socket options */
-    error = data->set.fsockopt(data->set.sockopt_client,
-                               s,
-                               CURLSOCKTYPE_ACCEPT);
-
-    if(error) {
-      Curl_closesocket(conn, s); /* close the socket and bail out */
-      conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
-      return CURLE_ABORTED_BY_CALLBACK;
-    }
-  }
-
-  return CURLE_OK;
-
-}
-
-/*
- * ftp_timeleft_accept() returns the amount of milliseconds left allowed for
- * waiting server to connect. If the value is negative, the timeout time has
- * already elapsed.
- *
- * The start time is stored in progress.t_acceptdata - as set with
- * Curl_pgrsTime(..., TIMER_STARTACCEPT);
- *
- */
-static long ftp_timeleft_accept(struct SessionHandle *data)
-{
-  long timeout_ms = DEFAULT_ACCEPT_TIMEOUT;
-  long other;
-  struct timeval now;
-
-  if(data->set.accepttimeout > 0)
-    timeout_ms = data->set.accepttimeout;
-
-  now = Curl_tvnow();
-
-  /* check if the generic timeout possibly is set shorter */
-  other =  Curl_timeleft(data, &now, FALSE);
-  if(other && (other < timeout_ms))
-    /* note that this also works fine for when other happens to be negative
-       due to it already having elapsed */
-    timeout_ms = other;
-  else {
-    /* subtract elapsed time */
-    timeout_ms -= Curl_tvdiff(now, data->progress.t_acceptdata);
-    if(!timeout_ms)
-      /* avoid returning 0 as that means no timeout! */
-      return -1;
-  }
-
-  return timeout_ms;
-}
-
-
-/***********************************************************************
- *
- * ReceivedServerConnect()
- *
- * After allowing server to connect to us from data port, this function
- * checks both data connection for connection establishment and ctrl
- * connection for a negative response regarding a failure in connecting
- *
- */
-static CURLcode ReceivedServerConnect(struct connectdata* conn, bool* received)
-{
-  struct SessionHandle *data = conn->data;
-  curl_socket_t ctrl_sock = conn->sock[FIRSTSOCKET];
-  curl_socket_t data_sock = conn->sock[SECONDARYSOCKET];
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-  struct pingpong *pp = &ftpc->pp;
-  int result;
-  long timeout_ms;
-  ssize_t nread;
-  int ftpcode;
-
-  *received = FALSE;
-
-  timeout_ms = ftp_timeleft_accept(data);
-  infof(data, "Checking for server connect\n");
-  if(timeout_ms < 0) {
-    /* if a timeout was already reached, bail out */
-    failf(data, "Accept timeout occurred while waiting server connect");
-    return CURLE_FTP_ACCEPT_TIMEOUT;
-  }
-
-  /* First check whether there is a cached response from server */
-  if(pp->cache_size && pp->cache && pp->cache[0] > '3') {
-    /* Data connection could not be established, let's return */
-    infof(data, "There is negative response in cache while serv connect\n");
-    Curl_GetFTPResponse(&nread, conn, &ftpcode);
-    return CURLE_FTP_ACCEPT_FAILED;
-  }
-
-  result = Curl_socket_check(ctrl_sock, data_sock, CURL_SOCKET_BAD, 0);
-
-  /* see if the connection request is already here */
-  switch (result) {
-  case -1: /* error */
-    /* let's die here */
-    failf(data, "Error while waiting for server connect");
-    return CURLE_FTP_ACCEPT_FAILED;
-  case 0:  /* Server connect is not received yet */
-    break; /* loop */
-  default:
-
-    if(result & CURL_CSELECT_IN2) {
-      infof(data, "Ready to accept data connection from server\n");
-      *received = TRUE;
-    }
-    else if(result & CURL_CSELECT_IN) {
-      infof(data, "Ctrl conn has data while waiting for data conn\n");
-      Curl_GetFTPResponse(&nread, conn, &ftpcode);
-
-      if(ftpcode/100 > 3)
-        return CURLE_FTP_ACCEPT_FAILED;
-
-      return CURLE_FTP_WEIRD_SERVER_REPLY;
-    }
-
-    break;
-  } /* switch() */
-
-  return CURLE_OK;
-}
-
-
-/***********************************************************************
- *
- * InitiateTransfer()
- *
- * After connection from server is accepted this function is called to
- * setup transfer parameters and initiate the data transfer.
- *
- */
-static CURLcode InitiateTransfer(struct connectdata *conn)
-{
-  struct SessionHandle *data = conn->data;
-  struct FTP *ftp = data->state.proto.ftp;
-  CURLcode result = CURLE_OK;
-
-  if(conn->ssl[SECONDARYSOCKET].use) {
-    /* since we only have a plaintext TCP connection here, we must now
-     * do the TLS stuff */
-    infof(data, "Doing the SSL/TLS handshake on the data stream\n");
-    result = Curl_ssl_connect(conn, SECONDARYSOCKET);
-    if(result)
-      return result;
-  }
-
-  if(conn->proto.ftpc.state_saved == FTP_STOR) {
-    *(ftp->bytecountp)=0;
-
-    /* When we know we're uploading a specified file, we can get the file
-       size prior to the actual upload. */
-
-    Curl_pgrsSetUploadSize(data, data->set.infilesize);
-
-    /* set the SO_SNDBUF for the secondary socket for those who need it */
-    Curl_sndbufset(conn->sock[SECONDARYSOCKET]);
-
-    Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
-                        SECONDARYSOCKET, ftp->bytecountp);
-  }
-  else {
-    /* FTP download: */
-    Curl_setup_transfer(conn, SECONDARYSOCKET,
-        conn->proto.ftpc.retr_size_saved, FALSE,
-        ftp->bytecountp, -1, NULL); /* no upload here */
-  }
-
-  conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
-  state(conn, FTP_STOP);
-
-  return CURLE_OK;
-}
-
-/***********************************************************************
- *
- * AllowServerConnect()
- *
- * When we've issue the PORT command, we have told the server to connect
- * to us. This function
- *   - will sit and wait here until the server has connected for easy interface
- *   - will check whether data connection is established if so it is accepted
- *   for multi interface
- *
- */
-static CURLcode AllowServerConnect(struct connectdata *conn, bool *connected)
-{
-  struct SessionHandle *data = conn->data;
-  long timeout_ms;
-  long interval_ms;
-  CURLcode ret = CURLE_OK;
-
-  *connected = FALSE;
-  infof(data, "Preparing for accepting server on data port\n");
-
-  /* Save the time we start accepting server connect */
-  Curl_pgrsTime(data, TIMER_STARTACCEPT);
-
-  for(;;) {
-    timeout_ms = ftp_timeleft_accept(data);
-    if(timeout_ms < 0) {
-      /* if a timeout was already reached, bail out */
-      failf(data, "Accept timeout occurred while waiting server connect");
-      return CURLE_FTP_ACCEPT_TIMEOUT;
-    }
-
-    /* see if the connection request is already here */
-    ret = ReceivedServerConnect(conn, connected);
-    if(ret)
-      return ret;
-
-    if(*connected) {
-      ret = AcceptServerConnect(conn);
-      if(ret)
-        return ret;
-
-      ret = InitiateTransfer(conn);
-      if(ret)
-        return ret;
-
-      break; /* connection is accepted, break the loop */
-    }
-    else {
-      if(data->state.used_interface == Curl_if_easy) {
-        interval_ms = 1000;
-        if(timeout_ms < interval_ms)
-          interval_ms = timeout_ms;
-
-        /* sleep for 1 second and then continue */
-        Curl_socket_ready(CURL_SOCKET_BAD, CURL_SOCKET_BAD, interval_ms);
-      }
-      else {
-        /* Add timeout to multi handle and break out of the loop */
-        if(ret == CURLE_OK && *connected == FALSE) {
-          if(data->set.accepttimeout > 0)
-            Curl_expire(data, data->set.accepttimeout);
-          else
-            Curl_expire(data, DEFAULT_ACCEPT_TIMEOUT);
-        }
-
-        break; /* connection was not accepted immediately */
-      }
-    }
-  }
-
-  return ret;
-}
-
-/* macro to check for a three-digit ftp status code at the start of the
-   given string */
-#define STATUSCODE(line) (ISDIGIT(line[0]) && ISDIGIT(line[1]) &&       \
-                          ISDIGIT(line[2]))
-
-/* macro to check for the last line in an FTP server response */
-#define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3]))
-
-static int ftp_endofresp(struct pingpong *pp,
-                         int *code)
-{
-  char *line = pp->linestart_resp;
-  size_t len = pp->nread_resp;
-
-  if((len > 3) && LASTLINE(line)) {
-    *code = curlx_sltosi(strtol(line, NULL, 10));
-    return 1;
-  }
-  return 0;
-}
-
-static CURLcode ftp_readresp(curl_socket_t sockfd,
-                             struct pingpong *pp,
-                             int *ftpcode, /* return the ftp-code if done */
-                             size_t *size) /* size of the response */
-{
-  struct connectdata *conn = pp->conn;
-  struct SessionHandle *data = conn->data;
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
-  char * const buf = data->state.buffer;
-#endif
-  CURLcode result = CURLE_OK;
-  int code;
-
-  result = Curl_pp_readresp(sockfd, pp, &code, size);
-
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
-  /* handle the security-oriented responses 6xx ***/
-  /* FIXME: some errorchecking perhaps... ***/
-  switch(code) {
-  case 631:
-    code = Curl_sec_read_msg(conn, buf, PROT_SAFE);
-    break;
-  case 632:
-    code = Curl_sec_read_msg(conn, buf, PROT_PRIVATE);
-    break;
-  case 633:
-    code = Curl_sec_read_msg(conn, buf, PROT_CONFIDENTIAL);
-    break;
-  default:
-    /* normal ftp stuff we pass through! */
-    break;
-  }
-#endif
-
-  /* store the latest code for later retrieval */
-  data->info.httpcode=code;
-
-  if(ftpcode)
-    *ftpcode = code;
-
-  if(421 == code) {
-    /* 421 means "Service not available, closing control connection." and FTP
-     * servers use it to signal that idle session timeout has been exceeded.
-     * If we ignored the response, it could end up hanging in some cases.
-     *
-     * This response code can come at any point so having it treated
-     * generically is a good idea.
-     */
-    infof(data, "We got a 421 - timeout!\n");
-    state(conn, FTP_STOP);
-    return CURLE_OPERATION_TIMEDOUT;
-  }
-
-  return result;
-}
-
-/* --- parse FTP server responses --- */
-
-/*
- * Curl_GetFTPResponse() is a BLOCKING function to read the full response
- * from a server after a command.
- *
- */
-
-CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
-                             struct connectdata *conn,
-                             int *ftpcode) /* return the ftp-code */
-{
-  /*
-   * We cannot read just one byte per read() and then go back to select() as
-   * the OpenSSL read() doesn't grok that properly.
-   *
-   * Alas, read as much as possible, split up into lines, use the ending
-   * line in a response or continue reading.  */
-
-  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
-  long timeout;              /* timeout in milliseconds */
-  long interval_ms;
-  struct SessionHandle *data = conn->data;
-  CURLcode result = CURLE_OK;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-  struct pingpong *pp = &ftpc->pp;
-  size_t nread;
-  int cache_skip=0;
-  int value_to_be_ignored=0;
-
-  if(ftpcode)
-    *ftpcode = 0; /* 0 for errors */
-  else
-    /* make the pointer point to something for the rest of this function */
-    ftpcode = &value_to_be_ignored;
-
-  *nreadp=0;
-
-  while(!*ftpcode && !result) {
-    /* check and reset timeout value every lap */
-    timeout = Curl_pp_state_timeout(pp);
-
-    if(timeout <=0 ) {
-      failf(data, "FTP response timeout");
-      return CURLE_OPERATION_TIMEDOUT; /* already too little time */
-    }
-
-    interval_ms = 1000;  /* use 1 second timeout intervals */
-    if(timeout < interval_ms)
-      interval_ms = timeout;
-
-    /*
-     * Since this function is blocking, we need to wait here for input on the
-     * connection and only then we call the response reading function. We do
-     * timeout at least every second to make the timeout check run.
-     *
-     * A caution here is that the ftp_readresp() function has a cache that may
-     * contain pieces of a response from the previous invoke and we need to
-     * make sure we don't just wait for input while there is unhandled data in
-     * that cache. But also, if the cache is there, we call ftp_readresp() and
-     * the cache wasn't good enough to continue we must not just busy-loop
-     * around this function.
-     *
-     */
-
-    if(pp->cache && (cache_skip < 2)) {
-      /*
-       * There's a cache left since before. We then skipping the wait for
-       * socket action, unless this is the same cache like the previous round
-       * as then the cache was deemed not enough to act on and we then need to
-       * wait for more data anyway.
-       */
-    }
-    else {
-      switch (Curl_socket_ready(sockfd, CURL_SOCKET_BAD, interval_ms)) {
-      case -1: /* select() error, stop reading */
-        failf(data, "FTP response aborted due to select/poll error: %d",
-              SOCKERRNO);
-        return CURLE_RECV_ERROR;
-
-      case 0: /* timeout */
-        if(Curl_pgrsUpdate(conn))
-          return CURLE_ABORTED_BY_CALLBACK;
-        continue; /* just continue in our loop for the timeout duration */
-
-      default: /* for clarity */
-        break;
-      }
-    }
-    result = ftp_readresp(sockfd, pp, ftpcode, &nread);
-    if(result)
-      break;
-
-    if(!nread && pp->cache)
-      /* bump cache skip counter as on repeated skips we must wait for more
-         data */
-      cache_skip++;
-    else
-      /* when we got data or there is no cache left, we reset the cache skip
-         counter */
-      cache_skip=0;
-
-    *nreadp += nread;
-
-  } /* while there's buffer left and loop is requested */
-
-  pp->pending_resp = FALSE;
-
-  return result;
-}
-
-/* This is the ONLY way to change FTP state! */
-static void state(struct connectdata *conn,
-                  ftpstate newstate)
-{
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
-  /* for debug purposes */
-  static const char * const names[]={
-    "STOP",
-    "WAIT220",
-    "AUTH",
-    "USER",
-    "PASS",
-    "ACCT",
-    "PBSZ",
-    "PROT",
-    "CCC",
-    "PWD",
-    "SYST",
-    "NAMEFMT",
-    "QUOTE",
-    "RETR_PREQUOTE",
-    "STOR_PREQUOTE",
-    "POSTQUOTE",
-    "CWD",
-    "MKD",
-    "MDTM",
-    "TYPE",
-    "LIST_TYPE",
-    "RETR_TYPE",
-    "STOR_TYPE",
-    "SIZE",
-    "RETR_SIZE",
-    "STOR_SIZE",
-    "REST",
-    "RETR_REST",
-    "PORT",
-    "PRET",
-    "PASV",
-    "LIST",
-    "RETR",
-    "STOR",
-    "QUIT"
-  };
-#endif
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
-  if(ftpc->state != newstate)
-    infof(conn->data, "FTP %p state change from %s to %s\n",
-          ftpc, names[ftpc->state], names[newstate]);
-#endif
-  ftpc->state = newstate;
-}
-
-static CURLcode ftp_state_user(struct connectdata *conn)
-{
-  CURLcode result;
-  struct FTP *ftp = conn->data->state.proto.ftp;
-  /* send USER */
-  PPSENDF(&conn->proto.ftpc.pp, "USER %s", ftp->user?ftp->user:"");
-
-  state(conn, FTP_USER);
-  conn->data->state.ftp_trying_alternative = FALSE;
-
-  return CURLE_OK;
-}
-
-static CURLcode ftp_state_pwd(struct connectdata *conn)
-{
-  CURLcode result;
-
-  /* send PWD to discover our entry point */
-  PPSENDF(&conn->proto.ftpc.pp, "PWD", NULL);
-  state(conn, FTP_PWD);
-
-  return CURLE_OK;
-}
-
-/* For the FTP "protocol connect" and "doing" phases only */
-static int ftp_getsock(struct connectdata *conn,
-                       curl_socket_t *socks,
-                       int numsocks)
-{
-  return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks);
-}
-
-/* For the FTP "DO_MORE" phase only */
-static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks,
-                              int numsocks)
-{
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-
-  if(!numsocks)
-    return GETSOCK_BLANK;
-
-  /* When in DO_MORE state, we could be either waiting for us to connect to a
-     remote site, or we could wait for that site to connect to us. Or just
-     handle ordinary commands.
-
-     When waiting for a connect, we will be in FTP_STOP state and then we wait
-     for the secondary socket to become writeable. If we're in another state,
-     we're still handling commands on the control (primary) connection.
-
-  */
-
-  switch(ftpc->state) {
-  case FTP_STOP:
-    break;
-  default:
-    return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks);
-  }
-
-  socks[0] = conn->sock[SECONDARYSOCKET];
-  if(ftpc->wait_data_conn) {
-    socks[1] = conn->sock[FIRSTSOCKET];
-    return GETSOCK_READSOCK(0) | GETSOCK_READSOCK(1);
-  }
-
-  return GETSOCK_READSOCK(0);
-}
-
-/* This is called after the FTP_QUOTE state is passed.
-
-   ftp_state_cwd() sends the range of CWD commands to the server to change to
-   the correct directory. It may also need to send MKD commands to create
-   missing ones, if that option is enabled.
-*/
-static CURLcode ftp_state_cwd(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-
-  if(ftpc->cwddone)
-    /* already done and fine */
-    result = ftp_state_post_cwd(conn);
-  else {
-    ftpc->count2 = 0; /* count2 counts failed CWDs */
-
-    /* count3 is set to allow a MKD to fail once. In the case when first CWD
-       fails and then MKD fails (due to another session raced it to create the
-       dir) this then allows for a second try to CWD to it */
-    ftpc->count3 = (conn->data->set.ftp_create_missing_dirs==2)?1:0;
-
-    if(conn->bits.reuse && ftpc->entrypath) {
-      /* This is a re-used connection. Since we change directory to where the
-         transfer is taking place, we must first get back to the original dir
-         where we ended up after login: */
-      ftpc->count1 = 0; /* we count this as the first path, then we add one
-                          for all upcoming ones in the ftp->dirs[] array */
-      PPSENDF(&conn->proto.ftpc.pp, "CWD %s", ftpc->entrypath);
-      state(conn, FTP_CWD);
-    }
-    else {
-      if(ftpc->dirdepth) {
-        ftpc->count1 = 1;
-        /* issue the first CWD, the rest is sent when the CWD responses are
-           received... */
-        PPSENDF(&conn->proto.ftpc.pp, "CWD %s", ftpc->dirs[ftpc->count1 -1]);
-        state(conn, FTP_CWD);
-      }
-      else {
-        /* No CWD necessary */
-        result = ftp_state_post_cwd(conn);
-      }
-    }
-  }
-  return result;
-}
-
-typedef enum {
-  EPRT,
-  PORT,
-  DONE
-} ftpport;
-
-static CURLcode ftp_state_use_port(struct connectdata *conn,
-                                   ftpport fcmd) /* start with this */
-
-{
-  CURLcode result = CURLE_OK;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-  struct SessionHandle *data=conn->data;
-  curl_socket_t portsock= CURL_SOCKET_BAD;
-  char myhost[256] = "";
-
-  struct Curl_sockaddr_storage ss;
-  Curl_addrinfo *res, *ai;
-  curl_socklen_t sslen;
-  char hbuf[NI_MAXHOST];
-  struct sockaddr *sa=(struct sockaddr *)&ss;
-  struct sockaddr_in * const sa4 = (void *)sa;
-#ifdef ENABLE_IPV6
-  struct sockaddr_in6 * const sa6 = (void *)sa;
-#endif
-  char tmp[1024];
-  static const char mode[][5] = { "EPRT", "PORT" };
-  int rc;
-  int error;
-  char *host = NULL;
-  char *string_ftpport = data->set.str[STRING_FTPPORT];
-  struct Curl_dns_entry *h=NULL;
-  unsigned short port_min = 0;
-  unsigned short port_max = 0;
-  unsigned short port;
-  bool possibly_non_local = TRUE;
-
-  char *addr = NULL;
-
-  /* Step 1, figure out what is requested,
-   * accepted format :
-   * (ipv4|ipv6|domain|interface)?(:port(-range)?)?
-   */
-
-  if(data->set.str[STRING_FTPPORT] &&
-     (strlen(data->set.str[STRING_FTPPORT]) > 1)) {
-
-#ifdef ENABLE_IPV6
-    size_t addrlen = INET6_ADDRSTRLEN > strlen(string_ftpport) ?
-      INET6_ADDRSTRLEN : strlen(string_ftpport);
-#else
-    size_t addrlen = INET_ADDRSTRLEN > strlen(string_ftpport) ?
-      INET_ADDRSTRLEN : strlen(string_ftpport);
-#endif
-    char *ip_start = string_ftpport;
-    char *ip_end = NULL;
-    char *port_start = NULL;
-    char *port_sep = NULL;
-
-    addr = calloc(addrlen+1, 1);
-    if(!addr)
-      return CURLE_OUT_OF_MEMORY;
-
-#ifdef ENABLE_IPV6
-    if(*string_ftpport == '[') {
-      /* [ipv6]:port(-range) */
-      ip_start = string_ftpport + 1;
-      if((ip_end = strchr(string_ftpport, ']')) != NULL )
-        strncpy(addr, ip_start, ip_end - ip_start);
-    }
-    else
-#endif
-      if(*string_ftpport == ':') {
-        /* :port */
-        ip_end = string_ftpport;
-    }
-    else if((ip_end = strchr(string_ftpport, ':')) != NULL) {
-        /* either ipv6 or (ipv4|domain|interface):port(-range) */
-#ifdef ENABLE_IPV6
-      if(Curl_inet_pton(AF_INET6, string_ftpport, sa6) == 1) {
-        /* ipv6 */
-        port_min = port_max = 0;
-        strcpy(addr, string_ftpport);
-        ip_end = NULL; /* this got no port ! */
-      }
-      else
-#endif
-        /* (ipv4|domain|interface):port(-range) */
-        strncpy(addr, string_ftpport, ip_end - ip_start );
-    }
-    else
-      /* ipv4|interface */
-      strcpy(addr, string_ftpport);
-
-    /* parse the port */
-    if(ip_end != NULL) {
-      if((port_start = strchr(ip_end, ':')) != NULL) {
-        port_min = curlx_ultous(strtoul(port_start+1, NULL, 10));
-        if((port_sep = strchr(port_start, '-')) != NULL) {
-          port_max = curlx_ultous(strtoul(port_sep + 1, NULL, 10));
-        }
-        else
-          port_max = port_min;
-      }
-    }
-
-    /* correct errors like:
-     *  :1234-1230
-     *  :-4711 , in this case port_min is (unsigned)-1,
-     *           therefore port_min > port_max for all cases
-     *           but port_max = (unsigned)-1
-     */
-    if(port_min > port_max )
-      port_min = port_max = 0;
-
-
-    if(*addr != '\0') {
-      /* attempt to get the address of the given interface name */
-      if(!Curl_if2ip(conn->ip_addr->ai_family, addr,
-                     hbuf, sizeof(hbuf)))
-        /* not an interface, use the given string as host name instead */
-        host = addr;
-      else
-        host = hbuf; /* use the hbuf for host name */
-    }
-    else
-      /* there was only a port(-range) given, default the host */
-      host = NULL;
-  } /* data->set.ftpport */
-
-  if(!host) {
-    /* not an interface and not a host name, get default by extracting
-       the IP from the control connection */
-
-    sslen = sizeof(ss);
-    if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
-      failf(data, "getsockname() failed: %s",
-          Curl_strerror(conn, SOCKERRNO) );
-      Curl_safefree(addr);
-      return CURLE_FTP_PORT_FAILED;
-    }
-    switch(sa->sa_family) {
-#ifdef ENABLE_IPV6
-    case AF_INET6:
-      Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
-      break;
-#endif
-    default:
-      Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf));
-      break;
-    }
-    host = hbuf; /* use this host name */
-    possibly_non_local = FALSE; /* we know it is local now */
-  }
-
-  /* resolv ip/host to ip */
-  rc = Curl_resolv(conn, host, 0, &h);
-  if(rc == CURLRESOLV_PENDING)
-    (void)Curl_resolver_wait_resolv(conn, &h);
-  if(h) {
-    res = h->addr;
-    /* when we return from this function, we can forget about this entry
-       to we can unlock it now already */
-    Curl_resolv_unlock(data, h);
-  } /* (h) */
-  else
-    res = NULL; /* failure! */
-
-  if(res == NULL) {
-    failf(data, "failed to resolve the address provided to PORT: %s", host);
-    Curl_safefree(addr);
-    return CURLE_FTP_PORT_FAILED;
-  }
-
-  Curl_safefree(addr);
-  host = NULL;
-
-  /* step 2, create a socket for the requested address */
-
-  portsock = CURL_SOCKET_BAD;
-  error = 0;
-  for(ai = res; ai; ai = ai->ai_next) {
-    result = Curl_socket(conn, ai, NULL, &portsock);
-    if(result) {
-      error = SOCKERRNO;
-      continue;
-    }
-    break;
-  }
-  if(!ai) {
-    failf(data, "socket failure: %s", Curl_strerror(conn, error));
-    return CURLE_FTP_PORT_FAILED;
-  }
-
-  /* step 3, bind to a suitable local address */
-
-  memcpy(sa, ai->ai_addr, ai->ai_addrlen);
-  sslen = ai->ai_addrlen;
-
-  for(port = port_min; port <= port_max;) {
-    if(sa->sa_family == AF_INET)
-      sa4->sin_port = htons(port);
-#ifdef ENABLE_IPV6
-    else
-      sa6->sin6_port = htons(port);
-#endif
-    /* Try binding the given address. */
-    if(bind(portsock, sa, sslen) ) {
-      /* It failed. */
-      error = SOCKERRNO;
-      if(possibly_non_local && (error == EADDRNOTAVAIL)) {
-        /* The requested bind address is not local.  Use the address used for
-         * the control connection instead and restart the port loop
-         */
-
-        infof(data, "bind(port=%hu) on non-local address failed: %s\n", port,
-              Curl_strerror(conn, error) );
-
-        sslen = sizeof(ss);
-        if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
-          failf(data, "getsockname() failed: %s",
-                Curl_strerror(conn, SOCKERRNO) );
-          Curl_closesocket(conn, portsock);
-          return CURLE_FTP_PORT_FAILED;
-        }
-        port = port_min;
-        possibly_non_local = FALSE; /* don't try this again */
-        continue;
-      }
-      else if(error != EADDRINUSE && error != EACCES) {
-        failf(data, "bind(port=%hu) failed: %s", port,
-              Curl_strerror(conn, error) );
-        Curl_closesocket(conn, portsock);
-        return CURLE_FTP_PORT_FAILED;
-      }
-    }
-    else
-      break;
-
-    port++;
-  }
-
-  /* maybe all ports were in use already*/
-  if(port > port_max) {
-    failf(data, "bind() failed, we ran out of ports!");
-    Curl_closesocket(conn, portsock);
-    return CURLE_FTP_PORT_FAILED;
-  }
-
-  /* get the name again after the bind() so that we can extract the
-     port number it uses now */
-  sslen = sizeof(ss);
-  if(getsockname(portsock, (struct sockaddr *)sa, &sslen)) {
-    failf(data, "getsockname() failed: %s",
-          Curl_strerror(conn, SOCKERRNO) );
-    Curl_closesocket(conn, portsock);
-    return CURLE_FTP_PORT_FAILED;
-  }
-
-  /* step 4, listen on the socket */
-
-  if(listen(portsock, 1)) {
-    failf(data, "socket failure: %s", Curl_strerror(conn, SOCKERRNO));
-    Curl_closesocket(conn, portsock);
-    return CURLE_FTP_PORT_FAILED;
-  }
-
-  /* step 5, send the proper FTP command */
-
-  /* get a plain printable version of the numerical address to work with
-     below */
-  Curl_printable_address(ai, myhost, sizeof(myhost));
-
-#ifdef ENABLE_IPV6
-  if(!conn->bits.ftp_use_eprt && conn->bits.ipv6)
-    /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
-       request and enable EPRT again! */
-    conn->bits.ftp_use_eprt = TRUE;
-#endif
-
-  for(; fcmd != DONE; fcmd++) {
-
-    if(!conn->bits.ftp_use_eprt && (EPRT == fcmd))
-      /* if disabled, goto next */
-      continue;
-
-    if((PORT == fcmd) && sa->sa_family != AF_INET)
-      /* PORT is ipv4 only */
-      continue;
-
-    switch (sa->sa_family) {
-    case AF_INET:
-      port = ntohs(sa4->sin_port);
-      break;
-#ifdef ENABLE_IPV6
-    case AF_INET6:
-      port = ntohs(sa6->sin6_port);
-      break;
-#endif
-    default:
-      continue; /* might as well skip this */
-    }
-
-    if(EPRT == fcmd) {
-      /*
-       * Two fine examples from RFC2428;
-       *
-       * EPRT |1|132.235.1.2|6275|
-       *
-       * EPRT |2|1080::8:800:200C:417A|5282|
-       */
-
-      result = Curl_pp_sendf(&ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd],
-                             sa->sa_family == AF_INET?1:2,
-                             myhost, port);
-      if(result) {
-        failf(data, "Failure sending EPRT command: %s",
-              curl_easy_strerror(result));
-        Curl_closesocket(conn, portsock);
-        /* don't retry using PORT */
-        ftpc->count1 = PORT;
-        /* bail out */
-        state(conn, FTP_STOP);
-        return result;
-      }
-      break;
-    }
-    else if(PORT == fcmd) {
-      char *source = myhost;
-      char *dest = tmp;
-
-      /* translate x.x.x.x to x,x,x,x */
-      while(source && *source) {
-        if(*source == '.')
-          *dest=',';
-        else
-          *dest = *source;
-        dest++;
-        source++;
-      }
-      *dest = 0;
-      snprintf(dest, 20, ",%d,%d", (int)(port>>8), (int)(port&0xff));
-
-      result = Curl_pp_sendf(&ftpc->pp, "%s %s", mode[fcmd], tmp);
-      if(result) {
-        failf(data, "Failure sending PORT command: %s",
-              curl_easy_strerror(result));
-        Curl_closesocket(conn, portsock);
-        /* bail out */
-        state(conn, FTP_STOP);
-        return result;
-      }
-      break;
-    }
-  }
-
-  /* store which command was sent */
-  ftpc->count1 = fcmd;
-
-  /* we set the secondary socket variable to this for now, it is only so that
-     the cleanup function will close it in case we fail before the true
-     secondary stuff is made */
-  if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
-    Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
-  conn->sock[SECONDARYSOCKET] = portsock;
-
-  /* this tcpconnect assignment below is a hackish work-around to make the
-     multi interface with active FTP work - as it will not wait for a
-     (passive) connect in Curl_is_connected().
-
-     The *proper* fix is to make sure that the active connection from the
-     server is done in a non-blocking way. Currently, it is still BLOCKING.
-  */
-  conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE;
-
-  state(conn, FTP_PORT);
-  return result;
-}
-
-static CURLcode ftp_state_use_pasv(struct connectdata *conn)
-{
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-  CURLcode result = CURLE_OK;
-  /*
-    Here's the excecutive summary on what to do:
-
-    PASV is RFC959, expect:
-    227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
-
-    LPSV is RFC1639, expect:
-    228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
-
-    EPSV is RFC2428, expect:
-    229 Entering Extended Passive Mode (|||port|)
-
-  */
-
-  static const char mode[][5] = { "EPSV", "PASV" };
-  int modeoff;
-
-#ifdef PF_INET6
-  if(!conn->bits.ftp_use_epsv && conn->bits.ipv6)
-    /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the
-       request and enable EPSV again! */
-    conn->bits.ftp_use_epsv = TRUE;
-#endif
-
-  modeoff = conn->bits.ftp_use_epsv?0:1;
-
-  PPSENDF(&ftpc->pp, "%s", mode[modeoff]);
-
-  ftpc->count1 = modeoff;
-  state(conn, FTP_PASV);
-  infof(conn->data, "Connect data stream passively\n");
-
-  return result;
-}
-
-/* REST is the last command in the chain of commands when a "head"-like
-   request is made. Thus, if an actual transfer is to be made this is where
-   we take off for real. */
-static CURLcode ftp_state_post_rest(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-  struct FTP *ftp = conn->data->state.proto.ftp;
-  struct SessionHandle *data = conn->data;
-
-  if(ftp->transfer != FTPTRANSFER_BODY) {
-    /* doesn't transfer any data */
-
-    /* still possibly do PRE QUOTE jobs */
-    state(conn, FTP_RETR_PREQUOTE);
-    result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE);
-  }
-  else if(data->set.ftp_use_port) {
-    /* We have chosen to use the PORT (or similar) command */
-    result = ftp_state_use_port(conn, EPRT);
-  }
-  else {
-    /* We have chosen (this is default) to use the PASV (or similar) command */
-    if(data->set.ftp_use_pret) {
-      /* The user has requested that we send a PRET command
-         to prepare the server for the upcoming PASV */
-      if(!conn->proto.ftpc.file) {
-        PPSENDF(&conn->proto.ftpc.pp, "PRET %s",
-                data->set.str[STRING_CUSTOMREQUEST]?
-                data->set.str[STRING_CUSTOMREQUEST]:
-                (data->set.ftp_list_only?"NLST":"LIST"));
-      }
-      else if(data->set.upload) {
-        PPSENDF(&conn->proto.ftpc.pp, "PRET STOR %s", conn->proto.ftpc.file);
-      }
-      else {
-        PPSENDF(&conn->proto.ftpc.pp, "PRET RETR %s", conn->proto.ftpc.file);
-      }
-      state(conn, FTP_PRET);
-    }
-    else {
-      result = ftp_state_use_pasv(conn);
-    }
-  }
-  return result;
-}
-
-static CURLcode ftp_state_post_size(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-  struct FTP *ftp = conn->data->state.proto.ftp;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-
-  if((ftp->transfer != FTPTRANSFER_BODY) && ftpc->file) {
-    /* if a "head"-like request is being made (on a file) */
-
-    /* Determine if server can respond to REST command and therefore
-       whether it supports range */
-    PPSENDF(&conn->proto.ftpc.pp, "REST %d", 0);
-
-    state(conn, FTP_REST);
-  }
-  else
-    result = ftp_state_post_rest(conn);
-
-  return result;
-}
-
-static CURLcode ftp_state_post_type(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-  struct FTP *ftp = conn->data->state.proto.ftp;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-
-  if((ftp->transfer == FTPTRANSFER_INFO) && ftpc->file) {
-    /* if a "head"-like request is being made (on a file) */
-
-    /* we know ftpc->file is a valid pointer to a file name */
-    PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
-
-    state(conn, FTP_SIZE);
-  }
-  else
-    result = ftp_state_post_size(conn);
-
-  return result;
-}
-
-static CURLcode ftp_state_post_listtype(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  /* If this output is to be machine-parsed, the NLST command might be better
-     to use, since the LIST command output is not specified or standard in any
-     way. It has turned out that the NLST list output is not the same on all
-     servers either... */
-
-  /*
-     if FTPFILE_NOCWD was specified, we are currently in
-     the user's home directory, so we should add the path
-     as argument for the LIST / NLST / or custom command.
-     Whether the server will support this, is uncertain.
-
-     The other ftp_filemethods will CWD into dir/dir/ first and
-     then just do LIST (in that case: nothing to do here)
-  */
-  char *cmd,*lstArg,*slashPos;
-
-  lstArg = NULL;
-  if((data->set.ftp_filemethod == FTPFILE_NOCWD) &&
-     data->state.path &&
-     data->state.path[0] &&
-     strchr(data->state.path,'/')) {
-
-    lstArg = strdup(data->state.path);
-    if(!lstArg)
-      return CURLE_OUT_OF_MEMORY;
-
-    /* Check if path does not end with /, as then we cut off the file part */
-    if(lstArg[strlen(lstArg) - 1] != '/')  {
-
-      /* chop off the file part if format is dir/dir/file */
-      slashPos = strrchr(lstArg,'/');
-      if(slashPos)
-        *(slashPos+1) = '\0';
-    }
-  }
-
-  cmd = aprintf( "%s%s%s",
-                 data->set.str[STRING_CUSTOMREQUEST]?
-                 data->set.str[STRING_CUSTOMREQUEST]:
-                 (data->set.ftp_list_only?"NLST":"LIST"),
-                 lstArg? " ": "",
-                 lstArg? lstArg: "" );
-
-  if(!cmd) {
-    if(lstArg)
-      free(lstArg);
-    return CURLE_OUT_OF_MEMORY;
-  }
-
-  result = Curl_pp_sendf(&conn->proto.ftpc.pp, "%s", cmd);
-
-  if(lstArg)
-    free(lstArg);
-
-  free(cmd);
-
-  if(result != CURLE_OK)
-    return result;
-
-  state(conn, FTP_LIST);
-
-  return result;
-}
-
-static CURLcode ftp_state_post_retrtype(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-
-  /* We've sent the TYPE, now we must send the list of prequote strings */
-
-  result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE);
-
-  return result;
-}
-
-static CURLcode ftp_state_post_stortype(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-
-  /* We've sent the TYPE, now we must send the list of prequote strings */
-
-  result = ftp_state_quote(conn, TRUE, FTP_STOR_PREQUOTE);
-
-  return result;
-}
-
-static CURLcode ftp_state_post_mdtm(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-  struct FTP *ftp = conn->data->state.proto.ftp;
-  struct SessionHandle *data = conn->data;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-
-  /* If we have selected NOBODY and HEADER, it means that we only want file
-     information. Which in FTP can't be much more than the file size and
-     date. */
-  if(data->set.opt_no_body && ftpc->file &&
-     ftp_need_type(conn, data->set.prefer_ascii)) {
-    /* The SIZE command is _not_ RFC 959 specified, and therefor many servers
-       may not support it! It is however the only way we have to get a file's
-       size! */
-
-    ftp->transfer = FTPTRANSFER_INFO;
-    /* this means no actual transfer will be made */
-
-    /* Some servers return different sizes for different modes, and thus we
-       must set the proper type before we check the size */
-    result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_TYPE);
-    if(result)
-      return result;
-  }
-  else
-    result = ftp_state_post_type(conn);
-
-  return result;
-}
-
-/* This is called after the CWD commands have been done in the beginning of
-   the DO phase */
-static CURLcode ftp_state_post_cwd(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-
-  /* Requested time of file or time-depended transfer? */
-  if((data->set.get_filetime || data->set.timecondition) && ftpc->file) {
-
-    /* we have requested to get the modified-time of the file, this is a white
-       spot as the MDTM is not mentioned in RFC959 */
-    PPSENDF(&ftpc->pp, "MDTM %s", ftpc->file);
-
-    state(conn, FTP_MDTM);
-  }
-  else
-    result = ftp_state_post_mdtm(conn);
-
-  return result;
-}
-
-
-/* This is called after the TYPE and possible quote commands have been sent */
-static CURLcode ftp_state_ul_setup(struct connectdata *conn,
-                                   bool sizechecked)
-{
-  CURLcode result = CURLE_OK;
-  struct FTP *ftp = conn->data->state.proto.ftp;
-  struct SessionHandle *data = conn->data;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-  int seekerr = CURL_SEEKFUNC_OK;
-
-  if((data->state.resume_from && !sizechecked) ||
-     ((data->state.resume_from > 0) && sizechecked)) {
-    /* we're about to continue the uploading of a file */
-    /* 1. get already existing file's size. We use the SIZE command for this
-       which may not exist in the server!  The SIZE command is not in
-       RFC959. */
-
-    /* 2. This used to set REST. But since we can do append, we
-       don't another ftp command. We just skip the source file
-       offset and then we APPEND the rest on the file instead */
-
-    /* 3. pass file-size number of bytes in the source file */
-    /* 4. lower the infilesize counter */
-    /* => transfer as usual */
-
-    if(data->state.resume_from < 0 ) {
-      /* Got no given size to start from, figure it out */
-      PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
-      state(conn, FTP_STOR_SIZE);
-      return result;
-    }
-
-    /* enable append */
-    data->set.ftp_append = TRUE;
-
-    /* Let's read off the proper amount of bytes from the input. */
-    if(conn->seek_func) {
-      seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
-                                SEEK_SET);
-    }
-
-    if(seekerr != CURL_SEEKFUNC_OK) {
-      if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
-        failf(data, "Could not seek stream");
-        return CURLE_FTP_COULDNT_USE_REST;
-      }
-      /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
-      else {
-        curl_off_t passed=0;
-        do {
-          size_t readthisamountnow =
-            (data->state.resume_from - passed > CURL_OFF_T_C(BUFSIZE)) ?
-            BUFSIZE : curlx_sotouz(data->state.resume_from - passed);
-
-          size_t actuallyread =
-            conn->fread_func(data->state.buffer, 1, readthisamountnow,
-                             conn->fread_in);
-
-          passed += actuallyread;
-          if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
-            /* this checks for greater-than only to make sure that the
-               CURL_READFUNC_ABORT return code still aborts */
-            failf(data, "Failed to read data");
-            return CURLE_FTP_COULDNT_USE_REST;
-          }
-        } while(passed < data->state.resume_from);
-      }
-    }
-    /* now, decrease the size of the read */
-    if(data->set.infilesize>0) {
-      data->set.infilesize -= data->state.resume_from;
-
-      if(data->set.infilesize <= 0) {
-        infof(data, "File already completely uploaded\n");
-
-        /* no data to transfer */
-        Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
-
-        /* Set ->transfer so that we won't get any error in
-         * ftp_done() because we didn't transfer anything! */
-        ftp->transfer = FTPTRANSFER_NONE;
-
-        state(conn, FTP_STOP);
-        return CURLE_OK;
-      }
-    }
-    /* we've passed, proceed as normal */
-  } /* resume_from */
-
-  PPSENDF(&ftpc->pp, data->set.ftp_append?"APPE %s":"STOR %s",
-          ftpc->file);
-
-  state(conn, FTP_STOR);
-
-  return result;
-}
-
-static CURLcode ftp_state_quote(struct connectdata *conn,
-                                bool init,
-                                ftpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  struct FTP *ftp = data->state.proto.ftp;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-  bool quote=FALSE;
-  struct curl_slist *item;
-
-  switch(instate) {
-  case FTP_QUOTE:
-  default:
-    item = data->set.quote;
-    break;
-  case FTP_RETR_PREQUOTE:
-  case FTP_STOR_PREQUOTE:
-    item = data->set.prequote;
-    break;
-  case FTP_POSTQUOTE:
-    item = data->set.postquote;
-    break;
-  }
-
-  /*
-   * This state uses:
-   * 'count1' to iterate over the commands to send
-   * 'count2' to store wether to allow commands to fail
-   */
-
-  if(init)
-    ftpc->count1 = 0;
-  else
-    ftpc->count1++;
-
-  if(item) {
-    int i = 0;
-
-    /* Skip count1 items in the linked list */
-    while((i< ftpc->count1) && item) {
-      item = item->next;
-      i++;
-    }
-    if(item) {
-      char *cmd = item->data;
-      if(cmd[0] == '*') {
-        cmd++;
-        ftpc->count2 = 1; /* the sent command is allowed to fail */
-      }
-      else
-        ftpc->count2 = 0; /* failure means cancel operation */
-
-      PPSENDF(&ftpc->pp, "%s", cmd);
-      state(conn, instate);
-      quote = TRUE;
-    }
-  }
-
-  if(!quote) {
-    /* No more quote to send, continue to ... */
-    switch(instate) {
-    case FTP_QUOTE:
-    default:
-      result = ftp_state_cwd(conn);
-      break;
-    case FTP_RETR_PREQUOTE:
-      if(ftp->transfer != FTPTRANSFER_BODY)
-        state(conn, FTP_STOP);
-      else {
-        if(ftpc->known_filesize != -1) {
-          Curl_pgrsSetDownloadSize(data, ftpc->known_filesize);
-          result = ftp_state_post_retr_size(conn, ftpc->known_filesize);
-        }
-        else {
-          PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
-          state(conn, FTP_RETR_SIZE);
-        }
-      }
-      break;
-    case FTP_STOR_PREQUOTE:
-      result = ftp_state_ul_setup(conn, FALSE);
-      break;
-    case FTP_POSTQUOTE:
-      break;
-    }
-  }
-
-  return result;
-}
-
-/* called from ftp_state_pasv_resp to switch to PASV in case of EPSV
-   problems */
-static CURLcode ftp_epsv_disable(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-  infof(conn->data, "got positive EPSV response, but can't connect. "
-        "Disabling EPSV\n");
-  /* disable it for next transfer */
-  conn->bits.ftp_use_epsv = FALSE;
-  conn->data->state.errorbuf = FALSE; /* allow error message to get
-                                         rewritten */
-  PPSENDF(&conn->proto.ftpc.pp, "PASV", NULL);
-  conn->proto.ftpc.count1++;
-  /* remain in the FTP_PASV state */
-  return result;
-}
-
-static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
-                                    int ftpcode)
-{
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-  CURLcode result;
-  struct SessionHandle *data=conn->data;
-  Curl_addrinfo *conninfo;
-  struct Curl_dns_entry *addr=NULL;
-  int rc;
-  unsigned short connectport; /* the local port connect() should use! */
-  unsigned short newport=0; /* remote port */
-  bool connected;
-
-  /* newhost must be able to hold a full IP-style address in ASCII, which
-     in the IPv6 case means 5*8-1 = 39 letters */
-#define NEWHOST_BUFSIZE 48
-  char newhost[NEWHOST_BUFSIZE];
-  char *str=&data->state.buffer[4];  /* start on the first letter */
-
-  if((ftpc->count1 == 0) &&
-     (ftpcode == 229)) {
-    /* positive EPSV response */
-    char *ptr = strchr(str, '(');
-    if(ptr) {
-      unsigned int num;
-      char separator[4];
-      ptr++;
-      if(5  == sscanf(ptr, "%c%c%c%u%c",
-                      &separator[0],
-                      &separator[1],
-                      &separator[2],
-                      &num,
-                      &separator[3])) {
-        const char sep1 = separator[0];
-        int i;
-
-        /* The four separators should be identical, or else this is an oddly
-           formatted reply and we bail out immediately. */
-        for(i=1; i<4; i++) {
-          if(separator[i] != sep1) {
-            ptr=NULL; /* set to NULL to signal error */
-            break;
-          }
-        }
-        if(ptr) {
-          newport = (unsigned short)(num & 0xffff);
-
-          if(conn->bits.tunnel_proxy ||
-             conn->proxytype == CURLPROXY_SOCKS5 ||
-             conn->proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
-             conn->proxytype == CURLPROXY_SOCKS4 ||
-             conn->proxytype == CURLPROXY_SOCKS4A)
-            /* proxy tunnel -> use other host info because ip_addr_str is the
-               proxy address not the ftp host */
-            snprintf(newhost, sizeof(newhost), "%s", conn->host.name);
-          else
-            /* use the same IP we are already connected to */
-            snprintf(newhost, NEWHOST_BUFSIZE, "%s", conn->ip_addr_str);
-        }
-      }
-      else
-        ptr=NULL;
-    }
-    if(!ptr) {
-      failf(data, "Weirdly formatted EPSV reply");
-      return CURLE_FTP_WEIRD_PASV_REPLY;
-    }
-  }
-  else if((ftpc->count1 == 1) &&
-          (ftpcode == 227)) {
-    /* positive PASV response */
-    int ip[4];
-    int port[2];
-
-    /*
-     * Scan for a sequence of six comma-separated numbers and use them as
-     * IP+port indicators.
-     *
-     * Found reply-strings include:
-     * "227 Entering Passive Mode (127,0,0,1,4,51)"
-     * "227 Data transfer will passively listen to 127,0,0,1,4,51"
-     * "227 Entering passive mode. 127,0,0,1,4,51"
-     */
-    while(*str) {
-      if(6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
-                      &ip[0], &ip[1], &ip[2], &ip[3],
-                      &port[0], &port[1]))
-        break;
-      str++;
-    }
-
-    if(!*str) {
-      failf(data, "Couldn't interpret the 227-response");
-      return CURLE_FTP_WEIRD_227_FORMAT;
-    }
-
-    /* we got OK from server */
-    if(data->set.ftp_skip_ip) {
-      /* told to ignore the remotely given IP but instead use the one we used
-         for the control connection */
-      infof(data, "Skips %d.%d.%d.%d for data connection, uses %s instead\n",
-            ip[0], ip[1], ip[2], ip[3],
-            conn->ip_addr_str);
-      if(conn->bits.tunnel_proxy ||
-         conn->proxytype == CURLPROXY_SOCKS5 ||
-         conn->proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
-         conn->proxytype == CURLPROXY_SOCKS4 ||
-         conn->proxytype == CURLPROXY_SOCKS4A)
-        /* proxy tunnel -> use other host info because ip_addr_str is the
-           proxy address not the ftp host */
-        snprintf(newhost, sizeof(newhost), "%s", conn->host.name);
-      else
-        snprintf(newhost, sizeof(newhost), "%s", conn->ip_addr_str);
-    }
-    else
-      snprintf(newhost, sizeof(newhost),
-               "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
-    newport = (unsigned short)(((port[0]<<8) + port[1]) & 0xffff);
-  }
-  else if(ftpc->count1 == 0) {
-    /* EPSV failed, move on to PASV */
-
-    /* disable it for next transfer */
-    conn->bits.ftp_use_epsv = FALSE;
-    infof(data, "disabling EPSV usage\n");
-
-    PPSENDF(&ftpc->pp, "PASV", NULL);
-    ftpc->count1++;
-    /* remain in the FTP_PASV state */
-    return result;
-  }
-  else {
-    failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
-    return CURLE_FTP_WEIRD_PASV_REPLY;
-  }
-
-  if(data->set.str[STRING_PROXY] && *data->set.str[STRING_PROXY]) {
-    /*
-     * This is a tunnel through a http proxy and we need to connect to the
-     * proxy again here.
-     *
-     * We don't want to rely on a former host lookup that might've expired
-     * now, instead we remake the lookup here and now!
-     */
-    rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &addr);
-    if(rc == CURLRESOLV_PENDING)
-      /* BLOCKING, ignores the return code but 'addr' will be NULL in
-         case of failure */
-      (void)Curl_resolver_wait_resolv(conn, &addr);
-
-    connectport =
-      (unsigned short)conn->port; /* we connect to the proxy's port */
-
-    if(!addr) {
-      failf(data, "Can't resolve proxy host %s:%hu",
-            conn->proxy.name, connectport);
-      return CURLE_FTP_CANT_GET_HOST;
-    }
-  }
-  else {
-    /* normal, direct, ftp connection */
-    rc = Curl_resolv(conn, newhost, newport, &addr);
-    if(rc == CURLRESOLV_PENDING)
-      /* BLOCKING */
-      (void)Curl_resolver_wait_resolv(conn, &addr);
-
-    connectport = newport; /* we connect to the remote port */
-
-    if(!addr) {
-      failf(data, "Can't resolve new host %s:%hu", newhost, connectport);
-      return CURLE_FTP_CANT_GET_HOST;
-    }
-  }
-
-  result = Curl_connecthost(conn,
-                            addr,
-                            &conn->sock[SECONDARYSOCKET],
-                            &conninfo,
-                            &connected);
-
-  Curl_resolv_unlock(data, addr); /* we're done using this address */
-
-  if(result) {
-    if(ftpc->count1 == 0 && ftpcode == 229)
-      return ftp_epsv_disable(conn);
-
-    return result;
-  }
-
-  conn->bits.tcpconnect[SECONDARYSOCKET] = connected;
-
-  /*
-   * When this is used from the multi interface, this might've returned with
-   * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
-   * connect to connect and we should not be "hanging" here waiting.
-   */
-
-  if(data->set.verbose)
-    /* this just dumps information about this second connection */
-    ftp_pasv_verbose(conn, conninfo, newhost, connectport);
-
-  switch(conn->proxytype) {
-    /* FIX: this MUST wait for a proper connect first if 'connected' is
-     * FALSE */
-  case CURLPROXY_SOCKS5:
-  case CURLPROXY_SOCKS5_HOSTNAME:
-    result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, newhost, newport,
-                         SECONDARYSOCKET, conn);
-    break;
-  case CURLPROXY_SOCKS4:
-    result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
-                         SECONDARYSOCKET, conn, FALSE);
-    break;
-  case CURLPROXY_SOCKS4A:
-    result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
-                         SECONDARYSOCKET, conn, TRUE);
-    break;
-  case CURLPROXY_HTTP:
-  case CURLPROXY_HTTP_1_0:
-    /* do nothing here. handled later. */
-    break;
-  default:
-    failf(data, "unknown proxytype option given");
-    result = CURLE_COULDNT_CONNECT;
-    break;
-  }
-
-  if(result) {
-    if(ftpc->count1 == 0 && ftpcode == 229)
-      return ftp_epsv_disable(conn);
-    return result;
-  }
-
-  if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
-    /* FIX: this MUST wait for a proper connect first if 'connected' is
-     * FALSE */
-
-    /* BLOCKING */
-    /* We want "seamless" FTP operations through HTTP proxy tunnel */
-
-    /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
-     * conn->proto.http; we want FTP through HTTP and we have to change the
-     * member temporarily for connecting to the HTTP proxy. After
-     * Curl_proxyCONNECT we have to set back the member to the original struct
-     * FTP pointer
-     */
-    struct HTTP http_proxy;
-    struct FTP *ftp_save = data->state.proto.ftp;
-    memset(&http_proxy, 0, sizeof(http_proxy));
-    data->state.proto.http = &http_proxy;
-
-    result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport);
-
-    data->state.proto.ftp = ftp_save;
-
-    if(result)
-      return result;
-  }
-
-  conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE;
-
-  state(conn, FTP_STOP); /* this phase is completed */
-
-  return result;
-}
-
-static CURLcode ftp_state_port_resp(struct connectdata *conn,
-                                    int ftpcode)
-{
-  struct SessionHandle *data = conn->data;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-  ftpport fcmd = (ftpport)ftpc->count1;
-  CURLcode result = CURLE_OK;
-
-  if(ftpcode != 200) {
-    /* the command failed */
-
-    if(EPRT == fcmd) {
-      infof(data, "disabling EPRT usage\n");
-      conn->bits.ftp_use_eprt = FALSE;
-    }
-    fcmd++;
-
-    if(fcmd == DONE) {
-      failf(data, "Failed to do PORT");
-      result = CURLE_FTP_PORT_FAILED;
-    }
-    else
-      /* try next */
-      result = ftp_state_use_port(conn, fcmd);
-  }
-  else {
-    infof(data, "Connect data stream actively\n");
-    state(conn, FTP_STOP); /* end of DO phase */
-  }
-
-  return result;
-}
-
-static CURLcode ftp_state_mdtm_resp(struct connectdata *conn,
-                                    int ftpcode)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data=conn->data;
-  struct FTP *ftp = data->state.proto.ftp;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-
-  switch(ftpcode) {
-  case 213:
-    {
-      /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
-         last .sss part is optional and means fractions of a second */
-      int year, month, day, hour, minute, second;
-      char *buf = data->state.buffer;
-      if(6 == sscanf(buf+4, "%04d%02d%02d%02d%02d%02d",
-                     &year, &month, &day, &hour, &minute, &second)) {
-        /* we have a time, reformat it */
-        time_t secs=time(NULL);
-        /* using the good old yacc/bison yuck */
-        snprintf(buf, sizeof(conn->data->state.buffer),
-                 "%04d%02d%02d %02d:%02d:%02d GMT",
-                 year, month, day, hour, minute, second);
-        /* now, convert this into a time() value: */
-        data->info.filetime = (long)curl_getdate(buf, &secs);
-      }
-
-#ifdef CURL_FTP_HTTPSTYLE_HEAD
-      /* If we asked for a time of the file and we actually got one as well,
-         we "emulate" a HTTP-style header in our output. */
-
-      if(data->set.opt_no_body &&
-         ftpc->file &&
-         data->set.get_filetime &&
-         (data->info.filetime>=0) ) {
-        time_t filetime = (time_t)data->info.filetime;
-        struct tm buffer;
-        const struct tm *tm = &buffer;
-
-        result = Curl_gmtime(filetime, &buffer);
-        if(result)
-          return result;
-
-        /* format: "Tue, 15 Nov 1994 12:45:26" */
-        snprintf(buf, BUFSIZE-1,
-                 "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
-                 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
-                 tm->tm_mday,
-                 Curl_month[tm->tm_mon],
-                 tm->tm_year + 1900,
-                 tm->tm_hour,
-                 tm->tm_min,
-                 tm->tm_sec);
-        result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
-        if(result)
-          return result;
-      } /* end of a ridiculous amount of conditionals */
-#endif
-    }
-    break;
-  default:
-    infof(data, "unsupported MDTM reply format\n");
-    break;
-  case 550: /* "No such file or directory" */
-    failf(data, "Given file does not exist");
-    result = CURLE_FTP_COULDNT_RETR_FILE;
-    break;
-  }
-
-  if(data->set.timecondition) {
-    if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
-      switch(data->set.timecondition) {
-      case CURL_TIMECOND_IFMODSINCE:
-      default:
-        if(data->info.filetime <= data->set.timevalue) {
-          infof(data, "The requested document is not new enough\n");
-          ftp->transfer = FTPTRANSFER_NONE; /* mark to not transfer data */
-          data->info.timecond = TRUE;
-          state(conn, FTP_STOP);
-          return CURLE_OK;
-        }
-        break;
-      case CURL_TIMECOND_IFUNMODSINCE:
-        if(data->info.filetime > data->set.timevalue) {
-          infof(data, "The requested document is not old enough\n");
-          ftp->transfer = FTPTRANSFER_NONE; /* mark to not transfer data */
-          data->info.timecond = TRUE;
-          state(conn, FTP_STOP);
-          return CURLE_OK;
-        }
-        break;
-      } /* switch */
-    }
-    else {
-      infof(data, "Skipping time comparison\n");
-    }
-  }
-
-  if(!result)
-    result = ftp_state_post_mdtm(conn);
-
-  return result;
-}
-
-static CURLcode ftp_state_type_resp(struct connectdata *conn,
-                                    int ftpcode,
-                                    ftpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data=conn->data;
-
-  if(ftpcode/100 != 2) {
-    /* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a
-       successful 'TYPE I'. While that is not as RFC959 says, it is still a
-       positive response code and we allow that. */
-    failf(data, "Couldn't set desired mode");
-    return CURLE_FTP_COULDNT_SET_TYPE;
-  }
-  if(ftpcode != 200)
-    infof(data, "Got a %03d response code instead of the assumed 200\n",
-          ftpcode);
-
-  if(instate == FTP_TYPE)
-    result = ftp_state_post_type(conn);
-  else if(instate == FTP_LIST_TYPE)
-    result = ftp_state_post_listtype(conn);
-  else if(instate == FTP_RETR_TYPE)
-    result = ftp_state_post_retrtype(conn);
-  else if(instate == FTP_STOR_TYPE)
-    result = ftp_state_post_stortype(conn);
-
-  return result;
-}
-
-static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
-                                         curl_off_t filesize)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data=conn->data;
-  struct FTP *ftp = data->state.proto.ftp;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-
-  if(data->set.max_filesize && (filesize > data->set.max_filesize)) {
-    failf(data, "Maximum file size exceeded");
-    return CURLE_FILESIZE_EXCEEDED;
-  }
-  ftp->downloadsize = filesize;
-
-  if(data->state.resume_from) {
-    /* We always (attempt to) get the size of downloads, so it is done before
-       this even when not doing resumes. */
-    if(filesize == -1) {
-      infof(data, "ftp server doesn't support SIZE\n");
-      /* We couldn't get the size and therefore we can't know if there really
-         is a part of the file left to get, although the server will just
-         close the connection when we start the connection so it won't cause
-         us any harm, just not make us exit as nicely. */
-    }
-    else {
-      /* We got a file size report, so we check that there actually is a
-         part of the file left to get, or else we go home.  */
-      if(data->state.resume_from< 0) {
-        /* We're supposed to download the last abs(from) bytes */
-        if(filesize < -data->state.resume_from) {
-          failf(data, "Offset (%" FORMAT_OFF_T
-                ") was beyond file size (%" FORMAT_OFF_T ")",
-                data->state.resume_from, filesize);
-          return CURLE_BAD_DOWNLOAD_RESUME;
-        }
-        /* convert to size to download */
-        ftp->downloadsize = -data->state.resume_from;
-        /* download from where? */
-        data->state.resume_from = filesize - ftp->downloadsize;
-      }
-      else {
-        if(filesize < data->state.resume_from) {
-          failf(data, "Offset (%" FORMAT_OFF_T
-                ") was beyond file size (%" FORMAT_OFF_T ")",
-                data->state.resume_from, filesize);
-          return CURLE_BAD_DOWNLOAD_RESUME;
-        }
-        /* Now store the number of bytes we are expected to download */
-        ftp->downloadsize = filesize-data->state.resume_from;
-      }
-    }
-
-    if(ftp->downloadsize == 0) {
-      /* no data to transfer */
-      Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
-      infof(data, "File already completely downloaded\n");
-
-      /* Set ->transfer so that we won't get any error in ftp_done()
-       * because we didn't transfer the any file */
-      ftp->transfer = FTPTRANSFER_NONE;
-      state(conn, FTP_STOP);
-      return CURLE_OK;
-    }
-
-    /* Set resume file transfer offset */
-    infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T
-          "\n", data->state.resume_from);
-
-    PPSENDF(&ftpc->pp, "REST %" FORMAT_OFF_T, data->state.resume_from);
-
-    state(conn, FTP_RETR_REST);
-
-  }
-  else {
-    /* no resume */
-    PPSENDF(&ftpc->pp, "RETR %s", ftpc->file);
-    state(conn, FTP_RETR);
-  }
-
-  return result;
-}
-
-static CURLcode ftp_state_size_resp(struct connectdata *conn,
-                                    int ftpcode,
-                                    ftpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data=conn->data;
-  curl_off_t filesize;
-  char *buf = data->state.buffer;
-
-  /* get the size from the ascii string: */
-  filesize = (ftpcode == 213)?curlx_strtoofft(buf+4, NULL, 0):-1;
-
-  if(instate == FTP_SIZE) {
-#ifdef CURL_FTP_HTTPSTYLE_HEAD
-    if(-1 != filesize) {
-      snprintf(buf, sizeof(data->state.buffer),
-               "Content-Length: %" FORMAT_OFF_T "\r\n", filesize);
-      result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
-      if(result)
-        return result;
-    }
-#endif
-    Curl_pgrsSetDownloadSize(data, filesize);
-    result = ftp_state_post_size(conn);
-  }
-  else if(instate == FTP_RETR_SIZE) {
-    Curl_pgrsSetDownloadSize(data, filesize);
-    result = ftp_state_post_retr_size(conn, filesize);
-  }
-  else if(instate == FTP_STOR_SIZE) {
-    data->state.resume_from = filesize;
-    result = ftp_state_ul_setup(conn, TRUE);
-  }
-
-  return result;
-}
-
-static CURLcode ftp_state_rest_resp(struct connectdata *conn,
-                                    int ftpcode,
-                                    ftpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-
-  switch(instate) {
-  case FTP_REST:
-  default:
-#ifdef CURL_FTP_HTTPSTYLE_HEAD
-    if(ftpcode == 350) {
-      char buffer[24]= { "Accept-ranges: bytes\r\n" };
-      result = Curl_client_write(conn, CLIENTWRITE_BOTH, buffer, 0);
-      if(result)
-        return result;
-    }
-#endif
-    result = ftp_state_post_rest(conn);
-    break;
-
-  case FTP_RETR_REST:
-    if(ftpcode != 350) {
-      failf(conn->data, "Couldn't use REST");
-      result = CURLE_FTP_COULDNT_USE_REST;
-    }
-    else {
-      PPSENDF(&ftpc->pp, "RETR %s", ftpc->file);
-      state(conn, FTP_RETR);
-    }
-    break;
-  }
-
-  return result;
-}
-
-static CURLcode ftp_state_stor_resp(struct connectdata *conn,
-                                    int ftpcode, ftpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  if(ftpcode>=400) {
-    failf(data, "Failed FTP upload: %0d", ftpcode);
-    state(conn, FTP_STOP);
-    /* oops, we never close the sockets! */
-    return CURLE_UPLOAD_FAILED;
-  }
-
-  conn->proto.ftpc.state_saved = instate;
-
-  /* PORT means we are now awaiting the server to connect to us. */
-  if(data->set.ftp_use_port) {
-    bool connected;
-
-    result = AllowServerConnect(conn, &connected);
-    if(result)
-      return result;
-
-    if(!connected) {
-      struct ftp_conn *ftpc = &conn->proto.ftpc;
-      infof(data, "Data conn was not available immediately\n");
-      ftpc->wait_data_conn = TRUE;
-    }
-
-    return CURLE_OK;
-  }
-  else
-    return InitiateTransfer(conn);
-}
-
-/* for LIST and RETR responses */
-static CURLcode ftp_state_get_resp(struct connectdata *conn,
-                                    int ftpcode,
-                                    ftpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  struct FTP *ftp = data->state.proto.ftp;
-  char *buf = data->state.buffer;
-
-  if((ftpcode == 150) || (ftpcode == 125)) {
-
-    /*
-      A;
-      150 Opening BINARY mode data connection for /etc/passwd (2241
-      bytes).  (ok, the file is being transferred)
-
-      B:
-      150 Opening ASCII mode data connection for /bin/ls
-
-      C:
-      150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
-
-      D:
-      150 Opening ASCII mode data connection for [file] (0.0.0.0,0) (545 bytes)
-
-      E:
-      125 Data connection already open; Transfer starting. */
-
-    curl_off_t size=-1; /* default unknown size */
-
-
-    /*
-     * It appears that there are FTP-servers that return size 0 for files when
-     * SIZE is used on the file while being in BINARY mode. To work around
-     * that (stupid) behavior, we attempt to parse the RETR response even if
-     * the SIZE returned size zero.
-     *
-     * Debugging help from Salvatore Sorrentino on February 26, 2003.
-     */
-
-    if((instate != FTP_LIST) &&
-       !data->set.prefer_ascii &&
-       (ftp->downloadsize < 1)) {
-      /*
-       * It seems directory listings either don't show the size or very
-       * often uses size 0 anyway. ASCII transfers may very well turn out
-       * that the transferred amount of data is not the same as this line
-       * tells, why using this number in those cases only confuses us.
-       *
-       * Example D above makes this parsing a little tricky */
-      char *bytes;
-      bytes=strstr(buf, " bytes");
-      if(bytes--) {
-        long in=(long)(bytes-buf);
-        /* this is a hint there is size information in there! ;-) */
-        while(--in) {
-          /* scan for the left parenthesis and break there */
-          if('(' == *bytes)
-            break;
-          /* skip only digits */
-          if(!ISDIGIT(*bytes)) {
-            bytes=NULL;
-            break;
-          }
-          /* one more estep backwards */
-          bytes--;
-        }
-        /* if we have nothing but digits: */
-        if(bytes++) {
-          /* get the number! */
-          size = curlx_strtoofft(bytes, NULL, 0);
-        }
-      }
-    }
-    else if(ftp->downloadsize > -1)
-      size = ftp->downloadsize;
-
-    if(size > data->req.maxdownload && data->req.maxdownload > 0)
-      size = data->req.size = data->req.maxdownload;
-    else if((instate != FTP_LIST) && (data->set.prefer_ascii))
-      size = -1; /* kludge for servers that understate ASCII mode file size */
-
-    infof(data, "Maxdownload = %" FORMAT_OFF_T "\n", data->req.maxdownload);
-
-    if(instate != FTP_LIST)
-      infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size);
-
-    /* FTP download: */
-    conn->proto.ftpc.state_saved = instate;
-    conn->proto.ftpc.retr_size_saved = size;
-
-    if(data->set.ftp_use_port) {
-      bool connected;
-
-      result = AllowServerConnect(conn, &connected);
-      if(result)
-        return result;
-
-      if(!connected) {
-        struct ftp_conn *ftpc = &conn->proto.ftpc;
-        infof(data, "Data conn was not available immediately\n");
-        state(conn, FTP_STOP);
-        ftpc->wait_data_conn = TRUE;
-      }
-    }
-    else
-      return InitiateTransfer(conn);
-  }
-  else {
-    if((instate == FTP_LIST) && (ftpcode == 450)) {
-      /* simply no matching files in the dir listing */
-      ftp->transfer = FTPTRANSFER_NONE; /* don't download anything */
-      state(conn, FTP_STOP); /* this phase is over */
-    }
-    else {
-      failf(data, "RETR response: %03d", ftpcode);
-      return instate == FTP_RETR && ftpcode == 550?
-        CURLE_REMOTE_FILE_NOT_FOUND:
-        CURLE_FTP_COULDNT_RETR_FILE;
-    }
-  }
-
-  return result;
-}
-
-/* after USER, PASS and ACCT */
-static CURLcode ftp_state_loggedin(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-
-#ifdef HAVE_KRB4
-  if(conn->data->set.krb) {
-    /* We may need to issue a KAUTH here to have access to the files
-     * do it if user supplied a password
-     */
-    if(conn->passwd && *conn->passwd) {
-      /* BLOCKING */
-      result = Curl_krb_kauth(conn);
-      if(result)
-        return result;
-    }
-  }
-#endif
-  if(conn->ssl[FIRSTSOCKET].use) {
-    /* PBSZ = PROTECTION BUFFER SIZE.
-
-    The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
-
-    Specifically, the PROT command MUST be preceded by a PBSZ
-    command and a PBSZ command MUST be preceded by a successful
-    security data exchange (the TLS negotiation in this case)
-
-    ... (and on page 8):
-
-    Thus the PBSZ command must still be issued, but must have a
-    parameter of '0' to indicate that no buffering is taking place
-    and the data connection should not be encapsulated.
-    */
-    PPSENDF(&conn->proto.ftpc.pp, "PBSZ %d", 0);
-    state(conn, FTP_PBSZ);
-  }
-  else {
-    result = ftp_state_pwd(conn);
-  }
-  return result;
-}
-
-/* for USER and PASS responses */
-static CURLcode ftp_state_user_resp(struct connectdata *conn,
-                                    int ftpcode,
-                                    ftpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  struct FTP *ftp = data->state.proto.ftp;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-  (void)instate; /* no use for this yet */
-
-  /* some need password anyway, and others just return 2xx ignored */
-  if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
-    /* 331 Password required for ...
-       (the server requires to send the user's password too) */
-    PPSENDF(&ftpc->pp, "PASS %s", ftp->passwd?ftp->passwd:"");
-    state(conn, FTP_PASS);
-  }
-  else if(ftpcode/100 == 2) {
-    /* 230 User ... logged in.
-       (the user logged in with or without password) */
-    result = ftp_state_loggedin(conn);
-  }
-  else if(ftpcode == 332) {
-    if(data->set.str[STRING_FTP_ACCOUNT]) {
-      PPSENDF(&ftpc->pp, "ACCT %s", data->set.str[STRING_FTP_ACCOUNT]);
-      state(conn, FTP_ACCT);
-    }
-    else {
-      failf(data, "ACCT requested but none available");
-      result = CURLE_LOGIN_DENIED;
-    }
-  }
-  else {
-    /* All other response codes, like:
-
-    530 User ... access denied
-    (the server denies to log the specified user) */
-
-    if(conn->data->set.str[STRING_FTP_ALTERNATIVE_TO_USER] &&
-        !conn->data->state.ftp_trying_alternative) {
-      /* Ok, USER failed.  Let's try the supplied command. */
-      PPSENDF(&conn->proto.ftpc.pp, "%s",
-              conn->data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
-      conn->data->state.ftp_trying_alternative = TRUE;
-      state(conn, FTP_USER);
-      result = CURLE_OK;
-    }
-    else {
-      failf(data, "Access denied: %03d", ftpcode);
-      result = CURLE_LOGIN_DENIED;
-    }
-  }
-  return result;
-}
-
-/* for ACCT response */
-static CURLcode ftp_state_acct_resp(struct connectdata *conn,
-                                    int ftpcode)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  if(ftpcode != 230) {
-    failf(data, "ACCT rejected by server: %03d", ftpcode);
-    result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */
-  }
-  else
-    result = ftp_state_loggedin(conn);
-
-  return result;
-}
-
-
-static CURLcode ftp_statemach_act(struct connectdata *conn)
-{
-  CURLcode result;
-  curl_socket_t sock = conn->sock[FIRSTSOCKET];
-  struct SessionHandle *data=conn->data;
-  int ftpcode;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-  struct pingpong *pp = &ftpc->pp;
-  static const char ftpauth[][4]  = { "SSL", "TLS" };
-  size_t nread = 0;
-
-  if(pp->sendleft)
-    return Curl_pp_flushsend(pp);
-
-  result = ftp_readresp(sock, pp, &ftpcode, &nread);
-  if(result)
-    return result;
-
-  if(ftpcode) {
-    /* we have now received a full FTP server response */
-    switch(ftpc->state) {
-    case FTP_WAIT220:
-      if(ftpcode != 220) {
-        failf(data, "Got a %03d ftp-server response when 220 was expected",
-              ftpcode);
-        return CURLE_FTP_WEIRD_SERVER_REPLY;
-      }
-
-      /* We have received a 220 response fine, now we proceed. */
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
-      if(data->set.krb) {
-        /* If not anonymous login, try a secure login. Note that this
-           procedure is still BLOCKING. */
-
-        Curl_sec_request_prot(conn, "private");
-        /* We set private first as default, in case the line below fails to
-           set a valid level */
-        Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]);
-
-        if(Curl_sec_login(conn) != CURLE_OK)
-          infof(data, "Logging in with password in cleartext!\n");
-        else
-          infof(data, "Authentication successful\n");
-      }
-#endif
-
-      if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
-        /* We don't have a SSL/TLS connection yet, but FTPS is
-           requested. Try a FTPS connection now */
-
-        ftpc->count3=0;
-        switch(data->set.ftpsslauth) {
-        case CURLFTPAUTH_DEFAULT:
-        case CURLFTPAUTH_SSL:
-          ftpc->count2 = 1; /* add one to get next */
-          ftpc->count1 = 0;
-          break;
-        case CURLFTPAUTH_TLS:
-          ftpc->count2 = -1; /* subtract one to get next */
-          ftpc->count1 = 1;
-          break;
-        default:
-          failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d",
-                (int)data->set.ftpsslauth);
-          return CURLE_UNKNOWN_OPTION; /* we don't know what to do */
-        }
-        PPSENDF(&ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
-        state(conn, FTP_AUTH);
-      }
-      else {
-        result = ftp_state_user(conn);
-        if(result)
-          return result;
-      }
-
-      break;
-
-    case FTP_AUTH:
-      /* we have gotten the response to a previous AUTH command */
-
-      /* RFC2228 (page 5) says:
-       *
-       * If the server is willing to accept the named security mechanism,
-       * and does not require any security data, it must respond with
-       * reply code 234/334.
-       */
-
-      if((ftpcode == 234) || (ftpcode == 334)) {
-        /* Curl_ssl_connect is BLOCKING */
-        result = Curl_ssl_connect(conn, FIRSTSOCKET);
-        if(CURLE_OK == result) {
-          conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */
-          result = ftp_state_user(conn);
-        }
-      }
-      else if(ftpc->count3 < 1) {
-        ftpc->count3++;
-        ftpc->count1 += ftpc->count2; /* get next attempt */
-        result = Curl_pp_sendf(&ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
-        /* remain in this same state */
-      }
-      else {
-        if(data->set.use_ssl > CURLUSESSL_TRY)
-          /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */
-          result = CURLE_USE_SSL_FAILED;
-        else
-          /* ignore the failure and continue */
-          result = ftp_state_user(conn);
-      }
-
-      if(result)
-        return result;
-      break;
-
-    case FTP_USER:
-    case FTP_PASS:
-      result = ftp_state_user_resp(conn, ftpcode, ftpc->state);
-      break;
-
-    case FTP_ACCT:
-      result = ftp_state_acct_resp(conn, ftpcode);
-      break;
-
-    case FTP_PBSZ:
-      PPSENDF(&ftpc->pp, "PROT %c",
-              data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
-      state(conn, FTP_PROT);
-
-      break;
-
-    case FTP_PROT:
-      if(ftpcode/100 == 2)
-        /* We have enabled SSL for the data connection! */
-        conn->ssl[SECONDARYSOCKET].use =
-          (data->set.use_ssl != CURLUSESSL_CONTROL) ? TRUE : FALSE;
-      /* FTP servers typically responds with 500 if they decide to reject
-         our 'P' request */
-      else if(data->set.use_ssl > CURLUSESSL_CONTROL)
-        /* we failed and bails out */
-        return CURLE_USE_SSL_FAILED;
-
-      if(data->set.ftp_ccc) {
-        /* CCC - Clear Command Channel
-         */
-        PPSENDF(&ftpc->pp, "CCC", NULL);
-        state(conn, FTP_CCC);
-      }
-      else {
-        result = ftp_state_pwd(conn);
-        if(result)
-          return result;
-      }
-      break;
-
-    case FTP_CCC:
-      if(ftpcode < 500) {
-        /* First shut down the SSL layer (note: this call will block) */
-        result = Curl_ssl_shutdown(conn, FIRSTSOCKET);
-
-        if(result) {
-          failf(conn->data, "Failed to clear the command channel (CCC)");
-          return result;
-        }
-      }
-
-      /* Then continue as normal */
-      result = ftp_state_pwd(conn);
-      if(result)
-        return result;
-      break;
-
-    case FTP_PWD:
-      if(ftpcode == 257) {
-        char *ptr=&data->state.buffer[4];  /* start on the first letter */
-        char *dir;
-        char *store;
-
-        dir = malloc(nread + 1);
-        if(!dir)
-          return CURLE_OUT_OF_MEMORY;
-
-        /* Reply format is like
-           257<space>"<directory-name>"<space><commentary> and the RFC959
-           says
-
-           The directory name can contain any character; embedded
-           double-quotes should be escaped by double-quotes (the
-           "quote-doubling" convention).
-        */
-        if('\"' == *ptr) {
-          /* it started good */
-          ptr++;
-          for(store = dir; *ptr;) {
-            if('\"' == *ptr) {
-              if('\"' == ptr[1]) {
-                /* "quote-doubling" */
-                *store = ptr[1];
-                ptr++;
-              }
-              else {
-                /* end of path */
-                *store = '\0'; /* zero terminate */
-                break; /* get out of this loop */
-              }
-            }
-            else
-              *store = *ptr;
-            store++;
-            ptr++;
-          }
-
-          /* If the path name does not look like an absolute path (i.e.: it
-             does not start with a '/'), we probably need some server-dependent
-             adjustments. For example, this is the case when connecting to
-             an OS400 FTP server: this server supports two name syntaxes,
-             the default one being incompatible with standard pathes. In
-             addition, this server switches automatically to the regular path
-             syntax when one is encountered in a command: this results in
-             having an entrypath in the wrong syntax when later used in CWD.
-               The method used here is to check the server OS: we do it only
-             if the path name looks strange to minimize overhead on other
-             systems. */
-
-          if(!ftpc->server_os && dir[0] != '/') {
-
-            result = Curl_pp_sendf(&ftpc->pp, "SYST", NULL);
-            if(result != CURLE_OK) {
-              free(dir);
-              return result;
-            }
-            Curl_safefree(ftpc->entrypath);
-            ftpc->entrypath = dir; /* remember this */
-            infof(data, "Entry path is '%s'\n", ftpc->entrypath);
-            /* also save it where getinfo can access it: */
-            data->state.most_recent_ftp_entrypath = ftpc->entrypath;
-            state(conn, FTP_SYST);
-            break;
-          }
-
-          Curl_safefree(ftpc->entrypath);
-          ftpc->entrypath = dir; /* remember this */
-          infof(data, "Entry path is '%s'\n", ftpc->entrypath);
-          /* also save it where getinfo can access it: */
-          data->state.most_recent_ftp_entrypath = ftpc->entrypath;
-        }
-        else {
-          /* couldn't get the path */
-          free(dir);
-          infof(data, "Failed to figure out path\n");
-        }
-      }
-      state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
-      DEBUGF(infof(data, "protocol connect phase DONE\n"));
-      break;
-
-    case FTP_SYST:
-      if(ftpcode == 215) {
-        char *ptr=&data->state.buffer[4];  /* start on the first letter */
-        char *os;
-        char *store;
-
-        os = malloc(nread + 1);
-        if(!os)
-          return CURLE_OUT_OF_MEMORY;
-
-        /* Reply format is like
-           215<space><OS-name><space><commentary>
-        */
-        while(*ptr == ' ')
-          ptr++;
-        for(store = os; *ptr && *ptr != ' ';)
-          *store++ = *ptr++;
-        *store = '\0'; /* zero terminate */
-
-        /* Check for special servers here. */
-
-        if(strequal(os, "OS/400")) {
-          /* Force OS400 name format 1. */
-          result = Curl_pp_sendf(&ftpc->pp, "SITE NAMEFMT 1", NULL);
-          if(result != CURLE_OK) {
-            free(os);
-            return result;
-          }
-          /* remember target server OS */
-          Curl_safefree(ftpc->server_os);
-          ftpc->server_os = os;
-          state(conn, FTP_NAMEFMT);
-          break;
-        }
-        else {
-          /* Nothing special for the target server. */
-          /* remember target server OS */
-          Curl_safefree(ftpc->server_os);
-          ftpc->server_os = os;
-        }
-      }
-      else {
-        /* Cannot identify server OS. Continue anyway and cross fingers. */
-      }
-
-      state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
-      DEBUGF(infof(data, "protocol connect phase DONE\n"));
-      break;
-
-    case FTP_NAMEFMT:
-      if(ftpcode == 250) {
-        /* Name format change successful: reload initial path. */
-        ftp_state_pwd(conn);
-        break;
-      }
-
-      state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
-      DEBUGF(infof(data, "protocol connect phase DONE\n"));
-      break;
-
-    case FTP_QUOTE:
-    case FTP_POSTQUOTE:
-    case FTP_RETR_PREQUOTE:
-    case FTP_STOR_PREQUOTE:
-      if((ftpcode >= 400) && !ftpc->count2) {
-        /* failure response code, and not allowed to fail */
-        failf(conn->data, "QUOT command failed with %03d", ftpcode);
-        return CURLE_QUOTE_ERROR;
-      }
-      result = ftp_state_quote(conn, FALSE, ftpc->state);
-      if(result)
-        return result;
-
-      break;
-
-    case FTP_CWD:
-      if(ftpcode/100 != 2) {
-        /* failure to CWD there */
-        if(conn->data->set.ftp_create_missing_dirs &&
-           ftpc->count1 && !ftpc->count2) {
-          /* try making it */
-          ftpc->count2++; /* counter to prevent CWD-MKD loops */
-          PPSENDF(&ftpc->pp, "MKD %s", ftpc->dirs[ftpc->count1 - 1]);
-          state(conn, FTP_MKD);
-        }
-        else {
-          /* return failure */
-          failf(data, "Server denied you to change to the given directory");
-          ftpc->cwdfail = TRUE; /* don't remember this path as we failed
-                                   to enter it */
-          return CURLE_REMOTE_ACCESS_DENIED;
-        }
-      }
-      else {
-        /* success */
-        ftpc->count2=0;
-        if(++ftpc->count1 <= ftpc->dirdepth) {
-          /* send next CWD */
-          PPSENDF(&ftpc->pp, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
-        }
-        else {
-          result = ftp_state_post_cwd(conn);
-          if(result)
-            return result;
-        }
-      }
-      break;
-
-    case FTP_MKD:
-      if((ftpcode/100 != 2) && !ftpc->count3--) {
-        /* failure to MKD the dir */
-        failf(data, "Failed to MKD dir: %03d", ftpcode);
-        return CURLE_REMOTE_ACCESS_DENIED;
-      }
-      state(conn, FTP_CWD);
-      /* send CWD */
-      PPSENDF(&ftpc->pp, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
-      break;
-
-    case FTP_MDTM:
-      result = ftp_state_mdtm_resp(conn, ftpcode);
-      break;
-
-    case FTP_TYPE:
-    case FTP_LIST_TYPE:
-    case FTP_RETR_TYPE:
-    case FTP_STOR_TYPE:
-      result = ftp_state_type_resp(conn, ftpcode, ftpc->state);
-      break;
-
-    case FTP_SIZE:
-    case FTP_RETR_SIZE:
-    case FTP_STOR_SIZE:
-      result = ftp_state_size_resp(conn, ftpcode, ftpc->state);
-      break;
-
-    case FTP_REST:
-    case FTP_RETR_REST:
-      result = ftp_state_rest_resp(conn, ftpcode, ftpc->state);
-      break;
-
-    case FTP_PRET:
-      if(ftpcode != 200) {
-        /* there only is this one standard OK return code. */
-        failf(data, "PRET command not accepted: %03d", ftpcode);
-        return CURLE_FTP_PRET_FAILED;
-      }
-      result = ftp_state_use_pasv(conn);
-      break;
-
-    case FTP_PASV:
-      result = ftp_state_pasv_resp(conn, ftpcode);
-      break;
-
-    case FTP_PORT:
-      result = ftp_state_port_resp(conn, ftpcode);
-      break;
-
-    case FTP_LIST:
-    case FTP_RETR:
-      result = ftp_state_get_resp(conn, ftpcode, ftpc->state);
-      break;
-
-    case FTP_STOR:
-      result = ftp_state_stor_resp(conn, ftpcode, ftpc->state);
-      break;
-
-    case FTP_QUIT:
-      /* fallthrough, just stop! */
-    default:
-      /* internal error */
-      state(conn, FTP_STOP);
-      break;
-    }
-  } /* if(ftpcode) */
-
-  return result;
-}
-
-
-/* called repeatedly until done from curl_multi.c */
-static CURLcode ftp_multi_statemach(struct connectdata *conn,
-                                    bool *done)
-{
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-  CURLcode result = Curl_pp_multi_statemach(&ftpc->pp);
-
-  /* Check for the state outside of the Curl_socket_ready() return code checks
-     since at times we are in fact already in this state when this function
-     gets called. */
-  *done = (ftpc->state == FTP_STOP) ? TRUE : FALSE;
-
-  return result;
-}
-
-static CURLcode ftp_easy_statemach(struct connectdata *conn)
-{
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-  struct pingpong *pp = &ftpc->pp;
-  CURLcode result = CURLE_OK;
-
-  while(ftpc->state != FTP_STOP) {
-    result = Curl_pp_easy_statemach(pp);
-    if(result)
-      break;
-  }
-
-  return result;
-}
-
-/*
- * Allocate and initialize the struct FTP for the current SessionHandle.  If
- * need be.
- */
-
-#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \
-    defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__)
-  /* workaround icc 9.1 optimizer issue */
-#pragma optimize("", off)
-#endif
-
-static CURLcode ftp_init(struct connectdata *conn)
-{
-  struct FTP *ftp;
-
-  if(NULL == conn->data->state.proto.ftp) {
-    conn->data->state.proto.ftp = malloc(sizeof(struct FTP));
-    if(NULL == conn->data->state.proto.ftp)
-      return CURLE_OUT_OF_MEMORY;
-  }
-
-  ftp = conn->data->state.proto.ftp;
-
-  /* get some initial data into the ftp struct */
-  ftp->bytecountp = &conn->data->req.bytecount;
-  ftp->transfer = FTPTRANSFER_BODY;
-  ftp->downloadsize = 0;
-
-  /* No need to duplicate user+password, the connectdata struct won't change
-     during a session, but we re-init them here since on subsequent inits
-     since the conn struct may have changed or been replaced.
-  */
-  ftp->user = conn->user;
-  ftp->passwd = conn->passwd;
-  if(isBadFtpString(ftp->user))
-    return CURLE_URL_MALFORMAT;
-  if(isBadFtpString(ftp->passwd))
-    return CURLE_URL_MALFORMAT;
-
-  conn->proto.ftpc.known_filesize = -1; /* unknown size for now */
-
-  return CURLE_OK;
-}
-
-#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \
-    defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__)
-  /* workaround icc 9.1 optimizer issue */
-#pragma optimize("", on)
-#endif
-
-/*
- * ftp_connect() should do everything that is to be considered a part of
- * the connection phase.
- *
- * The variable 'done' points to will be TRUE if the protocol-layer connect
- * phase is done when this function returns, or FALSE is not. When called as
- * a part of the easy interface, it will always be TRUE.
- */
-static CURLcode ftp_connect(struct connectdata *conn,
-                                 bool *done) /* see description above */
-{
-  CURLcode result;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-  struct SessionHandle *data=conn->data;
-  struct pingpong *pp = &ftpc->pp;
-
-  *done = FALSE; /* default to not done yet */
-
-  /* If there already is a protocol-specific struct allocated for this
-     sessionhandle, deal with it */
-  Curl_reset_reqproto(conn);
-
-  result = ftp_init(conn);
-  if(CURLE_OK != result)
-    return result;
-
-  /* We always support persistent connections on ftp */
-  conn->bits.close = FALSE;
-
-  pp->response_time = RESP_TIMEOUT; /* set default response time-out */
-  pp->statemach_act = ftp_statemach_act;
-  pp->endofresp = ftp_endofresp;
-  pp->conn = conn;
-
-  if(conn->handler->flags & PROTOPT_SSL) {
-    /* BLOCKING */
-    result = Curl_ssl_connect(conn, FIRSTSOCKET);
-    if(result)
-      return result;
-  }
-
-  Curl_pp_init(pp); /* init the generic pingpong data */
-
-  /* When we connect, we start in the state where we await the 220
-     response */
-  state(conn, FTP_WAIT220);
-
-  if(data->state.used_interface == Curl_if_multi)
-    result = ftp_multi_statemach(conn, done);
-  else {
-    result = ftp_easy_statemach(conn);
-    if(!result)
-      *done = TRUE;
-  }
-
-  return result;
-}
-
-/***********************************************************************
- *
- * ftp_done()
- *
- * The DONE function. This does what needs to be done after a single DO has
- * performed.
- *
- * Input argument is already checked for validity.
- */
-static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
-                              bool premature)
-{
-  struct SessionHandle *data = conn->data;
-  struct FTP *ftp = data->state.proto.ftp;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-  struct pingpong *pp = &ftpc->pp;
-  ssize_t nread;
-  int ftpcode;
-  CURLcode result = CURLE_OK;
-  bool was_ctl_valid = ftpc->ctl_valid;
-  char *path;
-  const char *path_to_use = data->state.path;
-
-  if(!ftp)
-    /* When the easy handle is removed from the multi while libcurl is still
-     * trying to resolve the host name, it seems that the ftp struct is not
-     * yet initialized, but the removal action calls Curl_done() which calls
-     * this function. So we simply return success if no ftp pointer is set.
-     */
-    return CURLE_OK;
-
-  switch(status) {
-  case CURLE_BAD_DOWNLOAD_RESUME:
-  case CURLE_FTP_WEIRD_PASV_REPLY:
-  case CURLE_FTP_PORT_FAILED:
-  case CURLE_FTP_ACCEPT_FAILED:
-  case CURLE_FTP_ACCEPT_TIMEOUT:
-  case CURLE_FTP_COULDNT_SET_TYPE:
-  case CURLE_FTP_COULDNT_RETR_FILE:
-  case CURLE_PARTIAL_FILE:
-  case CURLE_UPLOAD_FAILED:
-  case CURLE_REMOTE_ACCESS_DENIED:
-  case CURLE_FILESIZE_EXCEEDED:
-  case CURLE_REMOTE_FILE_NOT_FOUND:
-  case CURLE_WRITE_ERROR:
-    /* the connection stays alive fine even though this happened */
-    /* fall-through */
-  case CURLE_OK: /* doesn't affect the control connection's status */
-    if(!premature) {
-      ftpc->ctl_valid = was_ctl_valid;
-      break;
-    }
-    /* until we cope better with prematurely ended requests, let them
-     * fallback as if in complete failure */
-  default:       /* by default, an error means the control connection is
-                    wedged and should not be used anymore */
-    ftpc->ctl_valid = FALSE;
-    ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the
-                             current path, as this connection is going */
-    conn->bits.close = TRUE; /* marked for closure */
-    result = status;      /* use the already set error code */
-    break;
-  }
-
-  /* now store a copy of the directory we are in */
-  if(ftpc->prevpath)
-    free(ftpc->prevpath);
-
-  if(data->set.wildcardmatch) {
-    if(data->set.chunk_end && ftpc->file) {
-      data->set.chunk_end(data->wildcard.customptr);
-    }
-    ftpc->known_filesize = -1;
-  }
-
-  /* get the "raw" path */
-  path = curl_easy_unescape(data, path_to_use, 0, NULL);
-  if(!path) {
-    /* out of memory, but we can limp along anyway (and should try to
-     * since we may already be in the out of memory cleanup path) */
-    if(!result)
-      result = CURLE_OUT_OF_MEMORY;
-    ftpc->ctl_valid = FALSE; /* mark control connection as bad */
-    conn->bits.close = TRUE; /* mark for connection closure */
-    ftpc->prevpath = NULL; /* no path remembering */
-  }
-  else {
-    size_t flen = ftpc->file?strlen(ftpc->file):0; /* file is "raw" already */
-    size_t dlen = strlen(path)-flen;
-    if(!ftpc->cwdfail) {
-      if(dlen && (data->set.ftp_filemethod != FTPFILE_NOCWD)) {
-        ftpc->prevpath = path;
-        if(flen)
-          /* if 'path' is not the whole string */
-          ftpc->prevpath[dlen]=0; /* terminate */
-      }
-      else {
-        /* we never changed dir */
-        ftpc->prevpath=strdup("");
-        free(path);
-      }
-      if(ftpc->prevpath)
-        infof(data, "Remembering we are in dir \"%s\"\n", ftpc->prevpath);
-    }
-    else {
-      ftpc->prevpath = NULL; /* no path */
-      free(path);
-    }
-  }
-  /* free the dir tree and file parts */
-  freedirs(ftpc);
-
-  /* shut down the socket to inform the server we're done */
-
-#ifdef _WIN32_WCE
-  shutdown(conn->sock[SECONDARYSOCKET],2);  /* SD_BOTH */
-#endif
-
-  if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
-    if(!result && ftpc->dont_check && data->req.maxdownload > 0)
-      /* partial download completed */
-      result = Curl_pp_sendf(pp, "ABOR");
-      if(result) {
-        failf(data, "Failure sending ABOR command: %s",
-              curl_easy_strerror(result));
-        ftpc->ctl_valid = FALSE; /* mark control connection as bad */
-        conn->bits.close = TRUE; /* mark for connection closure */
-      }
-
-    if(conn->ssl[SECONDARYSOCKET].use) {
-      /* The secondary socket is using SSL so we must close down that part
-         first before we close the socket for real */
-      Curl_ssl_close(conn, SECONDARYSOCKET);
-
-      /* Note that we keep "use" set to TRUE since that (next) connection is
-         still requested to use SSL */
-    }
-    if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) {
-      Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
-      conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
-      conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE;
-    }
-  }
-
-  if(!result && (ftp->transfer == FTPTRANSFER_BODY) && ftpc->ctl_valid &&
-     pp->pending_resp && !premature) {
-    /*
-     * Let's see what the server says about the transfer we just performed,
-     * but lower the timeout as sometimes this connection has died while the
-     * data has been transferred. This happens when doing through NATs etc that
-     * abandon old silent connections.
-     */
-    long old_time = pp->response_time;
-
-    pp->response_time = 60*1000; /* give it only a minute for now */
-    pp->response = Curl_tvnow(); /* timeout relative now */
-
-    result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
-
-    pp->response_time = old_time; /* set this back to previous value */
-
-    if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
-      failf(data, "control connection looks dead");
-      ftpc->ctl_valid = FALSE; /* mark control connection as bad */
-      conn->bits.close = TRUE; /* mark for closure */
-    }
-
-    if(result)
-      return result;
-
-    if(ftpc->dont_check && data->req.maxdownload > 0) {
-      /* we have just sent ABOR and there is no reliable way to check if it was
-       * successful or not; we have to close the connection now */
-      infof(data, "partial download completed, closing connection\n");
-      conn->bits.close = TRUE; /* mark for closure */
-      return result;
-    }
-
-    if(!ftpc->dont_check) {
-      /* 226 Transfer complete, 250 Requested file action okay, completed. */
-      if((ftpcode != 226) && (ftpcode != 250)) {
-        failf(data, "server did not report OK, got %d", ftpcode);
-        result = CURLE_PARTIAL_FILE;
-      }
-    }
-  }
-
-  if(result || premature)
-    /* the response code from the transfer showed an error already so no
-       use checking further */
-    ;
-  else if(data->set.upload) {
-    if((-1 != data->set.infilesize) &&
-       (data->set.infilesize != *ftp->bytecountp) &&
-       !data->set.crlf &&
-       (ftp->transfer == FTPTRANSFER_BODY)) {
-      failf(data, "Uploaded unaligned file size (%" FORMAT_OFF_T
-            " out of %" FORMAT_OFF_T " bytes)",
-            *ftp->bytecountp, data->set.infilesize);
-      result = CURLE_PARTIAL_FILE;
-    }
-  }
-  else {
-    if((-1 != data->req.size) &&
-       (data->req.size != *ftp->bytecountp) &&
-#ifdef CURL_DO_LINEEND_CONV
-       /* Most FTP servers don't adjust their file SIZE response for CRLFs, so
-        * we'll check to see if the discrepancy can be explained by the number
-        * of CRLFs we've changed to LFs.
-        */
-       ((data->req.size + data->state.crlf_conversions) !=
-        *ftp->bytecountp) &&
-#endif /* CURL_DO_LINEEND_CONV */
-       (data->req.maxdownload != *ftp->bytecountp)) {
-      failf(data, "Received only partial file: %" FORMAT_OFF_T " bytes",
-            *ftp->bytecountp);
-      result = CURLE_PARTIAL_FILE;
-    }
-    else if(!ftpc->dont_check &&
-            !*ftp->bytecountp &&
-            (data->req.size>0)) {
-      failf(data, "No data was received!");
-      result = CURLE_FTP_COULDNT_RETR_FILE;
-    }
-  }
-
-  /* clear these for next connection */
-  ftp->transfer = FTPTRANSFER_BODY;
-  ftpc->dont_check = FALSE;
-
-  /* Send any post-transfer QUOTE strings? */
-  if(!status && !result && !premature && data->set.postquote)
-    result = ftp_sendquote(conn, data->set.postquote);
-
-  return result;
-}
-
-/***********************************************************************
- *
- * ftp_sendquote()
- *
- * Where a 'quote' means a list of custom commands to send to the server.
- * The quote list is passed as an argument.
- *
- * BLOCKING
- */
-
-static
-CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
-{
-  struct curl_slist *item;
-  ssize_t nread;
-  int ftpcode;
-  CURLcode result;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-  struct pingpong *pp = &ftpc->pp;
-
-  item = quote;
-  while(item) {
-    if(item->data) {
-      char *cmd = item->data;
-      bool acceptfail = FALSE;
-
-      /* if a command starts with an asterisk, which a legal FTP command never
-         can, the command will be allowed to fail without it causing any
-         aborts or cancels etc. It will cause libcurl to act as if the command
-         is successful, whatever the server reponds. */
-
-      if(cmd[0] == '*') {
-        cmd++;
-        acceptfail = TRUE;
-      }
-
-      FTPSENDF(conn, "%s", cmd);
-
-      pp->response = Curl_tvnow(); /* timeout relative now */
-
-      result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
-      if(result)
-        return result;
-
-      if(!acceptfail && (ftpcode >= 400)) {
-        failf(conn->data, "QUOT string not accepted: %s", cmd);
-        return CURLE_QUOTE_ERROR;
-      }
-    }
-
-    item = item->next;
-  }
-
-  return CURLE_OK;
-}
-
-/***********************************************************************
- *
- * ftp_need_type()
- *
- * Returns TRUE if we in the current situation should send TYPE
- */
-static int ftp_need_type(struct connectdata *conn,
-                         bool ascii_wanted)
-{
-  return conn->proto.ftpc.transfertype != (ascii_wanted?'A':'I');
-}
-
-/***********************************************************************
- *
- * ftp_nb_type()
- *
- * Set TYPE. We only deal with ASCII or BINARY so this function
- * sets one of them.
- * If the transfer type is not sent, simulate on OK response in newstate
- */
-static CURLcode ftp_nb_type(struct connectdata *conn,
-                            bool ascii, ftpstate newstate)
-{
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-  CURLcode result;
-  char want = (char)(ascii?'A':'I');
-
-  if(ftpc->transfertype == want) {
-    state(conn, newstate);
-    return ftp_state_type_resp(conn, 200, newstate);
-  }
-
-  PPSENDF(&ftpc->pp, "TYPE %c", want);
-  state(conn, newstate);
-
-  /* keep track of our current transfer type */
-  ftpc->transfertype = want;
-  return CURLE_OK;
-}
-
-/***************************************************************************
- *
- * ftp_pasv_verbose()
- *
- * This function only outputs some informationals about this second connection
- * when we've issued a PASV command before and thus we have connected to a
- * possibly new IP address.
- *
- */
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-static void
-ftp_pasv_verbose(struct connectdata *conn,
-                 Curl_addrinfo *ai,
-                 char *newhost, /* ascii version */
-                 int port)
-{
-  char buf[256];
-  Curl_printable_address(ai, buf, sizeof(buf));
-  infof(conn->data, "Connecting to %s (%s) port %d\n", newhost, buf, port);
-}
-#endif
-
-/*
-  Check if this is a range download, and if so, set the internal variables
-  properly.
- */
-
-static CURLcode ftp_range(struct connectdata *conn)
-{
-  curl_off_t from, to;
-  char *ptr;
-  char *ptr2;
-  struct SessionHandle *data = conn->data;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-
-  if(data->state.use_range && data->state.range) {
-    from=curlx_strtoofft(data->state.range, &ptr, 0);
-    while(*ptr && (ISSPACE(*ptr) || (*ptr=='-')))
-      ptr++;
-    to=curlx_strtoofft(ptr, &ptr2, 0);
-    if(ptr == ptr2) {
-      /* we didn't get any digit */
-      to=-1;
-    }
-    if((-1 == to) && (from>=0)) {
-      /* X - */
-      data->state.resume_from = from;
-      DEBUGF(infof(conn->data, "FTP RANGE %" FORMAT_OFF_T " to end of file\n",
-                   from));
-    }
-    else if(from < 0) {
-      /* -Y */
-      data->req.maxdownload = -from;
-      data->state.resume_from = from;
-      DEBUGF(infof(conn->data, "FTP RANGE the last %" FORMAT_OFF_T " bytes\n",
-                   -from));
-    }
-    else {
-      /* X-Y */
-      data->req.maxdownload = (to-from)+1; /* include last byte */
-      data->state.resume_from = from;
-      DEBUGF(infof(conn->data, "FTP RANGE from %" FORMAT_OFF_T
-                   " getting %" FORMAT_OFF_T " bytes\n",
-                   from, data->req.maxdownload));
-    }
-    DEBUGF(infof(conn->data, "range-download from %" FORMAT_OFF_T
-                 " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
-                 from, to, data->req.maxdownload));
-    ftpc->dont_check = TRUE; /* dont check for successful transfer */
-  }
-  else
-    data->req.maxdownload = -1;
-  return CURLE_OK;
-}
-
-
-/*
- * ftp_do_more()
- *
- * This function shall be called when the second FTP (data) connection is
- * connected.
- */
-
-static CURLcode ftp_do_more(struct connectdata *conn, bool *complete)
-{
-  struct SessionHandle *data=conn->data;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-  CURLcode result = CURLE_OK;
-  bool connected = FALSE;
-
-  /* the ftp struct is inited in ftp_connect() */
-  struct FTP *ftp = data->state.proto.ftp;
-
-  *complete = FALSE;
-
-  /* if the second connection isn't done yet, wait for it */
-  if(!conn->bits.tcpconnect[SECONDARYSOCKET]) {
-    result = Curl_is_connected(conn, SECONDARYSOCKET, &connected);
-
-    /* Ready to do more? */
-    if(connected) {
-      DEBUGF(infof(data, "DO-MORE connected phase starts\n"));
-    }
-    else
-      return result;
-  }
-
-  if((data->state.used_interface == Curl_if_multi) &&
-     ftpc->state) {
-    /* multi interface and already in a state so skip the intial commands.
-       They are only done to kickstart the do_more state */
-    result = ftp_multi_statemach(conn, complete);
-
-    /* if we got an error or if we don't wait for a data connection return
-       immediately */
-    if(result || (ftpc->wait_data_conn != TRUE))
-      return result;
-  }
-
-  if(ftp->transfer <= FTPTRANSFER_INFO) {
-    /* a transfer is about to take place, or if not a file name was given
-       so we'll do a SIZE on it later and then we need the right TYPE first */
-
-    if(ftpc->wait_data_conn == TRUE) {
-      bool serv_conned;
-
-      result = ReceivedServerConnect(conn, &serv_conned);
-      if(result)
-        return result; /* Failed to accept data connection */
-
-      if(serv_conned) {
-        /* It looks data connection is established */
-        result = AcceptServerConnect(conn);
-        ftpc->wait_data_conn = FALSE;
-        if(!result)
-          result = InitiateTransfer(conn);
-
-        if(result)
-          return result;
-      }
-    }
-    else if(data->set.upload) {
-      result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_STOR_TYPE);
-      if(result)
-        return result;
-    }
-    else {
-      /* download */
-      ftp->downloadsize = -1; /* unknown as of yet */
-
-      result = ftp_range(conn);
-      if(result)
-        ;
-      else if(data->set.ftp_list_only || !ftpc->file) {
-        /* The specified path ends with a slash, and therefore we think this
-           is a directory that is requested, use LIST. But before that we
-           need to set ASCII transfer mode. */
-
-        /* But only if a body transfer was requested. */
-        if(ftp->transfer == FTPTRANSFER_BODY) {
-          result = ftp_nb_type(conn, TRUE, FTP_LIST_TYPE);
-          if(result)
-            return result;
-        }
-        /* otherwise just fall through */
-      }
-      else {
-        result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_RETR_TYPE);
-        if(result)
-          return result;
-      }
-    }
-    if(data->state.used_interface == Curl_if_multi) {
-      result = ftp_multi_statemach(conn, complete);
-
-      return result;
-    }
-    else
-      result = ftp_easy_statemach(conn);
-  }
-
-  if((result == CURLE_OK) && (ftp->transfer != FTPTRANSFER_BODY))
-    /* no data to transfer. FIX: it feels like a kludge to have this here
-       too! */
-    Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
-
-  if(!ftpc->wait_data_conn) {
-    /* no waiting for the data connection so this is now complete */
-    *complete = TRUE;
-    DEBUGF(infof(data, "DO-MORE phase ends with %d\n", (int)result));
-  }
-
-  return result;
-}
-
-
-
-/***********************************************************************
- *
- * ftp_perform()
- *
- * This is the actual DO function for FTP. Get a file/directory according to
- * the options previously setup.
- */
-
-static
-CURLcode ftp_perform(struct connectdata *conn,
-                     bool *connected,  /* connect status after PASV / PORT */
-                     bool *dophase_done)
-{
-  /* this is FTP and no proxy */
-  CURLcode result=CURLE_OK;
-
-  DEBUGF(infof(conn->data, "DO phase starts\n"));
-
-  if(conn->data->set.opt_no_body) {
-    /* requested no body means no transfer... */
-    struct FTP *ftp = conn->data->state.proto.ftp;
-    ftp->transfer = FTPTRANSFER_INFO;
-  }
-
-
-  *dophase_done = FALSE; /* not done yet */
-
-  /* start the first command in the DO phase */
-  result = ftp_state_quote(conn, TRUE, FTP_QUOTE);
-  if(result)
-    return result;
-
-  /* run the state-machine */
-  if(conn->data->state.used_interface == Curl_if_multi)
-    result = ftp_multi_statemach(conn, dophase_done);
-  else {
-    result = ftp_easy_statemach(conn);
-    *dophase_done = TRUE; /* with the easy interface we are done here */
-  }
-  *connected = conn->bits.tcpconnect[FIRSTSOCKET];
-
-  if(*dophase_done)
-    DEBUGF(infof(conn->data, "DO phase is complete\n"));
-
-  return result;
-}
-
-static void wc_data_dtor(void *ptr)
-{
-  struct ftp_wc_tmpdata *tmp = ptr;
-  if(tmp)
-    Curl_ftp_parselist_data_free(&tmp->parser);
-  Curl_safefree(tmp);
-}
-
-static CURLcode init_wc_data(struct connectdata *conn)
-{
-  char *last_slash;
-  char *path = conn->data->state.path;
-  struct WildcardData *wildcard = &(conn->data->wildcard);
-  CURLcode ret = CURLE_OK;
-  struct ftp_wc_tmpdata *ftp_tmp;
-
-  last_slash = strrchr(conn->data->state.path, '/');
-  if(last_slash) {
-    last_slash++;
-    if(last_slash[0] == '\0') {
-      wildcard->state = CURLWC_CLEAN;
-      ret = ftp_parse_url_path(conn);
-      return ret;
-    }
-    else {
-      wildcard->pattern = strdup(last_slash);
-      if(!wildcard->pattern)
-        return CURLE_OUT_OF_MEMORY;
-      last_slash[0] = '\0'; /* cut file from path */
-    }
-  }
-  else { /* there is only 'wildcard pattern' or nothing */
-    if(path[0]) {
-      wildcard->pattern = strdup(path);
-      if(!wildcard->pattern)
-        return CURLE_OUT_OF_MEMORY;
-      path[0] = '\0';
-    }
-    else { /* only list */
-      wildcard->state = CURLWC_CLEAN;
-      ret = ftp_parse_url_path(conn);
-      return ret;
-    }
-  }
-
-  /* program continues only if URL is not ending with slash, allocate needed
-     resources for wildcard transfer */
-
-  /* allocate ftp protocol specific temporary wildcard data */
-  ftp_tmp = calloc(1, sizeof(struct ftp_wc_tmpdata));
-  if(!ftp_tmp) {
-    Curl_safefree(wildcard->pattern);
-    return CURLE_OUT_OF_MEMORY;
-  }
-
-  /* INITIALIZE parselist structure */
-  ftp_tmp->parser = Curl_ftp_parselist_data_alloc();
-  if(!ftp_tmp->parser) {
-    Curl_safefree(wildcard->pattern);
-    Curl_safefree(ftp_tmp);
-    return CURLE_OUT_OF_MEMORY;
-  }
-
-  wildcard->tmp = ftp_tmp; /* put it to the WildcardData tmp pointer */
-  wildcard->tmp_dtor = wc_data_dtor;
-
-  /* wildcard does not support NOCWD option (assert it?) */
-  if(conn->data->set.ftp_filemethod == FTPFILE_NOCWD)
-    conn->data->set.ftp_filemethod = FTPFILE_MULTICWD;
-
-  /* try to parse ftp url */
-  ret = ftp_parse_url_path(conn);
-  if(ret) {
-    Curl_safefree(wildcard->pattern);
-    wildcard->tmp_dtor(wildcard->tmp);
-    wildcard->tmp_dtor = ZERO_NULL;
-    wildcard->tmp = NULL;
-    return ret;
-  }
-
-  wildcard->path = strdup(conn->data->state.path);
-  if(!wildcard->path) {
-    Curl_safefree(wildcard->pattern);
-    wildcard->tmp_dtor(wildcard->tmp);
-    wildcard->tmp_dtor = ZERO_NULL;
-    wildcard->tmp = NULL;
-    return CURLE_OUT_OF_MEMORY;
-  }
-
-  /* backup old write_function */
-  ftp_tmp->backup.write_function = conn->data->set.fwrite_func;
-  /* parsing write function */
-  conn->data->set.fwrite_func = Curl_ftp_parselist;
-  /* backup old file descriptor */
-  ftp_tmp->backup.file_descriptor = conn->data->set.out;
-  /* let the writefunc callback know what curl pointer is working with */
-  conn->data->set.out = conn;
-
-  infof(conn->data, "Wildcard - Parsing started\n");
-  return CURLE_OK;
-}
-
-/* This is called recursively */
-static CURLcode wc_statemach(struct connectdata *conn)
-{
-  struct WildcardData * const wildcard = &(conn->data->wildcard);
-  CURLcode ret = CURLE_OK;
-
-  switch (wildcard->state) {
-  case CURLWC_INIT:
-    ret = init_wc_data(conn);
-    if(wildcard->state == CURLWC_CLEAN)
-      /* only listing! */
-      break;
-    else
-      wildcard->state = ret ? CURLWC_ERROR : CURLWC_MATCHING;
-    break;
-
-  case CURLWC_MATCHING: {
-    /* In this state is LIST response successfully parsed, so lets restore
-       previous WRITEFUNCTION callback and WRITEDATA pointer */
-    struct ftp_wc_tmpdata *ftp_tmp = wildcard->tmp;
-    conn->data->set.fwrite_func = ftp_tmp->backup.write_function;
-    conn->data->set.out = ftp_tmp->backup.file_descriptor;
-    ftp_tmp->backup.write_function = ZERO_NULL;
-    ftp_tmp->backup.file_descriptor = NULL;
-    wildcard->state = CURLWC_DOWNLOADING;
-
-    if(Curl_ftp_parselist_geterror(ftp_tmp->parser)) {
-      /* error found in LIST parsing */
-      wildcard->state = CURLWC_CLEAN;
-      return wc_statemach(conn);
-    }
-    else if(wildcard->filelist->size == 0) {
-      /* no corresponding file */
-      wildcard->state = CURLWC_CLEAN;
-      return CURLE_REMOTE_FILE_NOT_FOUND;
-    }
-    return wc_statemach(conn);
-  }
-
-  case CURLWC_DOWNLOADING: {
-    /* filelist has at least one file, lets get first one */
-    struct ftp_conn *ftpc = &conn->proto.ftpc;
-    struct curl_fileinfo *finfo = wildcard->filelist->head->ptr;
-    char *tmp_path = malloc(strlen(conn->data->state.path) +
-                      strlen(finfo->filename) + 1);
-    if(!tmp_path) {
-      return CURLE_OUT_OF_MEMORY;
-    }
-
-    tmp_path[0] = 0;
-    /* make full path to matched file */
-    strcat(tmp_path, wildcard->path);
-    strcat(tmp_path, finfo->filename);
-    /* switch default "state.pathbuffer" and tmp_path, good to see
-       ftp_parse_url_path function to understand this trick */
-    Curl_safefree(conn->data->state.pathbuffer);
-    conn->data->state.pathbuffer = tmp_path;
-    conn->data->state.path = tmp_path;
-
-    infof(conn->data, "Wildcard - START of \"%s\"\n", finfo->filename);
-    if(conn->data->set.chunk_bgn) {
-      long userresponse = conn->data->set.chunk_bgn(
-          finfo, wildcard->customptr, (int)wildcard->filelist->size);
-      switch(userresponse) {
-      case CURL_CHUNK_BGN_FUNC_SKIP:
-        infof(conn->data, "Wildcard - \"%s\" skipped by user\n",
-              finfo->filename);
-        wildcard->state = CURLWC_SKIP;
-        return wc_statemach(conn);
-      case CURL_CHUNK_BGN_FUNC_FAIL:
-        return CURLE_CHUNK_FAILED;
-      }
-    }
-
-    if(finfo->filetype != CURLFILETYPE_FILE) {
-      wildcard->state = CURLWC_SKIP;
-      return wc_statemach(conn);
-    }
-
-    if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
-      ftpc->known_filesize = finfo->size;
-
-    ret = ftp_parse_url_path(conn);
-    if(ret) {
-      return ret;
-    }
-
-    /* we don't need the Curl_fileinfo of first file anymore */
-    Curl_llist_remove(wildcard->filelist, wildcard->filelist->head, NULL);
-
-    if(wildcard->filelist->size == 0) { /* remains only one file to down. */
-      wildcard->state = CURLWC_CLEAN;
-      /* after that will be ftp_do called once again and no transfer
-         will be done because of CURLWC_CLEAN state */
-      return CURLE_OK;
-    }
-  } break;
-
-  case CURLWC_SKIP: {
-    if(conn->data->set.chunk_end)
-      conn->data->set.chunk_end(conn->data->wildcard.customptr);
-    Curl_llist_remove(wildcard->filelist, wildcard->filelist->head, NULL);
-    wildcard->state = (wildcard->filelist->size == 0) ?
-                      CURLWC_CLEAN : CURLWC_DOWNLOADING;
-    return wc_statemach(conn);
-  }
-
-  case CURLWC_CLEAN: {
-    struct ftp_wc_tmpdata *ftp_tmp = wildcard->tmp;
-    ret = CURLE_OK;
-    if(ftp_tmp) {
-      ret = Curl_ftp_parselist_geterror(ftp_tmp->parser);
-    }
-    wildcard->state = ret ? CURLWC_ERROR : CURLWC_DONE;
-  } break;
-
-  case CURLWC_DONE:
-  case CURLWC_ERROR:
-    break;
-  }
-
-  return ret;
-}
-
-/***********************************************************************
- *
- * ftp_do()
- *
- * This function is registered as 'curl_do' function. It decodes the path
- * parts etc as a wrapper to the actual DO function (ftp_perform).
- *
- * The input argument is already checked for validity.
- */
-static CURLcode ftp_do(struct connectdata *conn, bool *done)
-{
-  CURLcode retcode = CURLE_OK;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-
-  *done = FALSE; /* default to false */
-  ftpc->wait_data_conn = FALSE; /* default to no such wait */
-
-  /*
-    Since connections can be re-used between SessionHandles, this might be a
-    connection already existing but on a fresh SessionHandle struct so we must
-    make sure we have a good 'struct FTP' to play with. For new connections,
-    the struct FTP is allocated and setup in the ftp_connect() function.
-  */
-  Curl_reset_reqproto(conn);
-  retcode = ftp_init(conn);
-  if(retcode)
-    return retcode;
-
-  if(conn->data->set.wildcardmatch) {
-    retcode = wc_statemach(conn);
-    if(conn->data->wildcard.state == CURLWC_SKIP ||
-      conn->data->wildcard.state == CURLWC_DONE) {
-      /* do not call ftp_regular_transfer */
-      return CURLE_OK;
-    }
-    if(retcode) /* error, loop or skipping the file */
-      return retcode;
-  }
-  else { /* no wildcard FSM needed */
-    retcode = ftp_parse_url_path(conn);
-    if(retcode)
-      return retcode;
-  }
-
-  retcode = ftp_regular_transfer(conn, done);
-
-  return retcode;
-}
-
-
-CURLcode Curl_ftpsendf(struct connectdata *conn,
-                       const char *fmt, ...)
-{
-  ssize_t bytes_written;
-#define SBUF_SIZE 1024
-  char s[SBUF_SIZE];
-  size_t write_len;
-  char *sptr=s;
-  CURLcode res = CURLE_OK;
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
-  enum protection_level data_sec = conn->data_prot;
-#endif
-
-  va_list ap;
-  va_start(ap, fmt);
-  vsnprintf(s, SBUF_SIZE-3, fmt, ap);
-  va_end(ap);
-
-  strcat(s, "\r\n"); /* append a trailing CRLF */
-
-  bytes_written=0;
-  write_len = strlen(s);
-
-  res = Curl_convert_to_network(conn->data, s, write_len);
-  /* Curl_convert_to_network calls failf if unsuccessful */
-  if(res)
-    return(res);
-
-  for(;;) {
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
-    conn->data_prot = PROT_CMD;
-#endif
-    res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
-                     &bytes_written);
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
-    DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
-    conn->data_prot = data_sec;
-#endif
-
-    if(CURLE_OK != res)
-      break;
-
-    if(conn->data->set.verbose)
-      Curl_debug(conn->data, CURLINFO_HEADER_OUT,
-                 sptr, (size_t)bytes_written, conn);
-
-    if(bytes_written != (ssize_t)write_len) {
-      write_len -= bytes_written;
-      sptr += bytes_written;
-    }
-    else
-      break;
-  }
-
-  return res;
-}
-
-/***********************************************************************
- *
- * ftp_quit()
- *
- * This should be called before calling sclose() on an ftp control connection
- * (not data connections). We should then wait for the response from the
- * server before returning. The calling code should then try to close the
- * connection.
- *
- */
-static CURLcode ftp_quit(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-
-  if(conn->proto.ftpc.ctl_valid) {
-    result = Curl_pp_sendf(&conn->proto.ftpc.pp, "QUIT", NULL);
-    if(result) {
-      failf(conn->data, "Failure sending QUIT command: %s",
-            curl_easy_strerror(result));
-      conn->proto.ftpc.ctl_valid = FALSE; /* mark control connection as bad */
-      conn->bits.close = TRUE; /* mark for connection closure */
-      state(conn, FTP_STOP);
-      return result;
-    }
-
-    state(conn, FTP_QUIT);
-
-    result = ftp_easy_statemach(conn);
-  }
-
-  return result;
-}
-
-/***********************************************************************
- *
- * ftp_disconnect()
- *
- * Disconnect from an FTP server. Cleanup protocol-specific per-connection
- * resources. BLOCKING.
- */
-static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection)
-{
-  struct ftp_conn *ftpc= &conn->proto.ftpc;
-  struct pingpong *pp = &ftpc->pp;
-
-  /* We cannot send quit unconditionally. If this connection is stale or
-     bad in any way, sending quit and waiting around here will make the
-     disconnect wait in vain and cause more problems than we need to.
-
-     ftp_quit() will check the state of ftp->ctl_valid. If it's ok it
-     will try to send the QUIT command, otherwise it will just return.
-  */
-  if(dead_connection)
-    ftpc->ctl_valid = FALSE;
-
-  /* The FTP session may or may not have been allocated/setup at this point! */
-  (void)ftp_quit(conn); /* ignore errors on the QUIT */
-
-  if(ftpc->entrypath) {
-    struct SessionHandle *data = conn->data;
-    if(data->state.most_recent_ftp_entrypath == ftpc->entrypath) {
-      data->state.most_recent_ftp_entrypath = NULL;
-    }
-    free(ftpc->entrypath);
-    ftpc->entrypath = NULL;
-  }
-
-  freedirs(ftpc);
-  if(ftpc->prevpath) {
-    free(ftpc->prevpath);
-    ftpc->prevpath = NULL;
-  }
-  if(ftpc->server_os) {
-    free(ftpc->server_os);
-    ftpc->server_os = NULL;
-  }
-
-  Curl_pp_disconnect(pp);
-
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
-  Curl_sec_end(conn);
-#endif
-
-  return CURLE_OK;
-}
-
-/***********************************************************************
- *
- * ftp_parse_url_path()
- *
- * Parse the URL path into separate path components.
- *
- */
-static
-CURLcode ftp_parse_url_path(struct connectdata *conn)
-{
-  struct SessionHandle *data = conn->data;
-  /* the ftp struct is already inited in ftp_connect() */
-  struct FTP *ftp = data->state.proto.ftp;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-  const char *slash_pos;  /* position of the first '/' char in curpos */
-  const char *path_to_use = data->state.path;
-  const char *cur_pos;
-  const char *filename = NULL;
-
-  cur_pos = path_to_use; /* current position in path. point at the begin
-                            of next path component */
-
-  ftpc->ctl_valid = FALSE;
-  ftpc->cwdfail = FALSE;
-
-  switch(data->set.ftp_filemethod) {
-  case FTPFILE_NOCWD:
-    /* fastest, but less standard-compliant */
-
-    /*
-      The best time to check whether the path is a file or directory is right
-      here. so:
-
-      the first condition in the if() right here, is there just in case
-      someone decides to set path to NULL one day
-   */
-    if(data->state.path &&
-       data->state.path[0] &&
-       (data->state.path[strlen(data->state.path) - 1] != '/') )
-      filename = data->state.path;  /* this is a full file path */
-      /*
-        ftpc->file is not used anywhere other than for operations on a file.
-        In other words, never for directory operations.
-        So we can safely leave filename as NULL here and use it as a
-        argument in dir/file decisions.
-      */
-    break;
-
-  case FTPFILE_SINGLECWD:
-    /* get the last slash */
-    if(!path_to_use[0]) {
-      /* no dir, no file */
-      ftpc->dirdepth = 0;
-      break;
-    }
-    slash_pos=strrchr(cur_pos, '/');
-    if(slash_pos || !*cur_pos) {
-      ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0]));
-      if(!ftpc->dirs)
-        return CURLE_OUT_OF_MEMORY;
-
-      ftpc->dirs[0] = curl_easy_unescape(conn->data, slash_pos ? cur_pos : "/",
-                                         slash_pos ?
-                                         curlx_sztosi(slash_pos-cur_pos) : 1,
-                                         NULL);
-      if(!ftpc->dirs[0]) {
-        freedirs(ftpc);
-        return CURLE_OUT_OF_MEMORY;
-      }
-      ftpc->dirdepth = 1; /* we consider it to be a single dir */
-      filename = slash_pos ? slash_pos+1 : cur_pos; /* rest is file name */
-    }
-    else
-      filename = cur_pos;  /* this is a file name only */
-    break;
-
-  default: /* allow pretty much anything */
-  case FTPFILE_MULTICWD:
-    ftpc->dirdepth = 0;
-    ftpc->diralloc = 5; /* default dir depth to allocate */
-    ftpc->dirs = calloc(ftpc->diralloc, sizeof(ftpc->dirs[0]));
-    if(!ftpc->dirs)
-      return CURLE_OUT_OF_MEMORY;
-
-    /* we have a special case for listing the root dir only */
-    if(strequal(path_to_use, "/")) {
-      cur_pos++; /* make it point to the zero byte */
-      ftpc->dirs[0] = strdup("/");
-      ftpc->dirdepth++;
-    }
-    else {
-      /* parse the URL path into separate path components */
-      while((slash_pos = strchr(cur_pos, '/')) != NULL) {
-        /* 1 or 0 pointer offset to indicate absolute directory */
-        ssize_t absolute_dir = ((cur_pos - data->state.path > 0) &&
-                                (ftpc->dirdepth == 0))?1:0;
-
-        /* seek out the next path component */
-        if(slash_pos-cur_pos) {
-          /* we skip empty path components, like "x//y" since the FTP command
-             CWD requires a parameter and a non-existent parameter a) doesn't
-             work on many servers and b) has no effect on the others. */
-          int len = curlx_sztosi(slash_pos - cur_pos + absolute_dir);
-          ftpc->dirs[ftpc->dirdepth] =
-            curl_easy_unescape(conn->data, cur_pos - absolute_dir, len, NULL);
-          if(!ftpc->dirs[ftpc->dirdepth]) { /* run out of memory ... */
-            failf(data, "no memory");
-            freedirs(ftpc);
-            return CURLE_OUT_OF_MEMORY;
-          }
-          if(isBadFtpString(ftpc->dirs[ftpc->dirdepth])) {
-            free(ftpc->dirs[ftpc->dirdepth]);
-            freedirs(ftpc);
-            return CURLE_URL_MALFORMAT;
-          }
-        }
-        else {
-          cur_pos = slash_pos + 1; /* jump to the rest of the string */
-          continue;
-        }
-
-        cur_pos = slash_pos + 1; /* jump to the rest of the string */
-        if(++ftpc->dirdepth >= ftpc->diralloc) {
-          /* enlarge array */
-          char **bigger;
-          ftpc->diralloc *= 2; /* double the size each time */
-          bigger = realloc(ftpc->dirs, ftpc->diralloc * sizeof(ftpc->dirs[0]));
-          if(!bigger) {
-            freedirs(ftpc);
-            return CURLE_OUT_OF_MEMORY;
-          }
-          ftpc->dirs = bigger;
-        }
-      }
-    }
-    filename = cur_pos;  /* the rest is the file name */
-    break;
-  } /* switch */
-
-  if(filename && *filename) {
-    ftpc->file = curl_easy_unescape(conn->data, filename, 0, NULL);
-    if(NULL == ftpc->file) {
-      freedirs(ftpc);
-      failf(data, "no memory");
-      return CURLE_OUT_OF_MEMORY;
-    }
-    if(isBadFtpString(ftpc->file)) {
-      freedirs(ftpc);
-      return CURLE_URL_MALFORMAT;
-    }
-  }
-  else
-    ftpc->file=NULL; /* instead of point to a zero byte, we make it a NULL
-                       pointer */
-
-  if(data->set.upload && !ftpc->file && (ftp->transfer == FTPTRANSFER_BODY)) {
-    /* We need a file name when uploading. Return error! */
-    failf(data, "Uploading to a URL without a file name!");
-    return CURLE_URL_MALFORMAT;
-  }
-
-  ftpc->cwddone = FALSE; /* default to not done */
-
-  if(ftpc->prevpath) {
-    /* prevpath is "raw" so we convert the input path before we compare the
-       strings */
-    int dlen;
-    char *path = curl_easy_unescape(conn->data, data->state.path, 0, &dlen);
-    if(!path) {
-      freedirs(ftpc);
-      return CURLE_OUT_OF_MEMORY;
-    }
-
-    dlen -= ftpc->file?curlx_uztosi(strlen(ftpc->file)):0;
-    if((dlen == curlx_uztosi(strlen(ftpc->prevpath))) &&
-       strnequal(path, ftpc->prevpath, dlen)) {
-      infof(data, "Request has same path as previous transfer\n");
-      ftpc->cwddone = TRUE;
-    }
-    free(path);
-  }
-
-  return CURLE_OK;
-}
-
-/* call this when the DO phase has completed */
-static CURLcode ftp_dophase_done(struct connectdata *conn,
-                                 bool connected)
-{
-  struct FTP *ftp = conn->data->state.proto.ftp;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-
-  if(connected) {
-    bool completed;
-    CURLcode result = ftp_do_more(conn, &completed);
-
-    if(result) {
-      if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
-        /* close the second socket if it was created already */
-        Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
-        conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
-      }
-      return result;
-    }
-  }
-
-  if(ftp->transfer != FTPTRANSFER_BODY)
-    /* no data to transfer */
-    Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
-  else if(!connected)
-    /* since we didn't connect now, we want do_more to get called */
-    conn->bits.do_more = TRUE;
-
-  ftpc->ctl_valid = TRUE; /* seems good */
-
-  return CURLE_OK;
-}
-
-/* called from curl_multi.c while DOing */
-static CURLcode ftp_doing(struct connectdata *conn,
-                          bool *dophase_done)
-{
-  CURLcode result = ftp_multi_statemach(conn, dophase_done);
-
-  if(result)
-    DEBUGF(infof(conn->data, "DO phase failed\n"));
-  else if(*dophase_done) {
-    result = ftp_dophase_done(conn, FALSE /* not connected */);
-
-    DEBUGF(infof(conn->data, "DO phase is complete\n"));
-  }
-  return result;
-}
-
-/***********************************************************************
- *
- * ftp_regular_transfer()
- *
- * The input argument is already checked for validity.
- *
- * Performs all commands done before a regular transfer between a local and a
- * remote host.
- *
- * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
- * ftp_done() function without finding any major problem.
- */
-static
-CURLcode ftp_regular_transfer(struct connectdata *conn,
-                              bool *dophase_done)
-{
-  CURLcode result=CURLE_OK;
-  bool connected=FALSE;
-  struct SessionHandle *data = conn->data;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-  data->req.size = -1; /* make sure this is unknown at this point */
-
-  Curl_pgrsSetUploadCounter(data, 0);
-  Curl_pgrsSetDownloadCounter(data, 0);
-  Curl_pgrsSetUploadSize(data, 0);
-  Curl_pgrsSetDownloadSize(data, 0);
-
-  ftpc->ctl_valid = TRUE; /* starts good */
-
-  result = ftp_perform(conn,
-                       &connected, /* have we connected after PASV/PORT */
-                       dophase_done); /* all commands in the DO-phase done? */
-
-  if(CURLE_OK == result) {
-
-    if(!*dophase_done)
-      /* the DO phase has not completed yet */
-      return CURLE_OK;
-
-    result = ftp_dophase_done(conn, connected);
-    if(result)
-      return result;
-  }
-  else
-    freedirs(ftpc);
-
-  return result;
-}
-
-static CURLcode ftp_setup_connection(struct connectdata * conn)
-{
-  struct SessionHandle *data = conn->data;
-  char * type;
-  char command;
-
-  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 */
-#ifndef CURL_DISABLE_HTTP
-    if(conn->handler == &Curl_handler_ftp)
-      conn->handler = &Curl_handler_ftp_proxy;
-    else {
-#ifdef USE_SSL
-      conn->handler = &Curl_handler_ftps_proxy;
-#else
-      failf(data, "FTPS not supported!");
-      return CURLE_UNSUPPORTED_PROTOCOL;
-#endif
-    }
-    /*
-     * We explicitly mark this connection as persistent here as we're doing
-     * FTP over HTTP and thus we accidentally avoid setting this value
-     * otherwise.
-     */
-    conn->bits.close = FALSE;
-#else
-    failf(data, "FTP over http proxy requires HTTP support built-in!");
-    return CURLE_UNSUPPORTED_PROTOCOL;
-#endif
-  }
-
-  data->state.path++;   /* don't include the initial slash */
-  data->state.slash_removed = TRUE; /* we've skipped the slash */
-
-  /* FTP URLs support an extension like ";type=<typecode>" that
-   * we'll try to get now! */
-  type = strstr(data->state.path, ";type=");
-
-  if(!type)
-    type = strstr(conn->host.rawalloc, ";type=");
-
-  if(type) {
-    *type = 0;                     /* it was in the middle of the hostname */
-    command = Curl_raw_toupper(type[6]);
-    conn->bits.type_set = TRUE;
-
-    switch (command) {
-    case 'A': /* ASCII mode */
-      data->set.prefer_ascii = TRUE;
-      break;
-
-    case 'D': /* directory mode */
-      data->set.ftp_list_only = TRUE;
-      break;
-
-    case 'I': /* binary mode */
-    default:
-      /* switch off ASCII */
-      data->set.prefer_ascii = FALSE;
-      break;
-    }
-  }
-
-  return CURLE_OK;
-}
-
-#endif /* CURL_DISABLE_FTP */
diff --git a/lib/ftplistparser.c b/lib/ftplistparser.c
deleted file mode 100644 (file)
index a1a7d51..0000000
+++ /dev/null
@@ -1,1050 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-/**
- * Now implemented:
- *
- * 1) UNIX version 1
- * drwxr-xr-x 1 user01 ftp  512 Jan 29 23:32 prog
- * 2) UNIX version 2
- * drwxr-xr-x 1 user01 ftp  512 Jan 29 1997  prog
- * 3) UNIX version 3
- * drwxr-xr-x 1      1   1  512 Jan 29 23:32 prog
- * 4) UNIX symlink
- * lrwxr-xr-x 1 user01 ftp  512 Jan 29 23:32 prog -> prog2000
- * 5) DOS style
- * 01-29-97 11:32PM <DIR> prog
- */
-
-#include "curl_setup.h"
-
-#ifndef CURL_DISABLE_FTP
-
-#include <curl/curl.h>
-
-#include "curl_urldata.h"
-#include "curl_fileinfo.h"
-#include "curl_llist.h"
-#include "curl_strtoofft.h"
-#include "curl_rawstr.h"
-#include "curl_ftp.h"
-#include "curl_ftplistparser.h"
-#include "curl_fnmatch.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/* allocs buffer which will contain one line of LIST command response */
-#define FTP_BUFFER_ALLOCSIZE 160
-
-typedef enum {
-  PL_UNIX_TOTALSIZE = 0,
-  PL_UNIX_FILETYPE,
-  PL_UNIX_PERMISSION,
-  PL_UNIX_HLINKS,
-  PL_UNIX_USER,
-  PL_UNIX_GROUP,
-  PL_UNIX_SIZE,
-  PL_UNIX_TIME,
-  PL_UNIX_FILENAME,
-  PL_UNIX_SYMLINK
-} pl_unix_mainstate;
-
-typedef union {
-  enum {
-    PL_UNIX_TOTALSIZE_INIT = 0,
-    PL_UNIX_TOTALSIZE_READING
-  } total_dirsize;
-
-  enum {
-    PL_UNIX_HLINKS_PRESPACE = 0,
-    PL_UNIX_HLINKS_NUMBER
-  } hlinks;
-
-  enum {
-    PL_UNIX_USER_PRESPACE = 0,
-    PL_UNIX_USER_PARSING
-  } user;
-
-  enum {
-    PL_UNIX_GROUP_PRESPACE = 0,
-    PL_UNIX_GROUP_NAME
-  } group;
-
-  enum {
-    PL_UNIX_SIZE_PRESPACE = 0,
-    PL_UNIX_SIZE_NUMBER
-  } size;
-
-  enum {
-    PL_UNIX_TIME_PREPART1 = 0,
-    PL_UNIX_TIME_PART1,
-    PL_UNIX_TIME_PREPART2,
-    PL_UNIX_TIME_PART2,
-    PL_UNIX_TIME_PREPART3,
-    PL_UNIX_TIME_PART3
-  } time;
-
-  enum {
-    PL_UNIX_FILENAME_PRESPACE = 0,
-    PL_UNIX_FILENAME_NAME,
-    PL_UNIX_FILENAME_WINDOWSEOL
-  } filename;
-
-  enum {
-    PL_UNIX_SYMLINK_PRESPACE = 0,
-    PL_UNIX_SYMLINK_NAME,
-    PL_UNIX_SYMLINK_PRETARGET1,
-    PL_UNIX_SYMLINK_PRETARGET2,
-    PL_UNIX_SYMLINK_PRETARGET3,
-    PL_UNIX_SYMLINK_PRETARGET4,
-    PL_UNIX_SYMLINK_TARGET,
-    PL_UNIX_SYMLINK_WINDOWSEOL
-  } symlink;
-} pl_unix_substate;
-
-typedef enum {
-  PL_WINNT_DATE = 0,
-  PL_WINNT_TIME,
-  PL_WINNT_DIRORSIZE,
-  PL_WINNT_FILENAME
-} pl_winNT_mainstate;
-
-typedef union {
-  enum {
-    PL_WINNT_TIME_PRESPACE = 0,
-    PL_WINNT_TIME_TIME
-  } time;
-  enum {
-    PL_WINNT_DIRORSIZE_PRESPACE = 0,
-    PL_WINNT_DIRORSIZE_CONTENT
-  } dirorsize;
-  enum {
-    PL_WINNT_FILENAME_PRESPACE = 0,
-    PL_WINNT_FILENAME_CONTENT,
-    PL_WINNT_FILENAME_WINEOL
-  } filename;
-} pl_winNT_substate;
-
-/* This struct is used in wildcard downloading - for parsing LIST response */
-struct ftp_parselist_data {
-  enum {
-    OS_TYPE_UNKNOWN = 0,
-    OS_TYPE_UNIX,
-    OS_TYPE_WIN_NT
-  } os_type;
-
-  union {
-    struct {
-      pl_unix_mainstate main;
-      pl_unix_substate sub;
-    } UNIX;
-
-    struct {
-      pl_winNT_mainstate main;
-      pl_winNT_substate sub;
-    } NT;
-  } state;
-
-  CURLcode error;
-  struct curl_fileinfo *file_data;
-  unsigned int item_length;
-  size_t item_offset;
-  struct {
-    size_t filename;
-    size_t user;
-    size_t group;
-    size_t time;
-    size_t perm;
-    size_t symlink_target;
-  } offsets;
-};
-
-struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void)
-{
-  return calloc(1, sizeof(struct ftp_parselist_data));
-}
-
-
-void Curl_ftp_parselist_data_free(struct ftp_parselist_data **pl_data)
-{
-  if(*pl_data)
-    free(*pl_data);
-  *pl_data = NULL;
-}
-
-
-CURLcode Curl_ftp_parselist_geterror(struct ftp_parselist_data *pl_data)
-{
-  return pl_data->error;
-}
-
-
-#define FTP_LP_MALFORMATED_PERM 0x01000000
-
-static int ftp_pl_get_permission(const char *str)
-{
-  int permissions = 0;
-  /* USER */
-  if(str[0] == 'r')
-    permissions |= 1 << 8;
-  else if(str[0] != '-')
-    permissions |= FTP_LP_MALFORMATED_PERM;
-  if(str[1] == 'w')
-    permissions |= 1 << 7;
-  else if(str[1] != '-')
-    permissions |= FTP_LP_MALFORMATED_PERM;
-
-  if(str[2] == 'x')
-    permissions |= 1 << 6;
-  else if(str[2] == 's') {
-    permissions |= 1 << 6;
-    permissions |= 1 << 11;
-  }
-  else if(str[2] == 'S')
-    permissions |= 1 << 11;
-  else if(str[2] != '-')
-    permissions |= FTP_LP_MALFORMATED_PERM;
-  /* GROUP */
-  if(str[3] == 'r')
-    permissions |= 1 << 5;
-  else if(str[3] != '-')
-    permissions |= FTP_LP_MALFORMATED_PERM;
-  if(str[4] == 'w')
-    permissions |= 1 << 4;
-  else if(str[4] != '-')
-    permissions |= FTP_LP_MALFORMATED_PERM;
-  if(str[5] == 'x')
-    permissions |= 1 << 3;
-  else if(str[5] == 's') {
-    permissions |= 1 << 3;
-    permissions |= 1 << 10;
-  }
-  else if(str[5] == 'S')
-    permissions |= 1 << 10;
-  else if(str[5] != '-')
-    permissions |= FTP_LP_MALFORMATED_PERM;
-  /* others */
-  if(str[6] == 'r')
-    permissions |= 1 << 2;
-  else if(str[6] != '-')
-    permissions |= FTP_LP_MALFORMATED_PERM;
-  if(str[7] == 'w')
-    permissions |= 1 << 1;
-  else if(str[7] != '-')
-      permissions |= FTP_LP_MALFORMATED_PERM;
-  if(str[8] == 'x')
-    permissions |= 1;
-  else if(str[8] == 't') {
-    permissions |= 1;
-    permissions |= 1 << 9;
-  }
-  else if(str[8] == 'T')
-    permissions |= 1 << 9;
-  else if(str[8] != '-')
-    permissions |= FTP_LP_MALFORMATED_PERM;
-
-  return permissions;
-}
-
-static void PL_ERROR(struct connectdata *conn, CURLcode err)
-{
-  struct ftp_wc_tmpdata *tmpdata = conn->data->wildcard.tmp;
-  struct ftp_parselist_data *parser = tmpdata->parser;
-  if(parser->file_data)
-    Curl_fileinfo_dtor(NULL, parser->file_data);
-  parser->file_data = NULL;
-  parser->error = err;
-}
-
-static bool ftp_pl_gettime(struct ftp_parselist_data *parser, char *string)
-{
-  (void)parser;
-  (void)string;
-  /* TODO
-   * There could be possible parse timestamp from server. Leaving unimplemented
-   * for now.
-   * If you want implement this, please add CURLFINFOFLAG_KNOWN_TIME flag to
-   * parser->file_data->flags
-   *
-   * Ftp servers are giving usually these formats:
-   *  Apr 11  1998 (unknown time.. set it to 00:00:00?)
-   *  Apr 11 12:21 (unknown year -> set it to NOW() time?)
-   *  08-05-09  02:49PM  (ms-dos format)
-   *  20100421092538 -> for MLST/MLSD response
-   */
-
-  return FALSE;
-}
-
-static CURLcode ftp_pl_insert_finfo(struct connectdata *conn,
-                                    struct curl_fileinfo *finfo)
-{
-  curl_fnmatch_callback compare;
-  struct WildcardData *wc = &conn->data->wildcard;
-  struct ftp_wc_tmpdata *tmpdata = wc->tmp;
-  struct curl_llist *llist = wc->filelist;
-  struct ftp_parselist_data *parser = tmpdata->parser;
-  bool add = TRUE;
-
-  /* move finfo pointers to b_data */
-  char *str = finfo->b_data;
-  finfo->filename       = str + parser->offsets.filename;
-  finfo->strings.group  = parser->offsets.group ?
-                          str + parser->offsets.group : NULL;
-  finfo->strings.perm   = parser->offsets.perm ?
-                          str + parser->offsets.perm : NULL;
-  finfo->strings.target = parser->offsets.symlink_target ?
-                          str + parser->offsets.symlink_target : NULL;
-  finfo->strings.time   = str + parser->offsets.time;
-  finfo->strings.user   = parser->offsets.user ?
-                          str + parser->offsets.user : NULL;
-
-  /* get correct fnmatch callback */
-  compare = conn->data->set.fnmatch;
-  if(!compare)
-    compare = Curl_fnmatch;
-
-  /* filter pattern-corresponding filenames */
-  if(compare(conn->data->set.fnmatch_data, wc->pattern,
-             finfo->filename) == 0) {
-    /* discard symlink which is containing multiple " -> " */
-    if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target &&
-       (strstr(finfo->strings.target, " -> "))) {
-      add = FALSE;
-    }
-  }
-  else {
-    add = FALSE;
-  }
-
-  if(add) {
-    if(!Curl_llist_insert_next(llist, llist->tail, finfo)) {
-      Curl_fileinfo_dtor(NULL, finfo);
-      tmpdata->parser->file_data = NULL;
-      return CURLE_OUT_OF_MEMORY;
-    }
-  }
-  else {
-    Curl_fileinfo_dtor(NULL, finfo);
-  }
-
-  tmpdata->parser->file_data = NULL;
-  return CURLE_OK;
-}
-
-size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
-                          void *connptr)
-{
-  size_t bufflen = size*nmemb;
-  struct connectdata *conn = (struct connectdata *)connptr;
-  struct ftp_wc_tmpdata *tmpdata = conn->data->wildcard.tmp;
-  struct ftp_parselist_data *parser = tmpdata->parser;
-  struct curl_fileinfo *finfo;
-  unsigned long i = 0;
-  CURLcode rc;
-
-  if(parser->error) { /* error in previous call */
-    /* scenario:
-     * 1. call => OK..
-     * 2. call => OUT_OF_MEMORY (or other error)
-     * 3. (last) call => is skipped RIGHT HERE and the error is hadled later
-     *    in wc_statemach()
-     */
-    return bufflen;
-  }
-
-  if(parser->os_type == OS_TYPE_UNKNOWN && bufflen > 0) {
-    /* considering info about FILE response format */
-    parser->os_type = (buffer[0] >= '0' && buffer[0] <= '9') ?
-                       OS_TYPE_WIN_NT : OS_TYPE_UNIX;
-  }
-
-  while(i < bufflen) { /* FSM */
-
-    char c = buffer[i];
-    if(!parser->file_data) { /* tmp file data is not allocated yet */
-      parser->file_data = Curl_fileinfo_alloc();
-      if(!parser->file_data) {
-        parser->error = CURLE_OUT_OF_MEMORY;
-        return bufflen;
-      }
-      parser->file_data->b_data = malloc(FTP_BUFFER_ALLOCSIZE);
-      if(!parser->file_data->b_data) {
-        PL_ERROR(conn, CURLE_OUT_OF_MEMORY);
-        return bufflen;
-      }
-      parser->file_data->b_size = FTP_BUFFER_ALLOCSIZE;
-      parser->item_offset = 0;
-      parser->item_length = 0;
-    }
-
-    finfo = parser->file_data;
-    finfo->b_data[finfo->b_used++] = c;
-
-    if(finfo->b_used >= finfo->b_size - 1) {
-      /* if it is important, extend buffer space for file data */
-      char *tmp = realloc(finfo->b_data,
-                          finfo->b_size + FTP_BUFFER_ALLOCSIZE);
-      if(tmp) {
-        finfo->b_size += FTP_BUFFER_ALLOCSIZE;
-        finfo->b_data = tmp;
-      }
-      else {
-        Curl_fileinfo_dtor(NULL, parser->file_data);
-        parser->file_data = NULL;
-        parser->error = CURLE_OUT_OF_MEMORY;
-        PL_ERROR(conn, CURLE_OUT_OF_MEMORY);
-        return bufflen;
-      }
-    }
-
-    switch (parser->os_type) {
-    case OS_TYPE_UNIX:
-      switch (parser->state.UNIX.main) {
-      case PL_UNIX_TOTALSIZE:
-        switch(parser->state.UNIX.sub.total_dirsize) {
-        case PL_UNIX_TOTALSIZE_INIT:
-          if(c == 't') {
-            parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING;
-            parser->item_length++;
-          }
-          else {
-            parser->state.UNIX.main = PL_UNIX_FILETYPE;
-            /* start FSM again not considering size of directory */
-            finfo->b_used = 0;
-            i--;
-          }
-          break;
-        case PL_UNIX_TOTALSIZE_READING:
-          parser->item_length++;
-          if(c == '\r') {
-            parser->item_length--;
-            finfo->b_used--;
-          }
-          else if(c == '\n') {
-            finfo->b_data[parser->item_length - 1] = 0;
-            if(strncmp("total ", finfo->b_data, 6) == 0) {
-              char *endptr = finfo->b_data+6;
-              /* here we can deal with directory size */
-              while(ISSPACE(*endptr))
-                endptr++;
-              if(*endptr != 0) {
-                PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-                return bufflen;
-              }
-              else {
-                parser->state.UNIX.main = PL_UNIX_FILETYPE;
-                finfo->b_used = 0;
-              }
-            }
-            else {
-              PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-              return bufflen;
-            }
-          }
-          break;
-        }
-        break;
-      case PL_UNIX_FILETYPE:
-        switch (c) {
-        case '-':
-          finfo->filetype = CURLFILETYPE_FILE;
-          break;
-        case 'd':
-          finfo->filetype = CURLFILETYPE_DIRECTORY;
-          break;
-        case 'l':
-          finfo->filetype = CURLFILETYPE_SYMLINK;
-          break;
-        case 'p':
-          finfo->filetype = CURLFILETYPE_NAMEDPIPE;
-          break;
-        case 's':
-          finfo->filetype = CURLFILETYPE_SOCKET;
-          break;
-        case 'c':
-          finfo->filetype = CURLFILETYPE_DEVICE_CHAR;
-          break;
-        case 'b':
-          finfo->filetype = CURLFILETYPE_DEVICE_BLOCK;
-          break;
-        case 'D':
-          finfo->filetype = CURLFILETYPE_DOOR;
-          break;
-        default:
-          PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-          return bufflen;
-        }
-        parser->state.UNIX.main = PL_UNIX_PERMISSION;
-        parser->item_length = 0;
-        parser->item_offset = 1;
-        break;
-      case PL_UNIX_PERMISSION:
-        parser->item_length++;
-        if(parser->item_length <= 9) {
-          if(!strchr("rwx-tTsS", c)) {
-            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-            return bufflen;
-          }
-        }
-        else if(parser->item_length == 10) {
-          unsigned int perm;
-          if(c != ' ') {
-            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-            return bufflen;
-          }
-          finfo->b_data[10] = 0; /* terminate permissions */
-          perm = ftp_pl_get_permission(finfo->b_data + parser->item_offset);
-          if(perm & FTP_LP_MALFORMATED_PERM) {
-            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-            return bufflen;
-          }
-          parser->file_data->flags |= CURLFINFOFLAG_KNOWN_PERM;
-          parser->file_data->perm = perm;
-          parser->offsets.perm = parser->item_offset;
-
-          parser->item_length = 0;
-          parser->state.UNIX.main = PL_UNIX_HLINKS;
-          parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE;
-        }
-        break;
-      case PL_UNIX_HLINKS:
-        switch(parser->state.UNIX.sub.hlinks) {
-        case PL_UNIX_HLINKS_PRESPACE:
-          if(c != ' ') {
-            if(c >= '0' && c <= '9') {
-              parser->item_offset = finfo->b_used - 1;
-              parser->item_length = 1;
-              parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER;
-            }
-            else {
-              PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-              return bufflen;
-            }
-          }
-          break;
-        case PL_UNIX_HLINKS_NUMBER:
-          parser->item_length ++;
-          if(c == ' ') {
-            char *p;
-            long int hlinks;
-            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
-            hlinks = strtol(finfo->b_data + parser->item_offset, &p, 10);
-            if(p[0] == '\0' && hlinks != LONG_MAX && hlinks != LONG_MIN) {
-              parser->file_data->flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT;
-              parser->file_data->hardlinks = hlinks;
-            }
-            parser->item_length = 0;
-            parser->item_offset = 0;
-            parser->state.UNIX.main = PL_UNIX_USER;
-            parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE;
-          }
-          else if(c < '0' || c > '9') {
-            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-            return bufflen;
-          }
-          break;
-        }
-        break;
-      case PL_UNIX_USER:
-        switch(parser->state.UNIX.sub.user) {
-        case PL_UNIX_USER_PRESPACE:
-          if(c != ' ') {
-            parser->item_offset = finfo->b_used - 1;
-            parser->item_length = 1;
-            parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING;
-          }
-          break;
-        case PL_UNIX_USER_PARSING:
-          parser->item_length++;
-          if(c == ' ') {
-            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
-            parser->offsets.user = parser->item_offset;
-            parser->state.UNIX.main = PL_UNIX_GROUP;
-            parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE;
-            parser->item_offset = 0;
-            parser->item_length = 0;
-          }
-          break;
-        }
-        break;
-      case PL_UNIX_GROUP:
-        switch(parser->state.UNIX.sub.group) {
-        case PL_UNIX_GROUP_PRESPACE:
-          if(c != ' ') {
-            parser->item_offset = finfo->b_used - 1;
-            parser->item_length = 1;
-            parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME;
-          }
-          break;
-        case PL_UNIX_GROUP_NAME:
-          parser->item_length++;
-          if(c == ' ') {
-            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
-            parser->offsets.group = parser->item_offset;
-            parser->state.UNIX.main = PL_UNIX_SIZE;
-            parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE;
-            parser->item_offset = 0;
-            parser->item_length = 0;
-          }
-          break;
-        }
-        break;
-      case PL_UNIX_SIZE:
-        switch(parser->state.UNIX.sub.size) {
-        case PL_UNIX_SIZE_PRESPACE:
-          if(c != ' ') {
-            if(c >= '0' && c <= '9') {
-              parser->item_offset = finfo->b_used - 1;
-              parser->item_length = 1;
-              parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER;
-            }
-            else {
-              PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-              return bufflen;
-            }
-          }
-          break;
-        case PL_UNIX_SIZE_NUMBER:
-          parser->item_length++;
-          if(c == ' ') {
-            char *p;
-            curl_off_t fsize;
-            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
-            fsize = curlx_strtoofft(finfo->b_data+parser->item_offset, &p, 10);
-            if(p[0] == '\0' && fsize != CURL_OFF_T_MAX &&
-                               fsize != CURL_OFF_T_MIN) {
-              parser->file_data->flags |= CURLFINFOFLAG_KNOWN_SIZE;
-              parser->file_data->size = fsize;
-            }
-            parser->item_length = 0;
-            parser->item_offset = 0;
-            parser->state.UNIX.main = PL_UNIX_TIME;
-            parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1;
-          }
-          else if(!ISDIGIT(c)) {
-            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-            return bufflen;
-          }
-          break;
-        }
-        break;
-      case PL_UNIX_TIME:
-        switch(parser->state.UNIX.sub.time) {
-        case PL_UNIX_TIME_PREPART1:
-          if(c != ' ') {
-            if(ISALNUM(c)) {
-              parser->item_offset = finfo->b_used -1;
-              parser->item_length = 1;
-              parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1;
-            }
-            else {
-              PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-              return bufflen;
-            }
-          }
-          break;
-        case PL_UNIX_TIME_PART1:
-          parser->item_length++;
-          if(c == ' ') {
-            parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2;
-          }
-          else if(!ISALNUM(c) && c != '.') {
-            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-            return bufflen;
-          }
-          break;
-        case PL_UNIX_TIME_PREPART2:
-          parser->item_length++;
-          if(c != ' ') {
-            if(ISALNUM(c)) {
-              parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2;
-            }
-            else {
-              PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-              return bufflen;
-            }
-          }
-          break;
-        case PL_UNIX_TIME_PART2:
-          parser->item_length++;
-          if(c == ' ') {
-            parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3;
-          }
-          else if(!ISALNUM(c) && c != '.') {
-            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-            return bufflen;
-          }
-          break;
-        case PL_UNIX_TIME_PREPART3:
-          parser->item_length++;
-          if(c != ' ') {
-            if(ISALNUM(c)) {
-              parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3;
-            }
-            else {
-              PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-              return bufflen;
-            }
-          }
-          break;
-        case PL_UNIX_TIME_PART3:
-          parser->item_length++;
-          if(c == ' ') {
-            finfo->b_data[parser->item_offset + parser->item_length -1] = 0;
-            parser->offsets.time = parser->item_offset;
-            if(ftp_pl_gettime(parser, finfo->b_data + parser->item_offset)) {
-              parser->file_data->flags |= CURLFINFOFLAG_KNOWN_TIME;
-            }
-            if(finfo->filetype == CURLFILETYPE_SYMLINK) {
-              parser->state.UNIX.main = PL_UNIX_SYMLINK;
-              parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE;
-            }
-            else {
-              parser->state.UNIX.main = PL_UNIX_FILENAME;
-              parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE;
-            }
-          }
-          else if(!ISALNUM(c) && c != '.' && c != ':') {
-            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-            return bufflen;
-          }
-          break;
-        }
-        break;
-      case PL_UNIX_FILENAME:
-        switch(parser->state.UNIX.sub.filename) {
-        case PL_UNIX_FILENAME_PRESPACE:
-          if(c != ' ') {
-            parser->item_offset = finfo->b_used - 1;
-            parser->item_length = 1;
-            parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME;
-          }
-          break;
-        case PL_UNIX_FILENAME_NAME:
-          parser->item_length++;
-          if(c == '\r') {
-            parser->item_length--;
-            parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL;
-          }
-          else if(c == '\n') {
-            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
-            parser->offsets.filename = parser->item_offset;
-            parser->state.UNIX.main = PL_UNIX_FILETYPE;
-            rc = ftp_pl_insert_finfo(conn, finfo);
-            if(rc) {
-              PL_ERROR(conn, rc);
-              return bufflen;
-            }
-          }
-          break;
-        case PL_UNIX_FILENAME_WINDOWSEOL:
-          if(c == '\n') {
-            finfo->b_data[parser->item_offset + parser->item_length] = 0;
-            parser->offsets.filename = parser->item_offset;
-            parser->state.UNIX.main = PL_UNIX_FILETYPE;
-            rc = ftp_pl_insert_finfo(conn, finfo);
-            if(rc) {
-              PL_ERROR(conn, rc);
-              return bufflen;
-            }
-          }
-          else {
-            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-            return bufflen;
-          }
-          break;
-        }
-        break;
-      case PL_UNIX_SYMLINK:
-        switch(parser->state.UNIX.sub.symlink) {
-        case PL_UNIX_SYMLINK_PRESPACE:
-          if(c != ' ') {
-            parser->item_offset = finfo->b_used - 1;
-            parser->item_length = 1;
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
-          }
-          break;
-        case PL_UNIX_SYMLINK_NAME:
-          parser->item_length++;
-          if(c == ' ') {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1;
-          }
-          else if(c == '\r' || c == '\n') {
-            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-            return bufflen;
-          }
-          break;
-        case PL_UNIX_SYMLINK_PRETARGET1:
-          parser->item_length++;
-          if(c == '-') {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2;
-          }
-          else if(c == '\r' || c == '\n') {
-            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-            return bufflen;
-          }
-          else {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
-          }
-          break;
-        case PL_UNIX_SYMLINK_PRETARGET2:
-          parser->item_length++;
-          if(c == '>') {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3;
-          }
-          else if(c == '\r' || c == '\n') {
-            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-            return bufflen;
-          }
-          else {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
-          }
-          break;
-        case PL_UNIX_SYMLINK_PRETARGET3:
-          parser->item_length++;
-          if(c == ' ') {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4;
-            /* now place where is symlink following */
-            finfo->b_data[parser->item_offset + parser->item_length - 4] = 0;
-            parser->offsets.filename = parser->item_offset;
-            parser->item_length = 0;
-            parser->item_offset = 0;
-          }
-          else if(c == '\r' || c == '\n') {
-            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-            return bufflen;
-          }
-          else {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
-          }
-          break;
-        case PL_UNIX_SYMLINK_PRETARGET4:
-          if(c != '\r' && c != '\n') {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET;
-            parser->item_offset = finfo->b_used - 1;
-            parser->item_length = 1;
-          }
-          else {
-            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-            return bufflen;
-          }
-          break;
-        case PL_UNIX_SYMLINK_TARGET:
-          parser->item_length ++;
-          if(c == '\r') {
-            parser->item_length --;
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL;
-          }
-          else if(c == '\n') {
-            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
-            parser->offsets.symlink_target = parser->item_offset;
-            rc = ftp_pl_insert_finfo(conn, finfo);
-            if(rc) {
-              PL_ERROR(conn, rc);
-              return bufflen;
-            }
-            parser->state.UNIX.main = PL_UNIX_FILETYPE;
-          }
-          break;
-        case PL_UNIX_SYMLINK_WINDOWSEOL:
-          if(c == '\n') {
-            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
-            parser->offsets.symlink_target = parser->item_offset;
-            rc = ftp_pl_insert_finfo(conn, finfo);
-            if(rc) {
-              PL_ERROR(conn, rc);
-              return bufflen;
-            }
-            parser->state.UNIX.main = PL_UNIX_FILETYPE;
-          }
-          else {
-            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-            return bufflen;
-          }
-          break;
-        }
-        break;
-      }
-      break;
-    case OS_TYPE_WIN_NT:
-      switch(parser->state.NT.main) {
-      case PL_WINNT_DATE:
-        parser->item_length++;
-        if(parser->item_length < 9) {
-          if(!strchr("0123456789-", c)) { /* only simple control */
-            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-            return bufflen;
-          }
-        }
-        else if(parser->item_length == 9) {
-          if(c == ' ') {
-            parser->state.NT.main = PL_WINNT_TIME;
-            parser->state.NT.sub.time = PL_WINNT_TIME_PRESPACE;
-          }
-          else {
-            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-            return bufflen;
-          }
-        }
-        else {
-          PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-          return bufflen;
-        }
-        break;
-      case PL_WINNT_TIME:
-        parser->item_length++;
-        switch(parser->state.NT.sub.time) {
-        case PL_WINNT_TIME_PRESPACE:
-          if(!ISSPACE(c)) {
-            parser->state.NT.sub.time = PL_WINNT_TIME_TIME;
-          }
-          break;
-        case PL_WINNT_TIME_TIME:
-          if(c == ' ') {
-            parser->offsets.time = parser->item_offset;
-            finfo->b_data[parser->item_offset + parser->item_length -1] = 0;
-            parser->state.NT.main = PL_WINNT_DIRORSIZE;
-            parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE;
-            parser->item_length = 0;
-          }
-          else if(!strchr("APM0123456789:", c)) {
-            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-            return bufflen;
-          }
-          break;
-        }
-        break;
-      case PL_WINNT_DIRORSIZE:
-        switch(parser->state.NT.sub.dirorsize) {
-        case PL_WINNT_DIRORSIZE_PRESPACE:
-          if(c == ' ') {
-
-          }
-          else {
-            parser->item_offset = finfo->b_used - 1;
-            parser->item_length = 1;
-            parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT;
-          }
-          break;
-        case PL_WINNT_DIRORSIZE_CONTENT:
-          parser->item_length ++;
-          if(c == ' ') {
-            finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
-            if(strcmp("<DIR>", finfo->b_data + parser->item_offset) == 0) {
-              finfo->filetype = CURLFILETYPE_DIRECTORY;
-              finfo->size = 0;
-            }
-            else {
-              char *endptr;
-              finfo->size = curlx_strtoofft(finfo->b_data +
-                                            parser->item_offset,
-                                            &endptr, 10);
-              if(!*endptr) {
-                if(finfo->size == CURL_OFF_T_MAX ||
-                   finfo->size == CURL_OFF_T_MIN) {
-                  if(errno == ERANGE) {
-                    PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-                    return bufflen;
-                  }
-                }
-              }
-              else {
-                PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-                return bufflen;
-              }
-              /* correct file type */
-              parser->file_data->filetype = CURLFILETYPE_FILE;
-            }
-
-            parser->file_data->flags |= CURLFINFOFLAG_KNOWN_SIZE;
-            parser->item_length = 0;
-            parser->state.NT.main = PL_WINNT_FILENAME;
-            parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
-          }
-          break;
-        }
-        break;
-      case PL_WINNT_FILENAME:
-        switch (parser->state.NT.sub.filename) {
-        case PL_WINNT_FILENAME_PRESPACE:
-          if(c != ' ') {
-            parser->item_offset = finfo->b_used -1;
-            parser->item_length = 1;
-            parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT;
-          }
-          break;
-        case PL_WINNT_FILENAME_CONTENT:
-          parser->item_length++;
-          if(c == '\r') {
-            parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL;
-            finfo->b_data[finfo->b_used - 1] = 0;
-          }
-          else if(c == '\n') {
-            parser->offsets.filename = parser->item_offset;
-            finfo->b_data[finfo->b_used - 1] = 0;
-            parser->offsets.filename = parser->item_offset;
-            rc = ftp_pl_insert_finfo(conn, finfo);
-            if(rc) {
-              PL_ERROR(conn, rc);
-              return bufflen;
-            }
-            parser->state.NT.main = PL_WINNT_DATE;
-            parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
-          }
-          break;
-        case PL_WINNT_FILENAME_WINEOL:
-          if(c == '\n') {
-            parser->offsets.filename = parser->item_offset;
-            rc = ftp_pl_insert_finfo(conn, finfo);
-            if(rc) {
-              PL_ERROR(conn, rc);
-              return bufflen;
-            }
-            parser->state.NT.main = PL_WINNT_DATE;
-            parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
-          }
-          else {
-            PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST);
-            return bufflen;
-          }
-          break;
-        }
-        break;
-      }
-      break;
-    default:
-      return bufflen+1;
-    }
-
-    i++;
-  }
-
-  return bufflen;
-}
-
-#endif /* CURL_DISABLE_FTP */
diff --git a/lib/getenv.c b/lib/getenv.c
deleted file mode 100644 (file)
index cf8b036..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef __VMS
-#include <unixlib.h>
-#endif
-
-#include <curl/curl.h>
-#include "curl_memory.h"
-
-#include "curl_memdebug.h"
-
-static
-char *GetEnv(const char *variable)
-{
-#ifdef _WIN32_WCE
-  return NULL;
-#else
-#ifdef WIN32
-  char env[MAX_PATH]; /* MAX_PATH is from windef.h */
-  char *temp = getenv(variable);
-  env[0] = '\0';
-  if(temp != NULL)
-    ExpandEnvironmentStringsA(temp, env, sizeof(env));
-  return (env[0] != '\0')?strdup(env):NULL;
-#else
-  char *env = getenv(variable);
-#ifdef __VMS
-  if(env && strcmp("HOME",variable) == 0)
-    env = decc_translate_vms(env);
-#endif
-  return (env && env[0])?strdup(env):NULL;
-#endif
-#endif
-}
-
-char *curl_getenv(const char *v)
-{
-  return GetEnv(v);
-}
diff --git a/lib/getinfo.c b/lib/getinfo.c
deleted file mode 100644 (file)
index 0404c28..0000000
+++ /dev/null
@@ -1,330 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#include <curl/curl.h>
-
-#include "curl_urldata.h"
-#include "curl_getinfo.h"
-
-#include "curl_memory.h"
-#include "curl_sslgen.h"
-#include "curl_connect.h" /* Curl_getconnectinfo() */
-#include "curl_progress.h"
-
-/* Make this the last #include */
-#include "curl_memdebug.h"
-
-/*
- * This is supposed to be called in the beginning of a perform() session
- * and should reset all session-info variables
- */
-CURLcode Curl_initinfo(struct SessionHandle *data)
-{
-  struct Progress *pro = &data->progress;
-  struct PureInfo *info =&data->info;
-
-  pro->t_nslookup = 0;
-  pro->t_connect = 0;
-  pro->t_appconnect = 0;
-  pro->t_pretransfer = 0;
-  pro->t_starttransfer = 0;
-  pro->timespent = 0;
-  pro->t_redirect = 0;
-
-  info->httpcode = 0;
-  info->httpversion=0;
-  info->filetime=-1; /* -1 is an illegal time and thus means unknown */
-
-  if(info->contenttype)
-    free(info->contenttype);
-  info->contenttype = NULL;
-
-  info->header_size = 0;
-  info->request_size = 0;
-  info->numconnects = 0;
-
-  info->conn_primary_ip[0] = '\0';
-  info->conn_local_ip[0] = '\0';
-  info->conn_primary_port = 0;
-  info->conn_local_port = 0;
-
-  return CURLE_OK;
-}
-
-static CURLcode getinfo_char(struct SessionHandle *data, CURLINFO info,
-                             char **param_charp)
-{
-  switch(info) {
-  case CURLINFO_EFFECTIVE_URL:
-    *param_charp = data->change.url?data->change.url:(char *)"";
-    break;
-  case CURLINFO_CONTENT_TYPE:
-    *param_charp = data->info.contenttype;
-    break;
-  case CURLINFO_PRIVATE:
-    *param_charp = (char *) data->set.private_data;
-    break;
-  case CURLINFO_FTP_ENTRY_PATH:
-    /* Return the entrypath string from the most recent connection.
-       This pointer was copied from the connectdata structure by FTP.
-       The actual string may be free()ed by subsequent libcurl calls so
-       it must be copied to a safer area before the next libcurl call.
-       Callers must never free it themselves. */
-    *param_charp = data->state.most_recent_ftp_entrypath;
-    break;
-  case CURLINFO_REDIRECT_URL:
-    /* Return the URL this request would have been redirected to if that
-       option had been enabled! */
-    *param_charp = data->info.wouldredirect;
-    break;
-  case CURLINFO_PRIMARY_IP:
-    /* Return the ip address of the most recent (primary) connection */
-    *param_charp = data->info.conn_primary_ip;
-    break;
-  case CURLINFO_LOCAL_IP:
-    /* Return the source/local ip address of the most recent (primary)
-       connection */
-    *param_charp = data->info.conn_local_ip;
-    break;
-  case CURLINFO_RTSP_SESSION_ID:
-    *param_charp = data->set.str[STRING_RTSP_SESSION_ID];
-    break;
-
-  default:
-    return CURLE_BAD_FUNCTION_ARGUMENT;
-  }
-  return CURLE_OK;
-}
-
-static CURLcode getinfo_long(struct SessionHandle *data, CURLINFO info,
-                             long *param_longp)
-{
-  curl_socket_t sockfd;
-
-  union {
-    unsigned long *to_ulong;
-    long          *to_long;
-  } lptr;
-
-  switch(info) {
-  case CURLINFO_RESPONSE_CODE:
-    *param_longp = data->info.httpcode;
-    break;
-  case CURLINFO_HTTP_CONNECTCODE:
-    *param_longp = data->info.httpproxycode;
-    break;
-  case CURLINFO_FILETIME:
-    *param_longp = data->info.filetime;
-    break;
-  case CURLINFO_HEADER_SIZE:
-    *param_longp = data->info.header_size;
-    break;
-  case CURLINFO_REQUEST_SIZE:
-    *param_longp = data->info.request_size;
-    break;
-  case CURLINFO_SSL_VERIFYRESULT:
-    *param_longp = data->set.ssl.certverifyresult;
-    break;
-  case CURLINFO_REDIRECT_COUNT:
-    *param_longp = data->set.followlocation;
-    break;
-  case CURLINFO_HTTPAUTH_AVAIL:
-    lptr.to_long = param_longp;
-    *lptr.to_ulong = data->info.httpauthavail;
-    break;
-  case CURLINFO_PROXYAUTH_AVAIL:
-    lptr.to_long = param_longp;
-    *lptr.to_ulong = data->info.proxyauthavail;
-    break;
-  case CURLINFO_OS_ERRNO:
-    *param_longp = data->state.os_errno;
-    break;
-  case CURLINFO_NUM_CONNECTS:
-    *param_longp = data->info.numconnects;
-    break;
-  case CURLINFO_LASTSOCKET:
-    sockfd = Curl_getconnectinfo(data, NULL);
-
-    /* note: this is not a good conversion for systems with 64 bit sockets and
-       32 bit longs */
-    if(sockfd != CURL_SOCKET_BAD)
-      *param_longp = (long)sockfd;
-    else
-      /* this interface is documented to return -1 in case of badness, which
-         may not be the same as the CURL_SOCKET_BAD value */
-      *param_longp = -1;
-    break;
-  case CURLINFO_PRIMARY_PORT:
-    /* Return the (remote) port of the most recent (primary) connection */
-    *param_longp = data->info.conn_primary_port;
-    break;
-  case CURLINFO_LOCAL_PORT:
-    /* Return the local port of the most recent (primary) connection */
-    *param_longp = data->info.conn_local_port;
-    break;
-  case CURLINFO_CONDITION_UNMET:
-    /* return if the condition prevented the document to get transferred */
-    *param_longp = data->info.timecond;
-    break;
-  case CURLINFO_RTSP_CLIENT_CSEQ:
-    *param_longp = data->state.rtsp_next_client_CSeq;
-    break;
-  case CURLINFO_RTSP_SERVER_CSEQ:
-    *param_longp = data->state.rtsp_next_server_CSeq;
-    break;
-  case CURLINFO_RTSP_CSEQ_RECV:
-    *param_longp = data->state.rtsp_CSeq_recv;
-    break;
-
-  default:
-    return CURLE_BAD_FUNCTION_ARGUMENT;
-  }
-  return CURLE_OK;
-}
-
-static CURLcode getinfo_double(struct SessionHandle *data, CURLINFO info,
-                               double *param_doublep)
-{
-  switch(info) {
-  case CURLINFO_TOTAL_TIME:
-    *param_doublep = data->progress.timespent;
-    break;
-  case CURLINFO_NAMELOOKUP_TIME:
-    *param_doublep = data->progress.t_nslookup;
-    break;
-  case CURLINFO_CONNECT_TIME:
-    *param_doublep = data->progress.t_connect;
-    break;
-  case CURLINFO_APPCONNECT_TIME:
-    *param_doublep = data->progress.t_appconnect;
-    break;
-  case CURLINFO_PRETRANSFER_TIME:
-    *param_doublep =  data->progress.t_pretransfer;
-    break;
-  case CURLINFO_STARTTRANSFER_TIME:
-    *param_doublep = data->progress.t_starttransfer;
-    break;
-  case CURLINFO_SIZE_UPLOAD:
-    *param_doublep =  (double)data->progress.uploaded;
-    break;
-  case CURLINFO_SIZE_DOWNLOAD:
-    *param_doublep = (double)data->progress.downloaded;
-    break;
-  case CURLINFO_SPEED_DOWNLOAD:
-    *param_doublep =  (double)data->progress.dlspeed;
-    break;
-  case CURLINFO_SPEED_UPLOAD:
-    *param_doublep = (double)data->progress.ulspeed;
-    break;
-  case CURLINFO_CONTENT_LENGTH_DOWNLOAD:
-    *param_doublep = (data->progress.flags & PGRS_DL_SIZE_KNOWN)?
-      (double)data->progress.size_dl:-1;
-    break;
-  case CURLINFO_CONTENT_LENGTH_UPLOAD:
-    *param_doublep = (data->progress.flags & PGRS_UL_SIZE_KNOWN)?
-      (double)data->progress.size_ul:-1;
-    break;
-  case CURLINFO_REDIRECT_TIME:
-    *param_doublep =  data->progress.t_redirect;
-    break;
-
-  default:
-    return CURLE_BAD_FUNCTION_ARGUMENT;
-  }
-  return CURLE_OK;
-}
-
-static CURLcode getinfo_slist(struct SessionHandle *data, CURLINFO info,
-                              struct curl_slist **param_slistp)
-{
-  union {
-    struct curl_certinfo * to_certinfo;
-    struct curl_slist    * to_slist;
-  } ptr;
-
-  switch(info) {
-  case CURLINFO_SSL_ENGINES:
-    *param_slistp = Curl_ssl_engines_list(data);
-    break;
-  case CURLINFO_COOKIELIST:
-    *param_slistp = Curl_cookie_list(data);
-    break;
-  case CURLINFO_CERTINFO:
-    /* Return the a pointer to the certinfo struct. Not really an slist
-       pointer but we can pretend it is here */
-    ptr.to_certinfo = &data->info.certs;
-    *param_slistp = ptr.to_slist;
-    break;
-
-  default:
-    return CURLE_BAD_FUNCTION_ARGUMENT;
-  }
-  return CURLE_OK;
-}
-
-CURLcode Curl_getinfo(struct SessionHandle *data, CURLINFO info, ...)
-{
-  va_list arg;
-  long *param_longp=NULL;
-  double *param_doublep=NULL;
-  char **param_charp=NULL;
-  struct curl_slist **param_slistp=NULL;
-  int type;
-  /* default return code is to error out! */
-  CURLcode ret = CURLE_BAD_FUNCTION_ARGUMENT;
-
-  if(!data)
-    return ret;
-
-  va_start(arg, info);
-
-  type = CURLINFO_TYPEMASK & (int)info;
-  switch(type) {
-  case CURLINFO_STRING:
-    param_charp = va_arg(arg, char **);
-    if(NULL != param_charp)
-      ret = getinfo_char(data, info, param_charp);
-    break;
-  case CURLINFO_LONG:
-    param_longp = va_arg(arg, long *);
-    if(NULL != param_longp)
-      ret = getinfo_long(data, info, param_longp);
-    break;
-  case CURLINFO_DOUBLE:
-    param_doublep = va_arg(arg, double *);
-    if(NULL != param_doublep)
-      ret = getinfo_double(data, info, param_doublep);
-    break;
-  case CURLINFO_SLIST:
-    param_slistp = va_arg(arg, struct curl_slist **);
-    if(NULL != param_slistp)
-      ret = getinfo_slist(data, info, param_slistp);
-    break;
-  default:
-    break;
-  }
-
-  va_end(arg);
-  return ret;
-}
diff --git a/lib/gopher.c b/lib/gopher.c
deleted file mode 100644 (file)
index 80fc18e..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifndef CURL_DISABLE_GOPHER
-
-#include "curl_urldata.h"
-#include <curl/curl.h>
-#include "curl_transfer.h"
-#include "curl_sendf.h"
-
-#include "curl_progress.h"
-#include "curl_strequal.h"
-#include "curl_gopher.h"
-#include "curl_rawstr.h"
-#include "curl_select.h"
-#include "curl_url.h"
-#include "curl_warnless.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/*
- * Forward declarations.
- */
-
-static CURLcode gopher_do(struct connectdata *conn, bool *done);
-
-/*
- * Gopher protocol handler.
- * This is also a nice simple template to build off for simple
- * connect-command-download protocols.
- */
-
-const struct Curl_handler Curl_handler_gopher = {
-  "GOPHER",                             /* scheme */
-  ZERO_NULL,                            /* setup_connection */
-  gopher_do,                            /* do_it */
-  ZERO_NULL,                            /* done */
-  ZERO_NULL,                            /* do_more */
-  ZERO_NULL,                            /* connect_it */
-  ZERO_NULL,                            /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
-  ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* readwrite */
-  PORT_GOPHER,                          /* defport */
-  CURLPROTO_GOPHER,                     /* protocol */
-  PROTOPT_NONE                          /* flags */
-};
-
-static CURLcode gopher_do(struct connectdata *conn, bool *done)
-{
-  CURLcode result=CURLE_OK;
-  struct SessionHandle *data=conn->data;
-  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
-
-  curl_off_t *bytecount = &data->req.bytecount;
-  char *path = data->state.path;
-  char *sel;
-  char *sel_org = NULL;
-  ssize_t amount, k;
-
-  *done = TRUE; /* unconditionally */
-
-  /* Create selector. Degenerate cases: / and /1 => convert to "" */
-  if(strlen(path) <= 2)
-    sel = (char *)"";
-  else {
-    char *newp;
-    size_t j, i;
-    int len;
-
-    /* Otherwise, drop / and the first character (i.e., item type) ... */
-    newp = path;
-    newp+=2;
-
-    /* ... then turn ? into TAB for search servers, Veronica, etc. ... */
-    j = strlen(newp);
-    for(i=0; i<j; i++)
-      if(newp[i] == '?')
-        newp[i] = '\x09';
-
-    /* ... and finally unescape */
-    sel = curl_easy_unescape(data, newp, 0, &len);
-    if(!sel)
-      return CURLE_OUT_OF_MEMORY;
-    sel_org = sel;
-  }
-
-  /* We use Curl_write instead of Curl_sendf to make sure the entire buffer is
-     sent, which could be sizeable with long selectors. */
-  k = curlx_uztosz(strlen(sel));
-
-  for(;;) {
-    result = Curl_write(conn, sockfd, sel, k, &amount);
-    if(CURLE_OK == result) { /* Which may not have written it all! */
-      result = Curl_client_write(conn, CLIENTWRITE_HEADER, sel, amount);
-      if(result) {
-        Curl_safefree(sel_org);
-        return result;
-      }
-      k -= amount;
-      sel += amount;
-      if(k < 1)
-        break; /* but it did write it all */
-    }
-    else {
-      failf(data, "Failed sending Gopher request");
-      Curl_safefree(sel_org);
-      return result;
-    }
-    /* Don't busyloop. The entire loop thing is a work-around as it causes a
-       BLOCKING behavior which is a NO-NO. This function should rather be
-       split up in a do and a doing piece where the pieces that aren't
-       possible to send now will be sent in the doing function repeatedly
-       until the entire request is sent.
-
-       Wait a while for the socket to be writable. Note that this doesn't
-       acknowledge the timeout.
-    */
-    Curl_socket_ready(CURL_SOCKET_BAD, sockfd, 100);
-  }
-
-  Curl_safefree(sel_org);
-
-  /* We can use Curl_sendf to send the terminal \r\n relatively safely and
-     save allocing another string/doing another _write loop. */
-  result = Curl_sendf(sockfd, conn, "\r\n");
-  if(result != CURLE_OK) {
-    failf(data, "Failed sending Gopher request");
-    return result;
-  }
-  result = Curl_client_write(conn, CLIENTWRITE_HEADER, (char *)"\r\n", 2);
-  if(result)
-    return result;
-
-  Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount,
-                      -1, NULL); /* no upload */
-  return CURLE_OK;
-}
-#endif /*CURL_DISABLE_GOPHER*/
diff --git a/lib/gtls.c b/lib/gtls.c
deleted file mode 100644 (file)
index 5c9d165..0000000
+++ /dev/null
@@ -1,1118 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-/*
- * Source file for all GnuTLS-specific code for the TLS/SSL layer. No code
- * but curl_sslgen.c should ever call or use these functions.
- *
- * Note: don't use the GnuTLS' *_t variable type names in this source code,
- * since they were not present in 1.0.X.
- */
-
-#include "curl_setup.h"
-
-#ifdef USE_GNUTLS
-
-#include <gnutls/gnutls.h>
-#include <gnutls/x509.h>
-
-#ifdef USE_GNUTLS_NETTLE
-#include <gnutls/crypto.h>
-#include <nettle/md5.h>
-#else
-#include <gcrypt.h>
-#endif
-
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_inet_pton.h"
-#include "curl_gtls.h"
-#include "curl_sslgen.h"
-#include "curl_parsedate.h"
-#include "curl_connect.h" /* for the connect timeout */
-#include "curl_select.h"
-#include "curl_rawstr.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/*
- Some hackish cast macros based on:
- http://library.gnome.org/devel/glib/unstable/glib-Type-Conversion-Macros.html
-*/
-#ifndef GNUTLS_POINTER_TO_INT_CAST
-#define GNUTLS_POINTER_TO_INT_CAST(p) ((int) (long) (p))
-#endif
-#ifndef GNUTLS_INT_TO_POINTER_CAST
-#define GNUTLS_INT_TO_POINTER_CAST(i) ((void*) (long) (i))
-#endif
-
-/* Enable GnuTLS debugging by defining GTLSDEBUG */
-/*#define GTLSDEBUG */
-
-#ifdef GTLSDEBUG
-static void tls_log_func(int level, const char *str)
-{
-    fprintf(stderr, "|<%d>| %s", level, str);
-}
-#endif
-static bool gtls_inited = FALSE;
-
-#if defined(GNUTLS_VERSION_NUMBER)
-#  if (GNUTLS_VERSION_NUMBER >= 0x020c00)
-#    undef gnutls_transport_set_lowat
-#    define gnutls_transport_set_lowat(A,B) Curl_nop_stmt
-#    define USE_GNUTLS_PRIORITY_SET_DIRECT 1
-#  endif
-#  if (GNUTLS_VERSION_NUMBER >= 0x020c03)
-#    define GNUTLS_MAPS_WINSOCK_ERRORS 1
-#  endif
-#endif
-
-/*
- * Custom push and pull callback functions used by GNU TLS to read and write
- * to the socket.  These functions are simple wrappers to send() and recv()
- * (although here using sread/swrite macros as defined by curl_setup_once.h).
- * We use custom functions rather than the GNU TLS defaults because it allows
- * us to get specific about the fourth "flags" argument, and to use arbitrary
- * private data with gnutls_transport_set_ptr if we wish.
- *
- * When these custom push and pull callbacks fail, GNU TLS checks its own
- * session-specific error variable, and when not set also its own global
- * errno variable, in order to take appropriate action. GNU TLS does not
- * require that the transport is actually a socket. This implies that for
- * Windows builds these callbacks should ideally set the session-specific
- * error variable using function gnutls_transport_set_errno or as a last
- * resort global errno variable using gnutls_transport_set_global_errno,
- * with a transport agnostic error value. This implies that some winsock
- * error translation must take place in these callbacks.
- *
- * Paragraph above applies to GNU TLS versions older than 2.12.3, since
- * this version GNU TLS does its own internal winsock error translation
- * using system_errno() function.
- */
-
-#if defined(USE_WINSOCK) && !defined(GNUTLS_MAPS_WINSOCK_ERRORS)
-#  define gtls_EINTR  4
-#  define gtls_EIO    5
-#  define gtls_EAGAIN 11
-static int gtls_mapped_sockerrno(void)
-{
-  switch(SOCKERRNO) {
-  case WSAEWOULDBLOCK:
-    return gtls_EAGAIN;
-  case WSAEINTR:
-    return gtls_EINTR;
-  default:
-    break;
-  }
-  return gtls_EIO;
-}
-#endif
-
-static ssize_t Curl_gtls_push(void *s, const void *buf, size_t len)
-{
-  ssize_t ret = swrite(GNUTLS_POINTER_TO_INT_CAST(s), buf, len);
-#if defined(USE_WINSOCK) && !defined(GNUTLS_MAPS_WINSOCK_ERRORS)
-  if(ret < 0)
-    gnutls_transport_set_global_errno(gtls_mapped_sockerrno());
-#endif
-  return ret;
-}
-
-static ssize_t Curl_gtls_pull(void *s, void *buf, size_t len)
-{
-  ssize_t ret = sread(GNUTLS_POINTER_TO_INT_CAST(s), buf, len);
-#if defined(USE_WINSOCK) && !defined(GNUTLS_MAPS_WINSOCK_ERRORS)
-  if(ret < 0)
-    gnutls_transport_set_global_errno(gtls_mapped_sockerrno());
-#endif
-  return ret;
-}
-
-/* Curl_gtls_init()
- *
- * Global GnuTLS init, called from Curl_ssl_init(). This calls functions that
- * are not thread-safe and thus this function itself is not thread-safe and
- * must only be called from within curl_global_init() to keep the thread
- * situation under control!
- */
-int Curl_gtls_init(void)
-{
-  int ret = 1;
-  if(!gtls_inited) {
-    ret = gnutls_global_init()?0:1;
-#ifdef GTLSDEBUG
-    gnutls_global_set_log_function(tls_log_func);
-    gnutls_global_set_log_level(2);
-#endif
-    gtls_inited = TRUE;
-  }
-  return ret;
-}
-
-int Curl_gtls_cleanup(void)
-{
-  if(gtls_inited) {
-    gnutls_global_deinit();
-    gtls_inited = FALSE;
-  }
-  return 1;
-}
-
-static void showtime(struct SessionHandle *data,
-                     const char *text,
-                     time_t stamp)
-{
-  struct tm buffer;
-  const struct tm *tm = &buffer;
-  CURLcode result = Curl_gmtime(stamp, &buffer);
-  if(result)
-    return;
-
-  snprintf(data->state.buffer,
-           BUFSIZE,
-           "\t %s: %s, %02d %s %4d %02d:%02d:%02d GMT\n",
-           text,
-           Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
-           tm->tm_mday,
-           Curl_month[tm->tm_mon],
-           tm->tm_year + 1900,
-           tm->tm_hour,
-           tm->tm_min,
-           tm->tm_sec);
-  infof(data, "%s\n", data->state.buffer);
-}
-
-static gnutls_datum load_file (const char *file)
-{
-  FILE *f;
-  gnutls_datum loaded_file = { NULL, 0 };
-  long filelen;
-  void *ptr;
-
-  if(!(f = fopen(file, "r")))
-    return loaded_file;
-  if(fseek(f, 0, SEEK_END) != 0
-     || (filelen = ftell(f)) < 0
-     || fseek(f, 0, SEEK_SET) != 0
-     || !(ptr = malloc((size_t)filelen)))
-    goto out;
-  if(fread(ptr, 1, (size_t)filelen, f) < (size_t)filelen) {
-    free(ptr);
-    goto out;
-  }
-
-  loaded_file.data = ptr;
-  loaded_file.size = (unsigned int)filelen;
-out:
-  fclose(f);
-  return loaded_file;
-}
-
-static void unload_file(gnutls_datum data) {
-  free(data.data);
-}
-
-
-/* this function does a SSL/TLS (re-)handshake */
-static CURLcode handshake(struct connectdata *conn,
-                          int sockindex,
-                          bool duringconnect,
-                          bool nonblocking)
-{
-  struct SessionHandle *data = conn->data;
-  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-  gnutls_session session = conn->ssl[sockindex].session;
-  curl_socket_t sockfd = conn->sock[sockindex];
-  long timeout_ms;
-  int rc;
-  int what;
-
-  for(;;) {
-    /* check allowed time left */
-    timeout_ms = Curl_timeleft(data, NULL, duringconnect);
-
-    if(timeout_ms < 0) {
-      /* no need to continue if time already is up */
-      failf(data, "SSL connection timeout");
-      return CURLE_OPERATION_TIMEDOUT;
-    }
-
-    /* if ssl is expecting something, check if it's available. */
-    if(connssl->connecting_state == ssl_connect_2_reading
-       || connssl->connecting_state == ssl_connect_2_writing) {
-
-      curl_socket_t writefd = ssl_connect_2_writing==
-        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
-      curl_socket_t readfd = ssl_connect_2_reading==
-        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
-
-      what = Curl_socket_ready(readfd, writefd,
-                               nonblocking?0:
-                               timeout_ms?timeout_ms:1000);
-      if(what < 0) {
-        /* fatal error */
-        failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
-        return CURLE_SSL_CONNECT_ERROR;
-      }
-      else if(0 == what) {
-        if(nonblocking)
-          return CURLE_OK;
-        else if(timeout_ms) {
-          /* timeout */
-          failf(data, "SSL connection timeout at %ld", timeout_ms);
-          return CURLE_OPERATION_TIMEDOUT;
-        }
-      }
-      /* socket is readable or writable */
-    }
-
-    rc = gnutls_handshake(session);
-
-    if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) {
-      connssl->connecting_state =
-        gnutls_record_get_direction(session)?
-        ssl_connect_2_writing:ssl_connect_2_reading;
-      continue;
-      if(nonblocking)
-        return CURLE_OK;
-    }
-    else if((rc < 0) && !gnutls_error_is_fatal(rc)) {
-      const char *strerr = NULL;
-
-      if(rc == GNUTLS_E_WARNING_ALERT_RECEIVED) {
-        int alert = gnutls_alert_get(session);
-        strerr = gnutls_alert_get_name(alert);
-      }
-
-      if(strerr == NULL)
-        strerr = gnutls_strerror(rc);
-
-      failf(data, "gnutls_handshake() warning: %s", strerr);
-    }
-    else if(rc < 0) {
-      const char *strerr = NULL;
-
-      if(rc == GNUTLS_E_FATAL_ALERT_RECEIVED) {
-        int alert = gnutls_alert_get(session);
-        strerr = gnutls_alert_get_name(alert);
-      }
-
-      if(strerr == NULL)
-        strerr = gnutls_strerror(rc);
-
-      failf(data, "gnutls_handshake() failed: %s", strerr);
-      return CURLE_SSL_CONNECT_ERROR;
-    }
-
-    /* Reset our connect state machine */
-    connssl->connecting_state = ssl_connect_1;
-    return CURLE_OK;
-  }
-}
-
-static gnutls_x509_crt_fmt do_file_type(const char *type)
-{
-  if(!type || !type[0])
-    return GNUTLS_X509_FMT_PEM;
-  if(Curl_raw_equal(type, "PEM"))
-    return GNUTLS_X509_FMT_PEM;
-  if(Curl_raw_equal(type, "DER"))
-    return GNUTLS_X509_FMT_DER;
-  return -1;
-}
-
-static CURLcode
-gtls_connect_step1(struct connectdata *conn,
-                   int sockindex)
-{
-#ifndef USE_GNUTLS_PRIORITY_SET_DIRECT
-  static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 };
-#endif
-  struct SessionHandle *data = conn->data;
-  gnutls_session session;
-  int rc;
-  void *ssl_sessionid;
-  size_t ssl_idsize;
-  bool sni = TRUE; /* default is SNI enabled */
-#ifdef ENABLE_IPV6
-  struct in6_addr addr;
-#else
-  struct in_addr addr;
-#endif
-
-  if(conn->ssl[sockindex].state == ssl_connection_complete)
-    /* to make us tolerant against being called more than once for the
-       same connection */
-    return CURLE_OK;
-
-  if(!gtls_inited)
-    Curl_gtls_init();
-
-  /* GnuTLS only supports SSLv3 and TLSv1 */
-  if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) {
-    failf(data, "GnuTLS does not support SSLv2");
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-  else if(data->set.ssl.version == CURL_SSLVERSION_SSLv3)
-    sni = FALSE; /* SSLv3 has no SNI */
-
-  /* allocate a cred struct */
-  rc = gnutls_certificate_allocate_credentials(&conn->ssl[sockindex].cred);
-  if(rc != GNUTLS_E_SUCCESS) {
-    failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc));
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-
-#ifdef USE_TLS_SRP
-  if(data->set.ssl.authtype == CURL_TLSAUTH_SRP) {
-    infof(data, "Using TLS-SRP username: %s\n", data->set.ssl.username);
-
-    rc = gnutls_srp_allocate_client_credentials(
-           &conn->ssl[sockindex].srp_client_cred);
-    if(rc != GNUTLS_E_SUCCESS) {
-      failf(data, "gnutls_srp_allocate_client_cred() failed: %s",
-            gnutls_strerror(rc));
-      return CURLE_OUT_OF_MEMORY;
-    }
-
-    rc = gnutls_srp_set_client_credentials(conn->ssl[sockindex].
-                                           srp_client_cred,
-                                           data->set.ssl.username,
-                                           data->set.ssl.password);
-    if(rc != GNUTLS_E_SUCCESS) {
-      failf(data, "gnutls_srp_set_client_cred() failed: %s",
-            gnutls_strerror(rc));
-      return CURLE_BAD_FUNCTION_ARGUMENT;
-    }
-  }
-#endif
-
-  if(data->set.ssl.CAfile) {
-    /* set the trusted CA cert bundle file */
-    gnutls_certificate_set_verify_flags(conn->ssl[sockindex].cred,
-                                        GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
-
-    rc = gnutls_certificate_set_x509_trust_file(conn->ssl[sockindex].cred,
-                                                data->set.ssl.CAfile,
-                                                GNUTLS_X509_FMT_PEM);
-    if(rc < 0) {
-      infof(data, "error reading ca cert file %s (%s)\n",
-            data->set.ssl.CAfile, gnutls_strerror(rc));
-      if(data->set.ssl.verifypeer)
-        return CURLE_SSL_CACERT_BADFILE;
-    }
-    else
-      infof(data, "found %d certificates in %s\n",
-            rc, data->set.ssl.CAfile);
-  }
-
-  if(data->set.ssl.CRLfile) {
-    /* set the CRL list file */
-    rc = gnutls_certificate_set_x509_crl_file(conn->ssl[sockindex].cred,
-                                              data->set.ssl.CRLfile,
-                                              GNUTLS_X509_FMT_PEM);
-    if(rc < 0) {
-      failf(data, "error reading crl file %s (%s)",
-            data->set.ssl.CRLfile, gnutls_strerror(rc));
-      return CURLE_SSL_CRL_BADFILE;
-    }
-    else
-      infof(data, "found %d CRL in %s\n",
-            rc, data->set.ssl.CRLfile);
-  }
-
-  /* Initialize TLS session as a client */
-  rc = gnutls_init(&conn->ssl[sockindex].session, GNUTLS_CLIENT);
-  if(rc != GNUTLS_E_SUCCESS) {
-    failf(data, "gnutls_init() failed: %d", rc);
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-
-  /* convenient assign */
-  session = conn->ssl[sockindex].session;
-
-  if((0 == Curl_inet_pton(AF_INET, conn->host.name, &addr)) &&
-#ifdef ENABLE_IPV6
-     (0 == Curl_inet_pton(AF_INET6, conn->host.name, &addr)) &&
-#endif
-     sni &&
-     (gnutls_server_name_set(session, GNUTLS_NAME_DNS, conn->host.name,
-                             strlen(conn->host.name)) < 0))
-    infof(data, "WARNING: failed to configure server name indication (SNI) "
-          "TLS extension\n");
-
-  /* Use default priorities */
-  rc = gnutls_set_default_priority(session);
-  if(rc != GNUTLS_E_SUCCESS)
-    return CURLE_SSL_CONNECT_ERROR;
-
-  if(data->set.ssl.version == CURL_SSLVERSION_SSLv3) {
-#ifndef USE_GNUTLS_PRIORITY_SET_DIRECT
-    static const int protocol_priority[] = { GNUTLS_SSL3, 0 };
-    rc = gnutls_protocol_set_priority(session, protocol_priority);
-#else
-    const char *err;
-    /* the combination of the cipher ARCFOUR with SSL 3.0 and TLS 1.0 is not
-       vulnerable to attacks such as the BEAST, why this code now explicitly
-       asks for that
-    */
-    rc = gnutls_priority_set_direct(session,
-                                    "NORMAL:-VERS-TLS-ALL:+VERS-SSL3.0:"
-                                    "-CIPHER-ALL:+ARCFOUR-128",
-                                    &err);
-#endif
-    if(rc != GNUTLS_E_SUCCESS)
-      return CURLE_SSL_CONNECT_ERROR;
-  }
-
-#ifndef USE_GNUTLS_PRIORITY_SET_DIRECT
-  /* Sets the priority on the certificate types supported by gnutls. Priority
-     is higher for types specified before others. After specifying the types
-     you want, you must append a 0. */
-  rc = gnutls_certificate_type_set_priority(session, cert_type_priority);
-  if(rc != GNUTLS_E_SUCCESS)
-    return CURLE_SSL_CONNECT_ERROR;
-#endif
-
-  if(data->set.str[STRING_CERT]) {
-    if(gnutls_certificate_set_x509_key_file(
-         conn->ssl[sockindex].cred,
-         data->set.str[STRING_CERT],
-         data->set.str[STRING_KEY] ?
-         data->set.str[STRING_KEY] : data->set.str[STRING_CERT],
-         do_file_type(data->set.str[STRING_CERT_TYPE]) ) !=
-       GNUTLS_E_SUCCESS) {
-      failf(data, "error reading X.509 key or certificate file");
-      return CURLE_SSL_CONNECT_ERROR;
-    }
-  }
-
-#ifdef USE_TLS_SRP
-  /* put the credentials to the current session */
-  if(data->set.ssl.authtype == CURL_TLSAUTH_SRP) {
-    rc = gnutls_credentials_set(session, GNUTLS_CRD_SRP,
-                                conn->ssl[sockindex].srp_client_cred);
-    if(rc != GNUTLS_E_SUCCESS)
-      failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc));
-  }
-  else
-#endif
-    rc = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
-                                conn->ssl[sockindex].cred);
-
-  /* set the connection handle (file descriptor for the socket) */
-  gnutls_transport_set_ptr(session,
-                           GNUTLS_INT_TO_POINTER_CAST(conn->sock[sockindex]));
-
-  /* register callback functions to send and receive data. */
-  gnutls_transport_set_push_function(session, Curl_gtls_push);
-  gnutls_transport_set_pull_function(session, Curl_gtls_pull);
-
-  /* lowat must be set to zero when using custom push and pull functions. */
-  gnutls_transport_set_lowat(session, 0);
-
-  /* This might be a reconnect, so we check for a session ID in the cache
-     to speed up things */
-
-  if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, &ssl_idsize)) {
-    /* we got a session id, use it! */
-    gnutls_session_set_data(session, ssl_sessionid, ssl_idsize);
-
-    /* Informational message */
-    infof (data, "SSL re-using session ID\n");
-  }
-
-  return CURLE_OK;
-}
-
-static Curl_recv gtls_recv;
-static Curl_send gtls_send;
-
-static CURLcode
-gtls_connect_step3(struct connectdata *conn,
-                   int sockindex)
-{
-  unsigned int cert_list_size;
-  const gnutls_datum *chainp;
-  unsigned int verify_status;
-  gnutls_x509_crt x509_cert,x509_issuer;
-  gnutls_datum issuerp;
-  char certbuf[256]; /* big enough? */
-  size_t size;
-  unsigned int algo;
-  unsigned int bits;
-  time_t certclock;
-  const char *ptr;
-  struct SessionHandle *data = conn->data;
-  gnutls_session session = conn->ssl[sockindex].session;
-  int rc;
-  int incache;
-  void *ssl_sessionid;
-  CURLcode result = CURLE_OK;
-
-  /* This function will return the peer's raw certificate (chain) as sent by
-     the peer. These certificates are in raw format (DER encoded for
-     X.509). In case of a X.509 then a certificate list may be present. The
-     first certificate in the list is the peer's certificate, following the
-     issuer's certificate, then the issuer's issuer etc. */
-
-  chainp = gnutls_certificate_get_peers(session, &cert_list_size);
-  if(!chainp) {
-    if(data->set.ssl.verifypeer ||
-       data->set.ssl.verifyhost ||
-       data->set.ssl.issuercert) {
-#ifdef USE_TLS_SRP
-      if(data->set.ssl.authtype == CURL_TLSAUTH_SRP
-         && data->set.ssl.username != NULL
-         && !data->set.ssl.verifypeer
-         && gnutls_cipher_get(session)) {
-        /* no peer cert, but auth is ok if we have SRP user and cipher and no
-           peer verify */
-      }
-      else {
-#endif
-        failf(data, "failed to get server cert");
-        return CURLE_PEER_FAILED_VERIFICATION;
-#ifdef USE_TLS_SRP
-      }
-#endif
-    }
-    infof(data, "\t common name: WARNING couldn't obtain\n");
-  }
-
-  if(data->set.ssl.verifypeer) {
-    /* This function will try to verify the peer's certificate and return its
-       status (trusted, invalid etc.). The value of status should be one or
-       more of the gnutls_certificate_status_t enumerated elements bitwise
-       or'd. To avoid denial of service attacks some default upper limits
-       regarding the certificate key size and chain size are set. To override
-       them use gnutls_certificate_set_verify_limits(). */
-
-    rc = gnutls_certificate_verify_peers2(session, &verify_status);
-    if(rc < 0) {
-      failf(data, "server cert verify failed: %d", rc);
-      return CURLE_SSL_CONNECT_ERROR;
-    }
-
-    /* verify_status is a bitmask of gnutls_certificate_status bits */
-    if(verify_status & GNUTLS_CERT_INVALID) {
-      if(data->set.ssl.verifypeer) {
-        failf(data, "server certificate verification failed. CAfile: %s "
-              "CRLfile: %s", data->set.ssl.CAfile?data->set.ssl.CAfile:"none",
-              data->set.ssl.CRLfile?data->set.ssl.CRLfile:"none");
-        return CURLE_SSL_CACERT;
-      }
-      else
-        infof(data, "\t server certificate verification FAILED\n");
-    }
-    else
-      infof(data, "\t server certificate verification OK\n");
-  }
-  else {
-    infof(data, "\t server certificate verification SKIPPED\n");
-    goto after_server_cert_verification;
-  }
-
-  /* initialize an X.509 certificate structure. */
-  gnutls_x509_crt_init(&x509_cert);
-
-  /* convert the given DER or PEM encoded Certificate to the native
-     gnutls_x509_crt_t format */
-  gnutls_x509_crt_import(x509_cert, chainp, GNUTLS_X509_FMT_DER);
-
-  if(data->set.ssl.issuercert) {
-    gnutls_x509_crt_init(&x509_issuer);
-    issuerp = load_file(data->set.ssl.issuercert);
-    gnutls_x509_crt_import(x509_issuer, &issuerp, GNUTLS_X509_FMT_PEM);
-    rc = gnutls_x509_crt_check_issuer(x509_cert,x509_issuer);
-    unload_file(issuerp);
-    if(rc <= 0) {
-      failf(data, "server certificate issuer check failed (IssuerCert: %s)",
-            data->set.ssl.issuercert?data->set.ssl.issuercert:"none");
-      return CURLE_SSL_ISSUER_ERROR;
-    }
-    infof(data,"\t server certificate issuer check OK (Issuer Cert: %s)\n",
-          data->set.ssl.issuercert?data->set.ssl.issuercert:"none");
-  }
-
-  size=sizeof(certbuf);
-  rc = gnutls_x509_crt_get_dn_by_oid(x509_cert, GNUTLS_OID_X520_COMMON_NAME,
-                                     0, /* the first and only one */
-                                     FALSE,
-                                     certbuf,
-                                     &size);
-  if(rc) {
-    infof(data, "error fetching CN from cert:%s\n",
-          gnutls_strerror(rc));
-  }
-
-  /* This function will check if the given certificate's subject matches the
-     given hostname. This is a basic implementation of the matching described
-     in RFC2818 (HTTPS), which takes into account wildcards, and the subject
-     alternative name PKIX extension. Returns non zero on success, and zero on
-     failure. */
-  rc = gnutls_x509_crt_check_hostname(x509_cert, conn->host.name);
-
-  if(!rc) {
-    if(data->set.ssl.verifyhost) {
-      failf(data, "SSL: certificate subject name (%s) does not match "
-            "target host name '%s'", certbuf, conn->host.dispname);
-      gnutls_x509_crt_deinit(x509_cert);
-      return CURLE_PEER_FAILED_VERIFICATION;
-    }
-    else
-      infof(data, "\t common name: %s (does not match '%s')\n",
-            certbuf, conn->host.dispname);
-  }
-  else
-    infof(data, "\t common name: %s (matched)\n", certbuf);
-
-  /* Check for time-based validity */
-  certclock = gnutls_x509_crt_get_expiration_time(x509_cert);
-
-  if(certclock == (time_t)-1) {
-    failf(data, "server cert expiration date verify failed");
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-
-  if(certclock < time(NULL)) {
-    if(data->set.ssl.verifypeer) {
-      failf(data, "server certificate expiration date has passed.");
-      return CURLE_PEER_FAILED_VERIFICATION;
-    }
-    else
-      infof(data, "\t server certificate expiration date FAILED\n");
-  }
-  else
-    infof(data, "\t server certificate expiration date OK\n");
-
-  certclock = gnutls_x509_crt_get_activation_time(x509_cert);
-
-  if(certclock == (time_t)-1) {
-    failf(data, "server cert activation date verify failed");
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-
-  if(certclock > time(NULL)) {
-    if(data->set.ssl.verifypeer) {
-      failf(data, "server certificate not activated yet.");
-      return CURLE_PEER_FAILED_VERIFICATION;
-    }
-    else
-      infof(data, "\t server certificate activation date FAILED\n");
-  }
-  else
-    infof(data, "\t server certificate activation date OK\n");
-
-  /* Show:
-
-  - ciphers used
-  - subject
-  - start date
-  - expire date
-  - common name
-  - issuer
-
-  */
-
-  /* public key algorithm's parameters */
-  algo = gnutls_x509_crt_get_pk_algorithm(x509_cert, &bits);
-  infof(data, "\t certificate public key: %s\n",
-        gnutls_pk_algorithm_get_name(algo));
-
-  /* version of the X.509 certificate. */
-  infof(data, "\t certificate version: #%d\n",
-        gnutls_x509_crt_get_version(x509_cert));
-
-
-  size = sizeof(certbuf);
-  gnutls_x509_crt_get_dn(x509_cert, certbuf, &size);
-  infof(data, "\t subject: %s\n", certbuf);
-
-  certclock = gnutls_x509_crt_get_activation_time(x509_cert);
-  showtime(data, "start date", certclock);
-
-  certclock = gnutls_x509_crt_get_expiration_time(x509_cert);
-  showtime(data, "expire date", certclock);
-
-  size = sizeof(certbuf);
-  gnutls_x509_crt_get_issuer_dn(x509_cert, certbuf, &size);
-  infof(data, "\t issuer: %s\n", certbuf);
-
-  gnutls_x509_crt_deinit(x509_cert);
-
-after_server_cert_verification:
-
-  /* compression algorithm (if any) */
-  ptr = gnutls_compression_get_name(gnutls_compression_get(session));
-  /* the *_get_name() says "NULL" if GNUTLS_COMP_NULL is returned */
-  infof(data, "\t compression: %s\n", ptr);
-
-  /* the name of the cipher used. ie 3DES. */
-  ptr = gnutls_cipher_get_name(gnutls_cipher_get(session));
-  infof(data, "\t cipher: %s\n", ptr);
-
-  /* the MAC algorithms name. ie SHA1 */
-  ptr = gnutls_mac_get_name(gnutls_mac_get(session));
-  infof(data, "\t MAC: %s\n", ptr);
-
-  conn->ssl[sockindex].state = ssl_connection_complete;
-  conn->recv[sockindex] = gtls_recv;
-  conn->send[sockindex] = gtls_send;
-
-  {
-    /* we always unconditionally get the session id here, as even if we
-       already got it from the cache and asked to use it in the connection, it
-       might've been rejected and then a new one is in use now and we need to
-       detect that. */
-    void *connect_sessionid;
-    size_t connect_idsize;
-
-    /* get the session ID data size */
-    gnutls_session_get_data(session, NULL, &connect_idsize);
-    connect_sessionid = malloc(connect_idsize); /* get a buffer for it */
-
-    if(connect_sessionid) {
-      /* extract session ID to the allocated buffer */
-      gnutls_session_get_data(session, connect_sessionid, &connect_idsize);
-
-      incache = !(Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL));
-      if(incache) {
-        /* there was one before in the cache, so instead of risking that the
-           previous one was rejected, we just kill that and store the new */
-        Curl_ssl_delsessionid(conn, ssl_sessionid);
-      }
-
-      /* store this session id */
-      result = Curl_ssl_addsessionid(conn, connect_sessionid, connect_idsize);
-      if(result) {
-        free(connect_sessionid);
-        result = CURLE_OUT_OF_MEMORY;
-      }
-    }
-    else
-      result = CURLE_OUT_OF_MEMORY;
-  }
-
-  return result;
-}
-
-
-/*
- * This function is called after the TCP connect has completed. Setup the TLS
- * layer and do all necessary magic.
- */
-/* We use connssl->connecting_state to keep track of the connection status;
-   there are three states: 'ssl_connect_1' (not started yet or complete),
-   'ssl_connect_2_reading' (waiting for data from server), and
-   'ssl_connect_2_writing' (waiting to be able to write).
- */
-static CURLcode
-gtls_connect_common(struct connectdata *conn,
-                    int sockindex,
-                    bool nonblocking,
-                    bool *done)
-{
-  int rc;
-  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-
-  /* Initiate the connection, if not already done */
-  if(ssl_connect_1==connssl->connecting_state) {
-    rc = gtls_connect_step1 (conn, sockindex);
-    if(rc)
-      return rc;
-  }
-
-  rc = handshake(conn, sockindex, TRUE, nonblocking);
-  if(rc)
-    /* handshake() sets its own error message with failf() */
-    return rc;
-
-  /* Finish connecting once the handshake is done */
-  if(ssl_connect_1==connssl->connecting_state) {
-    rc = gtls_connect_step3(conn, sockindex);
-    if(rc)
-      return rc;
-  }
-
-  *done = ssl_connect_1==connssl->connecting_state;
-
-  return CURLE_OK;
-}
-
-CURLcode
-Curl_gtls_connect_nonblocking(struct connectdata *conn,
-                              int sockindex,
-                              bool *done)
-{
-  return gtls_connect_common(conn, sockindex, TRUE, done);
-}
-
-CURLcode
-Curl_gtls_connect(struct connectdata *conn,
-                  int sockindex)
-
-{
-  CURLcode retcode;
-  bool done = FALSE;
-
-  retcode = gtls_connect_common(conn, sockindex, FALSE, &done);
-  if(retcode)
-    return retcode;
-
-  DEBUGASSERT(done);
-
-  return CURLE_OK;
-}
-
-static ssize_t gtls_send(struct connectdata *conn,
-                         int sockindex,
-                         const void *mem,
-                         size_t len,
-                         CURLcode *curlcode)
-{
-  ssize_t rc = gnutls_record_send(conn->ssl[sockindex].session, mem, len);
-
-  if(rc < 0 ) {
-    *curlcode = (rc == GNUTLS_E_AGAIN)
-      ? CURLE_AGAIN
-      : CURLE_SEND_ERROR;
-
-    rc = -1;
-  }
-
-  return rc;
-}
-
-void Curl_gtls_close_all(struct SessionHandle *data)
-{
-  /* FIX: make the OpenSSL code more generic and use parts of it here */
-  (void)data;
-}
-
-static void close_one(struct connectdata *conn,
-                      int idx)
-{
-  if(conn->ssl[idx].session) {
-    gnutls_bye(conn->ssl[idx].session, GNUTLS_SHUT_RDWR);
-    gnutls_deinit(conn->ssl[idx].session);
-    conn->ssl[idx].session = NULL;
-  }
-  if(conn->ssl[idx].cred) {
-    gnutls_certificate_free_credentials(conn->ssl[idx].cred);
-    conn->ssl[idx].cred = NULL;
-  }
-#ifdef USE_TLS_SRP
-  if(conn->ssl[idx].srp_client_cred) {
-    gnutls_srp_free_client_credentials(conn->ssl[idx].srp_client_cred);
-    conn->ssl[idx].srp_client_cred = NULL;
-  }
-#endif
-}
-
-void Curl_gtls_close(struct connectdata *conn, int sockindex)
-{
-  close_one(conn, sockindex);
-}
-
-/*
- * This function is called to shut down the SSL layer but keep the
- * socket open (CCC - Clear Command Channel)
- */
-int Curl_gtls_shutdown(struct connectdata *conn, int sockindex)
-{
-  ssize_t result;
-  int retval = 0;
-  struct SessionHandle *data = conn->data;
-  int done = 0;
-  char buf[120];
-
-  /* This has only been tested on the proftpd server, and the mod_tls code
-     sends a close notify alert without waiting for a close notify alert in
-     response. Thus we wait for a close notify alert from the server, but
-     we do not send one. Let's hope other servers do the same... */
-
-  if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE)
-      gnutls_bye(conn->ssl[sockindex].session, GNUTLS_SHUT_WR);
-
-  if(conn->ssl[sockindex].session) {
-    while(!done) {
-      int what = Curl_socket_ready(conn->sock[sockindex],
-                                   CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT);
-      if(what > 0) {
-        /* Something to read, let's do it and hope that it is the close
-           notify alert from the server */
-        result = gnutls_record_recv(conn->ssl[sockindex].session,
-                                    buf, sizeof(buf));
-        switch(result) {
-        case 0:
-          /* This is the expected response. There was no data but only
-             the close notify alert */
-          done = 1;
-          break;
-        case GNUTLS_E_AGAIN:
-        case GNUTLS_E_INTERRUPTED:
-          infof(data, "GNUTLS_E_AGAIN || GNUTLS_E_INTERRUPTED\n");
-          break;
-        default:
-          retval = -1;
-          done = 1;
-          break;
-        }
-      }
-      else if(0 == what) {
-        /* timeout */
-        failf(data, "SSL shutdown timeout");
-        done = 1;
-        break;
-      }
-      else {
-        /* anything that gets here is fatally bad */
-        failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
-        retval = -1;
-        done = 1;
-      }
-    }
-    gnutls_deinit(conn->ssl[sockindex].session);
-  }
-  gnutls_certificate_free_credentials(conn->ssl[sockindex].cred);
-
-#ifdef USE_TLS_SRP
-  if(data->set.ssl.authtype == CURL_TLSAUTH_SRP
-     && data->set.ssl.username != NULL)
-    gnutls_srp_free_client_credentials(conn->ssl[sockindex].srp_client_cred);
-#endif
-
-  conn->ssl[sockindex].cred = NULL;
-  conn->ssl[sockindex].session = NULL;
-
-  return retval;
-}
-
-static ssize_t gtls_recv(struct connectdata *conn, /* connection data */
-                         int num,                  /* socketindex */
-                         char *buf,                /* store read data here */
-                         size_t buffersize,        /* max amount to read */
-                         CURLcode *curlcode)
-{
-  ssize_t ret;
-
-  ret = gnutls_record_recv(conn->ssl[num].session, buf, buffersize);
-  if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) {
-    *curlcode = CURLE_AGAIN;
-    return -1;
-  }
-
-  if(ret == GNUTLS_E_REHANDSHAKE) {
-    /* BLOCKING call, this is bad but a work-around for now. Fixing this "the
-       proper way" takes a whole lot of work. */
-    CURLcode rc = handshake(conn, num, FALSE, FALSE);
-    if(rc)
-      /* handshake() writes error message on its own */
-      *curlcode = rc;
-    else
-      *curlcode = CURLE_AGAIN; /* then return as if this was a wouldblock */
-    return -1;
-  }
-
-  if(ret < 0) {
-    failf(conn->data, "GnuTLS recv error (%d): %s",
-          (int)ret, gnutls_strerror((int)ret));
-    *curlcode = CURLE_RECV_ERROR;
-    return -1;
-  }
-
-  return ret;
-}
-
-void Curl_gtls_session_free(void *ptr)
-{
-  free(ptr);
-}
-
-size_t Curl_gtls_version(char *buffer, size_t size)
-{
-  return snprintf(buffer, size, "GnuTLS/%s", gnutls_check_version(NULL));
-}
-
-int Curl_gtls_seed(struct SessionHandle *data)
-{
-  /* we have the "SSL is seeded" boolean static to prevent multiple
-     time-consuming seedings in vain */
-  static bool ssl_seeded = FALSE;
-
-  /* Quickly add a bit of entropy */
-#ifndef USE_GNUTLS_NETTLE
-  gcry_fast_random_poll();
-#endif
-
-  if(!ssl_seeded || data->set.str[STRING_SSL_RANDOM_FILE] ||
-     data->set.str[STRING_SSL_EGDSOCKET]) {
-
-    /* TODO: to a good job seeding the RNG
-       This may involve the gcry_control function and these options:
-       GCRYCTL_SET_RANDOM_SEED_FILE
-       GCRYCTL_SET_RNDEGD_SOCKET
-    */
-    ssl_seeded = TRUE;
-  }
-  return 0;
-}
-
-void Curl_gtls_random(struct SessionHandle *data,
-                      unsigned char *entropy,
-                      size_t length)
-{
-#if defined(USE_GNUTLS_NETTLE)
-  (void)data;
-  gnutls_rnd(GNUTLS_RND_RANDOM, entropy, length);
-#elif defined(USE_GNUTLS)
-  Curl_gtls_seed(data); /* Initiate the seed if not already done */
-  gcry_randomize(entropy, length, GCRY_STRONG_RANDOM);
-#endif
-}
-
-void Curl_gtls_md5sum(unsigned char *tmp, /* input */
-                      size_t tmplen,
-                      unsigned char *md5sum, /* output */
-                      size_t md5len)
-{
-#if defined(USE_GNUTLS_NETTLE)
-  struct md5_ctx MD5pw;
-  md5_init(&MD5pw);
-  md5_update(&MD5pw, tmplen, tmp);
-  md5_digest(&MD5pw, md5len, md5sum);
-#elif defined(USE_GNUTLS)
-  gcry_md_hd_t MD5pw;
-  gcry_md_open(&MD5pw, GCRY_MD_MD5, 0);
-  gcry_md_write(MD5pw, tmp, tmplen);
-  memcpy(md5sum, gcry_md_read (MD5pw, 0), md5len);
-  gcry_md_close(MD5pw);
-#endif
-}
-
-#endif /* USE_GNUTLS */
diff --git a/lib/hash.c b/lib/hash.c
deleted file mode 100644 (file)
index 732dbcf..0000000
+++ /dev/null
@@ -1,400 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#include "curl_hash.h"
-#include "curl_llist.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-static void
-hash_element_dtor(void *user, void *element)
-{
-  struct curl_hash *h = (struct curl_hash *) user;
-  struct curl_hash_element *e = (struct curl_hash_element *) element;
-
-  Curl_safefree(e->key);
-
-  if(e->ptr) {
-    h->dtor(e->ptr);
-    e->ptr = NULL;
-  }
-
-  e->key_len = 0;
-
-  free(e);
-}
-
-/* return 1 on error, 0 is fine */
-int
-Curl_hash_init(struct curl_hash *h,
-               int slots,
-               hash_function hfunc,
-               comp_function comparator,
-               curl_hash_dtor dtor)
-{
-  int i;
-
-  if(!slots || !hfunc || !comparator ||!dtor) {
-    return 1; /* failure */
-  }
-
-  h->hash_func = hfunc;
-  h->comp_func = comparator;
-  h->dtor = dtor;
-  h->size = 0;
-  h->slots = slots;
-
-  h->table = malloc(slots * sizeof(struct curl_llist *));
-  if(h->table) {
-    for(i = 0; i < slots; ++i) {
-      h->table[i] = Curl_llist_alloc((curl_llist_dtor) hash_element_dtor);
-      if(!h->table[i]) {
-        while(i--) {
-          Curl_llist_destroy(h->table[i], NULL);
-          h->table[i] = NULL;
-        }
-        free(h->table);
-        h->table = NULL;
-        h->slots = 0;
-        return 1; /* failure */
-      }
-    }
-    return 0; /* fine */
-  }
-  else {
-    h->slots = 0;
-    return 1; /* failure */
-  }
-}
-
-struct curl_hash *
-Curl_hash_alloc(int slots,
-                hash_function hfunc,
-                comp_function comparator,
-                curl_hash_dtor dtor)
-{
-  struct curl_hash *h;
-
-  if(!slots || !hfunc || !comparator ||!dtor) {
-    return NULL; /* failure */
-  }
-
-  h = malloc(sizeof(struct curl_hash));
-  if(h) {
-    if(Curl_hash_init(h, slots, hfunc, comparator, dtor)) {
-      /* failure */
-      free(h);
-      h = NULL;
-    }
-  }
-
-  return h;
-}
-
-
-
-static struct curl_hash_element *
-mk_hash_element(const void *key, size_t key_len, const void *p)
-{
-  struct curl_hash_element *he = malloc(sizeof(struct curl_hash_element));
-
-  if(he) {
-    void *dupkey = malloc(key_len);
-    if(dupkey) {
-      /* copy the key */
-      memcpy(dupkey, key, key_len);
-
-      he->key = dupkey;
-      he->key_len = key_len;
-      he->ptr = (void *) p;
-    }
-    else {
-      /* failed to duplicate the key, free memory and fail */
-      free(he);
-      he = NULL;
-    }
-  }
-  return he;
-}
-
-#define FETCH_LIST(x,y,z) x->table[x->hash_func(y, z, x->slots)]
-
-/* Insert the data in the hash. If there already was a match in the hash,
- * that data is replaced.
- *
- * @unittest: 1305
- */
-void *
-Curl_hash_add(struct curl_hash *h, void *key, size_t key_len, void *p)
-{
-  struct curl_hash_element  *he;
-  struct curl_llist_element *le;
-  struct curl_llist *l = FETCH_LIST (h, key, key_len);
-
-  for(le = l->head; le; le = le->next) {
-    he = (struct curl_hash_element *) le->ptr;
-    if(h->comp_func(he->key, he->key_len, key, key_len)) {
-      Curl_llist_remove(l, le, (void *)h);
-      --h->size;
-      break;
-    }
-  }
-
-  he = mk_hash_element(key, key_len, p);
-  if(he) {
-    if(Curl_llist_insert_next(l, l->tail, he)) {
-      ++h->size;
-      return p; /* return the new entry */
-    }
-    /*
-     * Couldn't insert it, destroy the 'he' element and the key again. We
-     * don't call hash_element_dtor() since that would also call the
-     * "destructor" for the actual data 'p'. When we fail, we shall not touch
-     * that data.
-     */
-    free(he->key);
-    free(he);
-  }
-
-  return NULL; /* failure */
-}
-
-/* remove the identified hash entry, returns non-zero on failure */
-int Curl_hash_delete(struct curl_hash *h, void *key, size_t key_len)
-{
-  struct curl_llist_element *le;
-  struct curl_hash_element  *he;
-  struct curl_llist *l = FETCH_LIST(h, key, key_len);
-
-  for(le = l->head; le; le = le->next) {
-    he = le->ptr;
-    if(h->comp_func(he->key, he->key_len, key, key_len)) {
-      Curl_llist_remove(l, le, (void *) h);
-      --h->size;
-      return 0;
-    }
-  }
-  return 1;
-}
-
-void *
-Curl_hash_pick(struct curl_hash *h, void *key, size_t key_len)
-{
-  struct curl_llist_element *le;
-  struct curl_hash_element  *he;
-  struct curl_llist *l;
-
-  if(h) {
-    l = FETCH_LIST(h, key, key_len);
-    for(le = l->head; le; le = le->next) {
-      he = le->ptr;
-      if(h->comp_func(he->key, he->key_len, key, key_len)) {
-        return he->ptr;
-      }
-    }
-  }
-
-  return NULL;
-}
-
-#if defined(DEBUGBUILD) && defined(AGGRESIVE_TEST)
-void
-Curl_hash_apply(curl_hash *h, void *user,
-                void (*cb)(void *user, void *ptr))
-{
-  struct curl_llist_element  *le;
-  int                  i;
-
-  for(i = 0; i < h->slots; ++i) {
-    for(le = (h->table[i])->head;
-        le;
-        le = le->next) {
-      curl_hash_element *el = le->ptr;
-      cb(user, el->ptr);
-    }
-  }
-}
-#endif
-
-void
-Curl_hash_clean(struct curl_hash *h)
-{
-  int i;
-
-  for(i = 0; i < h->slots; ++i) {
-    Curl_llist_destroy(h->table[i], (void *) h);
-    h->table[i] = NULL;
-  }
-
-  Curl_safefree(h->table);
-  h->size = 0;
-  h->slots = 0;
-}
-
-void
-Curl_hash_clean_with_criterium(struct curl_hash *h, void *user,
-                               int (*comp)(void *, void *))
-{
-  struct curl_llist_element *le;
-  struct curl_llist_element *lnext;
-  struct curl_llist *list;
-  int i;
-
-  if(!h)
-    return;
-
-  for(i = 0; i < h->slots; ++i) {
-    list = h->table[i];
-    le = list->head; /* get first list entry */
-    while(le) {
-      struct curl_hash_element *he = le->ptr;
-      lnext = le->next;
-      /* ask the callback function if we shall remove this entry or not */
-      if(comp(user, he->ptr)) {
-        Curl_llist_remove(list, le, (void *) h);
-        --h->size; /* one less entry in the hash now */
-      }
-      le = lnext;
-    }
-  }
-}
-
-void
-Curl_hash_destroy(struct curl_hash *h)
-{
-  if(!h)
-    return;
-
-  Curl_hash_clean(h);
-
-  free(h);
-}
-
-size_t Curl_hash_str(void* key, size_t key_length, size_t slots_num)
-{
-  const char* key_str = (const char *) key;
-  const char *end = key_str + key_length;
-  unsigned long h = 5381;
-
-  while(key_str < end) {
-    h += h << 5;
-    h ^= (unsigned long) *key_str++;
-  }
-
-  return (h % slots_num);
-}
-
-size_t Curl_str_key_compare(void*k1, size_t key1_len, void*k2, size_t key2_len)
-{
-  char *key1 = (char *)k1;
-  char *key2 = (char *)k2;
-
-  if(key1_len == key2_len &&
-      *key1 == *key2 &&
-      memcmp(key1, key2, key1_len) == 0) {
-    return 1;
-  }
-
-  return 0;
-}
-
-void Curl_hash_start_iterate(struct curl_hash *hash,
-                             struct curl_hash_iterator *iter)
-{
-  iter->hash = hash;
-  iter->slot_index = 0;
-  iter->current_element = NULL;
-}
-
-struct curl_hash_element *
-Curl_hash_next_element(struct curl_hash_iterator *iter)
-{
-  int i;
-  struct curl_hash *h = iter->hash;
-
-  /* Get the next element in the current list, if any */
-  if(iter->current_element)
-    iter->current_element = iter->current_element->next;
-
-  /* If we have reached the end of the list, find the next one */
-  if(!iter->current_element) {
-    for(i = iter->slot_index;i < h->slots;i++) {
-      if(h->table[i]->head) {
-        iter->current_element = h->table[i]->head;
-        iter->slot_index = i+1;
-        break;
-      }
-    }
-  }
-
-  if(iter->current_element) {
-    struct curl_hash_element *he = iter->current_element->ptr;
-    return he;
-  }
-  else {
-    iter->current_element = NULL;
-    return NULL;
-  }
-}
-
-#if 0 /* useful function for debugging hashes and their contents */
-void Curl_hash_print(struct curl_hash *h,
-                     void (*func)(void *))
-{
-  struct curl_hash_iterator iter;
-  struct curl_hash_element *he;
-  int last_index = -1;
-
-  if(!h)
-    return;
-
-  fprintf(stderr, "=Hash dump=\n");
-
-  Curl_hash_start_iterate(h, &iter);
-
-  he = Curl_hash_next_element(&iter);
-  while(he) {
-    if(iter.slot_index != last_index) {
-      fprintf(stderr, "index %d:", iter.slot_index);
-      if(last_index >= 0) {
-        fprintf(stderr, "\n");
-      }
-      last_index = iter.slot_index;
-    }
-
-    if(func)
-      func(he->ptr);
-    else
-      fprintf(stderr, " [%p]", he->ptr);
-
-    he = Curl_hash_next_element(&iter);
-  }
-  fprintf(stderr, "\n");
-}
-#endif
diff --git a/lib/hmac.c b/lib/hmac.c
deleted file mode 100644 (file)
index 692d279..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * RFC2104 Keyed-Hashing for Message Authentication
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifndef CURL_DISABLE_CRYPTO_AUTH
-
-#include "curl_hmac.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/*
- * Generic HMAC algorithm.
- *
- *   This module computes HMAC digests based on any hash function. Parameters
- * and computing procedures are set-up dynamically at HMAC computation
- * context initialisation.
- */
-
-static const unsigned char hmac_ipad = 0x36;
-static const unsigned char hmac_opad = 0x5C;
-
-
-
-HMAC_context *
-Curl_HMAC_init(const HMAC_params * hashparams,
-               const unsigned char * key,
-               unsigned int keylen)
-{
-  size_t i;
-  HMAC_context * ctxt;
-  unsigned char * hkey;
-  unsigned char b;
-
-  /* Create HMAC context. */
-  i = sizeof *ctxt + 2 * hashparams->hmac_ctxtsize +
-    hashparams->hmac_resultlen;
-  ctxt = malloc(i);
-
-  if(!ctxt)
-    return ctxt;
-
-  ctxt->hmac_hash = hashparams;
-  ctxt->hmac_hashctxt1 = (void *) (ctxt + 1);
-  ctxt->hmac_hashctxt2 = (void *) ((char *) ctxt->hmac_hashctxt1 +
-      hashparams->hmac_ctxtsize);
-
-  /* If the key is too long, replace it by its hash digest. */
-  if(keylen > hashparams->hmac_maxkeylen) {
-    (*hashparams->hmac_hinit)(ctxt->hmac_hashctxt1);
-    (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt1, key, keylen);
-    hkey = (unsigned char *) ctxt->hmac_hashctxt2 + hashparams->hmac_ctxtsize;
-    (*hashparams->hmac_hfinal)(hkey, ctxt->hmac_hashctxt1);
-    key = hkey;
-    keylen = hashparams->hmac_resultlen;
-  }
-
-  /* Prime the two hash contexts with the modified key. */
-  (*hashparams->hmac_hinit)(ctxt->hmac_hashctxt1);
-  (*hashparams->hmac_hinit)(ctxt->hmac_hashctxt2);
-
-  for(i = 0; i < keylen; i++) {
-    b = (unsigned char)(*key ^ hmac_ipad);
-    (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt1, &b, 1);
-    b = (unsigned char)(*key++ ^ hmac_opad);
-    (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt2, &b, 1);
-  }
-
-  for(; i < hashparams->hmac_maxkeylen; i++) {
-    (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt1, &hmac_ipad, 1);
-    (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt2, &hmac_opad, 1);
-  }
-
-  /* Done, return pointer to HMAC context. */
-  return ctxt;
-}
-
-int Curl_HMAC_update(HMAC_context * ctxt,
-                     const unsigned char * data,
-                     unsigned int len)
-{
-  /* Update first hash calculation. */
-  (*ctxt->hmac_hash->hmac_hupdate)(ctxt->hmac_hashctxt1, data, len);
-  return 0;
-}
-
-
-int Curl_HMAC_final(HMAC_context * ctxt, unsigned char * result)
-{
-  const HMAC_params * hashparams = ctxt->hmac_hash;
-
-  /* Do not get result if called with a null parameter: only release
-     storage. */
-
-  if(!result)
-    result = (unsigned char *) ctxt->hmac_hashctxt2 +
-     ctxt->hmac_hash->hmac_ctxtsize;
-
-  (*hashparams->hmac_hfinal)(result, ctxt->hmac_hashctxt1);
-  (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt2,
-   result, hashparams->hmac_resultlen);
-  (*hashparams->hmac_hfinal)(result, ctxt->hmac_hashctxt2);
-  free((char *) ctxt);
-  return 0;
-}
-
-#endif /* CURL_DISABLE_CRYPTO_AUTH */
diff --git a/lib/hostasyn.c b/lib/hostasyn.c
deleted file mode 100644 (file)
index 0097b6c..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef __VMS
-#include <in.h>
-#include <inet.h>
-#endif
-
-#ifdef HAVE_PROCESS_H
-#include <process.h>
-#endif
-
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_hostip.h"
-#include "curl_hash.h"
-#include "curl_share.h"
-#include "curl_strerror.h"
-#include "curl_url.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/***********************************************************************
- * Only for builds using asynchronous name resolves
- **********************************************************************/
-#ifdef CURLRES_ASYNCH
-
-/*
- * Curl_addrinfo_callback() gets called by ares, gethostbyname_thread()
- * or getaddrinfo_thread() when we got the name resolved (or not!).
- *
- * If the status argument is CURL_ASYNC_SUCCESS, this function takes
- * ownership of the Curl_addrinfo passed, storing the resolved data
- * in the DNS cache.
- *
- * The storage operation locks and unlocks the DNS cache.
- */
-CURLcode Curl_addrinfo_callback(struct connectdata *conn,
-                                int status,
-                                struct Curl_addrinfo *ai)
-{
-  struct Curl_dns_entry *dns = NULL;
-  CURLcode rc = CURLE_OK;
-
-  conn->async.status = status;
-
-  if(CURL_ASYNC_SUCCESS == status) {
-    if(ai) {
-      struct SessionHandle *data = conn->data;
-
-      if(data->share)
-        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
-      dns = Curl_cache_addr(data, ai,
-                            conn->async.hostname,
-                            conn->async.port);
-      if(!dns) {
-        /* failed to store, cleanup and return error */
-        Curl_freeaddrinfo(ai);
-        rc = CURLE_OUT_OF_MEMORY;
-      }
-
-      if(data->share)
-        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
-    }
-    else {
-      rc = CURLE_OUT_OF_MEMORY;
-    }
-  }
-
-  conn->async.dns = dns;
-
- /* Set async.done TRUE last in this function since it may be used multi-
-    threaded and once this is TRUE the other thread may read fields from the
-    async struct */
-  conn->async.done = TRUE;
-
-  /* ipv4: The input hostent struct will be freed by ares when we return from
-     this function */
-  return rc;
-}
-
-/* Call this function after Curl_connect() has returned async=TRUE and
-   then a successful name resolve has been received.
-
-   Note: this function disconnects and frees the conn data in case of
-   resolve failure */
-CURLcode Curl_async_resolved(struct connectdata *conn,
-                             bool *protocol_done)
-{
-  CURLcode code;
-
-  if(conn->async.dns) {
-    conn->dns_entry = conn->async.dns;
-    conn->async.dns = NULL;
-  }
-
-  code = Curl_setup_conn(conn, protocol_done);
-
-  if(code)
-    /* We're not allowed to return failure with memory left allocated
-       in the connectdata struct, free those here */
-    Curl_disconnect(conn, FALSE); /* close the connection */
-
-  return code;
-}
-
-/*
- * Curl_getaddrinfo() is the generic low-level name resolve API within this
- * source file. There are several versions of this function - for different
- * name resolve layers (selected at build-time). They all take this same set
- * of arguments
- */
-Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
-                                const char *hostname,
-                                int port,
-                                int *waitp)
-{
-  return Curl_resolver_getaddrinfo(conn, hostname, port, waitp);
-}
-
-#endif /* CURLRES_ASYNCH */
diff --git a/lib/hostcheck.c b/lib/hostcheck.c
deleted file mode 100644 (file)
index a5bf8b0..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#if defined(USE_SSLEAY) || defined(USE_AXTLS)
-/* these two backends use functions from this file */
-
-#include "curl_hostcheck.h"
-#include "curl_rawstr.h"
-
-/*
- * Match a hostname against a wildcard pattern.
- * E.g.
- *  "foo.host.com" matches "*.host.com".
- *
- * We use the matching rule described in RFC6125, section 6.4.3.
- * http://tools.ietf.org/html/rfc6125#section-6.4.3
- */
-
-static int hostmatch(const char *hostname, const char *pattern)
-{
-  const char *pattern_label_end, *pattern_wildcard, *hostname_label_end;
-  int wildcard_enabled;
-  size_t prefixlen, suffixlen;
-  pattern_wildcard = strchr(pattern, '*');
-  if(pattern_wildcard == NULL)
-    return Curl_raw_equal(pattern, hostname) ?
-      CURL_HOST_MATCH : CURL_HOST_NOMATCH;
-
-  /* We require at least 2 dots in pattern to avoid too wide wildcard
-     match. */
-  wildcard_enabled = 1;
-  pattern_label_end = strchr(pattern, '.');
-  if(pattern_label_end == NULL || strchr(pattern_label_end+1, '.') == NULL ||
-     pattern_wildcard > pattern_label_end ||
-     Curl_raw_nequal(pattern, "xn--", 4)) {
-    wildcard_enabled = 0;
-  }
-  if(!wildcard_enabled)
-    return Curl_raw_equal(pattern, hostname) ?
-      CURL_HOST_MATCH : CURL_HOST_NOMATCH;
-
-  hostname_label_end = strchr(hostname, '.');
-  if(hostname_label_end == NULL ||
-     !Curl_raw_equal(pattern_label_end, hostname_label_end))
-    return CURL_HOST_NOMATCH;
-
-  /* The wildcard must match at least one character, so the left-most
-     label of the hostname is at least as large as the left-most label
-     of the pattern. */
-  if(hostname_label_end - hostname < pattern_label_end - pattern)
-    return CURL_HOST_NOMATCH;
-
-  prefixlen = pattern_wildcard - pattern;
-  suffixlen = pattern_label_end - (pattern_wildcard+1);
-  return Curl_raw_nequal(pattern, hostname, prefixlen) &&
-    Curl_raw_nequal(pattern_wildcard+1, hostname_label_end - suffixlen,
-                    suffixlen) ?
-    CURL_HOST_MATCH : CURL_HOST_NOMATCH;
-}
-
-int Curl_cert_hostcheck(const char *match_pattern, const char *hostname)
-{
-  if(!match_pattern || !*match_pattern ||
-      !hostname || !*hostname) /* sanity check */
-    return 0;
-
-  if(Curl_raw_equal(hostname, match_pattern)) /* trivial case */
-    return 1;
-
-  if(hostmatch(hostname,match_pattern) == CURL_HOST_MATCH)
-    return 1;
-  return 0;
-}
-
-#endif /* SSLEAY or AXTLS */
diff --git a/lib/hostip.c b/lib/hostip.c
deleted file mode 100644 (file)
index 7cc51f8..0000000
+++ /dev/null
@@ -1,820 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef __VMS
-#include <in.h>
-#include <inet.h>
-#endif
-
-#ifdef HAVE_SETJMP_H
-#include <setjmp.h>
-#endif
-#ifdef HAVE_SIGNAL_H
-#include <signal.h>
-#endif
-
-#ifdef HAVE_PROCESS_H
-#include <process.h>
-#endif
-
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_hostip.h"
-#include "curl_hash.h"
-#include "curl_share.h"
-#include "curl_strerror.h"
-#include "curl_url.h"
-#include "curl_inet_ntop.h"
-#include "curl_warnless.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-#if defined(CURLRES_SYNCH) && \
-    defined(HAVE_ALARM) && defined(SIGALRM) && defined(HAVE_SIGSETJMP)
-/* alarm-based timeouts can only be used with all the dependencies satisfied */
-#define USE_ALARM_TIMEOUT
-#endif
-
-/*
- * curl_hostip.c explained
- * =======================
- *
- * The main COMPILE-TIME DEFINES to keep in mind when reading the curl_host*.c
- * source file are these:
- *
- * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
- * that. The host may not be able to resolve IPv6, but we don't really have to
- * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4
- * defined.
- *
- * CURLRES_ARES - is defined if libcurl is built to use c-ares for
- * asynchronous name resolves. This can be Windows or *nix.
- *
- * CURLRES_THREADED - is defined if libcurl is built to run under (native)
- * Windows, and then the name resolve will be done in a new thread, and the
- * supported API will be the same as for ares-builds.
- *
- * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
- * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
- * defined.
- *
- * The curl_host*.c sources files are split up like this:
- *
- * curl_hostip.c   - method-independent resolver and utility functions
- * curl_hostasyn.c - functions for asynchronous name resolves
- * curl_hostsyn.c  - functions for synchronous name resolves
- * curl_hostip4.c  - ipv4-specific functions
- * curl_hostip6.c  - ipv6-specific functions
- *
- * The two asynchronous name resolver backends are implemented in:
- * curl_asyn_ares.c   - functions for ares-using name resolves
- * curl_asyn_thread.c - functions for threaded name resolves
-
- * The curl_hostip.h is the united header file for all this. It defines the
- * CURLRES_* defines based on the config*.h and curl_setup.h defines.
- */
-
-/* These two symbols are for the global DNS cache */
-static struct curl_hash hostname_cache;
-static int host_cache_initialized;
-
-static void freednsentry(void *freethis);
-
-/*
- * Curl_global_host_cache_init() initializes and sets up a global DNS cache.
- * Global DNS cache is general badness. Do not use. This will be removed in
- * a future version. Use the share interface instead!
- *
- * Returns a struct curl_hash pointer on success, NULL on failure.
- */
-struct curl_hash *Curl_global_host_cache_init(void)
-{
-  int rc = 0;
-  if(!host_cache_initialized) {
-    rc = Curl_hash_init(&hostname_cache, 7, Curl_hash_str,
-                        Curl_str_key_compare, freednsentry);
-    if(!rc)
-      host_cache_initialized = 1;
-  }
-  return rc?NULL:&hostname_cache;
-}
-
-/*
- * Destroy and cleanup the global DNS cache
- */
-void Curl_global_host_cache_dtor(void)
-{
-  if(host_cache_initialized) {
-    Curl_hash_clean(&hostname_cache);
-    host_cache_initialized = 0;
-  }
-}
-
-/*
- * Return # of adresses in a Curl_addrinfo struct
- */
-int Curl_num_addresses(const Curl_addrinfo *addr)
-{
-  int i = 0;
-  while(addr) {
-    addr = addr->ai_next;
-    i++;
-  }
-  return i;
-}
-
-/*
- * Curl_printable_address() returns a printable version of the 1st address
- * given in the 'ai' argument. The result will be stored in the buf that is
- * bufsize bytes big.
- *
- * If the conversion fails, it returns NULL.
- */
-const char *
-Curl_printable_address(const Curl_addrinfo *ai, char *buf, size_t bufsize)
-{
-  const struct sockaddr_in *sa4;
-  const struct in_addr *ipaddr4;
-#ifdef ENABLE_IPV6
-  const struct sockaddr_in6 *sa6;
-  const struct in6_addr *ipaddr6;
-#endif
-
-  switch (ai->ai_family) {
-    case AF_INET:
-      sa4 = (const void *)ai->ai_addr;
-      ipaddr4 = &sa4->sin_addr;
-      return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf,
-                            bufsize);
-#ifdef ENABLE_IPV6
-    case AF_INET6:
-      sa6 = (const void *)ai->ai_addr;
-      ipaddr6 = &sa6->sin6_addr;
-      return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf,
-                            bufsize);
-#endif
-    default:
-      break;
-  }
-  return NULL;
-}
-
-/*
- * Return a hostcache id string for the provided host + port, to be used by
- * the DNS caching.
- */
-static char *
-create_hostcache_id(const char *name, int port)
-{
-  /* create and return the new allocated entry */
-  char *id = aprintf("%s:%d", name, port);
-  char *ptr = id;
-  if(ptr) {
-    /* lower case the name part */
-    while(*ptr && (*ptr != ':')) {
-      *ptr = (char)TOLOWER(*ptr);
-      ptr++;
-    }
-  }
-  return id;
-}
-
-struct hostcache_prune_data {
-  long cache_timeout;
-  time_t now;
-};
-
-/*
- * This function is set as a callback to be called for every entry in the DNS
- * cache when we want to prune old unused entries.
- *
- * Returning non-zero means remove the entry, return 0 to keep it in the
- * cache.
- */
-static int
-hostcache_timestamp_remove(void *datap, void *hc)
-{
-  struct hostcache_prune_data *data =
-    (struct hostcache_prune_data *) datap;
-  struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
-
-  return (data->now - c->timestamp >= data->cache_timeout);
-}
-
-/*
- * Prune the DNS cache. This assumes that a lock has already been taken.
- */
-static void
-hostcache_prune(struct curl_hash *hostcache, long cache_timeout, time_t now)
-{
-  struct hostcache_prune_data user;
-
-  user.cache_timeout = cache_timeout;
-  user.now = now;
-
-  Curl_hash_clean_with_criterium(hostcache,
-                                 (void *) &user,
-                                 hostcache_timestamp_remove);
-}
-
-/*
- * Library-wide function for pruning the DNS cache. This function takes and
- * returns the appropriate locks.
- */
-void Curl_hostcache_prune(struct SessionHandle *data)
-{
-  time_t now;
-
-  if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
-    /* cache forever means never prune, and NULL hostcache means
-       we can't do it */
-    return;
-
-  if(data->share)
-    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
-  time(&now);
-
-  /* Remove outdated and unused entries from the hostcache */
-  hostcache_prune(data->dns.hostcache,
-                  data->set.dns_cache_timeout,
-                  now);
-
-  if(data->share)
-    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
-}
-
-/*
- * Check if the entry should be pruned. Assumes a locked cache.
- */
-static int
-remove_entry_if_stale(struct SessionHandle *data, struct Curl_dns_entry *dns)
-{
-  struct hostcache_prune_data user;
-
-  if(!dns || (data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
-    /* cache forever means never prune, and NULL hostcache means
-       we can't do it */
-    return 0;
-
-  time(&user.now);
-  user.cache_timeout = data->set.dns_cache_timeout;
-
-  if(!hostcache_timestamp_remove(&user,dns) )
-    return 0;
-
-  Curl_hash_clean_with_criterium(data->dns.hostcache,
-                                 (void *) &user,
-                                 hostcache_timestamp_remove);
-
-  return 1;
-}
-
-
-#ifdef HAVE_SIGSETJMP
-/* Beware this is a global and unique instance. This is used to store the
-   return address that we can jump back to from inside a signal handler. This
-   is not thread-safe stuff. */
-sigjmp_buf curl_jmpenv;
-#endif
-
-
-/*
- * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
- *
- * When calling Curl_resolv() has resulted in a response with a returned
- * address, we call this function to store the information in the dns
- * cache etc
- *
- * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
- */
-struct Curl_dns_entry *
-Curl_cache_addr(struct SessionHandle *data,
-                Curl_addrinfo *addr,
-                const char *hostname,
-                int port)
-{
-  char *entry_id;
-  size_t entry_len;
-  struct Curl_dns_entry *dns;
-  struct Curl_dns_entry *dns2;
-
-  /* Create an entry id, based upon the hostname and port */
-  entry_id = create_hostcache_id(hostname, port);
-  /* If we can't create the entry id, fail */
-  if(!entry_id)
-    return NULL;
-  entry_len = strlen(entry_id);
-
-  /* Create a new cache entry */
-  dns = calloc(1, sizeof(struct Curl_dns_entry));
-  if(!dns) {
-    free(entry_id);
-    return NULL;
-  }
-
-  dns->inuse = 0;   /* init to not used */
-  dns->addr = addr; /* this is the address(es) */
-  time(&dns->timestamp);
-  if(dns->timestamp == 0)
-    dns->timestamp = 1;   /* zero indicates that entry isn't in hash table */
-
-  /* Store the resolved data in our DNS cache. */
-  dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len+1,
-                       (void *)dns);
-  if(!dns2) {
-    free(dns);
-    free(entry_id);
-    return NULL;
-  }
-
-  dns = dns2;
-  dns->inuse++;         /* mark entry as in-use */
-
-  /* free the allocated entry_id */
-  free(entry_id);
-
-  return dns;
-}
-
-/*
- * Curl_resolv() is the main name resolve function within libcurl. It resolves
- * a name and returns a pointer to the entry in the 'entry' argument (if one
- * is provided). This function might return immediately if we're using asynch
- * resolves. See the return codes.
- *
- * The cache entry we return will get its 'inuse' counter increased when this
- * function is used. You MUST call Curl_resolv_unlock() later (when you're
- * done using this struct) to decrease the counter again.
- *
- * In debug mode, we specifically test for an interface name "LocalHost"
- * and resolve "localhost" instead as a means to permit test cases
- * to connect to a local test server with any host name.
- *
- * Return codes:
- *
- * CURLRESOLV_ERROR   (-1) = error, no pointer
- * CURLRESOLV_RESOLVED (0) = OK, pointer provided
- * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
- */
-
-int Curl_resolv(struct connectdata *conn,
-                const char *hostname,
-                int port,
-                struct Curl_dns_entry **entry)
-{
-  char *entry_id = NULL;
-  struct Curl_dns_entry *dns = NULL;
-  size_t entry_len;
-  struct SessionHandle *data = conn->data;
-  CURLcode result;
-  int rc = CURLRESOLV_ERROR; /* default to failure */
-
-  *entry = NULL;
-
-  /* Create an entry id, based upon the hostname and port */
-  entry_id = create_hostcache_id(hostname, port);
-  /* If we can't create the entry id, fail */
-  if(!entry_id)
-    return rc;
-
-  entry_len = strlen(entry_id);
-
-  if(data->share)
-    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
-  /* See if its already in our dns cache */
-  dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
-
-  /* free the allocated entry_id again */
-  free(entry_id);
-
-  /* See whether the returned entry is stale. Done before we release lock */
-  if(remove_entry_if_stale(data, dns))
-    dns = NULL; /* the memory deallocation is being handled by the hash */
-
-  if(dns) {
-    dns->inuse++; /* we use it! */
-    rc = CURLRESOLV_RESOLVED;
-  }
-
-  if(data->share)
-    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
-
-  if(!dns) {
-    /* The entry was not in the cache. Resolve it to IP address */
-
-    Curl_addrinfo *addr;
-    int respwait;
-
-    /* Check what IP specifics the app has requested and if we can provide it.
-     * If not, bail out. */
-    if(!Curl_ipvalid(conn))
-      return CURLRESOLV_ERROR;
-
-    /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
-       non-zero value indicating that we need to wait for the response to the
-       resolve call */
-    addr = Curl_getaddrinfo(conn,
-#ifdef DEBUGBUILD
-                            (data->set.str[STRING_DEVICE]
-                             && !strcmp(data->set.str[STRING_DEVICE],
-                                        "LocalHost"))?"localhost":
-#endif
-                            hostname, port, &respwait);
-
-    if(!addr) {
-      if(respwait) {
-        /* the response to our resolve call will come asynchronously at
-           a later time, good or bad */
-        /* First, check that we haven't received the info by now */
-        result = Curl_resolver_is_resolved(conn, &dns);
-        if(result) /* error detected */
-          return CURLRESOLV_ERROR;
-        if(dns)
-          rc = CURLRESOLV_RESOLVED; /* pointer provided */
-        else
-          rc = CURLRESOLV_PENDING; /* no info yet */
-      }
-    }
-    else {
-      if(data->share)
-        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
-      /* we got a response, store it in the cache */
-      dns = Curl_cache_addr(data, addr, hostname, port);
-
-      if(data->share)
-        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
-
-      if(!dns)
-        /* returned failure, bail out nicely */
-        Curl_freeaddrinfo(addr);
-      else
-        rc = CURLRESOLV_RESOLVED;
-    }
-  }
-
-  *entry = dns;
-
-  return rc;
-}
-
-#ifdef USE_ALARM_TIMEOUT
-/*
- * This signal handler jumps back into the main libcurl code and continues
- * execution.  This effectively causes the remainder of the application to run
- * within a signal handler which is nonportable and could lead to problems.
- */
-static
-RETSIGTYPE alarmfunc(int sig)
-{
-  /* this is for "-ansi -Wall -pedantic" to stop complaining!   (rabe) */
-  (void)sig;
-  siglongjmp(curl_jmpenv, 1);
-  return;
-}
-#endif /* USE_ALARM_TIMEOUT */
-
-/*
- * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
- * timeout.  This function might return immediately if we're using asynch
- * resolves. See the return codes.
- *
- * The cache entry we return will get its 'inuse' counter increased when this
- * function is used. You MUST call Curl_resolv_unlock() later (when you're
- * done using this struct) to decrease the counter again.
- *
- * If built with a synchronous resolver and use of signals is not
- * disabled by the application, then a nonzero timeout will cause a
- * timeout after the specified number of milliseconds. Otherwise, timeout
- * is ignored.
- *
- * Return codes:
- *
- * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired
- * CURLRESOLV_ERROR   (-1) = error, no pointer
- * CURLRESOLV_RESOLVED (0) = OK, pointer provided
- * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
- */
-
-int Curl_resolv_timeout(struct connectdata *conn,
-                        const char *hostname,
-                        int port,
-                        struct Curl_dns_entry **entry,
-                        long timeoutms)
-{
-#ifdef USE_ALARM_TIMEOUT
-#ifdef HAVE_SIGACTION
-  struct sigaction keep_sigact;   /* store the old struct here */
-  volatile bool keep_copysig = FALSE; /* wether old sigact has been saved */
-  struct sigaction sigact;
-#else
-#ifdef HAVE_SIGNAL
-  void (*keep_sigact)(int);       /* store the old handler here */
-#endif /* HAVE_SIGNAL */
-#endif /* HAVE_SIGACTION */
-  volatile long timeout;
-  volatile unsigned int prev_alarm = 0;
-  struct SessionHandle *data = conn->data;
-#endif /* USE_ALARM_TIMEOUT */
-  int rc;
-
-  *entry = NULL;
-
-  if(timeoutms < 0)
-    /* got an already expired timeout */
-    return CURLRESOLV_TIMEDOUT;
-
-#ifdef USE_ALARM_TIMEOUT
-  if(data->set.no_signal)
-    /* Ignore the timeout when signals are disabled */
-    timeout = 0;
-  else
-    timeout = timeoutms;
-
-  if(!timeout)
-    /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
-    return Curl_resolv(conn, hostname, port, entry);
-
-  if(timeout < 1000)
-    /* The alarm() function only provides integer second resolution, so if
-       we want to wait less than one second we must bail out already now. */
-    return CURLRESOLV_TIMEDOUT;
-
-  /*************************************************************
-   * Set signal handler to catch SIGALRM
-   * Store the old value to be able to set it back later!
-   *************************************************************/
-#ifdef HAVE_SIGACTION
-  sigaction(SIGALRM, NULL, &sigact);
-  keep_sigact = sigact;
-  keep_copysig = TRUE; /* yes, we have a copy */
-  sigact.sa_handler = alarmfunc;
-#ifdef SA_RESTART
-  /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
-  sigact.sa_flags &= ~SA_RESTART;
-#endif
-  /* now set the new struct */
-  sigaction(SIGALRM, &sigact, NULL);
-#else /* HAVE_SIGACTION */
-  /* no sigaction(), revert to the much lamer signal() */
-#ifdef HAVE_SIGNAL
-  keep_sigact = signal(SIGALRM, alarmfunc);
-#endif
-#endif /* HAVE_SIGACTION */
-
-  /* alarm() makes a signal get sent when the timeout fires off, and that
-     will abort system calls */
-  prev_alarm = alarm(curlx_sltoui(timeout/1000L));
-
-  /* This allows us to time-out from the name resolver, as the timeout
-     will generate a signal and we will siglongjmp() from that here.
-     This technique has problems (see alarmfunc).
-     This should be the last thing we do before calling Curl_resolv(),
-     as otherwise we'd have to worry about variables that get modified
-     before we invoke Curl_resolv() (and thus use "volatile"). */
-  if(sigsetjmp(curl_jmpenv, 1)) {
-    /* this is coming from a siglongjmp() after an alarm signal */
-    failf(data, "name lookup timed out");
-    rc = CURLRESOLV_ERROR;
-    goto clean_up;
-  }
-
-#else
-#ifndef CURLRES_ASYNCH
-  if(timeoutms)
-    infof(conn->data, "timeout on name lookup is not supported\n");
-#else
-  (void)timeoutms; /* timeoutms not used with an async resolver */
-#endif
-#endif /* USE_ALARM_TIMEOUT */
-
-  /* Perform the actual name resolution. This might be interrupted by an
-   * alarm if it takes too long.
-   */
-  rc = Curl_resolv(conn, hostname, port, entry);
-
-#ifdef USE_ALARM_TIMEOUT
-clean_up:
-
-  if(!prev_alarm)
-    /* deactivate a possibly active alarm before uninstalling the handler */
-    alarm(0);
-
-#ifdef HAVE_SIGACTION
-  if(keep_copysig) {
-    /* we got a struct as it looked before, now put that one back nice
-       and clean */
-    sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
-  }
-#else
-#ifdef HAVE_SIGNAL
-  /* restore the previous SIGALRM handler */
-  signal(SIGALRM, keep_sigact);
-#endif
-#endif /* HAVE_SIGACTION */
-
-  /* switch back the alarm() to either zero or to what it was before minus
-     the time we spent until now! */
-  if(prev_alarm) {
-    /* there was an alarm() set before us, now put it back */
-    unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);
-
-    /* the alarm period is counted in even number of seconds */
-    unsigned long alarm_set = prev_alarm - elapsed_ms/1000;
-
-    if(!alarm_set ||
-       ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
-      /* if the alarm time-left reached zero or turned "negative" (counted
-         with unsigned values), we should fire off a SIGALRM here, but we
-         won't, and zero would be to switch it off so we never set it to
-         less than 1! */
-      alarm(1);
-      rc = CURLRESOLV_TIMEDOUT;
-      failf(data, "Previous alarm fired off!");
-    }
-    else
-      alarm((unsigned int)alarm_set);
-  }
-#endif /* USE_ALARM_TIMEOUT */
-
-  return rc;
-}
-
-/*
- * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
- * made, the struct may be destroyed due to pruning. It is important that only
- * one unlock is made for each Curl_resolv() call.
- */
-void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns)
-{
-  DEBUGASSERT(dns && (dns->inuse>0));
-
-  if(data->share)
-    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
-  dns->inuse--;
-  /* only free if nobody is using AND it is not in hostcache (timestamp ==
-     0) */
-  if(dns->inuse == 0 && dns->timestamp == 0) {
-    Curl_freeaddrinfo(dns->addr);
-    free(dns);
-  }
-
-  if(data->share)
-    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
-}
-
-/*
- * File-internal: free a cache dns entry.
- */
-static void freednsentry(void *freethis)
-{
-  struct Curl_dns_entry *p = (struct Curl_dns_entry *) freethis;
-
-  /* mark the entry as not in hostcache */
-  p->timestamp = 0;
-  if(p->inuse == 0) {
-    Curl_freeaddrinfo(p->addr);
-    free(p);
-  }
-}
-
-/*
- * Curl_mk_dnscache() creates a new DNS cache and returns the handle for it.
- */
-struct curl_hash *Curl_mk_dnscache(void)
-{
-  return Curl_hash_alloc(7, Curl_hash_str, Curl_str_key_compare, freednsentry);
-}
-
-static int hostcache_inuse(void *data, void *hc)
-{
-  struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
-
-  if(c->inuse == 1)
-    Curl_resolv_unlock(data, c);
-
-  return 1; /* free all entries */
-}
-
-void Curl_hostcache_clean(struct SessionHandle *data)
-{
-  /* Entries added to the hostcache with the CURLOPT_RESOLVE function are
-   * still present in the cache with the inuse counter set to 1. Detect them
-   * and cleanup!
-   */
-  Curl_hash_clean_with_criterium(data->dns.hostcache, data, hostcache_inuse);
-}
-
-void Curl_hostcache_destroy(struct SessionHandle *data)
-{
-  Curl_hostcache_clean(data);
-  Curl_hash_destroy(data->dns.hostcache);
-  data->dns.hostcachetype = HCACHE_NONE;
-  data->dns.hostcache = NULL;
-}
-
-CURLcode Curl_loadhostpairs(struct SessionHandle *data)
-{
-  struct curl_slist *hostp;
-  char hostname[256];
-  char address[256];
-  int port;
-
-  for(hostp = data->change.resolve; hostp; hostp = hostp->next ) {
-    if(!hostp->data)
-      continue;
-    if(hostp->data[0] == '-') {
-      /* TODO: mark an entry for removal */
-    }
-    else if(3 == sscanf(hostp->data, "%255[^:]:%d:%255s", hostname, &port,
-                        address)) {
-      struct Curl_dns_entry *dns;
-      Curl_addrinfo *addr;
-      char *entry_id;
-      size_t entry_len;
-
-      addr = Curl_str2addr(address, port);
-      if(!addr) {
-        infof(data, "Resolve %s found illegal!\n", hostp->data);
-        continue;
-      }
-
-      /* Create an entry id, based upon the hostname and port */
-      entry_id = create_hostcache_id(hostname, port);
-      /* If we can't create the entry id, fail */
-      if(!entry_id) {
-        Curl_freeaddrinfo(addr);
-        return CURLE_OUT_OF_MEMORY;
-      }
-
-      entry_len = strlen(entry_id);
-
-      if(data->share)
-        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
-      /* See if its already in our dns cache */
-      dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
-
-      /* free the allocated entry_id again */
-      free(entry_id);
-
-      if(!dns)
-        /* if not in the cache already, put this host in the cache */
-        dns = Curl_cache_addr(data, addr, hostname, port);
-      else
-        /* this is a duplicate, free it again */
-        Curl_freeaddrinfo(addr);
-
-      if(data->share)
-        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
-
-      if(!dns) {
-        Curl_freeaddrinfo(addr);
-        return CURLE_OUT_OF_MEMORY;
-      }
-      infof(data, "Added %s:%d:%s to DNS cache\n",
-            hostname, port, address);
-    }
-  }
-  data->change.resolve = NULL; /* dealt with now */
-
-  return CURLE_OK;
-}
diff --git a/lib/hostip4.c b/lib/hostip4.c
deleted file mode 100644 (file)
index 5b64b46..0000000
+++ /dev/null
@@ -1,310 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef __VMS
-#include <in.h>
-#include <inet.h>
-#endif
-
-#ifdef HAVE_PROCESS_H
-#include <process.h>
-#endif
-
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_hostip.h"
-#include "curl_hash.h"
-#include "curl_share.h"
-#include "curl_strerror.h"
-#include "curl_url.h"
-#include "curl_inet_pton.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/***********************************************************************
- * Only for plain-ipv4 builds
- **********************************************************************/
-#ifdef CURLRES_IPV4 /* plain ipv4 code coming up */
-/*
- * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've
- * been set and returns TRUE if they are OK.
- */
-bool Curl_ipvalid(struct connectdata *conn)
-{
-  if(conn->ip_version == CURL_IPRESOLVE_V6)
-    /* an ipv6 address was requested and we can't get/use one */
-    return FALSE;
-
-  return TRUE; /* OK, proceed */
-}
-
-#ifdef CURLRES_SYNCH
-
-/*
- * Curl_getaddrinfo() - the ipv4 synchronous version.
- *
- * The original code to this function was from the Dancer source code, written
- * by Bjorn Reese, it has since been patched and modified considerably.
- *
- * gethostbyname_r() is the thread-safe version of the gethostbyname()
- * function. When we build for plain IPv4, we attempt to use this
- * function. There are _three_ different gethostbyname_r() versions, and we
- * detect which one this platform supports in the configure script and set up
- * the HAVE_GETHOSTBYNAME_R_3, HAVE_GETHOSTBYNAME_R_5 or
- * HAVE_GETHOSTBYNAME_R_6 defines accordingly. Note that HAVE_GETADDRBYNAME
- * has the corresponding rules. This is primarily on *nix. Note that some unix
- * flavours have thread-safe versions of the plain gethostbyname() etc.
- *
- */
-Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
-                                const char *hostname,
-                                int port,
-                                int *waitp)
-{
-  Curl_addrinfo *ai = NULL;
-
-#ifdef CURL_DISABLE_VERBOSE_STRINGS
-  (void)conn;
-#endif
-
-  *waitp = 0; /* synchronous response only */
-
-  ai = Curl_ipv4_resolve_r(hostname, port);
-  if(!ai)
-    infof(conn->data, "Curl_ipv4_resolve_r failed for %s\n", hostname);
-
-  return ai;
-}
-#endif /* CURLRES_SYNCH */
-#endif /* CURLRES_IPV4 */
-
-#if defined(CURLRES_IPV4) && !defined(CURLRES_ARES)
-
-/*
- * Curl_ipv4_resolve_r() - ipv4 threadsafe resolver function.
- *
- * This is used for both synchronous and asynchronous resolver builds,
- * implying that only threadsafe code and function calls may be used.
- *
- */
-Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname,
-                                   int port)
-{
-#if !defined(HAVE_GETADDRINFO_THREADSAFE) && defined(HAVE_GETHOSTBYNAME_R_3)
-  int res;
-#endif
-  Curl_addrinfo *ai = NULL;
-  struct hostent *h = NULL;
-  struct in_addr in;
-  struct hostent *buf = NULL;
-
-  if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
-    /* This is a dotted IP address 123.123.123.123-style */
-    return Curl_ip2addr(AF_INET, &in, hostname, port);
-
-#if defined(HAVE_GETADDRINFO_THREADSAFE)
-  else {
-    struct addrinfo hints;
-    char sbuf[NI_MAXSERV];
-    char *sbufptr = NULL;
-
-    memset(&hints, 0, sizeof(hints));
-    hints.ai_family = PF_INET;
-    hints.ai_socktype = SOCK_STREAM;
-    if(port) {
-      snprintf(sbuf, sizeof(sbuf), "%d", port);
-      sbufptr = sbuf;
-    }
-
-    (void)Curl_getaddrinfo_ex(hostname, sbufptr, &hints, &ai);
-
-#elif defined(HAVE_GETHOSTBYNAME_R)
-  /*
-   * gethostbyname_r() is the preferred resolve function for many platforms.
-   * Since there are three different versions of it, the following code is
-   * somewhat #ifdef-ridden.
-   */
-  else {
-    int h_errnop;
-
-    buf = calloc(1, CURL_HOSTENT_SIZE);
-    if(!buf)
-      return NULL; /* major failure */
-    /*
-     * The clearing of the buffer is a workaround for a gethostbyname_r bug in
-     * qnx nto and it is also _required_ for some of these functions on some
-     * platforms.
-     */
-
-#if defined(HAVE_GETHOSTBYNAME_R_5)
-    /* Solaris, IRIX and more */
-    h = gethostbyname_r(hostname,
-                        (struct hostent *)buf,
-                        (char *)buf + sizeof(struct hostent),
-                        CURL_HOSTENT_SIZE - sizeof(struct hostent),
-                        &h_errnop);
-
-    /* If the buffer is too small, it returns NULL and sets errno to
-     * ERANGE. The errno is thread safe if this is compiled with
-     * -D_REENTRANT as then the 'errno' variable is a macro defined to get
-     * used properly for threads.
-     */
-
-    if(h) {
-      ;
-    }
-    else
-#elif defined(HAVE_GETHOSTBYNAME_R_6)
-    /* Linux */
-
-    (void)gethostbyname_r(hostname,
-                        (struct hostent *)buf,
-                        (char *)buf + sizeof(struct hostent),
-                        CURL_HOSTENT_SIZE - sizeof(struct hostent),
-                        &h, /* DIFFERENCE */
-                        &h_errnop);
-    /* Redhat 8, using glibc 2.2.93 changed the behavior. Now all of a
-     * sudden this function returns EAGAIN if the given buffer size is too
-     * small. Previous versions are known to return ERANGE for the same
-     * problem.
-     *
-     * This wouldn't be such a big problem if older versions wouldn't
-     * sometimes return EAGAIN on a common failure case. Alas, we can't
-     * assume that EAGAIN *or* ERANGE means ERANGE for any given version of
-     * glibc.
-     *
-     * For now, we do that and thus we may call the function repeatedly and
-     * fail for older glibc versions that return EAGAIN, until we run out of
-     * buffer size (step_size grows beyond CURL_HOSTENT_SIZE).
-     *
-     * If anyone has a better fix, please tell us!
-     *
-     * -------------------------------------------------------------------
-     *
-     * On October 23rd 2003, Dan C dug up more details on the mysteries of
-     * gethostbyname_r() in glibc:
-     *
-     * In glibc 2.2.5 the interface is different (this has also been
-     * discovered in glibc 2.1.1-6 as shipped by Redhat 6). What I can't
-     * explain, is that tests performed on glibc 2.2.4-34 and 2.2.4-32
-     * (shipped/upgraded by Redhat 7.2) don't show this behavior!
-     *
-     * In this "buggy" version, the return code is -1 on error and 'errno'
-     * is set to the ERANGE or EAGAIN code. Note that 'errno' is not a
-     * thread-safe variable.
-     */
-
-    if(!h) /* failure */
-#elif defined(HAVE_GETHOSTBYNAME_R_3)
-    /* AIX, Digital Unix/Tru64, HPUX 10, more? */
-
-    /* For AIX 4.3 or later, we don't use gethostbyname_r() at all, because of
-     * the plain fact that it does not return unique full buffers on each
-     * call, but instead several of the pointers in the hostent structs will
-     * point to the same actual data! This have the unfortunate down-side that
-     * our caching system breaks down horribly. Luckily for us though, AIX 4.3
-     * and more recent versions have a "completely thread-safe"[*] libc where
-     * all the data is stored in thread-specific memory areas making calls to
-     * the plain old gethostbyname() work fine even for multi-threaded
-     * programs.
-     *
-     * This AIX 4.3 or later detection is all made in the configure script.
-     *
-     * Troels Walsted Hansen helped us work this out on March 3rd, 2003.
-     *
-     * [*] = much later we've found out that it isn't at all "completely
-     * thread-safe", but at least the gethostbyname() function is.
-     */
-
-    if(CURL_HOSTENT_SIZE >=
-       (sizeof(struct hostent)+sizeof(struct hostent_data))) {
-
-      /* August 22nd, 2000: Albert Chin-A-Young brought an updated version
-       * that should work! September 20: Richard Prescott worked on the buffer
-       * size dilemma.
-       */
-
-      res = gethostbyname_r(hostname,
-                            (struct hostent *)buf,
-                            (struct hostent_data *)((char *)buf +
-                                                    sizeof(struct hostent)));
-      h_errnop = SOCKERRNO; /* we don't deal with this, but set it anyway */
-    }
-    else
-      res = -1; /* failure, too smallish buffer size */
-
-    if(!res) { /* success */
-
-      h = buf; /* result expected in h */
-
-      /* This is the worst kind of the different gethostbyname_r() interfaces.
-       * Since we don't know how big buffer this particular lookup required,
-       * we can't realloc down the huge alloc without doing closer analysis of
-       * the returned data. Thus, we always use CURL_HOSTENT_SIZE for every
-       * name lookup. Fixing this would require an extra malloc() and then
-       * calling Curl_addrinfo_copy() that subsequent realloc()s down the new
-       * memory area to the actually used amount.
-       */
-    }
-    else
-#endif /* HAVE_...BYNAME_R_5 || HAVE_...BYNAME_R_6 || HAVE_...BYNAME_R_3 */
-    {
-      h = NULL; /* set return code to NULL */
-      free(buf);
-    }
-#else /* HAVE_GETADDRINFO_THREADSAFE || HAVE_GETHOSTBYNAME_R */
-    /*
-     * Here is code for platforms that don't have a thread safe
-     * getaddrinfo() nor gethostbyname_r() function or for which
-     * gethostbyname() is the preferred one.
-     */
-  else {
-    h = gethostbyname((void*)hostname);
-#endif /* HAVE_GETADDRINFO_THREADSAFE || HAVE_GETHOSTBYNAME_R */
-  }
-
-  if(h) {
-    ai = Curl_he2ai(h, port);
-
-    if(buf) /* used a *_r() function */
-      free(buf);
-  }
-
-  return ai;
-}
-#endif /* defined(CURLRES_IPV4) && !defined(CURLRES_ARES) */
diff --git a/lib/hostip6.c b/lib/hostip6.c
deleted file mode 100644 (file)
index cfd6081..0000000
+++ /dev/null
@@ -1,224 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef __VMS
-#include <in.h>
-#include <inet.h>
-#endif
-
-#ifdef HAVE_PROCESS_H
-#include <process.h>
-#endif
-
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_hostip.h"
-#include "curl_hash.h"
-#include "curl_share.h"
-#include "curl_strerror.h"
-#include "curl_url.h"
-#include "curl_inet_pton.h"
-#include "curl_connect.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/***********************************************************************
- * Only for ipv6-enabled builds
- **********************************************************************/
-#ifdef CURLRES_IPV6
-
-
-#if defined(CURLDEBUG) && defined(HAVE_GETNAMEINFO)
-/* These are strictly for memory tracing and are using the same style as the
- * family otherwise present in curl_memdebug.c. I put these ones here since
- * they require a bunch of structs I didn't want to include there.
- */
-
-/*
- * For CURLRES_ARS, this should be written using ares_gethostbyaddr()
- * (ignoring the fact c-ares doesn't return 'serv').
- */
-
-int curl_dogetnameinfo(GETNAMEINFO_QUAL_ARG1 GETNAMEINFO_TYPE_ARG1 sa,
-                       GETNAMEINFO_TYPE_ARG2 salen,
-                       char *host, GETNAMEINFO_TYPE_ARG46 hostlen,
-                       char *serv, GETNAMEINFO_TYPE_ARG46 servlen,
-                       GETNAMEINFO_TYPE_ARG7 flags,
-                       int line, const char *source)
-{
-  int res = (getnameinfo)(sa, salen,
-                          host, hostlen,
-                          serv, servlen,
-                          flags);
-  if(0 == res)
-    /* success */
-    curl_memlog("GETNAME %s:%d getnameinfo()\n",
-                source, line);
-  else
-    curl_memlog("GETNAME %s:%d getnameinfo() failed = %d\n",
-                source, line, res);
-  return res;
-}
-#endif /* defined(CURLDEBUG) && defined(HAVE_GETNAMEINFO) */
-
-/*
- * Curl_ipv6works() returns TRUE if ipv6 seems to work.
- */
-bool Curl_ipv6works(void)
-{
-  /* the nature of most system is that IPv6 status doesn't come and go
-     during a program's lifetime so we only probe the first time and then we
-     have the info kept for fast re-use */
-  static int ipv6_works = -1;
-  if(-1 == ipv6_works) {
-    /* probe to see if we have a working IPv6 stack */
-    curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0);
-    if(s == CURL_SOCKET_BAD)
-      /* an ipv6 address was requested but we can't get/use one */
-      ipv6_works = 0;
-    else {
-      ipv6_works = 1;
-      Curl_closesocket(NULL, s);
-    }
-  }
-  return (ipv6_works>0)?TRUE:FALSE;
-}
-
-/*
- * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've
- * been set and returns TRUE if they are OK.
- */
-bool Curl_ipvalid(struct connectdata *conn)
-{
-  if(conn->ip_version == CURL_IPRESOLVE_V6)
-    return Curl_ipv6works();
-  return TRUE;
-}
-
-#if defined(CURLRES_SYNCH)
-
-#ifdef DEBUG_ADDRINFO
-static void dump_addrinfo(struct connectdata *conn, const Curl_addrinfo *ai)
-{
-  printf("dump_addrinfo:\n");
-  for(; ai; ai = ai->ai_next) {
-    char  buf[INET6_ADDRSTRLEN];
-
-    printf("    fam %2d, CNAME %s, ",
-           ai->ai_family, ai->ai_canonname ? ai->ai_canonname : "<none>");
-    if(Curl_printable_address(ai, buf, sizeof(buf)))
-      printf("%s\n", buf);
-    else
-      printf("failed; %s\n", Curl_strerror(conn, SOCKERRNO));
-  }
-}
-#else
-#define dump_addrinfo(x,y) Curl_nop_stmt
-#endif
-
-/*
- * Curl_getaddrinfo() when built ipv6-enabled (non-threading and
- * non-ares version).
- *
- * Returns name information about the given hostname and port number. If
- * successful, the 'addrinfo' is returned and the forth argument will point to
- * memory we need to free after use. That memory *MUST* be freed with
- * Curl_freeaddrinfo(), nothing else.
- */
-Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
-                                const char *hostname,
-                                int port,
-                                int *waitp)
-{
-  struct addrinfo hints;
-  Curl_addrinfo *res;
-  int error;
-  char sbuf[NI_MAXSERV];
-  char *sbufptr = NULL;
-  char addrbuf[128];
-  int pf;
-  struct SessionHandle *data = conn->data;
-
-  *waitp = 0; /* synchronous response only */
-
-  /*
-   * Check if a limited name resolve has been requested.
-   */
-  switch(conn->ip_version) {
-  case CURL_IPRESOLVE_V4:
-    pf = PF_INET;
-    break;
-  case CURL_IPRESOLVE_V6:
-    pf = PF_INET6;
-    break;
-  default:
-    pf = PF_UNSPEC;
-    break;
-  }
-
-  if((pf != PF_INET) && !Curl_ipv6works())
-    /* the stack seems to be a non-ipv6 one */
-    pf = PF_INET;
-
-  memset(&hints, 0, sizeof(hints));
-  hints.ai_family = pf;
-  hints.ai_socktype = conn->socktype;
-
-  if((1 == Curl_inet_pton(AF_INET, hostname, addrbuf)) ||
-     (1 == Curl_inet_pton(AF_INET6, hostname, addrbuf))) {
-    /* the given address is numerical only, prevent a reverse lookup */
-    hints.ai_flags = AI_NUMERICHOST;
-  }
-
-  if(port) {
-    snprintf(sbuf, sizeof(sbuf), "%d", port);
-    sbufptr=sbuf;
-  }
-  error = Curl_getaddrinfo_ex(hostname, sbufptr, &hints, &res);
-  if(error) {
-    infof(data, "getaddrinfo(3) failed for %s:%d\n", hostname, port);
-    return NULL;
-  }
-
-  dump_addrinfo(conn, res);
-
-  return res;
-}
-#endif /* CURLRES_SYNCH */
-#endif /* CURLRES_IPV6 */
-
diff --git a/lib/hostsyn.c b/lib/hostsyn.c
deleted file mode 100644 (file)
index 9a26f8d..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef __VMS
-#include <in.h>
-#include <inet.h>
-#endif
-
-#ifdef HAVE_PROCESS_H
-#include <process.h>
-#endif
-
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_hostip.h"
-#include "curl_hash.h"
-#include "curl_share.h"
-#include "curl_strerror.h"
-#include "curl_url.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/***********************************************************************
- * Only for builds using synchronous name resolves
- **********************************************************************/
-#ifdef CURLRES_SYNCH
-
-/*
- * Function provided by the resolver backend to set DNS servers to use.
- */
-CURLcode Curl_set_dns_servers(struct SessionHandle *data,
-                              char *servers)
-{
-  (void)data;
-  (void)servers;
-  return CURLE_NOT_BUILT_IN;
-
-}
-
-#endif /* truly sync */
diff --git a/lib/http.c b/lib/http.c
deleted file mode 100644 (file)
index 420361c..0000000
+++ /dev/null
@@ -1,3506 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifndef CURL_DISABLE_HTTP
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef HAVE_NET_IF_H
-#include <net/if.h>
-#endif
-#ifdef HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-
-#ifdef HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-
-#include "curl_urldata.h"
-#include <curl/curl.h>
-#include "curl_transfer.h"
-#include "curl_sendf.h"
-#include "curl_formdata.h"
-#include "curl_progress.h"
-#include "curl_base64.h"
-#include "curl_cookie.h"
-#include "curl_strequal.h"
-#include "curl_sslgen.h"
-#include "curl_http_digest.h"
-#include "curl_ntlm.h"
-#include "curl_ntlm_wb.h"
-#include "curl_http_negotiate.h"
-#include "curl_url.h"
-#include "curl_share.h"
-#include "curl_hostip.h"
-#include "curl_http.h"
-#include "curl_memory.h"
-#include "curl_select.h"
-#include "curl_parsedate.h" /* for the week day and month names */
-#include "curl_strtoofft.h"
-#include "curl_multiif.h"
-#include "curl_rawstr.h"
-#include "curl_content_encoding.h"
-#include "curl_http_proxy.h"
-#include "curl_warnless.h"
-#include "curl_non_ascii.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/*
- * Forward declarations.
- */
-
-static int http_getsock_do(struct connectdata *conn,
-                           curl_socket_t *socks,
-                           int numsocks);
-static int http_should_fail(struct connectdata *conn);
-
-#ifdef USE_SSL
-static CURLcode https_connecting(struct connectdata *conn, bool *done);
-static int https_getsock(struct connectdata *conn,
-                         curl_socket_t *socks,
-                         int numsocks);
-#else
-#define https_connecting(x,y) CURLE_COULDNT_CONNECT
-#endif
-
-/*
- * HTTP handler interface.
- */
-const struct Curl_handler Curl_handler_http = {
-  "HTTP",                               /* scheme */
-  ZERO_NULL,                            /* setup_connection */
-  Curl_http,                            /* do_it */
-  Curl_http_done,                       /* done */
-  ZERO_NULL,                            /* do_more */
-  Curl_http_connect,                    /* connect_it */
-  ZERO_NULL,                            /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  http_getsock_do,                      /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
-  ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* readwrite */
-  PORT_HTTP,                            /* defport */
-  CURLPROTO_HTTP,                       /* protocol */
-  PROTOPT_NONE                          /* flags */
-};
-
-#ifdef USE_SSL
-/*
- * HTTPS handler interface.
- */
-const struct Curl_handler Curl_handler_https = {
-  "HTTPS",                              /* scheme */
-  ZERO_NULL,                            /* setup_connection */
-  Curl_http,                            /* do_it */
-  Curl_http_done,                       /* done */
-  ZERO_NULL,                            /* do_more */
-  Curl_http_connect,                    /* connect_it */
-  https_connecting,                     /* connecting */
-  ZERO_NULL,                            /* doing */
-  https_getsock,                        /* proto_getsock */
-  http_getsock_do,                      /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
-  ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* readwrite */
-  PORT_HTTPS,                           /* defport */
-  CURLPROTO_HTTP | CURLPROTO_HTTPS,     /* protocol */
-  PROTOPT_SSL                           /* flags */
-};
-#endif
-
-
-/*
- * checkheaders() checks the linked list of custom HTTP headers for a
- * particular header (prefix).
- *
- * Returns a pointer to the first matching header or NULL if none matched.
- */
-char *Curl_checkheaders(struct SessionHandle *data, const char *thisheader)
-{
-  struct curl_slist *head;
-  size_t thislen = strlen(thisheader);
-
-  for(head = data->set.headers; head; head=head->next) {
-    if(Curl_raw_nequal(head->data, thisheader, thislen))
-      return head->data;
-  }
-  return NULL;
-}
-
-/*
- * Strip off leading and trailing whitespace from the value in the
- * given HTTP header line and return a strdupped copy. Returns NULL in
- * case of allocation failure. Returns an empty string if the header value
- * consists entirely of whitespace.
- */
-static char *copy_header_value(const char *h)
-{
-  const char *start;
-  const char *end;
-  char *value;
-  size_t len;
-
-  DEBUGASSERT(h);
-
-  /* Find the end of the header name */
-  while(*h && (*h != ':'))
-    ++h;
-
-  if(*h)
-    /* Skip over colon */
-    ++h;
-
-  /* Find the first non-space letter */
-  start = h;
-  while(*start && ISSPACE(*start))
-    start++;
-
-  /* data is in the host encoding so
-     use '\r' and '\n' instead of 0x0d and 0x0a */
-  end = strchr(start, '\r');
-  if(!end)
-    end = strchr(start, '\n');
-  if(!end)
-    end = strchr(start, '\0');
-  if(!end)
-    return NULL;
-
-  /* skip all trailing space letters */
-  while((end > start) && ISSPACE(*end))
-    end--;
-
-  /* get length of the type */
-  len = end-start+1;
-
-  value = malloc(len + 1);
-  if(!value)
-    return NULL;
-
-  memcpy(value, start, len);
-  value[len] = 0; /* zero terminate */
-
-  return value;
-}
-
-/*
- * http_output_basic() sets up an Authorization: header (or the proxy version)
- * for HTTP Basic authentication.
- *
- * Returns CURLcode.
- */
-static CURLcode http_output_basic(struct connectdata *conn, bool proxy)
-{
-  size_t size = 0;
-  char *authorization = NULL;
-  struct SessionHandle *data = conn->data;
-  char **userp;
-  const char *user;
-  const char *pwd;
-  CURLcode error;
-
-  if(proxy) {
-    userp = &conn->allocptr.proxyuserpwd;
-    user = conn->proxyuser;
-    pwd = conn->proxypasswd;
-  }
-  else {
-    userp = &conn->allocptr.userpwd;
-    user = conn->user;
-    pwd = conn->passwd;
-  }
-
-  snprintf(data->state.buffer, sizeof(data->state.buffer), "%s:%s", user, pwd);
-
-  error = Curl_base64_encode(data,
-                             data->state.buffer, strlen(data->state.buffer),
-                             &authorization, &size);
-  if(error)
-    return error;
-
-  if(!authorization)
-    return CURLE_REMOTE_ACCESS_DENIED;
-
-  Curl_safefree(*userp);
-  *userp = aprintf("%sAuthorization: Basic %s\r\n",
-                   proxy?"Proxy-":"",
-                   authorization);
-  free(authorization);
-  if(!*userp)
-    return CURLE_OUT_OF_MEMORY;
-
-  return CURLE_OK;
-}
-
-/* pickoneauth() selects the most favourable authentication method from the
- * ones available and the ones we want.
- *
- * return TRUE if one was picked
- */
-static bool pickoneauth(struct auth *pick)
-{
-  bool picked;
-  /* only deal with authentication we want */
-  unsigned long avail = pick->avail & pick->want;
-  picked = TRUE;
-
-  /* The order of these checks is highly relevant, as this will be the order
-     of preference in case of the existence of multiple accepted types. */
-  if(avail & CURLAUTH_GSSNEGOTIATE)
-    pick->picked = CURLAUTH_GSSNEGOTIATE;
-  else if(avail & CURLAUTH_DIGEST)
-    pick->picked = CURLAUTH_DIGEST;
-  else if(avail & CURLAUTH_NTLM)
-    pick->picked = CURLAUTH_NTLM;
-  else if(avail & CURLAUTH_NTLM_WB)
-    pick->picked = CURLAUTH_NTLM_WB;
-  else if(avail & CURLAUTH_BASIC)
-    pick->picked = CURLAUTH_BASIC;
-  else {
-    pick->picked = CURLAUTH_PICKNONE; /* we select to use nothing */
-    picked = FALSE;
-  }
-  pick->avail = CURLAUTH_NONE; /* clear it here */
-
-  return picked;
-}
-
-/*
- * Curl_http_perhapsrewind()
- *
- * If we are doing POST or PUT {
- *   If we have more data to send {
- *     If we are doing NTLM {
- *       Keep sending since we must not disconnect
- *     }
- *     else {
- *       If there is more than just a little data left to send, close
- *       the current connection by force.
- *     }
- *   }
- *   If we have sent any data {
- *     If we don't have track of all the data {
- *       call app to tell it to rewind
- *     }
- *     else {
- *       rewind internally so that the operation can restart fine
- *     }
- *   }
- * }
- */
-static CURLcode http_perhapsrewind(struct connectdata *conn)
-{
-  struct SessionHandle *data = conn->data;
-  struct HTTP *http = data->state.proto.http;
-  curl_off_t bytessent;
-  curl_off_t expectsend = -1; /* default is unknown */
-
-  if(!http)
-    /* If this is still NULL, we have not reach very far and we can safely
-       skip this rewinding stuff */
-    return CURLE_OK;
-
-  switch(data->set.httpreq) {
-  case HTTPREQ_GET:
-  case HTTPREQ_HEAD:
-    return CURLE_OK;
-  default:
-    break;
-  }
-
-  bytessent = http->writebytecount;
-
-  if(conn->bits.authneg)
-    /* This is a state where we are known to be negotiating and we don't send
-       any data then. */
-    expectsend = 0;
-  else {
-    /* figure out how much data we are expected to send */
-    switch(data->set.httpreq) {
-    case HTTPREQ_POST:
-      if(data->set.postfieldsize != -1)
-        expectsend = data->set.postfieldsize;
-      else if(data->set.postfields)
-        expectsend = (curl_off_t)strlen(data->set.postfields);
-      break;
-    case HTTPREQ_PUT:
-      if(data->set.infilesize != -1)
-        expectsend = data->set.infilesize;
-      break;
-    case HTTPREQ_POST_FORM:
-      expectsend = http->postsize;
-      break;
-    default:
-      break;
-    }
-  }
-
-  conn->bits.rewindaftersend = FALSE; /* default */
-
-  if((expectsend == -1) || (expectsend > bytessent)) {
-    /* There is still data left to send */
-    if((data->state.authproxy.picked == CURLAUTH_NTLM) ||
-       (data->state.authhost.picked == CURLAUTH_NTLM) ||
-       (data->state.authproxy.picked == CURLAUTH_NTLM_WB) ||
-       (data->state.authhost.picked == CURLAUTH_NTLM_WB)) {
-      if(((expectsend - bytessent) < 2000) ||
-         (conn->ntlm.state != NTLMSTATE_NONE) ||
-         (conn->proxyntlm.state != NTLMSTATE_NONE)) {
-        /* The NTLM-negotiation has started *OR* there is just a little (<2K)
-           data left to send, keep on sending. */
-
-        /* rewind data when completely done sending! */
-        if(!conn->bits.authneg) {
-          conn->bits.rewindaftersend = TRUE;
-          infof(data, "Rewind stream after send\n");
-        }
-
-        return CURLE_OK;
-      }
-      if(conn->bits.close)
-        /* this is already marked to get closed */
-        return CURLE_OK;
-
-      infof(data, "NTLM send, close instead of sending %" FORMAT_OFF_T
-            " bytes\n", (curl_off_t)(expectsend - bytessent));
-    }
-
-    /* This is not NTLM or many bytes left to send: close
-     */
-    conn->bits.close = TRUE;
-    data->req.size = 0; /* don't download any more than 0 bytes */
-
-    /* There still is data left to send, but this connection is marked for
-       closure so we can safely do the rewind right now */
-  }
-
-  if(bytessent)
-    /* we rewind now at once since if we already sent something */
-    return Curl_readrewind(conn);
-
-  return CURLE_OK;
-}
-
-/*
- * Curl_http_auth_act() gets called when all HTTP headers have been received
- * and it checks what authentication methods that are available and decides
- * which one (if any) to use. It will set 'newurl' if an auth method was
- * picked.
- */
-
-CURLcode Curl_http_auth_act(struct connectdata *conn)
-{
-  struct SessionHandle *data = conn->data;
-  bool pickhost = FALSE;
-  bool pickproxy = FALSE;
-  CURLcode code = CURLE_OK;
-
-  if(100 <= data->req.httpcode && 199 >= data->req.httpcode)
-    /* this is a transient response code, ignore */
-    return CURLE_OK;
-
-  if(data->state.authproblem)
-    return data->set.http_fail_on_error?CURLE_HTTP_RETURNED_ERROR:CURLE_OK;
-
-  if(conn->bits.user_passwd &&
-     ((data->req.httpcode == 401) ||
-      (conn->bits.authneg && data->req.httpcode < 300))) {
-    pickhost = pickoneauth(&data->state.authhost);
-    if(!pickhost)
-      data->state.authproblem = TRUE;
-  }
-  if(conn->bits.proxy_user_passwd &&
-     ((data->req.httpcode == 407) ||
-      (conn->bits.authneg && data->req.httpcode < 300))) {
-    pickproxy = pickoneauth(&data->state.authproxy);
-    if(!pickproxy)
-      data->state.authproblem = TRUE;
-  }
-
-  if(pickhost || pickproxy) {
-    /* In case this is GSS auth, the newurl field is already allocated so
-       we must make sure to free it before allocating a new one. As figured
-       out in bug #2284386 */
-    Curl_safefree(data->req.newurl);
-    data->req.newurl = strdup(data->change.url); /* clone URL */
-    if(!data->req.newurl)
-      return CURLE_OUT_OF_MEMORY;
-
-    if((data->set.httpreq != HTTPREQ_GET) &&
-       (data->set.httpreq != HTTPREQ_HEAD) &&
-       !conn->bits.rewindaftersend) {
-      code = http_perhapsrewind(conn);
-      if(code)
-        return code;
-    }
-  }
-
-  else if((data->req.httpcode < 300) &&
-          (!data->state.authhost.done) &&
-          conn->bits.authneg) {
-    /* no (known) authentication available,
-       authentication is not "done" yet and
-       no authentication seems to be required and
-       we didn't try HEAD or GET */
-    if((data->set.httpreq != HTTPREQ_GET) &&
-       (data->set.httpreq != HTTPREQ_HEAD)) {
-      data->req.newurl = strdup(data->change.url); /* clone URL */
-      if(!data->req.newurl)
-        return CURLE_OUT_OF_MEMORY;
-      data->state.authhost.done = TRUE;
-    }
-  }
-  if(http_should_fail(conn)) {
-    failf (data, "The requested URL returned error: %d",
-           data->req.httpcode);
-    code = CURLE_HTTP_RETURNED_ERROR;
-  }
-
-  return code;
-}
-
-
-/*
- * Output the correct authentication header depending on the auth type
- * and whether or not it is to a proxy.
- */
-static CURLcode
-output_auth_headers(struct connectdata *conn,
-                    struct auth *authstatus,
-                    const char *request,
-                    const char *path,
-                    bool proxy)
-{
-  struct SessionHandle *data = conn->data;
-  const char *auth=NULL;
-  CURLcode result = CURLE_OK;
-#ifdef USE_HTTP_NEGOTIATE
-  struct negotiatedata *negdata = proxy?
-    &data->state.proxyneg:&data->state.negotiate;
-#endif
-
-#ifdef CURL_DISABLE_CRYPTO_AUTH
-  (void)request;
-  (void)path;
-#endif
-
-#ifdef USE_HTTP_NEGOTIATE
-  negdata->state = GSS_AUTHNONE;
-  if((authstatus->picked == CURLAUTH_GSSNEGOTIATE) &&
-     negdata->context && !GSS_ERROR(negdata->status)) {
-    auth="GSS-Negotiate";
-    result = Curl_output_negotiate(conn, proxy);
-    if(result)
-      return result;
-    authstatus->done = TRUE;
-    negdata->state = GSS_AUTHSENT;
-  }
-  else
-#endif
-#ifdef USE_NTLM
-  if(authstatus->picked == CURLAUTH_NTLM) {
-    auth="NTLM";
-    result = Curl_output_ntlm(conn, proxy);
-    if(result)
-      return result;
-  }
-  else
-#endif
-#if defined(USE_NTLM) && defined(NTLM_WB_ENABLED)
-  if(authstatus->picked == CURLAUTH_NTLM_WB) {
-    auth="NTLM_WB";
-    result = Curl_output_ntlm_wb(conn, proxy);
-    if(result)
-      return result;
-  }
-  else
-#endif
-#ifndef CURL_DISABLE_CRYPTO_AUTH
-  if(authstatus->picked == CURLAUTH_DIGEST) {
-    auth="Digest";
-    result = Curl_output_digest(conn,
-                                proxy,
-                                (const unsigned char *)request,
-                                (const unsigned char *)path);
-    if(result)
-      return result;
-  }
-  else
-#endif
-  if(authstatus->picked == CURLAUTH_BASIC) {
-    /* Basic */
-    if((proxy && conn->bits.proxy_user_passwd &&
-       !Curl_checkheaders(data, "Proxy-authorization:")) ||
-       (!proxy && conn->bits.user_passwd &&
-       !Curl_checkheaders(data, "Authorization:"))) {
-      auth="Basic";
-      result = http_output_basic(conn, proxy);
-      if(result)
-        return result;
-    }
-    /* NOTE: this function should set 'done' TRUE, as the other auth
-       functions work that way */
-    authstatus->done = TRUE;
-  }
-
-  if(auth) {
-    infof(data, "%s auth using %s with user '%s'\n",
-          proxy?"Proxy":"Server", auth,
-          proxy?(conn->proxyuser?conn->proxyuser:""):
-                (conn->user?conn->user:""));
-    authstatus->multi = (!authstatus->done) ? TRUE : FALSE;
-  }
-  else
-    authstatus->multi = FALSE;
-
-  return CURLE_OK;
-}
-
-/**
- * Curl_http_output_auth() setups the authentication headers for the
- * host/proxy and the correct authentication
- * method. conn->data->state.authdone is set to TRUE when authentication is
- * done.
- *
- * @param conn all information about the current connection
- * @param request pointer to the request keyword
- * @param path pointer to the requested path
- * @param proxytunnel boolean if this is the request setting up a "proxy
- * tunnel"
- *
- * @returns CURLcode
- */
-CURLcode
-Curl_http_output_auth(struct connectdata *conn,
-                      const char *request,
-                      const char *path,
-                      bool proxytunnel) /* TRUE if this is the request setting
-                                           up the proxy tunnel */
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  struct auth *authhost;
-  struct auth *authproxy;
-
-  DEBUGASSERT(data);
-
-  authhost = &data->state.authhost;
-  authproxy = &data->state.authproxy;
-
-  if((conn->bits.httpproxy && conn->bits.proxy_user_passwd) ||
-     conn->bits.user_passwd)
-    /* continue please */ ;
-  else {
-    authhost->done = TRUE;
-    authproxy->done = TRUE;
-    return CURLE_OK; /* no authentication with no user or password */
-  }
-
-  if(authhost->want && !authhost->picked)
-    /* The app has selected one or more methods, but none has been picked
-       so far by a server round-trip. Then we set the picked one to the
-       want one, and if this is one single bit it'll be used instantly. */
-    authhost->picked = authhost->want;
-
-  if(authproxy->want && !authproxy->picked)
-    /* The app has selected one or more methods, but none has been picked so
-       far by a proxy round-trip. Then we set the picked one to the want one,
-       and if this is one single bit it'll be used instantly. */
-    authproxy->picked = authproxy->want;
-
-#ifndef CURL_DISABLE_PROXY
-  /* Send proxy authentication header if needed */
-  if(conn->bits.httpproxy &&
-      (conn->bits.tunnel_proxy == proxytunnel)) {
-    result = output_auth_headers(conn, authproxy, request, path, TRUE);
-    if(result)
-      return result;
-  }
-  else
-#else
-  (void)proxytunnel;
-#endif /* CURL_DISABLE_PROXY */
-    /* we have no proxy so let's pretend we're done authenticating
-       with it */
-    authproxy->done = TRUE;
-
-  /* To prevent the user+password to get sent to other than the original
-     host due to a location-follow, we do some weirdo checks here */
-  if(!data->state.this_is_a_follow ||
-     conn->bits.netrc ||
-     !data->state.first_host ||
-     data->set.http_disable_hostname_check_before_authentication ||
-     Curl_raw_equal(data->state.first_host, conn->host.name)) {
-    result = output_auth_headers(conn, authhost, request, path, FALSE);
-  }
-  else
-    authhost->done = TRUE;
-
-  return result;
-}
-
-
-/*
- * Curl_http_input_auth() deals with Proxy-Authenticate: and WWW-Authenticate:
- * headers. They are dealt with both in the curl_transfer.c main loop and in
- * the proxy CONNECT loop.
- */
-
-CURLcode Curl_http_input_auth(struct connectdata *conn,
-                              int httpcode,
-                              const char *header) /* the first non-space */
-{
-  /*
-   * This resource requires authentication
-   */
-  struct SessionHandle *data = conn->data;
-
-  unsigned long *availp;
-  const char *start;
-  struct auth *authp;
-
-  if(httpcode == 407) {
-    start = header+strlen("Proxy-authenticate:");
-    availp = &data->info.proxyauthavail;
-    authp = &data->state.authproxy;
-  }
-  else {
-    start = header+strlen("WWW-Authenticate:");
-    availp = &data->info.httpauthavail;
-    authp = &data->state.authhost;
-  }
-
-  /* pass all white spaces */
-  while(*start && ISSPACE(*start))
-    start++;
-
-  /*
-   * Here we check if we want the specific single authentication (using ==) and
-   * if we do, we initiate usage of it.
-   *
-   * If the provided authentication is wanted as one out of several accepted
-   * types (using &), we OR this authentication type to the authavail
-   * variable.
-   *
-   * Note:
-   *
-   * ->picked is first set to the 'want' value (one or more bits) before the
-   * request is sent, and then it is again set _after_ all response 401/407
-   * headers have been received but then only to a single preferred method
-   * (bit).
-   *
-   */
-
-  while(*start) {
-#ifdef USE_HTTP_NEGOTIATE
-    if(checkprefix("GSS-Negotiate", start) ||
-       checkprefix("Negotiate", start)) {
-      int neg;
-      *availp |= CURLAUTH_GSSNEGOTIATE;
-      authp->avail |= CURLAUTH_GSSNEGOTIATE;
-
-      if(authp->picked == CURLAUTH_GSSNEGOTIATE) {
-        if(data->state.negotiate.state == GSS_AUTHSENT) {
-          /* if we sent GSS authentication in the outgoing request and we get
-             this back, we're in trouble */
-          infof(data, "Authentication problem. Ignoring this.\n");
-          data->state.authproblem = TRUE;
-        }
-        else {
-          neg = Curl_input_negotiate(conn, (bool)(httpcode == 407), start);
-          if(neg == 0) {
-            DEBUGASSERT(!data->req.newurl);
-            data->req.newurl = strdup(data->change.url);
-            if(!data->req.newurl)
-              return CURLE_OUT_OF_MEMORY;
-            data->state.authproblem = FALSE;
-            /* we received GSS auth info and we dealt with it fine */
-            data->state.negotiate.state = GSS_AUTHRECV;
-          }
-          else
-            data->state.authproblem = TRUE;
-        }
-      }
-    }
-    else
-#endif
-#ifdef USE_NTLM
-      /* NTLM support requires the SSL crypto libs */
-      if(checkprefix("NTLM", start)) {
-        *availp |= CURLAUTH_NTLM;
-        authp->avail |= CURLAUTH_NTLM;
-        if(authp->picked == CURLAUTH_NTLM ||
-           authp->picked == CURLAUTH_NTLM_WB) {
-          /* NTLM authentication is picked and activated */
-          CURLcode ntlm =
-            Curl_input_ntlm(conn, (httpcode == 407)?TRUE:FALSE, start);
-          if(CURLE_OK == ntlm) {
-            data->state.authproblem = FALSE;
-#ifdef NTLM_WB_ENABLED
-            if(authp->picked == CURLAUTH_NTLM_WB) {
-              *availp &= ~CURLAUTH_NTLM;
-              authp->avail &= ~CURLAUTH_NTLM;
-              *availp |= CURLAUTH_NTLM_WB;
-              authp->avail |= CURLAUTH_NTLM_WB;
-
-              /* Get the challenge-message which will be passed to
-               * ntlm_auth for generating the type 3 message later */
-              while(*start && ISSPACE(*start))
-                start++;
-              if(checkprefix("NTLM", start)) {
-                start += strlen("NTLM");
-                while(*start && ISSPACE(*start))
-                  start++;
-                if(*start)
-                  if((conn->challenge_header = strdup(start)) == NULL)
-                    return CURLE_OUT_OF_MEMORY;
-              }
-            }
-#endif
-          }
-          else {
-            infof(data, "Authentication problem. Ignoring this.\n");
-            data->state.authproblem = TRUE;
-          }
-        }
-      }
-      else
-#endif
-#ifndef CURL_DISABLE_CRYPTO_AUTH
-        if(checkprefix("Digest", start)) {
-          if((authp->avail & CURLAUTH_DIGEST) != 0) {
-            infof(data, "Ignoring duplicate digest auth header.\n");
-          }
-          else {
-            CURLdigest dig;
-            *availp |= CURLAUTH_DIGEST;
-            authp->avail |= CURLAUTH_DIGEST;
-
-            /* We call this function on input Digest headers even if Digest
-             * authentication isn't activated yet, as we need to store the
-             * incoming data from this header in case we are gonna use
-             * Digest. */
-            dig = Curl_input_digest(conn, (httpcode == 407)?TRUE:FALSE, start);
-
-            if(CURLDIGEST_FINE != dig) {
-              infof(data, "Authentication problem. Ignoring this.\n");
-              data->state.authproblem = TRUE;
-            }
-          }
-        }
-        else
-#endif
-          if(checkprefix("Basic", start)) {
-            *availp |= CURLAUTH_BASIC;
-            authp->avail |= CURLAUTH_BASIC;
-            if(authp->picked == CURLAUTH_BASIC) {
-              /* We asked for Basic authentication but got a 40X back
-                 anyway, which basically means our name+password isn't
-                 valid. */
-              authp->avail = CURLAUTH_NONE;
-              infof(data, "Authentication problem. Ignoring this.\n");
-              data->state.authproblem = TRUE;
-            }
-          }
-
-    /* there may be multiple methods on one line, so keep reading */
-    while(*start && *start != ',') /* read up to the next comma */
-      start++;
-    if(*start == ',') /* if we're on a comma, skip it */
-      start++;
-    while(*start && ISSPACE(*start))
-      start++;
-  }
-  return CURLE_OK;
-}
-
-/**
- * http_should_fail() determines whether an HTTP response has gotten us
- * into an error state or not.
- *
- * @param conn all information about the current connection
- *
- * @retval 0 communications should continue
- *
- * @retval 1 communications should not continue
- */
-static int http_should_fail(struct connectdata *conn)
-{
-  struct SessionHandle *data;
-  int httpcode;
-
-  DEBUGASSERT(conn);
-  data = conn->data;
-  DEBUGASSERT(data);
-
-  httpcode = data->req.httpcode;
-
-  /*
-  ** If we haven't been asked to fail on error,
-  ** don't fail.
-  */
-  if(!data->set.http_fail_on_error)
-    return 0;
-
-  /*
-  ** Any code < 400 is never terminal.
-  */
-  if(httpcode < 400)
-    return 0;
-
-  if(data->state.resume_from &&
-     (data->set.httpreq==HTTPREQ_GET) &&
-     (httpcode == 416)) {
-    /* "Requested Range Not Satisfiable", just proceed and
-       pretend this is no error */
-    return 0;
-  }
-
-  /*
-  ** Any code >= 400 that's not 401 or 407 is always
-  ** a terminal error
-  */
-  if((httpcode != 401) &&
-      (httpcode != 407))
-    return 1;
-
-  /*
-  ** All we have left to deal with is 401 and 407
-  */
-  DEBUGASSERT((httpcode == 401) || (httpcode == 407));
-
-  /*
-  ** Examine the current authentication state to see if this
-  ** is an error.  The idea is for this function to get
-  ** called after processing all the headers in a response
-  ** message.  So, if we've been to asked to authenticate a
-  ** particular stage, and we've done it, we're OK.  But, if
-  ** we're already completely authenticated, it's not OK to
-  ** get another 401 or 407.
-  **
-  ** It is possible for authentication to go stale such that
-  ** the client needs to reauthenticate.  Once that info is
-  ** available, use it here.
-  */
-
-  /*
-  ** Either we're not authenticating, or we're supposed to
-  ** be authenticating something else.  This is an error.
-  */
-  if((httpcode == 401) && !conn->bits.user_passwd)
-    return TRUE;
-  if((httpcode == 407) && !conn->bits.proxy_user_passwd)
-    return TRUE;
-
-  return data->state.authproblem;
-}
-
-/*
- * readmoredata() is a "fread() emulation" to provide POST and/or request
- * data. It is used when a huge POST is to be made and the entire chunk wasn't
- * sent in the first send(). This function will then be called from the
- * curl_transfer.c loop when more data is to be sent to the peer.
- *
- * Returns the amount of bytes it filled the buffer with.
- */
-static size_t readmoredata(char *buffer,
-                           size_t size,
-                           size_t nitems,
-                           void *userp)
-{
-  struct connectdata *conn = (struct connectdata *)userp;
-  struct HTTP *http = conn->data->state.proto.http;
-  size_t fullsize = size * nitems;
-
-  if(0 == http->postsize)
-    /* nothing to return */
-    return 0;
-
-  /* make sure that a HTTP request is never sent away chunked! */
-  conn->data->req.forbidchunk = (http->sending == HTTPSEND_REQUEST)?TRUE:FALSE;
-
-  if(http->postsize <= (curl_off_t)fullsize) {
-    memcpy(buffer, http->postdata, (size_t)http->postsize);
-    fullsize = (size_t)http->postsize;
-
-    if(http->backup.postsize) {
-      /* move backup data into focus and continue on that */
-      http->postdata = http->backup.postdata;
-      http->postsize = http->backup.postsize;
-      conn->fread_func = http->backup.fread_func;
-      conn->fread_in = http->backup.fread_in;
-
-      http->sending++; /* move one step up */
-
-      http->backup.postsize=0;
-    }
-    else
-      http->postsize = 0;
-
-    return fullsize;
-  }
-
-  memcpy(buffer, http->postdata, fullsize);
-  http->postdata += fullsize;
-  http->postsize -= fullsize;
-
-  return fullsize;
-}
-
-/* ------------------------------------------------------------------------- */
-/* add_buffer functions */
-
-/*
- * Curl_add_buffer_init() sets up and returns a fine buffer struct
- */
-Curl_send_buffer *Curl_add_buffer_init(void)
-{
-  return calloc(1, sizeof(Curl_send_buffer));
-}
-
-/*
- * Curl_add_buffer_send() sends a header buffer and frees all associated
- * memory.  Body data may be appended to the header data if desired.
- *
- * Returns CURLcode
- */
-CURLcode Curl_add_buffer_send(Curl_send_buffer *in,
-                              struct connectdata *conn,
-
-                               /* add the number of sent bytes to this
-                                  counter */
-                              long *bytes_written,
-
-                               /* how much of the buffer contains body data */
-                              size_t included_body_bytes,
-                              int socketindex)
-
-{
-  ssize_t amount;
-  CURLcode res;
-  char *ptr;
-  size_t size;
-  struct HTTP *http = conn->data->state.proto.http;
-  size_t sendsize;
-  curl_socket_t sockfd;
-  size_t headersize;
-
-  DEBUGASSERT(socketindex <= SECONDARYSOCKET);
-
-  sockfd = conn->sock[socketindex];
-
-  /* The looping below is required since we use non-blocking sockets, but due
-     to the circumstances we will just loop and try again and again etc */
-
-  ptr = in->buffer;
-  size = in->size_used;
-
-  headersize = size - included_body_bytes; /* the initial part that isn't body
-                                              is header */
-
-  DEBUGASSERT(size > included_body_bytes);
-
-  res = Curl_convert_to_network(conn->data, ptr, headersize);
-  /* Curl_convert_to_network calls failf if unsuccessful */
-  if(res) {
-    /* conversion failed, free memory and return to the caller */
-    if(in->buffer)
-      free(in->buffer);
-    free(in);
-    return res;
-  }
-
-  if(conn->handler->flags & PROTOPT_SSL) {
-    /* We never send more than CURL_MAX_WRITE_SIZE bytes in one single chunk
-       when we speak HTTPS, as if only a fraction of it is sent now, this data
-       needs to fit into the normal read-callback buffer later on and that
-       buffer is using this size.
-    */
-
-    sendsize= (size > CURL_MAX_WRITE_SIZE)?CURL_MAX_WRITE_SIZE:size;
-
-    /* OpenSSL is very picky and we must send the SAME buffer pointer to the
-       library when we attempt to re-send this buffer. Sending the same data
-       is not enough, we must use the exact same address. For this reason, we
-       must copy the data to the uploadbuffer first, since that is the buffer
-       we will be using if this send is retried later.
-    */
-    memcpy(conn->data->state.uploadbuffer, ptr, sendsize);
-    ptr = conn->data->state.uploadbuffer;
-  }
-  else
-    sendsize = size;
-
-  res = Curl_write(conn, sockfd, ptr, sendsize, &amount);
-
-  if(CURLE_OK == res) {
-    /*
-     * Note that we may not send the entire chunk at once, and we have a set
-     * number of data bytes at the end of the big buffer (out of which we may
-     * only send away a part).
-     */
-    /* how much of the header that was sent */
-    size_t headlen = (size_t)amount>headersize?headersize:(size_t)amount;
-    size_t bodylen = amount - headlen;
-
-    if(conn->data->set.verbose) {
-      /* this data _may_ contain binary stuff */
-      Curl_debug(conn->data, CURLINFO_HEADER_OUT, ptr, headlen, conn);
-      if(bodylen) {
-        /* there was body data sent beyond the initial header part, pass that
-           on to the debug callback too */
-        Curl_debug(conn->data, CURLINFO_DATA_OUT,
-                   ptr+headlen, bodylen, conn);
-      }
-    }
-    if(bodylen)
-      /* since we sent a piece of the body here, up the byte counter for it
-         accordingly */
-      http->writebytecount += bodylen;
-
-    /* 'amount' can never be a very large value here so typecasting it so a
-       signed 31 bit value should not cause problems even if ssize_t is
-       64bit */
-    *bytes_written += (long)amount;
-
-    if(http) {
-      if((size_t)amount != size) {
-        /* The whole request could not be sent in one system call. We must
-           queue it up and send it later when we get the chance. We must not
-           loop here and wait until it might work again. */
-
-        size -= amount;
-
-        ptr = in->buffer + amount;
-
-        /* backup the currently set pointers */
-        http->backup.fread_func = conn->fread_func;
-        http->backup.fread_in = conn->fread_in;
-        http->backup.postdata = http->postdata;
-        http->backup.postsize = http->postsize;
-
-        /* set the new pointers for the request-sending */
-        conn->fread_func = (curl_read_callback)readmoredata;
-        conn->fread_in = (void *)conn;
-        http->postdata = ptr;
-        http->postsize = (curl_off_t)size;
-
-        http->send_buffer = in;
-        http->sending = HTTPSEND_REQUEST;
-
-        return CURLE_OK;
-      }
-      http->sending = HTTPSEND_BODY;
-      /* the full buffer was sent, clean up and return */
-    }
-    else {
-      if((size_t)amount != size)
-        /* We have no continue-send mechanism now, fail. This can only happen
-           when this function is used from the CONNECT sending function. We
-           currently (stupidly) assume that the whole request is always sent
-           away in the first single chunk.
-
-           This needs FIXing.
-        */
-        return CURLE_SEND_ERROR;
-      else
-        conn->writechannel_inuse = FALSE;
-    }
-  }
-  if(in->buffer)
-    free(in->buffer);
-  free(in);
-
-  return res;
-}
-
-
-/*
- * add_bufferf() add the formatted input to the buffer.
- */
-CURLcode Curl_add_bufferf(Curl_send_buffer *in, const char *fmt, ...)
-{
-  char *s;
-  va_list ap;
-  va_start(ap, fmt);
-  s = vaprintf(fmt, ap); /* this allocs a new string to append */
-  va_end(ap);
-
-  if(s) {
-    CURLcode result = Curl_add_buffer(in, s, strlen(s));
-    free(s);
-    return result;
-  }
-  /* If we failed, we cleanup the whole buffer and return error */
-  if(in->buffer)
-    free(in->buffer);
-  free(in);
-  return CURLE_OUT_OF_MEMORY;
-}
-
-/*
- * add_buffer() appends a memory chunk to the existing buffer
- */
-CURLcode Curl_add_buffer(Curl_send_buffer *in, const void *inptr, size_t size)
-{
-  char *new_rb;
-  size_t new_size;
-
-  if(~size < in->size_used) {
-    /* If resulting used size of send buffer would wrap size_t, cleanup
-       the whole buffer and return error. Otherwise the required buffer
-       size will fit into a single allocatable memory chunk */
-    Curl_safefree(in->buffer);
-    free(in);
-    return CURLE_OUT_OF_MEMORY;
-  }
-
-  if(!in->buffer ||
-     ((in->size_used + size) > (in->size_max - 1))) {
-
-    /* If current buffer size isn't enough to hold the result, use a
-       buffer size that doubles the required size. If this new size
-       would wrap size_t, then just use the largest possible one */
-
-    if((size > (size_t)-1/2) || (in->size_used > (size_t)-1/2) ||
-       (~(size*2) < (in->size_used*2)))
-      new_size = (size_t)-1;
-    else
-      new_size = (in->size_used+size)*2;
-
-    if(in->buffer)
-      /* we have a buffer, enlarge the existing one */
-      new_rb = realloc(in->buffer, new_size);
-    else
-      /* create a new buffer */
-      new_rb = malloc(new_size);
-
-    if(!new_rb) {
-      /* If we failed, we cleanup the whole buffer and return error */
-      Curl_safefree(in->buffer);
-      free(in);
-      return CURLE_OUT_OF_MEMORY;
-    }
-
-    in->buffer = new_rb;
-    in->size_max = new_size;
-  }
-  memcpy(&in->buffer[in->size_used], inptr, size);
-
-  in->size_used += size;
-
-  return CURLE_OK;
-}
-
-/* end of the add_buffer functions */
-/* ------------------------------------------------------------------------- */
-
-
-
-/*
- * Curl_compareheader()
- *
- * Returns TRUE if 'headerline' contains the 'header' with given 'content'.
- * Pass headers WITH the colon.
- */
-bool
-Curl_compareheader(const char *headerline, /* line to check */
-                   const char *header,  /* header keyword _with_ colon */
-                   const char *content) /* content string to find */
-{
-  /* RFC2616, section 4.2 says: "Each header field consists of a name followed
-   * by a colon (":") and the field value. Field names are case-insensitive.
-   * The field value MAY be preceded by any amount of LWS, though a single SP
-   * is preferred." */
-
-  size_t hlen = strlen(header);
-  size_t clen;
-  size_t len;
-  const char *start;
-  const char *end;
-
-  if(!Curl_raw_nequal(headerline, header, hlen))
-    return FALSE; /* doesn't start with header */
-
-  /* pass the header */
-  start = &headerline[hlen];
-
-  /* pass all white spaces */
-  while(*start && ISSPACE(*start))
-    start++;
-
-  /* find the end of the header line */
-  end = strchr(start, '\r'); /* lines end with CRLF */
-  if(!end) {
-    /* in case there's a non-standard compliant line here */
-    end = strchr(start, '\n');
-
-    if(!end)
-      /* hm, there's no line ending here, use the zero byte! */
-      end = strchr(start, '\0');
-  }
-
-  len = end-start; /* length of the content part of the input line */
-  clen = strlen(content); /* length of the word to find */
-
-  /* find the content string in the rest of the line */
-  for(;len>=clen;len--, start++) {
-    if(Curl_raw_nequal(start, content, clen))
-      return TRUE; /* match! */
-  }
-
-  return FALSE; /* no match */
-}
-
-/*
- * Curl_http_connect() performs HTTP stuff to do at connect-time, called from
- * the generic Curl_connect().
- */
-CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
-{
-  struct SessionHandle *data;
-  CURLcode result;
-
-  data=conn->data;
-
-  /* We default to persistent connections. We set this already in this connect
-     function to make the re-use checks properly be able to check this bit. */
-  conn->bits.close = FALSE;
-
-  if(data->state.used_interface == Curl_if_multi) {
-    /* when the multi interface is used, the CONNECT procedure might not have
-       been completed */
-    result = Curl_proxy_connect(conn);
-    if(result)
-      return result;
-  }
-
-  if(conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
-    /* nothing else to do except wait right now - we're not done here. */
-    return CURLE_OK;
-
-  if(conn->given->flags & PROTOPT_SSL) {
-    /* perform SSL initialization */
-    if(data->state.used_interface == Curl_if_multi) {
-      result = https_connecting(conn, done);
-      if(result)
-        return result;
-    }
-    else {
-      /* BLOCKING */
-      result = Curl_ssl_connect(conn, FIRSTSOCKET);
-      if(result)
-        return result;
-      *done = TRUE;
-    }
-  }
-  else {
-    *done = TRUE;
-  }
-
-  return CURLE_OK;
-}
-
-/* this returns the socket to wait for in the DO and DOING state for the multi
-   interface and then we're always _sending_ a request and thus we wait for
-   the single socket to become writable only */
-static int http_getsock_do(struct connectdata *conn,
-                           curl_socket_t *socks,
-                           int numsocks)
-{
-  /* write mode */
-  (void)numsocks; /* unused, we trust it to be at least 1 */
-  socks[0] = conn->sock[FIRSTSOCKET];
-  return GETSOCK_WRITESOCK(0);
-}
-
-#ifdef USE_SSL
-static CURLcode https_connecting(struct connectdata *conn, bool *done)
-{
-  CURLcode result;
-  DEBUGASSERT((conn) && (conn->handler->flags & PROTOPT_SSL));
-
-  /* perform SSL initialization for this socket */
-  result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, done);
-  if(result)
-    conn->bits.close = TRUE; /* a failed connection is marked for closure
-                                to prevent (bad) re-use or similar */
-  return result;
-}
-#endif
-
-#if defined(USE_SSLEAY) || defined(USE_GNUTLS) || defined(USE_SCHANNEL) || \
-    defined(USE_DARWINSSL)
-/* This function is for OpenSSL, GnuTLS, darwinssl, and schannel only.
-   It should be made to query the generic SSL layer instead. */
-static int https_getsock(struct connectdata *conn,
-                         curl_socket_t *socks,
-                         int numsocks)
-{
-  if(conn->handler->flags & PROTOPT_SSL) {
-    struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET];
-
-    if(!numsocks)
-      return GETSOCK_BLANK;
-
-    if(connssl->connecting_state == ssl_connect_2_writing) {
-      /* write mode */
-      socks[0] = conn->sock[FIRSTSOCKET];
-      return GETSOCK_WRITESOCK(0);
-    }
-    else if(connssl->connecting_state == ssl_connect_2_reading) {
-      /* read mode */
-      socks[0] = conn->sock[FIRSTSOCKET];
-      return GETSOCK_READSOCK(0);
-    }
-  }
-  return CURLE_OK;
-}
-#else
-#ifdef USE_SSL
-static int https_getsock(struct connectdata *conn,
-                         curl_socket_t *socks,
-                         int numsocks)
-{
-  (void)conn;
-  (void)socks;
-  (void)numsocks;
-  return GETSOCK_BLANK;
-}
-#endif /* USE_SSL */
-#endif /* USE_SSLEAY || USE_GNUTLS || USE_SCHANNEL */
-
-/*
- * Curl_http_done() gets called from Curl_done() after a single HTTP request
- * has been performed.
- */
-
-CURLcode Curl_http_done(struct connectdata *conn,
-                        CURLcode status, bool premature)
-{
-  struct SessionHandle *data = conn->data;
-  struct HTTP *http =data->state.proto.http;
-
-  Curl_unencode_cleanup(conn);
-
-  /* set the proper values (possibly modified on POST) */
-  conn->fread_func = data->set.fread_func; /* restore */
-  conn->fread_in = data->set.in; /* restore */
-  conn->seek_func = data->set.seek_func; /* restore */
-  conn->seek_client = data->set.seek_client; /* restore */
-
-  if(http == NULL)
-    return CURLE_OK;
-
-  if(http->send_buffer) {
-    Curl_send_buffer *buff = http->send_buffer;
-
-    free(buff->buffer);
-    free(buff);
-    http->send_buffer = NULL; /* clear the pointer */
-  }
-
-  if(HTTPREQ_POST_FORM == data->set.httpreq) {
-    data->req.bytecount = http->readbytecount + http->writebytecount;
-
-    Curl_formclean(&http->sendit); /* Now free that whole lot */
-    if(http->form.fp) {
-      /* a file being uploaded was left opened, close it! */
-      fclose(http->form.fp);
-      http->form.fp = NULL;
-    }
-  }
-  else if(HTTPREQ_PUT == data->set.httpreq)
-    data->req.bytecount = http->readbytecount + http->writebytecount;
-
-  if(status != CURLE_OK)
-    return (status);
-
-  if(!premature && /* this check is pointless when DONE is called before the
-                      entire operation is complete */
-     !conn->bits.retry &&
-     ((http->readbytecount +
-       data->req.headerbytecount -
-       data->req.deductheadercount)) <= 0) {
-    /* If this connection isn't simply closed to be retried, AND nothing was
-       read from the HTTP server (that counts), this can't be right so we
-       return an error here */
-    failf(data, "Empty reply from server");
-    return CURLE_GOT_NOTHING;
-  }
-
-  return CURLE_OK;
-}
-
-
-/* Determine if we should use HTTP 1.1 for this request. Reasons to avoid it
-   are if the user specifically requested HTTP 1.0, if the server we are
-   connected to only supports 1.0, or if any server previously contacted to
-   handle this request only supports 1.0. */
-static bool use_http_1_1(const struct SessionHandle *data,
-                         const struct connectdata *conn)
-{
-  return ((data->set.httpversion == CURL_HTTP_VERSION_1_1) ||
-         ((data->set.httpversion != CURL_HTTP_VERSION_1_0) &&
-          ((conn->httpversion == 11) ||
-           ((conn->httpversion != 10) &&
-            (data->state.httpversion != 10))))) ? TRUE : FALSE;
-}
-
-/* check and possibly add an Expect: header */
-static CURLcode expect100(struct SessionHandle *data,
-                          struct connectdata *conn,
-                          Curl_send_buffer *req_buffer)
-{
-  CURLcode result = CURLE_OK;
-  const char *ptr;
-  data->state.expect100header = FALSE; /* default to false unless it is set
-                                          to TRUE below */
-  if(use_http_1_1(data, conn)) {
-    /* if not doing HTTP 1.0 or disabled explicitly, we add a Expect:
-       100-continue to the headers which actually speeds up post operations
-       (as there is one packet coming back from the web server) */
-    ptr = Curl_checkheaders(data, "Expect:");
-    if(ptr) {
-      data->state.expect100header =
-        Curl_compareheader(ptr, "Expect:", "100-continue");
-    }
-    else {
-      result = Curl_add_bufferf(req_buffer,
-                         "Expect: 100-continue\r\n");
-      if(result == CURLE_OK)
-        data->state.expect100header = TRUE;
-    }
-  }
-  return result;
-}
-
-CURLcode Curl_add_custom_headers(struct connectdata *conn,
-                                   Curl_send_buffer *req_buffer)
-{
-  char *ptr;
-  struct curl_slist *headers=conn->data->set.headers;
-
-  while(headers) {
-    ptr = strchr(headers->data, ':');
-    if(ptr) {
-      /* we require a colon for this to be a true header */
-
-      ptr++; /* pass the colon */
-      while(*ptr && ISSPACE(*ptr))
-        ptr++;
-
-      if(*ptr) {
-        /* only send this if the contents was non-blank */
-
-        if(conn->allocptr.host &&
-           /* a Host: header was sent already, don't pass on any custom Host:
-              header as that will produce *two* in the same request! */
-           checkprefix("Host:", headers->data))
-          ;
-        else if(conn->data->set.httpreq == HTTPREQ_POST_FORM &&
-                /* this header (extended by curl_formdata.c) is sent later */
-                checkprefix("Content-Type:", headers->data))
-          ;
-        else if(conn->bits.authneg &&
-                /* while doing auth neg, don't allow the custom length since
-                   we will force length zero then */
-                checkprefix("Content-Length", headers->data))
-          ;
-        else if(conn->allocptr.te &&
-                /* when asking for Transfer-Encoding, don't pass on a custom
-                   Connection: */
-                checkprefix("Connection", headers->data))
-          ;
-        else {
-          CURLcode result = Curl_add_bufferf(req_buffer, "%s\r\n",
-                                             headers->data);
-          if(result)
-            return result;
-        }
-      }
-    }
-    else {
-      ptr = strchr(headers->data, ';');
-      if(ptr) {
-
-        ptr++; /* pass the semicolon */
-        while(*ptr && ISSPACE(*ptr))
-          ptr++;
-
-        if(*ptr) {
-          /* this may be used for something else in the future */
-        }
-        else {
-          if(*(--ptr) == ';') {
-            CURLcode result;
-
-            /* send no-value custom header if terminated by semicolon */
-            *ptr = ':';
-            result = Curl_add_bufferf(req_buffer, "%s\r\n",
-                                             headers->data);
-            if(result)
-              return result;
-          }
-        }
-      }
-    }
-    headers = headers->next;
-  }
-  return CURLE_OK;
-}
-
-CURLcode Curl_add_timecondition(struct SessionHandle *data,
-                                Curl_send_buffer *req_buffer)
-{
-  const struct tm *tm;
-  char *buf = data->state.buffer;
-  CURLcode result = CURLE_OK;
-  struct tm keeptime;
-
-  result = Curl_gmtime(data->set.timevalue, &keeptime);
-  if(result) {
-    failf(data, "Invalid TIMEVALUE");
-    return result;
-  }
-  tm = &keeptime;
-
-  /* The If-Modified-Since header family should have their times set in
-   * GMT as RFC2616 defines: "All HTTP date/time stamps MUST be
-   * represented in Greenwich Mean Time (GMT), without exception. For the
-   * purposes of HTTP, GMT is exactly equal to UTC (Coordinated Universal
-   * Time)." (see page 20 of RFC2616).
-   */
-
-  /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
-  snprintf(buf, BUFSIZE-1,
-           "%s, %02d %s %4d %02d:%02d:%02d GMT",
-           Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
-           tm->tm_mday,
-           Curl_month[tm->tm_mon],
-           tm->tm_year + 1900,
-           tm->tm_hour,
-           tm->tm_min,
-           tm->tm_sec);
-
-  switch(data->set.timecondition) {
-  case CURL_TIMECOND_IFMODSINCE:
-  default:
-    result = Curl_add_bufferf(req_buffer,
-                              "If-Modified-Since: %s\r\n", buf);
-    break;
-  case CURL_TIMECOND_IFUNMODSINCE:
-    result = Curl_add_bufferf(req_buffer,
-                              "If-Unmodified-Since: %s\r\n", buf);
-    break;
-  case CURL_TIMECOND_LASTMOD:
-    result = Curl_add_bufferf(req_buffer,
-                              "Last-Modified: %s\r\n", buf);
-    break;
-  }
-
-  return result;
-}
-
-/*
- * Curl_http() gets called from the generic Curl_do() function when a HTTP
- * request is to be performed. This creates and sends a properly constructed
- * HTTP request.
- */
-CURLcode Curl_http(struct connectdata *conn, bool *done)
-{
-  struct SessionHandle *data=conn->data;
-  CURLcode result=CURLE_OK;
-  struct HTTP *http;
-  const char *ppath = data->state.path;
-  bool paste_ftp_userpwd = FALSE;
-  char ftp_typecode[sizeof("/;type=?")] = "";
-  const char *host = conn->host.name;
-  const char *te = ""; /* transfer-encoding */
-  const char *ptr;
-  const char *request;
-  Curl_HttpReq httpreq = data->set.httpreq;
-  char *addcookies = NULL;
-  curl_off_t included_body = 0;
-  const char *httpstring;
-  Curl_send_buffer *req_buffer;
-  curl_off_t postsize = 0; /* curl_off_t to handle large file sizes */
-  int seekerr = CURL_SEEKFUNC_OK;
-
-  /* Always consider the DO phase done after this function call, even if there
-     may be parts of the request that is not yet sent, since we can deal with
-     the rest of the request in the PERFORM phase. */
-  *done = TRUE;
-
-  /* If there already is a protocol-specific struct allocated for this
-     sessionhandle, deal with it */
-  Curl_reset_reqproto(conn);
-
-  if(!data->state.proto.http) {
-    /* Only allocate this struct if we don't already have it! */
-
-    http = calloc(1, sizeof(struct HTTP));
-    if(!http)
-      return CURLE_OUT_OF_MEMORY;
-    data->state.proto.http = http;
-  }
-  else
-    http = data->state.proto.http;
-
-  if(!data->state.this_is_a_follow) {
-    /* this is not a followed location, get the original host name */
-    if(data->state.first_host)
-      /* Free to avoid leaking memory on multiple requests*/
-      free(data->state.first_host);
-
-    data->state.first_host = strdup(conn->host.name);
-    if(!data->state.first_host)
-      return CURLE_OUT_OF_MEMORY;
-  }
-  http->writebytecount = http->readbytecount = 0;
-
-  if((conn->handler->protocol&(CURLPROTO_HTTP|CURLPROTO_FTP)) &&
-     data->set.upload) {
-    httpreq = HTTPREQ_PUT;
-  }
-
-  /* Now set the 'request' pointer to the proper request string */
-  if(data->set.str[STRING_CUSTOMREQUEST])
-    request = data->set.str[STRING_CUSTOMREQUEST];
-  else {
-    if(data->set.opt_no_body)
-      request = "HEAD";
-    else {
-      DEBUGASSERT((httpreq > HTTPREQ_NONE) && (httpreq < HTTPREQ_LAST));
-      switch(httpreq) {
-      case HTTPREQ_POST:
-      case HTTPREQ_POST_FORM:
-        request = "POST";
-        break;
-      case HTTPREQ_PUT:
-        request = "PUT";
-        break;
-      default: /* this should never happen */
-      case HTTPREQ_GET:
-        request = "GET";
-        break;
-      case HTTPREQ_HEAD:
-        request = "HEAD";
-        break;
-      }
-    }
-  }
-
-  /* The User-Agent string might have been allocated in curl_url.c already,
-     because it might have been used in the proxy connect, but if we have
-     got a header with the user-agent string specified, we erase the
-     previously made string here. */
-  if(Curl_checkheaders(data, "User-Agent:") && conn->allocptr.uagent) {
-    free(conn->allocptr.uagent);
-    conn->allocptr.uagent=NULL;
-  }
-
-  /* setup the authentication headers */
-  result = Curl_http_output_auth(conn, request, ppath, FALSE);
-  if(result)
-    return result;
-
-  if((data->state.authhost.multi || data->state.authproxy.multi) &&
-     (httpreq != HTTPREQ_GET) &&
-     (httpreq != HTTPREQ_HEAD)) {
-    /* Auth is required and we are not authenticated yet. Make a PUT or POST
-       with content-length zero as a "probe". */
-    conn->bits.authneg = TRUE;
-  }
-  else
-    conn->bits.authneg = FALSE;
-
-  Curl_safefree(conn->allocptr.ref);
-  if(data->change.referer && !Curl_checkheaders(data, "Referer:"))
-    conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer);
-  else
-    conn->allocptr.ref = NULL;
-
-  if(data->set.str[STRING_COOKIE] && !Curl_checkheaders(data, "Cookie:"))
-    addcookies = data->set.str[STRING_COOKIE];
-
-  if(!Curl_checkheaders(data, "Accept-Encoding:") &&
-     data->set.str[STRING_ENCODING]) {
-    Curl_safefree(conn->allocptr.accept_encoding);
-    conn->allocptr.accept_encoding =
-      aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
-    if(!conn->allocptr.accept_encoding)
-      return CURLE_OUT_OF_MEMORY;
-  }
-
-#ifdef HAVE_LIBZ
-  /* we only consider transfer-encoding magic if libz support is built-in */
-
-  if(!Curl_checkheaders(data, "TE:") && data->set.http_transfer_encoding) {
-    /* When we are to insert a TE: header in the request, we must also insert
-       TE in a Connection: header, so we need to merge the custom provided
-       Connection: header and prevent the original to get sent. Note that if
-       the user has inserted his/hers own TE: header we don't do this magic
-       but then assume that the user will handle it all! */
-    char *cptr = Curl_checkheaders(data, "Connection:");
-#define TE_HEADER "TE: gzip\r\n"
-
-    Curl_safefree(conn->allocptr.te);
-
-    /* Create the (updated) Connection: header */
-    conn->allocptr.te = cptr? aprintf("%s, TE\r\n" TE_HEADER, cptr):
-      strdup("Connection: TE\r\n" TE_HEADER);
-
-    if(!conn->allocptr.te)
-      return CURLE_OUT_OF_MEMORY;
-  }
-#endif
-
-  ptr = Curl_checkheaders(data, "Transfer-Encoding:");
-  if(ptr) {
-    /* Some kind of TE is requested, check if 'chunked' is chosen */
-    data->req.upload_chunky =
-      Curl_compareheader(ptr, "Transfer-Encoding:", "chunked");
-  }
-  else {
-    if((conn->handler->protocol&CURLPROTO_HTTP) &&
-       data->set.upload &&
-       (data->set.infilesize == -1)) {
-      if(conn->bits.authneg)
-        /* don't enable chunked during auth neg */
-        ;
-      else if(use_http_1_1(data, conn)) {
-        /* HTTP, upload, unknown file size and not HTTP 1.0 */
-        data->req.upload_chunky = TRUE;
-      }
-      else {
-        failf(data, "Chunky upload is not supported by HTTP 1.0");
-        return CURLE_UPLOAD_FAILED;
-      }
-    }
-    else {
-      /* else, no chunky upload */
-      data->req.upload_chunky = FALSE;
-    }
-
-    if(data->req.upload_chunky)
-      te = "Transfer-Encoding: chunked\r\n";
-  }
-
-  Curl_safefree(conn->allocptr.host);
-
-  ptr = Curl_checkheaders(data, "Host:");
-  if(ptr && (!data->state.this_is_a_follow ||
-             Curl_raw_equal(data->state.first_host, conn->host.name))) {
-#if !defined(CURL_DISABLE_COOKIES)
-    /* If we have a given custom Host: header, we extract the host name in
-       order to possibly use it for cookie reasons later on. We only allow the
-       custom Host: header if this is NOT a redirect, as setting Host: in the
-       redirected request is being out on thin ice. Except if the host name
-       is the same as the first one! */
-    char *cookiehost = copy_header_value(ptr);
-    if(!cookiehost)
-      return CURLE_OUT_OF_MEMORY;
-    if(!*cookiehost)
-      /* ignore empty data */
-      free(cookiehost);
-    else {
-      /* If the host begins with '[', we start searching for the port after
-         the bracket has been closed */
-      int startsearch = 0;
-      if(*cookiehost == '[') {
-        char *closingbracket;
-        /* since the 'cookiehost' is an allocated memory area that will be
-           freed later we cannot simply increment the pointer */
-        memmove(cookiehost, cookiehost + 1, strlen(cookiehost) - 1);
-        closingbracket = strchr(cookiehost, ']');
-        if(closingbracket)
-          *closingbracket = 0;
-      }
-      else {
-        char *colon = strchr(cookiehost + startsearch, ':');
-        if(colon)
-          *colon = 0; /* The host must not include an embedded port number */
-      }
-      Curl_safefree(conn->allocptr.cookiehost);
-      conn->allocptr.cookiehost = cookiehost;
-    }
-#endif
-
-    conn->allocptr.host = NULL;
-  }
-  else {
-    /* When building Host: headers, we must put the host name within
-       [brackets] if the host name is a plain IPv6-address. RFC2732-style. */
-
-    if(((conn->given->protocol&CURLPROTO_HTTPS) &&
-        (conn->remote_port == PORT_HTTPS)) ||
-       ((conn->given->protocol&CURLPROTO_HTTP) &&
-        (conn->remote_port == PORT_HTTP)) )
-      /* if(HTTPS on port 443) OR (HTTP on port 80) then don't include
-         the port number in the host string */
-      conn->allocptr.host = aprintf("Host: %s%s%s\r\n",
-                                    conn->bits.ipv6_ip?"[":"",
-                                    host,
-                                    conn->bits.ipv6_ip?"]":"");
-    else
-      conn->allocptr.host = aprintf("Host: %s%s%s:%hu\r\n",
-                                    conn->bits.ipv6_ip?"[":"",
-                                    host,
-                                    conn->bits.ipv6_ip?"]":"",
-                                    conn->remote_port);
-
-    if(!conn->allocptr.host)
-      /* without Host: we can't make a nice request */
-      return CURLE_OUT_OF_MEMORY;
-  }
-
-#ifndef CURL_DISABLE_PROXY
-  if(conn->bits.httpproxy && !conn->bits.tunnel_proxy)  {
-    /* Using a proxy but does not tunnel through it */
-
-    /* The path sent to the proxy is in fact the entire URL. But if the remote
-       host is a IDN-name, we must make sure that the request we produce only
-       uses the encoded host name! */
-    if(conn->host.dispname != conn->host.name) {
-      char *url = data->change.url;
-      ptr = strstr(url, conn->host.dispname);
-      if(ptr) {
-        /* This is where the display name starts in the URL, now replace this
-           part with the encoded name. TODO: This method of replacing the host
-           name is rather crude as I believe there's a slight risk that the
-           user has entered a user name or password that contain the host name
-           string. */
-        size_t currlen = strlen(conn->host.dispname);
-        size_t newlen = strlen(conn->host.name);
-        size_t urllen = strlen(url);
-
-        char *newurl;
-
-        newurl = malloc(urllen + newlen - currlen + 1);
-        if(newurl) {
-          /* copy the part before the host name */
-          memcpy(newurl, url, ptr - url);
-          /* append the new host name instead of the old */
-          memcpy(newurl + (ptr - url), conn->host.name, newlen);
-          /* append the piece after the host name */
-          memcpy(newurl + newlen + (ptr - url),
-                 ptr + currlen, /* copy the trailing zero byte too */
-                 urllen - (ptr-url) - currlen + 1);
-          if(data->change.url_alloc) {
-            Curl_safefree(data->change.url);
-            data->change.url_alloc = FALSE;
-          }
-          data->change.url = newurl;
-          data->change.url_alloc = TRUE;
-        }
-        else
-          return CURLE_OUT_OF_MEMORY;
-      }
-    }
-    ppath = data->change.url;
-    if(checkprefix("ftp://", ppath)) {
-      if(data->set.proxy_transfer_mode) {
-        /* when doing ftp, append ;type=<a|i> if not present */
-        char *type = strstr(ppath, ";type=");
-        if(type && type[6] && type[7] == 0) {
-          switch (Curl_raw_toupper(type[6])) {
-          case 'A':
-          case 'D':
-          case 'I':
-            break;
-          default:
-            type = NULL;
-          }
-        }
-        if(!type) {
-          char *p = ftp_typecode;
-          /* avoid sending invalid URLs like ftp://example.com;type=i if the
-           * user specified ftp://example.com without the slash */
-          if(!*data->state.path && ppath[strlen(ppath) - 1] != '/') {
-            *p++ = '/';
-          }
-          snprintf(p, sizeof(ftp_typecode) - 1, ";type=%c",
-                   data->set.prefer_ascii ? 'a' : 'i');
-        }
-      }
-      if(conn->bits.user_passwd && !conn->bits.userpwd_in_url)
-        paste_ftp_userpwd = TRUE;
-    }
-  }
-#endif /* CURL_DISABLE_PROXY */
-
-  if(HTTPREQ_POST_FORM == httpreq) {
-    /* we must build the whole post sequence first, so that we have a size of
-       the whole transfer before we start to send it */
-    result = Curl_getformdata(data, &http->sendit, data->set.httppost,
-                              Curl_checkheaders(data, "Content-Type:"),
-                              &http->postsize);
-    if(result)
-      return result;
-  }
-
-  http->p_accept = Curl_checkheaders(data, "Accept:")?NULL:"Accept: */*\r\n";
-
-  if(( (HTTPREQ_POST == httpreq) ||
-       (HTTPREQ_POST_FORM == httpreq) ||
-       (HTTPREQ_PUT == httpreq) ) &&
-     data->state.resume_from) {
-    /**********************************************************************
-     * Resuming upload in HTTP means that we PUT or POST and that we have
-     * got a resume_from value set. The resume value has already created
-     * a Range: header that will be passed along. We need to "fast forward"
-     * the file the given number of bytes and decrease the assume upload
-     * file size before we continue this venture in the dark lands of HTTP.
-     *********************************************************************/
-
-    if(data->state.resume_from < 0 ) {
-      /*
-       * This is meant to get the size of the present remote-file by itself.
-       * We don't support this now. Bail out!
-       */
-      data->state.resume_from = 0;
-    }
-
-    if(data->state.resume_from && !data->state.this_is_a_follow) {
-      /* do we still game? */
-
-      /* Now, let's read off the proper amount of bytes from the
-         input. */
-      if(conn->seek_func) {
-        seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
-                                  SEEK_SET);
-      }
-
-      if(seekerr != CURL_SEEKFUNC_OK) {
-        if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
-          failf(data, "Could not seek stream");
-          return CURLE_READ_ERROR;
-        }
-        /* when seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
-        else {
-          curl_off_t passed=0;
-          do {
-            size_t readthisamountnow =
-              (data->state.resume_from - passed > CURL_OFF_T_C(BUFSIZE)) ?
-              BUFSIZE : curlx_sotouz(data->state.resume_from - passed);
-
-            size_t actuallyread =
-              data->set.fread_func(data->state.buffer, 1, readthisamountnow,
-                                   data->set.in);
-
-            passed += actuallyread;
-            if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
-              /* this checks for greater-than only to make sure that the
-                 CURL_READFUNC_ABORT return code still aborts */
-              failf(data, "Could only read %" FORMAT_OFF_T
-                    " bytes from the input",
-                    passed);
-              return CURLE_READ_ERROR;
-            }
-          } while(passed < data->state.resume_from);
-        }
-      }
-
-      /* now, decrease the size of the read */
-      if(data->set.infilesize>0) {
-        data->set.infilesize -= data->state.resume_from;
-
-        if(data->set.infilesize <= 0) {
-          failf(data, "File already completely uploaded");
-          return CURLE_PARTIAL_FILE;
-        }
-      }
-      /* we've passed, proceed as normal */
-    }
-  }
-  if(data->state.use_range) {
-    /*
-     * A range is selected. We use different headers whether we're downloading
-     * or uploading and we always let customized headers override our internal
-     * ones if any such are specified.
-     */
-    if(((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) &&
-       !Curl_checkheaders(data, "Range:")) {
-      /* if a line like this was already allocated, free the previous one */
-      if(conn->allocptr.rangeline)
-        free(conn->allocptr.rangeline);
-      conn->allocptr.rangeline = aprintf("Range: bytes=%s\r\n",
-                                         data->state.range);
-    }
-    else if((httpreq != HTTPREQ_GET) &&
-            !Curl_checkheaders(data, "Content-Range:")) {
-
-      /* if a line like this was already allocated, free the previous one */
-      if(conn->allocptr.rangeline)
-        free(conn->allocptr.rangeline);
-
-      if(data->set.set_resume_from < 0) {
-        /* Upload resume was asked for, but we don't know the size of the
-           remote part so we tell the server (and act accordingly) that we
-           upload the whole file (again) */
-        conn->allocptr.rangeline =
-          aprintf("Content-Range: bytes 0-%" FORMAT_OFF_T
-                  "/%" FORMAT_OFF_T "\r\n",
-                  data->set.infilesize - 1, data->set.infilesize);
-
-      }
-      else if(data->state.resume_from) {
-        /* This is because "resume" was selected */
-        curl_off_t total_expected_size=
-          data->state.resume_from + data->set.infilesize;
-        conn->allocptr.rangeline =
-          aprintf("Content-Range: bytes %s%" FORMAT_OFF_T
-                  "/%" FORMAT_OFF_T "\r\n",
-                  data->state.range, total_expected_size-1,
-                  total_expected_size);
-      }
-      else {
-        /* Range was selected and then we just pass the incoming range and
-           append total size */
-        conn->allocptr.rangeline =
-          aprintf("Content-Range: bytes %s/%" FORMAT_OFF_T "\r\n",
-                  data->state.range, data->set.infilesize);
-      }
-      if(!conn->allocptr.rangeline)
-        return CURLE_OUT_OF_MEMORY;
-    }
-  }
-
-  /* Use 1.1 unless the user specifically asked for 1.0 or the server only
-     supports 1.0 */
-  httpstring= use_http_1_1(data, conn)?"1.1":"1.0";
-
-  /* initialize a dynamic send-buffer */
-  req_buffer = Curl_add_buffer_init();
-
-  if(!req_buffer)
-    return CURLE_OUT_OF_MEMORY;
-
-  /* add the main request stuff */
-  /* GET/HEAD/POST/PUT */
-  result = Curl_add_bufferf(req_buffer, "%s ", request);
-  if(result)
-    return result;
-
-  /* url */
-  if(paste_ftp_userpwd)
-    result = Curl_add_bufferf(req_buffer, "ftp://%s:%s@%s",
-                              conn->user, conn->passwd,
-                              ppath + sizeof("ftp://") - 1);
-  else
-    result = Curl_add_buffer(req_buffer, ppath, strlen(ppath));
-  if(result)
-    return result;
-
-  result =
-    Curl_add_bufferf(req_buffer,
-                     "%s" /* ftp typecode (;type=x) */
-                     " HTTP/%s\r\n" /* HTTP version */
-                     "%s" /* proxyuserpwd */
-                     "%s" /* userpwd */
-                     "%s" /* range */
-                     "%s" /* user agent */
-                     "%s" /* host */
-                     "%s" /* accept */
-                     "%s" /* TE: */
-                     "%s" /* accept-encoding */
-                     "%s" /* referer */
-                     "%s" /* Proxy-Connection */
-                     "%s",/* transfer-encoding */
-
-                     ftp_typecode,
-                     httpstring,
-                     conn->allocptr.proxyuserpwd?
-                     conn->allocptr.proxyuserpwd:"",
-                     conn->allocptr.userpwd?conn->allocptr.userpwd:"",
-                     (data->state.use_range && conn->allocptr.rangeline)?
-                     conn->allocptr.rangeline:"",
-                     (data->set.str[STRING_USERAGENT] &&
-                      *data->set.str[STRING_USERAGENT] &&
-                      conn->allocptr.uagent)?
-                     conn->allocptr.uagent:"",
-                     (conn->allocptr.host?conn->allocptr.host:""),
-                     http->p_accept?http->p_accept:"",
-                     conn->allocptr.te?conn->allocptr.te:"",
-                     (data->set.str[STRING_ENCODING] &&
-                      *data->set.str[STRING_ENCODING] &&
-                      conn->allocptr.accept_encoding)?
-                     conn->allocptr.accept_encoding:"",
-                     (data->change.referer && conn->allocptr.ref)?
-                     conn->allocptr.ref:"" /* Referer: <data> */,
-                     (conn->bits.httpproxy &&
-                      !conn->bits.tunnel_proxy &&
-                      !Curl_checkheaders(data, "Proxy-Connection:"))?
-                     "Proxy-Connection: Keep-Alive\r\n":"",
-                     te
-      );
-
-  /*
-   * Free userpwd now --- cannot reuse this for Negotiate and possibly NTLM
-   * with basic and digest, it will be freed anyway by the next request
-   */
-
-  Curl_safefree (conn->allocptr.userpwd);
-  conn->allocptr.userpwd = NULL;
-
-  if(result)
-    return result;
-
-#if !defined(CURL_DISABLE_COOKIES)
-  if(data->cookies || addcookies) {
-    struct Cookie *co=NULL; /* no cookies from start */
-    int count=0;
-
-    if(data->cookies) {
-      Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
-      co = Curl_cookie_getlist(data->cookies,
-                               conn->allocptr.cookiehost?
-                               conn->allocptr.cookiehost:host,
-                               data->state.path,
-                               (conn->handler->protocol&CURLPROTO_HTTPS)?
-                               TRUE:FALSE);
-      Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
-    }
-    if(co) {
-      struct Cookie *store=co;
-      /* now loop through all cookies that matched */
-      while(co) {
-        if(co->value) {
-          if(0 == count) {
-            result = Curl_add_bufferf(req_buffer, "Cookie: ");
-            if(result)
-              break;
-          }
-          result = Curl_add_bufferf(req_buffer,
-                                    "%s%s=%s", count?"; ":"",
-                                    co->name, co->value);
-          if(result)
-            break;
-          count++;
-        }
-        co = co->next; /* next cookie please */
-      }
-      Curl_cookie_freelist(store, FALSE); /* free the cookie list */
-    }
-    if(addcookies && (CURLE_OK == result)) {
-      if(!count)
-        result = Curl_add_bufferf(req_buffer, "Cookie: ");
-      if(CURLE_OK == result) {
-        result = Curl_add_bufferf(req_buffer, "%s%s",
-                                  count?"; ":"",
-                                  addcookies);
-        count++;
-      }
-    }
-    if(count && (CURLE_OK == result))
-      result = Curl_add_buffer(req_buffer, "\r\n", 2);
-
-    if(result)
-      return result;
-  }
-#endif
-
-  if(data->set.timecondition) {
-    result = Curl_add_timecondition(data, req_buffer);
-    if(result)
-      return result;
-  }
-
-  result = Curl_add_custom_headers(conn, req_buffer);
-  if(result)
-    return result;
-
-  http->postdata = NULL;  /* nothing to post at this point */
-  Curl_pgrsSetUploadSize(data, 0); /* upload size is 0 atm */
-
-  /* If 'authdone' is FALSE, we must not set the write socket index to the
-     Curl_transfer() call below, as we're not ready to actually upload any
-     data yet. */
-
-  switch(httpreq) {
-
-  case HTTPREQ_POST_FORM:
-    if(!http->sendit || conn->bits.authneg) {
-      /* nothing to post! */
-      result = Curl_add_bufferf(req_buffer, "Content-Length: 0\r\n\r\n");
-      if(result)
-        return result;
-
-      result = Curl_add_buffer_send(req_buffer, conn,
-                                    &data->info.request_size, 0, FIRSTSOCKET);
-      if(result)
-        failf(data, "Failed sending POST request");
-      else
-        /* setup variables for the upcoming transfer */
-        Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount,
-                            -1, NULL);
-      break;
-    }
-
-    if(Curl_FormInit(&http->form, http->sendit)) {
-      failf(data, "Internal HTTP POST error!");
-      return CURLE_HTTP_POST_ERROR;
-    }
-
-    /* Get the currently set callback function pointer and store that in the
-       form struct since we might want the actual user-provided callback later
-       on. The conn->fread_func pointer itself will be changed for the
-       multipart case to the function that returns a multipart formatted
-       stream. */
-    http->form.fread_func = conn->fread_func;
-
-    /* Set the read function to read from the generated form data */
-    conn->fread_func = (curl_read_callback)Curl_FormReader;
-    conn->fread_in = &http->form;
-
-    http->sending = HTTPSEND_BODY;
-
-    if(!data->req.upload_chunky &&
-       !Curl_checkheaders(data, "Content-Length:")) {
-      /* only add Content-Length if not uploading chunked */
-      result = Curl_add_bufferf(req_buffer,
-                                "Content-Length: %" FORMAT_OFF_T "\r\n",
-                                http->postsize);
-      if(result)
-        return result;
-    }
-
-    result = expect100(data, conn, req_buffer);
-    if(result)
-      return result;
-
-    {
-
-      /* Get Content-Type: line from Curl_formpostheader.
-       */
-      char *contentType;
-      size_t linelength=0;
-      contentType = Curl_formpostheader((void *)&http->form,
-                                        &linelength);
-      if(!contentType) {
-        failf(data, "Could not get Content-Type header line!");
-        return CURLE_HTTP_POST_ERROR;
-      }
-
-      result = Curl_add_buffer(req_buffer, contentType, linelength);
-      if(result)
-        return result;
-    }
-
-    /* make the request end in a true CRLF */
-    result = Curl_add_buffer(req_buffer, "\r\n", 2);
-    if(result)
-      return result;
-
-    /* set upload size to the progress meter */
-    Curl_pgrsSetUploadSize(data, http->postsize);
-
-    /* fire away the whole request to the server */
-    result = Curl_add_buffer_send(req_buffer, conn,
-                                  &data->info.request_size, 0, FIRSTSOCKET);
-    if(result)
-      failf(data, "Failed sending POST request");
-    else
-      /* setup variables for the upcoming transfer */
-      Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
-                          &http->readbytecount, FIRSTSOCKET,
-                          &http->writebytecount);
-
-    if(result) {
-      Curl_formclean(&http->sendit); /* free that whole lot */
-      return result;
-    }
-
-    /* convert the form data */
-    result = Curl_convert_form(data, http->sendit);
-    if(result) {
-      Curl_formclean(&http->sendit); /* free that whole lot */
-      return result;
-    }
-
-    break;
-
-  case HTTPREQ_PUT: /* Let's PUT the data to the server! */
-
-    if(conn->bits.authneg)
-      postsize = 0;
-    else
-      postsize = data->set.infilesize;
-
-    if((postsize != -1) && !data->req.upload_chunky &&
-       !Curl_checkheaders(data, "Content-Length:")) {
-      /* only add Content-Length if not uploading chunked */
-      result = Curl_add_bufferf(req_buffer,
-                                "Content-Length: %" FORMAT_OFF_T "\r\n",
-                                postsize );
-      if(result)
-        return result;
-    }
-
-    result = expect100(data, conn, req_buffer);
-    if(result)
-      return result;
-
-    result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers */
-    if(result)
-      return result;
-
-    /* set the upload size to the progress meter */
-    Curl_pgrsSetUploadSize(data, postsize);
-
-    /* this sends the buffer and frees all the buffer resources */
-    result = Curl_add_buffer_send(req_buffer, conn,
-                                  &data->info.request_size, 0, FIRSTSOCKET);
-    if(result)
-      failf(data, "Failed sending PUT request");
-    else
-      /* prepare for transfer */
-      Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
-                          &http->readbytecount, postsize?FIRSTSOCKET:-1,
-                          postsize?&http->writebytecount:NULL);
-    if(result)
-      return result;
-    break;
-
-  case HTTPREQ_POST:
-    /* this is the simple POST, using x-www-form-urlencoded style */
-
-    if(conn->bits.authneg)
-      postsize = 0;
-    else {
-      /* figure out the size of the postfields */
-      postsize = (data->set.postfieldsize != -1)?
-        data->set.postfieldsize:
-        (data->set.postfields? (curl_off_t)strlen(data->set.postfields):-1);
-    }
-    if(!data->req.upload_chunky) {
-      /* We only set Content-Length and allow a custom Content-Length if
-         we don't upload data chunked, as RFC2616 forbids us to set both
-         kinds of headers (Transfer-Encoding: chunked and Content-Length) */
-
-      if(conn->bits.authneg || !Curl_checkheaders(data, "Content-Length:")) {
-        /* we allow replacing this header if not during auth negotiation,
-           although it isn't very wise to actually set your own */
-        result = Curl_add_bufferf(req_buffer,
-                                  "Content-Length: %" FORMAT_OFF_T"\r\n",
-                                  postsize);
-        if(result)
-          return result;
-      }
-    }
-
-    if(!Curl_checkheaders(data, "Content-Type:")) {
-      result = Curl_add_bufferf(req_buffer,
-                                "Content-Type: application/"
-                                "x-www-form-urlencoded\r\n");
-      if(result)
-        return result;
-    }
-
-    /* For really small posts we don't use Expect: headers at all, and for
-       the somewhat bigger ones we allow the app to disable it. Just make
-       sure that the expect100header is always set to the preferred value
-       here. */
-    ptr = Curl_checkheaders(data, "Expect:");
-    if(ptr) {
-      data->state.expect100header =
-        Curl_compareheader(ptr, "Expect:", "100-continue");
-    }
-    else if(postsize > TINY_INITIAL_POST_SIZE || postsize < 0) {
-      result = expect100(data, conn, req_buffer);
-      if(result)
-        return result;
-    }
-    else
-      data->state.expect100header = FALSE;
-
-    if(data->set.postfields) {
-
-      if(!data->state.expect100header &&
-         (postsize < MAX_INITIAL_POST_SIZE))  {
-        /* if we don't use expect: 100  AND
-           postsize is less than MAX_INITIAL_POST_SIZE
-
-           then append the post data to the HTTP request header. This limit
-           is no magic limit but only set to prevent really huge POSTs to
-           get the data duplicated with malloc() and family. */
-
-        result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
-        if(result)
-          return result;
-
-        if(!data->req.upload_chunky) {
-          /* We're not sending it 'chunked', append it to the request
-             already now to reduce the number if send() calls */
-          result = Curl_add_buffer(req_buffer, data->set.postfields,
-                                   (size_t)postsize);
-          included_body = postsize;
-        }
-        else {
-          if(postsize) {
-            /* Append the POST data chunky-style */
-            result = Curl_add_bufferf(req_buffer, "%x\r\n", (int)postsize);
-            if(CURLE_OK == result) {
-              result = Curl_add_buffer(req_buffer, data->set.postfields,
-                                       (size_t)postsize);
-              if(CURLE_OK == result)
-                 result = Curl_add_buffer(req_buffer, "\r\n", 2);
-              included_body = postsize + 2;
-            }
-          }
-          if(CURLE_OK == result)
-            result = Curl_add_buffer(req_buffer,
-                                     "\x30\x0d\x0a\x0d\x0a", 5);
-          /* 0  CR  LF  CR  LF */
-          included_body += 5;
-        }
-        if(result)
-          return result;
-        /* Make sure the progress information is accurate */
-        Curl_pgrsSetUploadSize(data, postsize);
-      }
-      else {
-        /* A huge POST coming up, do data separate from the request */
-        http->postsize = postsize;
-        http->postdata = data->set.postfields;
-
-        http->sending = HTTPSEND_BODY;
-
-        conn->fread_func = (curl_read_callback)readmoredata;
-        conn->fread_in = (void *)conn;
-
-        /* set the upload size to the progress meter */
-        Curl_pgrsSetUploadSize(data, http->postsize);
-
-        result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
-        if(result)
-          return result;
-      }
-    }
-    else {
-      result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
-      if(result)
-        return result;
-
-      if(data->req.upload_chunky && conn->bits.authneg) {
-        /* Chunky upload is selected and we're negotiating auth still, send
-           end-of-data only */
-        result = Curl_add_buffer(req_buffer,
-                                 "\x30\x0d\x0a\x0d\x0a", 5);
-        /* 0  CR  LF  CR  LF */
-        if(result)
-          return result;
-      }
-
-      else if(data->set.postfieldsize) {
-        /* set the upload size to the progress meter */
-        Curl_pgrsSetUploadSize(data, postsize?postsize:-1);
-
-        /* set the pointer to mark that we will send the post body using the
-           read callback, but only if we're not in authenticate
-           negotiation  */
-        if(!conn->bits.authneg) {
-          http->postdata = (char *)&http->postdata;
-          http->postsize = postsize;
-        }
-      }
-    }
-    /* issue the request */
-    result = Curl_add_buffer_send(req_buffer, conn, &data->info.request_size,
-                                  (size_t)included_body, FIRSTSOCKET);
-
-    if(result)
-      failf(data, "Failed sending HTTP POST request");
-    else
-      Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
-                          &http->readbytecount, http->postdata?FIRSTSOCKET:-1,
-                          http->postdata?&http->writebytecount:NULL);
-    break;
-
-  default:
-    result = Curl_add_buffer(req_buffer, "\r\n", 2);
-    if(result)
-      return result;
-
-    /* issue the request */
-    result = Curl_add_buffer_send(req_buffer, conn,
-                                  &data->info.request_size, 0, FIRSTSOCKET);
-
-    if(result)
-      failf(data, "Failed sending HTTP request");
-    else
-      /* HTTP GET/HEAD download: */
-      Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount,
-                          http->postdata?FIRSTSOCKET:-1,
-                          http->postdata?&http->writebytecount:NULL);
-  }
-  if(result)
-    return result;
-
-  if(http->writebytecount) {
-    /* if a request-body has been sent off, we make sure this progress is noted
-       properly */
-    Curl_pgrsSetUploadCounter(data, http->writebytecount);
-    if(Curl_pgrsUpdate(conn))
-      result = CURLE_ABORTED_BY_CALLBACK;
-
-    if(http->writebytecount >= postsize) {
-      /* already sent the entire request body, mark the "upload" as
-         complete */
-      infof(data, "upload completely sent off: %" FORMAT_OFF_T " out of "
-            "%" FORMAT_OFF_T " bytes\n",
-            http->writebytecount, postsize);
-      data->req.upload_done = TRUE;
-      data->req.keepon &= ~KEEP_SEND; /* we're done writing */
-      data->req.exp100 = EXP100_SEND_DATA; /* already sent */
-    }
-  }
-
-  return result;
-}
-
-/*
- * checkhttpprefix()
- *
- * Returns TRUE if member of the list matches prefix of string
- */
-static bool
-checkhttpprefix(struct SessionHandle *data,
-                const char *s)
-{
-  struct curl_slist *head = data->set.http200aliases;
-  bool rc = FALSE;
-#ifdef CURL_DOES_CONVERSIONS
-  /* convert from the network encoding using a scratch area */
-  char *scratch = strdup(s);
-  if(NULL == scratch) {
-    failf (data, "Failed to allocate memory for conversion!");
-    return FALSE; /* can't return CURLE_OUT_OF_MEMORY so return FALSE */
-  }
-  if(CURLE_OK != Curl_convert_from_network(data, scratch, strlen(s)+1)) {
-    /* Curl_convert_from_network calls failf if unsuccessful */
-    free(scratch);
-    return FALSE; /* can't return CURLE_foobar so return FALSE */
-  }
-  s = scratch;
-#endif /* CURL_DOES_CONVERSIONS */
-
-  while(head) {
-    if(checkprefix(head->data, s)) {
-      rc = TRUE;
-      break;
-    }
-    head = head->next;
-  }
-
-  if(!rc && (checkprefix("HTTP/", s)))
-    rc = TRUE;
-
-#ifdef CURL_DOES_CONVERSIONS
-  free(scratch);
-#endif /* CURL_DOES_CONVERSIONS */
-  return rc;
-}
-
-#ifndef CURL_DISABLE_RTSP
-static bool
-checkrtspprefix(struct SessionHandle *data,
-                const char *s)
-{
-
-#ifdef CURL_DOES_CONVERSIONS
-  /* convert from the network encoding using a scratch area */
-  char *scratch = strdup(s);
-  if(NULL == scratch) {
-    failf (data, "Failed to allocate memory for conversion!");
-    return FALSE; /* can't return CURLE_OUT_OF_MEMORY so return FALSE */
-  }
-  if(CURLE_OK != Curl_convert_from_network(data, scratch, strlen(s)+1)) {
-    /* Curl_convert_from_network calls failf if unsuccessful */
-    free(scratch);
-    return FALSE; /* can't return CURLE_foobar so return FALSE */
-  }
-  s = scratch;
-#else
-  (void)data; /* unused */
-#endif /* CURL_DOES_CONVERSIONS */
-  if(checkprefix("RTSP/", s))
-    return TRUE;
-  else
-    return FALSE;
-}
-#endif /* CURL_DISABLE_RTSP */
-
-static bool
-checkprotoprefix(struct SessionHandle *data, struct connectdata *conn,
-                 const char *s)
-{
-#ifndef CURL_DISABLE_RTSP
-  if(conn->handler->protocol & CURLPROTO_RTSP)
-    return checkrtspprefix(data, s);
-#else
-  (void)conn;
-#endif /* CURL_DISABLE_RTSP */
-
-  return checkhttpprefix(data, s);
-}
-
-/*
- * header_append() copies a chunk of data to the end of the already received
- * header. We make sure that the full string fit in the allocated header
- * buffer, or else we enlarge it.
- */
-static CURLcode header_append(struct SessionHandle *data,
-                              struct SingleRequest *k,
-                              size_t length)
-{
-  if(k->hbuflen + length >= data->state.headersize) {
-    /* We enlarge the header buffer as it is too small */
-    char *newbuff;
-    size_t hbufp_index;
-    size_t newsize;
-
-    if(k->hbuflen + length > CURL_MAX_HTTP_HEADER) {
-      /* The reason to have a max limit for this is to avoid the risk of a bad
-         server feeding libcurl with a never-ending header that will cause
-         reallocs infinitely */
-      failf (data, "Avoided giant realloc for header (max is %d)!",
-             CURL_MAX_HTTP_HEADER);
-      return CURLE_OUT_OF_MEMORY;
-    }
-
-    newsize=CURLMAX((k->hbuflen+ length)*3/2, data->state.headersize*2);
-    hbufp_index = k->hbufp - data->state.headerbuff;
-    newbuff = realloc(data->state.headerbuff, newsize);
-    if(!newbuff) {
-      failf (data, "Failed to alloc memory for big header!");
-      return CURLE_OUT_OF_MEMORY;
-    }
-    data->state.headersize=newsize;
-    data->state.headerbuff = newbuff;
-    k->hbufp = data->state.headerbuff + hbufp_index;
-  }
-  memcpy(k->hbufp, k->str_start, length);
-  k->hbufp += length;
-  k->hbuflen += length;
-  *k->hbufp = 0;
-
-  return CURLE_OK;
-}
-
-static void print_http_error(struct SessionHandle *data)
-{
-  struct SingleRequest *k = &data->req;
-  char *beg = k->p;
-
-  /* make sure that data->req.p points to the HTTP status line */
-  if(!strncmp(beg, "HTTP", 4)) {
-
-    /* skip to HTTP status code */
-    beg = strchr(beg, ' ');
-    if(beg && *++beg) {
-
-      /* find trailing CR */
-      char end_char = '\r';
-      char *end = strchr(beg, end_char);
-      if(!end) {
-        /* try to find LF (workaround for non-compliant HTTP servers) */
-        end_char = '\n';
-        end = strchr(beg, end_char);
-      }
-
-      if(end) {
-        /* temporarily replace CR or LF by NUL and print the error message */
-        *end = '\0';
-        failf(data, "The requested URL returned error: %s", beg);
-
-        /* restore the previously replaced CR or LF */
-        *end = end_char;
-        return;
-      }
-    }
-  }
-
-  /* fall-back to printing the HTTP status code only */
-  failf(data, "The requested URL returned error: %d", k->httpcode);
-}
-
-/*
- * Read any HTTP header lines from the server and pass them to the client app.
- */
-CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
-                                       struct connectdata *conn,
-                                       ssize_t *nread,
-                                       bool *stop_reading)
-{
-  CURLcode result;
-  struct SingleRequest *k = &data->req;
-
-  /* header line within buffer loop */
-  do {
-    size_t rest_length;
-    size_t full_length;
-    int writetype;
-
-    /* str_start is start of line within buf */
-    k->str_start = k->str;
-
-    /* data is in network encoding so use 0x0a instead of '\n' */
-    k->end_ptr = memchr(k->str_start, 0x0a, *nread);
-
-    if(!k->end_ptr) {
-      /* Not a complete header line within buffer, append the data to
-         the end of the headerbuff. */
-      result = header_append(data, k, *nread);
-      if(result)
-        return result;
-
-      if(!k->headerline && (k->hbuflen>5)) {
-        /* make a first check that this looks like a protocol header */
-        if(!checkprotoprefix(data, conn, data->state.headerbuff)) {
-          /* this is not the beginning of a protocol first header line */
-          k->header = FALSE;
-          k->badheader = HEADER_ALLBAD;
-          break;
-        }
-      }
-
-      break; /* read more and try again */
-    }
-
-    /* decrease the size of the remaining (supposed) header line */
-    rest_length = (k->end_ptr - k->str)+1;
-    *nread -= (ssize_t)rest_length;
-
-    k->str = k->end_ptr + 1; /* move past new line */
-
-    full_length = k->str - k->str_start;
-
-    result = header_append(data, k, full_length);
-    if(result)
-      return result;
-
-    k->end_ptr = k->hbufp;
-    k->p = data->state.headerbuff;
-
-    /****
-     * We now have a FULL header line that p points to
-     *****/
-
-    if(!k->headerline) {
-      /* the first read header */
-      if((k->hbuflen>5) &&
-         !checkprotoprefix(data, conn, data->state.headerbuff)) {
-        /* this is not the beginning of a protocol first header line */
-        k->header = FALSE;
-        if(*nread)
-          /* since there's more, this is a partial bad header */
-          k->badheader = HEADER_PARTHEADER;
-        else {
-          /* this was all we read so it's all a bad header */
-          k->badheader = HEADER_ALLBAD;
-          *nread = (ssize_t)rest_length;
-        }
-        break;
-      }
-    }
-
-    /* headers are in network encoding so
-       use 0x0a and 0x0d instead of '\n' and '\r' */
-    if((0x0a == *k->p) || (0x0d == *k->p)) {
-      size_t headerlen;
-      /* Zero-length header line means end of headers! */
-
-#ifdef CURL_DOES_CONVERSIONS
-      if(0x0d == *k->p) {
-        *k->p = '\r'; /* replace with CR in host encoding */
-        k->p++;       /* pass the CR byte */
-      }
-      if(0x0a == *k->p) {
-        *k->p = '\n'; /* replace with LF in host encoding */
-        k->p++;       /* pass the LF byte */
-      }
-#else
-      if('\r' == *k->p)
-        k->p++; /* pass the \r byte */
-      if('\n' == *k->p)
-        k->p++; /* pass the \n byte */
-#endif /* CURL_DOES_CONVERSIONS */
-
-      if(100 <= k->httpcode && 199 >= k->httpcode) {
-        /*
-         * We have made a HTTP PUT or POST and this is 1.1-lingo
-         * that tells us that the server is OK with this and ready
-         * to receive the data.
-         * However, we'll get more headers now so we must get
-         * back into the header-parsing state!
-         */
-        k->header = TRUE;
-        k->headerline = 0; /* restart the header line counter */
-
-        /* if we did wait for this do enable write now! */
-        if(k->exp100) {
-          k->exp100 = EXP100_SEND_DATA;
-          k->keepon |= KEEP_SEND;
-        }
-      }
-      else {
-        k->header = FALSE; /* no more header to parse! */
-
-        if((k->size == -1) && !k->chunk && !conn->bits.close &&
-           (conn->httpversion >= 11) &&
-           !(conn->handler->protocol & CURLPROTO_RTSP) &&
-           data->set.httpreq != HTTPREQ_HEAD) {
-          /* On HTTP 1.1, when connection is not to get closed, but no
-             Content-Length nor Content-Encoding chunked have been
-             received, according to RFC2616 section 4.4 point 5, we
-             assume that the server will close the connection to
-             signal the end of the document. */
-          infof(data, "no chunk, no close, no size. Assume close to "
-                "signal end\n");
-          conn->bits.close = TRUE;
-        }
-      }
-
-      /*
-       * When all the headers have been parsed, see if we should give
-       * up and return an error.
-       */
-      if(http_should_fail(conn)) {
-        failf (data, "The requested URL returned error: %d",
-               k->httpcode);
-        return CURLE_HTTP_RETURNED_ERROR;
-      }
-
-      /* now, only output this if the header AND body are requested:
-       */
-      writetype = CLIENTWRITE_HEADER;
-      if(data->set.include_header)
-        writetype |= CLIENTWRITE_BODY;
-
-      headerlen = k->p - data->state.headerbuff;
-
-      result = Curl_client_write(conn, writetype,
-                                 data->state.headerbuff,
-                                 headerlen);
-      if(result)
-        return result;
-
-      data->info.header_size += (long)headerlen;
-      data->req.headerbytecount += (long)headerlen;
-
-      data->req.deductheadercount =
-        (100 <= k->httpcode && 199 >= k->httpcode)?data->req.headerbytecount:0;
-
-      if(!*stop_reading) {
-        /* Curl_http_auth_act() checks what authentication methods
-         * that are available and decides which one (if any) to
-         * use. It will set 'newurl' if an auth method was picked. */
-        result = Curl_http_auth_act(conn);
-
-        if(result)
-          return result;
-
-        if(k->httpcode >= 300) {
-          if((!conn->bits.authneg) && !conn->bits.close &&
-             !conn->bits.rewindaftersend) {
-            /*
-             * General treatment of errors when about to send data. Including :
-             * "417 Expectation Failed", while waiting for 100-continue.
-             *
-             * The check for close above is done simply because of something
-             * else has already deemed the connection to get closed then
-             * something else should've considered the big picture and we
-             * avoid this check.
-             *
-             * rewindaftersend indicates that something has told libcurl to
-             * continue sending even if it gets discarded
-             */
-
-            switch(data->set.httpreq) {
-            case HTTPREQ_PUT:
-            case HTTPREQ_POST:
-            case HTTPREQ_POST_FORM:
-              /* We got an error response. If this happened before the whole
-               * request body has been sent we stop sending and mark the
-               * connection for closure after we've read the entire response.
-               */
-              if(!k->upload_done) {
-                infof(data, "HTTP error before end of send, stop sending\n");
-                conn->bits.close = TRUE; /* close after this */
-                k->upload_done = TRUE;
-                k->keepon &= ~KEEP_SEND; /* don't send */
-                if(data->state.expect100header)
-                  k->exp100 = EXP100_FAILED;
-              }
-              break;
-
-            default: /* default label present to avoid compiler warnings */
-              break;
-            }
-          }
-        }
-
-        if(conn->bits.rewindaftersend) {
-          /* We rewind after a complete send, so thus we continue
-             sending now */
-          infof(data, "Keep sending data to get tossed away!\n");
-          k->keepon |= KEEP_SEND;
-        }
-      }
-
-      if(!k->header) {
-        /*
-         * really end-of-headers.
-         *
-         * If we requested a "no body", this is a good time to get
-         * out and return home.
-         */
-        if(data->set.opt_no_body)
-          *stop_reading = TRUE;
-        else {
-          /* If we know the expected size of this document, we set the
-             maximum download size to the size of the expected
-             document or else, we won't know when to stop reading!
-
-             Note that we set the download maximum even if we read a
-             "Connection: close" header, to make sure that
-             "Content-Length: 0" still prevents us from attempting to
-             read the (missing) response-body.
-          */
-          /* According to RFC2616 section 4.4, we MUST ignore
-             Content-Length: headers if we are now receiving data
-             using chunked Transfer-Encoding.
-          */
-          if(k->chunk)
-            k->maxdownload = k->size = -1;
-        }
-        if(-1 != k->size) {
-          /* We do this operation even if no_body is true, since this
-             data might be retrieved later with curl_easy_getinfo()
-             and its CURLINFO_CONTENT_LENGTH_DOWNLOAD option. */
-
-          Curl_pgrsSetDownloadSize(data, k->size);
-          k->maxdownload = k->size;
-        }
-
-        /* If max download size is *zero* (nothing) we already
-           have nothing and can safely return ok now! */
-        if(0 == k->maxdownload)
-          *stop_reading = TRUE;
-
-        if(*stop_reading) {
-          /* we make sure that this socket isn't read more now */
-          k->keepon &= ~KEEP_RECV;
-        }
-
-        if(data->set.verbose)
-          Curl_debug(data, CURLINFO_HEADER_IN,
-                     k->str_start, headerlen, conn);
-        break;          /* exit header line loop */
-      }
-
-      /* We continue reading headers, so reset the line-based
-         header parsing variables hbufp && hbuflen */
-      k->hbufp = data->state.headerbuff;
-      k->hbuflen = 0;
-      continue;
-    }
-
-    /*
-     * Checks for special headers coming up.
-     */
-
-    if(!k->headerline++) {
-      /* This is the first header, it MUST be the error code line
-         or else we consider this to be the body right away! */
-      int httpversion_major;
-      int rtspversion_major;
-      int nc = 0;
-#ifdef CURL_DOES_CONVERSIONS
-#define HEADER1 scratch
-#define SCRATCHSIZE 21
-      CURLcode res;
-      char scratch[SCRATCHSIZE+1]; /* "HTTP/major.minor 123" */
-      /* We can't really convert this yet because we
-         don't know if it's the 1st header line or the body.
-         So we do a partial conversion into a scratch area,
-         leaving the data at k->p as-is.
-      */
-      strncpy(&scratch[0], k->p, SCRATCHSIZE);
-      scratch[SCRATCHSIZE] = 0; /* null terminate */
-      res = Curl_convert_from_network(data,
-                                      &scratch[0],
-                                      SCRATCHSIZE);
-      if(res)
-        /* Curl_convert_from_network calls failf if unsuccessful */
-        return res;
-#else
-#define HEADER1 k->p /* no conversion needed, just use k->p */
-#endif /* CURL_DOES_CONVERSIONS */
-
-      if(conn->handler->protocol & CURLPROTO_HTTP) {
-        nc = sscanf(HEADER1,
-                    " HTTP/%d.%d %3d",
-                    &httpversion_major,
-                    &conn->httpversion,
-                    &k->httpcode);
-        if(nc==3) {
-          conn->httpversion += 10 * httpversion_major;
-        }
-        else {
-          /* this is the real world, not a Nirvana
-             NCSA 1.5.x returns this crap when asked for HTTP/1.1
-          */
-          nc=sscanf(HEADER1, " HTTP %3d", &k->httpcode);
-          conn->httpversion = 10;
-
-          /* If user has set option HTTP200ALIASES,
-             compare header line against list of aliases
-          */
-          if(!nc) {
-            if(checkhttpprefix(data, k->p)) {
-              nc = 1;
-              k->httpcode = 200;
-              conn->httpversion = 10;
-            }
-          }
-        }
-      }
-      else if(conn->handler->protocol & CURLPROTO_RTSP) {
-        nc = sscanf(HEADER1,
-                    " RTSP/%d.%d %3d",
-                    &rtspversion_major,
-                    &conn->rtspversion,
-                    &k->httpcode);
-        if(nc==3) {
-          conn->rtspversion += 10 * rtspversion_major;
-          conn->httpversion = 11; /* For us, RTSP acts like HTTP 1.1 */
-        }
-        else {
-          /* TODO: do we care about the other cases here? */
-          nc = 0;
-        }
-      }
-
-      if(nc) {
-        data->info.httpcode = k->httpcode;
-
-        data->info.httpversion = conn->httpversion;
-        if(!data->state.httpversion ||
-           data->state.httpversion > conn->httpversion)
-          /* store the lowest server version we encounter */
-          data->state.httpversion = conn->httpversion;
-
-        /*
-         * This code executes as part of processing the header.  As a
-         * result, it's not totally clear how to interpret the
-         * response code yet as that depends on what other headers may
-         * be present.  401 and 407 may be errors, but may be OK
-         * depending on how authentication is working.  Other codes
-         * are definitely errors, so give up here.
-         */
-        if(data->set.http_fail_on_error && (k->httpcode >= 400) &&
-           ((k->httpcode != 401) || !conn->bits.user_passwd) &&
-           ((k->httpcode != 407) || !conn->bits.proxy_user_passwd) ) {
-
-          if(data->state.resume_from &&
-             (data->set.httpreq==HTTPREQ_GET) &&
-             (k->httpcode == 416)) {
-            /* "Requested Range Not Satisfiable", just proceed and
-               pretend this is no error */
-          }
-          else {
-            /* serious error, go home! */
-            print_http_error(data);
-            return CURLE_HTTP_RETURNED_ERROR;
-          }
-        }
-
-        if(conn->httpversion == 10) {
-          /* Default action for HTTP/1.0 must be to close, unless
-             we get one of those fancy headers that tell us the
-             server keeps it open for us! */
-          infof(data, "HTTP 1.0, assume close after body\n");
-          conn->bits.close = TRUE;
-        }
-        else if(conn->httpversion >= 11 &&
-                !conn->bits.close) {
-
-          /* If HTTP version is >= 1.1 and connection is persistent
-             server supports pipelining. */
-          DEBUGF(infof(data,
-                       "HTTP 1.1 or later with persistent connection, "
-                       "pipelining supported\n"));
-          conn->server_supports_pipelining = TRUE;
-        }
-
-        switch(k->httpcode) {
-        case 204:
-          /* (quote from RFC2616, section 10.2.5): The server has
-           * fulfilled the request but does not need to return an
-           * entity-body ... The 204 response MUST NOT include a
-           * message-body, and thus is always terminated by the first
-           * empty line after the header fields. */
-          /* FALLTHROUGH */
-        case 304:
-          /* (quote from RFC2616, section 10.3.5): The 304 response
-           * MUST NOT contain a message-body, and thus is always
-           * terminated by the first empty line after the header
-           * fields.  */
-          if(data->set.timecondition)
-            data->info.timecond = TRUE;
-          k->size=0;
-          k->maxdownload=0;
-          k->ignorecl = TRUE; /* ignore Content-Length headers */
-          break;
-        default:
-          /* nothing */
-          break;
-        }
-      }
-      else {
-        k->header = FALSE;   /* this is not a header line */
-        break;
-      }
-    }
-
-    result = Curl_convert_from_network(data, k->p, strlen(k->p));
-    /* Curl_convert_from_network calls failf if unsuccessful */
-    if(result)
-      return result;
-
-    /* Check for Content-Length: header lines to get size */
-    if(!k->ignorecl && !data->set.ignorecl &&
-       checkprefix("Content-Length:", k->p)) {
-      curl_off_t contentlength = curlx_strtoofft(k->p+15, NULL, 10);
-      if(data->set.max_filesize &&
-         contentlength > data->set.max_filesize) {
-        failf(data, "Maximum file size exceeded");
-        return CURLE_FILESIZE_EXCEEDED;
-      }
-      if(contentlength >= 0) {
-        k->size = contentlength;
-        k->maxdownload = k->size;
-        /* we set the progress download size already at this point
-           just to make it easier for apps/callbacks to extract this
-           info as soon as possible */
-        Curl_pgrsSetDownloadSize(data, k->size);
-      }
-      else {
-        /* Negative Content-Length is really odd, and we know it
-           happens for example when older Apache servers send large
-           files */
-        conn->bits.close = TRUE;
-        infof(data, "Negative content-length: %" FORMAT_OFF_T
-              ", closing after transfer\n", contentlength);
-      }
-    }
-    /* check for Content-Type: header lines to get the MIME-type */
-    else if(checkprefix("Content-Type:", k->p)) {
-      char *contenttype = copy_header_value(k->p);
-      if(!contenttype)
-        return CURLE_OUT_OF_MEMORY;
-      if(!*contenttype)
-        /* ignore empty data */
-        free(contenttype);
-      else {
-        Curl_safefree(data->info.contenttype);
-        data->info.contenttype = contenttype;
-      }
-    }
-    else if((conn->httpversion == 10) &&
-            conn->bits.httpproxy &&
-            Curl_compareheader(k->p,
-                               "Proxy-Connection:", "keep-alive")) {
-      /*
-       * When a HTTP/1.0 reply comes when using a proxy, the
-       * 'Proxy-Connection: keep-alive' line tells us the
-       * connection will be kept alive for our pleasure.
-       * Default action for 1.0 is to close.
-       */
-      conn->bits.close = FALSE; /* don't close when done */
-      infof(data, "HTTP/1.0 proxy connection set to keep alive!\n");
-    }
-    else if((conn->httpversion == 11) &&
-            conn->bits.httpproxy &&
-            Curl_compareheader(k->p,
-                               "Proxy-Connection:", "close")) {
-      /*
-       * We get a HTTP/1.1 response from a proxy and it says it'll
-       * close down after this transfer.
-       */
-      conn->bits.close = TRUE; /* close when done */
-      infof(data, "HTTP/1.1 proxy connection set close!\n");
-    }
-    else if((conn->httpversion == 10) &&
-            Curl_compareheader(k->p, "Connection:", "keep-alive")) {
-      /*
-       * A HTTP/1.0 reply with the 'Connection: keep-alive' line
-       * tells us the connection will be kept alive for our
-       * pleasure.  Default action for 1.0 is to close.
-       *
-       * [RFC2068, section 19.7.1] */
-      conn->bits.close = FALSE; /* don't close when done */
-      infof(data, "HTTP/1.0 connection set to keep alive!\n");
-    }
-    else if(Curl_compareheader(k->p, "Connection:", "close")) {
-      /*
-       * [RFC 2616, section 8.1.2.1]
-       * "Connection: close" is HTTP/1.1 language and means that
-       * the connection will close when this request has been
-       * served.
-       */
-      conn->bits.close = TRUE; /* close when done */
-    }
-    else if(checkprefix("Transfer-Encoding:", k->p)) {
-      /* One or more encodings. We check for chunked and/or a compression
-         algorithm. */
-      /*
-       * [RFC 2616, section 3.6.1] A 'chunked' transfer encoding
-       * means that the server will send a series of "chunks". Each
-       * chunk starts with line with info (including size of the
-       * coming block) (terminated with CRLF), then a block of data
-       * with the previously mentioned size. There can be any amount
-       * of chunks, and a chunk-data set to zero signals the
-       * end-of-chunks. */
-
-      char *start;
-
-      /* Find the first non-space letter */
-      start = k->p + 18;
-
-      for(;;) {
-        /* skip whitespaces and commas */
-        while(*start && (ISSPACE(*start) || (*start == ',')))
-          start++;
-
-        if(checkprefix("chunked", start)) {
-          k->chunk = TRUE; /* chunks coming our way */
-
-          /* init our chunky engine */
-          Curl_httpchunk_init(conn);
-
-          start += 7;
-        }
-
-        if(k->auto_decoding)
-          /* TODO: we only support the first mentioned compression for now */
-          break;
-
-        if(checkprefix("identity", start)) {
-          k->auto_decoding = IDENTITY;
-          start += 8;
-        }
-        else if(checkprefix("deflate", start)) {
-          k->auto_decoding = DEFLATE;
-          start += 7;
-        }
-        else if(checkprefix("gzip", start)) {
-          k->auto_decoding = GZIP;
-          start += 4;
-        }
-        else if(checkprefix("x-gzip", start)) {
-          k->auto_decoding = GZIP;
-          start += 6;
-        }
-        else if(checkprefix("compress", start)) {
-          k->auto_decoding = COMPRESS;
-          start += 8;
-        }
-        else if(checkprefix("x-compress", start)) {
-          k->auto_decoding = COMPRESS;
-          start += 10;
-        }
-        else
-          /* unknown! */
-          break;
-
-      }
-
-    }
-    else if(checkprefix("Content-Encoding:", k->p) &&
-            data->set.str[STRING_ENCODING]) {
-      /*
-       * Process Content-Encoding. Look for the values: identity,
-       * gzip, deflate, compress, x-gzip and x-compress. x-gzip and
-       * x-compress are the same as gzip and compress. (Sec 3.5 RFC
-       * 2616). zlib cannot handle compress.  However, errors are
-       * handled further down when the response body is processed
-       */
-      char *start;
-
-      /* Find the first non-space letter */
-      start = k->p + 17;
-      while(*start && ISSPACE(*start))
-        start++;
-
-      /* Record the content-encoding for later use */
-      if(checkprefix("identity", start))
-        k->auto_decoding = IDENTITY;
-      else if(checkprefix("deflate", start))
-        k->auto_decoding = DEFLATE;
-      else if(checkprefix("gzip", start)
-              || checkprefix("x-gzip", start))
-        k->auto_decoding = GZIP;
-      else if(checkprefix("compress", start)
-              || checkprefix("x-compress", start))
-        k->auto_decoding = COMPRESS;
-    }
-    else if(checkprefix("Content-Range:", k->p)) {
-      /* Content-Range: bytes [num]-
-         Content-Range: bytes: [num]-
-         Content-Range: [num]-
-
-         The second format was added since Sun's webserver
-         JavaWebServer/1.1.1 obviously sends the header this way!
-         The third added since some servers use that!
-      */
-
-      char *ptr = k->p + 14;
-
-      /* Move forward until first digit */
-      while(*ptr && !ISDIGIT(*ptr))
-        ptr++;
-
-      k->offset = curlx_strtoofft(ptr, NULL, 10);
-
-      if(data->state.resume_from == k->offset)
-        /* we asked for a resume and we got it */
-        k->content_range = TRUE;
-    }
-#if !defined(CURL_DISABLE_COOKIES)
-    else if(data->cookies &&
-            checkprefix("Set-Cookie:", k->p)) {
-      Curl_share_lock(data, CURL_LOCK_DATA_COOKIE,
-                      CURL_LOCK_ACCESS_SINGLE);
-      Curl_cookie_add(data,
-                      data->cookies, TRUE, k->p+11,
-                      /* If there is a custom-set Host: name, use it
-                         here, or else use real peer host name. */
-                      conn->allocptr.cookiehost?
-                      conn->allocptr.cookiehost:conn->host.name,
-                      data->state.path);
-      Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
-    }
-#endif
-    else if(checkprefix("Last-Modified:", k->p) &&
-            (data->set.timecondition || data->set.get_filetime) ) {
-      time_t secs=time(NULL);
-      k->timeofdoc = curl_getdate(k->p+strlen("Last-Modified:"),
-                                  &secs);
-      if(data->set.get_filetime)
-        data->info.filetime = (long)k->timeofdoc;
-    }
-    else if((checkprefix("WWW-Authenticate:", k->p) &&
-             (401 == k->httpcode)) ||
-            (checkprefix("Proxy-authenticate:", k->p) &&
-             (407 == k->httpcode))) {
-      result = Curl_http_input_auth(conn, k->httpcode, k->p);
-      if(result)
-        return result;
-    }
-    else if((k->httpcode >= 300 && k->httpcode < 400) &&
-            checkprefix("Location:", k->p) &&
-            !data->req.location) {
-      /* this is the URL that the server advises us to use instead */
-      char *location = copy_header_value(k->p);
-      if(!location)
-        return CURLE_OUT_OF_MEMORY;
-      if(!*location)
-        /* ignore empty data */
-        free(location);
-      else {
-        data->req.location = location;
-
-        if(data->set.http_follow_location) {
-          DEBUGASSERT(!data->req.newurl);
-          data->req.newurl = strdup(data->req.location); /* clone */
-          if(!data->req.newurl)
-            return CURLE_OUT_OF_MEMORY;
-
-          /* some cases of POST and PUT etc needs to rewind the data
-             stream at this point */
-          result = http_perhapsrewind(conn);
-          if(result)
-            return result;
-        }
-      }
-    }
-    else if(conn->handler->protocol & CURLPROTO_RTSP) {
-      result = Curl_rtsp_parseheader(conn, k->p);
-      if(result)
-        return result;
-    }
-
-    /*
-     * End of header-checks. Write them to the client.
-     */
-
-    writetype = CLIENTWRITE_HEADER;
-    if(data->set.include_header)
-      writetype |= CLIENTWRITE_BODY;
-
-    if(data->set.verbose)
-      Curl_debug(data, CURLINFO_HEADER_IN,
-                 k->p, (size_t)k->hbuflen, conn);
-
-    result = Curl_client_write(conn, writetype, k->p, k->hbuflen);
-    if(result)
-      return result;
-
-    data->info.header_size += (long)k->hbuflen;
-    data->req.headerbytecount += (long)k->hbuflen;
-
-    /* reset hbufp pointer && hbuflen */
-    k->hbufp = data->state.headerbuff;
-    k->hbuflen = 0;
-  }
-  while(!*stop_reading && *k->str); /* header line within buffer */
-
-  /* We might have reached the end of the header part here, but
-     there might be a non-header part left in the end of the read
-     buffer. */
-
-  return CURLE_OK;
-}
-
-#endif /* CURL_DISABLE_HTTP */
diff --git a/lib/http_chunks.c b/lib/http_chunks.c
deleted file mode 100644 (file)
index 2112f72..0000000
+++ /dev/null
@@ -1,397 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifndef CURL_DISABLE_HTTP
-
-#include "curl_urldata.h" /* it includes curl_http_chunks.h */
-#include "curl_sendf.h"   /* for the client write stuff */
-
-#include "curl_content_encoding.h"
-#include "curl_http.h"
-#include "curl_memory.h"
-#include "curl_non_ascii.h" /* for Curl_convert_to_network prototype */
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/*
- * Chunk format (simplified):
- *
- * <HEX SIZE>[ chunk extension ] CRLF
- * <DATA> CRLF
- *
- * Highlights from RFC2616 section 3.6 say:
-
-   The chunked encoding modifies the body of a message in order to
-   transfer it as a series of chunks, each with its own size indicator,
-   followed by an OPTIONAL trailer containing entity-header fields. This
-   allows dynamically produced content to be transferred along with the
-   information necessary for the recipient to verify that it has
-   received the full message.
-
-       Chunked-Body   = *chunk
-                        last-chunk
-                        trailer
-                        CRLF
-
-       chunk          = chunk-size [ chunk-extension ] CRLF
-                        chunk-data CRLF
-       chunk-size     = 1*HEX
-       last-chunk     = 1*("0") [ chunk-extension ] CRLF
-
-       chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
-       chunk-ext-name = token
-       chunk-ext-val  = token | quoted-string
-       chunk-data     = chunk-size(OCTET)
-       trailer        = *(entity-header CRLF)
-
-   The chunk-size field is a string of hex digits indicating the size of
-   the chunk. The chunked encoding is ended by any chunk whose size is
-   zero, followed by the trailer, which is terminated by an empty line.
-
- */
-
-/* Check for an ASCII hex digit.
- We avoid the use of isxdigit to accommodate non-ASCII hosts. */
-static bool Curl_isxdigit(char digit)
-{
-  return ( (digit >= 0x30 && digit <= 0x39) /* 0-9 */
-        || (digit >= 0x41 && digit <= 0x46) /* A-F */
-        || (digit >= 0x61 && digit <= 0x66) /* a-f */ ) ? TRUE : FALSE;
-}
-
-void Curl_httpchunk_init(struct connectdata *conn)
-{
-  struct Curl_chunker *chunk = &conn->chunk;
-  chunk->hexindex=0; /* start at 0 */
-  chunk->dataleft=0; /* no data left yet! */
-  chunk->state = CHUNK_HEX; /* we get hex first! */
-}
-
-/*
- * chunk_read() returns a OK for normal operations, or a positive return code
- * for errors. STOP means this sequence of chunks is complete.  The 'wrote'
- * argument is set to tell the caller how many bytes we actually passed to the
- * client (for byte-counting and whatever).
- *
- * The states and the state-machine is further explained in the header file.
- *
- * This function always uses ASCII hex values to accommodate non-ASCII hosts.
- * For example, 0x0d and 0x0a are used instead of '\r' and '\n'.
- */
-CHUNKcode Curl_httpchunk_read(struct connectdata *conn,
-                              char *datap,
-                              ssize_t datalen,
-                              ssize_t *wrotep)
-{
-  CURLcode result=CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  struct Curl_chunker *ch = &conn->chunk;
-  struct SingleRequest *k = &data->req;
-  size_t piece;
-  size_t length = (size_t)datalen;
-  size_t *wrote = (size_t *)wrotep;
-
-  *wrote = 0; /* nothing's written yet */
-
-  /* the original data is written to the client, but we go on with the
-     chunk read process, to properly calculate the content length*/
-  if(data->set.http_te_skip && !k->ignorebody) {
-    result = Curl_client_write(conn, CLIENTWRITE_BODY, datap, datalen);
-    if(result)
-      return CHUNKE_WRITE_ERROR;
-  }
-
-  while(length) {
-    switch(ch->state) {
-    case CHUNK_HEX:
-      if(Curl_isxdigit(*datap)) {
-        if(ch->hexindex < MAXNUM_SIZE) {
-          ch->hexbuffer[ch->hexindex] = *datap;
-          datap++;
-          length--;
-          ch->hexindex++;
-        }
-        else {
-          return CHUNKE_TOO_LONG_HEX; /* longer hex than we support */
-        }
-      }
-      else {
-        if(0 == ch->hexindex) {
-          /* This is illegal data, we received junk where we expected
-             a hexadecimal digit. */
-          return CHUNKE_ILLEGAL_HEX;
-        }
-        /* length and datap are unmodified */
-        ch->hexbuffer[ch->hexindex]=0;
-
-        /* convert to host encoding before calling strtoul */
-        result = Curl_convert_from_network(conn->data, ch->hexbuffer,
-                                           ch->hexindex);
-        if(result) {
-          /* Curl_convert_from_network calls failf if unsuccessful */
-          /* Treat it as a bad hex character */
-          return(CHUNKE_ILLEGAL_HEX);
-        }
-
-        ch->datasize=strtoul(ch->hexbuffer, NULL, 16);
-        ch->state = CHUNK_POSTHEX;
-      }
-      break;
-
-    case CHUNK_POSTHEX:
-      /* In this state, we're waiting for CRLF to arrive. We support
-         this to allow so called chunk-extensions to show up here
-         before the CRLF comes. */
-      if(*datap == 0x0d)
-        ch->state = CHUNK_CR;
-      length--;
-      datap++;
-      break;
-
-    case CHUNK_CR:
-      /* waiting for the LF */
-      if(*datap == 0x0a) {
-        /* we're now expecting data to come, unless size was zero! */
-        if(0 == ch->datasize) {
-          ch->state = CHUNK_TRAILER; /* now check for trailers */
-          conn->trlPos=0;
-        }
-        else {
-          ch->state = CHUNK_DATA;
-        }
-      }
-      else
-        /* previously we got a fake CR, go back to CR waiting! */
-        ch->state = CHUNK_CR;
-      datap++;
-      length--;
-      break;
-
-    case CHUNK_DATA:
-      /* we get pure and fine data
-
-         We expect another 'datasize' of data. We have 'length' right now,
-         it can be more or less than 'datasize'. Get the smallest piece.
-      */
-      piece = (ch->datasize >= length)?length:ch->datasize;
-
-      /* Write the data portion available */
-#ifdef HAVE_LIBZ
-      switch (conn->data->set.http_ce_skip?
-              IDENTITY : data->req.auto_decoding) {
-      case IDENTITY:
-#endif
-        if(!k->ignorebody) {
-          if(!data->set.http_te_skip)
-            result = Curl_client_write(conn, CLIENTWRITE_BODY, datap,
-                                       piece);
-          else
-            result = CURLE_OK;
-        }
-#ifdef HAVE_LIBZ
-        break;
-
-      case DEFLATE:
-        /* update data->req.keep.str to point to the chunk data. */
-        data->req.str = datap;
-        result = Curl_unencode_deflate_write(conn, &data->req,
-                                             (ssize_t)piece);
-        break;
-
-      case GZIP:
-        /* update data->req.keep.str to point to the chunk data. */
-        data->req.str = datap;
-        result = Curl_unencode_gzip_write(conn, &data->req,
-                                          (ssize_t)piece);
-        break;
-
-      case COMPRESS:
-      default:
-        failf (conn->data,
-               "Unrecognized content encoding type. "
-               "libcurl understands `identity', `deflate' and `gzip' "
-               "content encodings.");
-        return CHUNKE_BAD_ENCODING;
-      }
-#endif
-
-      if(result)
-        return CHUNKE_WRITE_ERROR;
-
-      *wrote += piece;
-
-      ch->datasize -= piece; /* decrease amount left to expect */
-      datap += piece;    /* move read pointer forward */
-      length -= piece;   /* decrease space left in this round */
-
-      if(0 == ch->datasize)
-        /* end of data this round, we now expect a trailing CRLF */
-        ch->state = CHUNK_POSTCR;
-      break;
-
-    case CHUNK_POSTCR:
-      if(*datap == 0x0d) {
-        ch->state = CHUNK_POSTLF;
-        datap++;
-        length--;
-      }
-      else
-        return CHUNKE_BAD_CHUNK;
-
-      break;
-
-    case CHUNK_POSTLF:
-      if(*datap == 0x0a) {
-        /*
-         * The last one before we go back to hex state and start all
-         * over.
-         */
-        Curl_httpchunk_init(conn);
-        datap++;
-        length--;
-      }
-      else
-        return CHUNKE_BAD_CHUNK;
-
-      break;
-
-    case CHUNK_TRAILER:
-      if(*datap == 0x0d) {
-        /* this is the end of a trailer, but if the trailer was zero bytes
-           there was no trailer and we move on */
-
-        if(conn->trlPos) {
-          /* we allocate trailer with 3 bytes extra room to fit this */
-          conn->trailer[conn->trlPos++]=0x0d;
-          conn->trailer[conn->trlPos++]=0x0a;
-          conn->trailer[conn->trlPos]=0;
-
-          /* Convert to host encoding before calling Curl_client_write */
-          result = Curl_convert_from_network(conn->data, conn->trailer,
-                                             conn->trlPos);
-          if(result)
-            /* Curl_convert_from_network calls failf if unsuccessful */
-            /* Treat it as a bad chunk */
-            return CHUNKE_BAD_CHUNK;
-
-          if(!data->set.http_te_skip) {
-            result = Curl_client_write(conn, CLIENTWRITE_HEADER,
-                                       conn->trailer, conn->trlPos);
-            if(result)
-              return CHUNKE_WRITE_ERROR;
-          }
-          conn->trlPos=0;
-          ch->state = CHUNK_TRAILER_CR;
-        }
-        else {
-          /* no trailer, we're on the final CRLF pair */
-          ch->state = CHUNK_TRAILER_POSTCR;
-          break; /* don't advance the pointer */
-        }
-      }
-      else {
-        /* conn->trailer is assumed to be freed in curl_url.c on a
-           connection basis */
-        if(conn->trlPos >= conn->trlMax) {
-          /* we always allocate three extra bytes, just because when the full
-             header has been received we append CRLF\0 */
-          char *ptr;
-          if(conn->trlMax) {
-            conn->trlMax *= 2;
-            ptr = realloc(conn->trailer, conn->trlMax + 3);
-          }
-          else {
-            conn->trlMax=128;
-            ptr = malloc(conn->trlMax + 3);
-          }
-          if(!ptr)
-            return CHUNKE_OUT_OF_MEMORY;
-          conn->trailer = ptr;
-        }
-        conn->trailer[conn->trlPos++]=*datap;
-      }
-      datap++;
-      length--;
-      break;
-
-    case CHUNK_TRAILER_CR:
-      if(*datap == 0x0a) {
-        ch->state = CHUNK_TRAILER_POSTCR;
-        datap++;
-        length--;
-      }
-      else
-        return CHUNKE_BAD_CHUNK;
-      break;
-
-    case CHUNK_TRAILER_POSTCR:
-      /* We enter this state when a CR should arrive so we expect to
-         have to first pass a CR before we wait for LF */
-      if(*datap != 0x0d) {
-        /* not a CR then it must be another header in the trailer */
-        ch->state = CHUNK_TRAILER;
-        break;
-      }
-      datap++;
-      length--;
-      /* now wait for the final LF */
-      ch->state = CHUNK_STOP;
-      break;
-
-    case CHUNK_STOPCR:
-      /* Read the final CRLF that ends all chunk bodies */
-
-      if(*datap == 0x0d) {
-        ch->state = CHUNK_STOP;
-        datap++;
-        length--;
-      }
-      else
-        return CHUNKE_BAD_CHUNK;
-      break;
-
-    case CHUNK_STOP:
-      if(*datap == 0x0a) {
-        length--;
-
-        /* Record the length of any data left in the end of the buffer
-           even if there's no more chunks to read */
-
-        ch->dataleft = length;
-        return CHUNKE_STOP; /* return stop */
-      }
-      else
-        return CHUNKE_BAD_CHUNK;
-
-    default:
-      return CHUNKE_STATE_ERROR;
-    }
-  }
-  return CHUNKE_OK;
-}
-#endif /* CURL_DISABLE_HTTP */
diff --git a/lib/http_digest.c b/lib/http_digest.c
deleted file mode 100644 (file)
index dae6799..0000000
+++ /dev/null
@@ -1,583 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
-
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_rawstr.h"
-#include "curl_base64.h"
-#include "curl_md5.h"
-#include "curl_http_digest.h"
-#include "curl_strtok.h"
-#include "curl_url.h"
-#include "curl_memory.h"
-#include "curl_non_ascii.h" /* included for Curl_convert_... prototypes */
-#include "curl_warnless.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-#define MAX_VALUE_LENGTH 256
-#define MAX_CONTENT_LENGTH 1024
-
-static void digest_cleanup_one(struct digestdata *dig);
-
-/*
- * Return 0 on success and then the buffers are filled in fine.
- *
- * Non-zero means failure to parse.
- */
-static int get_pair(const char *str, char *value, char *content,
-                    const char **endptr)
-{
-  int c;
-  bool starts_with_quote = FALSE;
-  bool escape = FALSE;
-
-  for(c=MAX_VALUE_LENGTH-1; (*str && (*str != '=') && c--); )
-    *value++ = *str++;
-  *value=0;
-
-  if('=' != *str++)
-    /* eek, no match */
-    return 1;
-
-  if('\"' == *str) {
-    /* this starts with a quote so it must end with one as well! */
-    str++;
-    starts_with_quote = TRUE;
-  }
-
-  for(c=MAX_CONTENT_LENGTH-1; *str && c--; str++) {
-    switch(*str) {
-    case '\\':
-      if(!escape) {
-        /* possibly the start of an escaped quote */
-        escape = TRUE;
-        *content++ = '\\'; /* even though this is an escape character, we still
-                              store it as-is in the target buffer */
-        continue;
-      }
-      break;
-    case ',':
-      if(!starts_with_quote) {
-        /* this signals the end of the content if we didn't get a starting
-           quote and then we do "sloppy" parsing */
-        c=0; /* the end */
-        continue;
-      }
-      break;
-    case '\r':
-    case '\n':
-      /* end of string */
-      c=0;
-      continue;
-    case '\"':
-      if(!escape && starts_with_quote) {
-        /* end of string */
-        c=0;
-        continue;
-      }
-      break;
-    }
-    escape = FALSE;
-    *content++ = *str;
-  }
-  *content=0;
-
-  *endptr = str;
-
-  return 0; /* all is fine! */
-}
-
-/* Test example headers:
-
-WWW-Authenticate: Digest realm="testrealm", nonce="1053604598"
-Proxy-Authenticate: Digest realm="testrealm", nonce="1053604598"
-
-*/
-
-CURLdigest Curl_input_digest(struct connectdata *conn,
-                             bool proxy,
-                             const char *header) /* rest of the *-authenticate:
-                                                    header */
-{
-  char *token = NULL;
-  char *tmp = NULL;
-  bool foundAuth = FALSE;
-  bool foundAuthInt = FALSE;
-  struct SessionHandle *data=conn->data;
-  bool before = FALSE; /* got a nonce before */
-  struct digestdata *d;
-
-  if(proxy) {
-    d = &data->state.proxydigest;
-  }
-  else {
-    d = &data->state.digest;
-  }
-
-  /* skip initial whitespaces */
-  while(*header && ISSPACE(*header))
-    header++;
-
-  if(checkprefix("Digest", header)) {
-    header += strlen("Digest");
-
-    /* If we already have received a nonce, keep that in mind */
-    if(d->nonce)
-      before = TRUE;
-
-    /* clear off any former leftovers and init to defaults */
-    digest_cleanup_one(d);
-
-    for(;;) {
-      char value[MAX_VALUE_LENGTH];
-      char content[MAX_CONTENT_LENGTH];
-
-      while(*header && ISSPACE(*header))
-        header++;
-
-      /* extract a value=content pair */
-      if(!get_pair(header, value, content, &header)) {
-        if(Curl_raw_equal(value, "nonce")) {
-          d->nonce = strdup(content);
-          if(!d->nonce)
-            return CURLDIGEST_NOMEM;
-        }
-        else if(Curl_raw_equal(value, "stale")) {
-          if(Curl_raw_equal(content, "true")) {
-            d->stale = TRUE;
-            d->nc = 1; /* we make a new nonce now */
-          }
-        }
-        else if(Curl_raw_equal(value, "realm")) {
-          d->realm = strdup(content);
-          if(!d->realm)
-            return CURLDIGEST_NOMEM;
-        }
-        else if(Curl_raw_equal(value, "opaque")) {
-          d->opaque = strdup(content);
-          if(!d->opaque)
-            return CURLDIGEST_NOMEM;
-        }
-        else if(Curl_raw_equal(value, "qop")) {
-          char *tok_buf;
-          /* tokenize the list and choose auth if possible, use a temporary
-             clone of the buffer since strtok_r() ruins it */
-          tmp = strdup(content);
-          if(!tmp)
-            return CURLDIGEST_NOMEM;
-          token = strtok_r(tmp, ",", &tok_buf);
-          while(token != NULL) {
-            if(Curl_raw_equal(token, "auth")) {
-              foundAuth = TRUE;
-            }
-            else if(Curl_raw_equal(token, "auth-int")) {
-              foundAuthInt = TRUE;
-            }
-            token = strtok_r(NULL, ",", &tok_buf);
-          }
-          free(tmp);
-          /*select only auth o auth-int. Otherwise, ignore*/
-          if(foundAuth) {
-            d->qop = strdup("auth");
-            if(!d->qop)
-              return CURLDIGEST_NOMEM;
-          }
-          else if(foundAuthInt) {
-            d->qop = strdup("auth-int");
-            if(!d->qop)
-              return CURLDIGEST_NOMEM;
-          }
-        }
-        else if(Curl_raw_equal(value, "algorithm")) {
-          d->algorithm = strdup(content);
-          if(!d->algorithm)
-            return CURLDIGEST_NOMEM;
-          if(Curl_raw_equal(content, "MD5-sess"))
-            d->algo = CURLDIGESTALGO_MD5SESS;
-          else if(Curl_raw_equal(content, "MD5"))
-            d->algo = CURLDIGESTALGO_MD5;
-          else
-            return CURLDIGEST_BADALGO;
-        }
-        else {
-          /* unknown specifier, ignore it! */
-        }
-      }
-      else
-        break; /* we're done here */
-
-      /* pass all additional spaces here */
-      while(*header && ISSPACE(*header))
-        header++;
-      if(',' == *header)
-        /* allow the list to be comma-separated */
-        header++;
-    }
-    /* We had a nonce since before, and we got another one now without
-       'stale=true'. This means we provided bad credentials in the previous
-       request */
-    if(before && !d->stale)
-      return CURLDIGEST_BAD;
-
-    /* We got this header without a nonce, that's a bad Digest line! */
-    if(!d->nonce)
-      return CURLDIGEST_BAD;
-  }
-  else
-    /* else not a digest, get out */
-    return CURLDIGEST_NONE;
-
-  return CURLDIGEST_FINE;
-}
-
-/* convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/
-static void md5_to_ascii(unsigned char *source, /* 16 bytes */
-                         unsigned char *dest) /* 33 bytes */
-{
-  int i;
-  for(i=0; i<16; i++)
-    snprintf((char *)&dest[i*2], 3, "%02x", source[i]);
-}
-
-CURLcode Curl_output_digest(struct connectdata *conn,
-                            bool proxy,
-                            const unsigned char *request,
-                            const unsigned char *uripath)
-{
-  /* We have a Digest setup for this, use it!  Now, to get all the details for
-     this sorted out, I must urge you dear friend to read up on the RFC2617
-     section 3.2.2, */
-  unsigned char md5buf[16]; /* 16 bytes/128 bits */
-  unsigned char request_digest[33];
-  unsigned char *md5this;
-  unsigned char *ha1;
-  unsigned char ha2[33];/* 32 digits and 1 zero byte */
-  char cnoncebuf[33];
-  char *cnonce = NULL;
-  size_t cnonce_sz = 0;
-  char *tmp = NULL;
-  struct timeval now;
-
-  char **allocuserpwd;
-  const char *userp;
-  const char *passwdp;
-  struct auth *authp;
-
-  struct SessionHandle *data = conn->data;
-  struct digestdata *d;
-  CURLcode rc;
-/* The CURL_OUTPUT_DIGEST_CONV macro below is for non-ASCII machines.
-   It converts digest text to ASCII so the MD5 will be correct for
-   what ultimately goes over the network.
-*/
-#define CURL_OUTPUT_DIGEST_CONV(a, b) \
-  rc = Curl_convert_to_network(a, (char *)b, strlen((const char*)b)); \
-  if(rc != CURLE_OK) { \
-    free(b); \
-    return rc; \
-  }
-
-  if(proxy) {
-    d = &data->state.proxydigest;
-    allocuserpwd = &conn->allocptr.proxyuserpwd;
-    userp = conn->proxyuser;
-    passwdp = conn->proxypasswd;
-    authp = &data->state.authproxy;
-  }
-  else {
-    d = &data->state.digest;
-    allocuserpwd = &conn->allocptr.userpwd;
-    userp = conn->user;
-    passwdp = conn->passwd;
-    authp = &data->state.authhost;
-  }
-
-  if(*allocuserpwd) {
-    Curl_safefree(*allocuserpwd);
-    *allocuserpwd = NULL;
-  }
-
-  /* not set means empty */
-  if(!userp)
-    userp="";
-
-  if(!passwdp)
-    passwdp="";
-
-  if(!d->nonce) {
-    authp->done = FALSE;
-    return CURLE_OK;
-  }
-  authp->done = TRUE;
-
-  if(!d->nc)
-    d->nc = 1;
-
-  if(!d->cnonce) {
-    /* Generate a cnonce */
-    now = Curl_tvnow();
-    snprintf(cnoncebuf, sizeof(cnoncebuf), "%32ld",
-             (long)now.tv_sec + now.tv_usec);
-
-    rc = Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf),
-                            &cnonce, &cnonce_sz);
-    if(rc)
-      return rc;
-    d->cnonce = cnonce;
-  }
-
-  /*
-    if the algorithm is "MD5" or unspecified (which then defaults to MD5):
-
-    A1 = unq(username-value) ":" unq(realm-value) ":" passwd
-
-    if the algorithm is "MD5-sess" then:
-
-    A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd )
-         ":" unq(nonce-value) ":" unq(cnonce-value)
-  */
-
-  md5this = (unsigned char *)
-    aprintf("%s:%s:%s", userp, d->realm, passwdp);
-  if(!md5this)
-    return CURLE_OUT_OF_MEMORY;
-
-  CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
-  Curl_md5it(md5buf, md5this);
-  free(md5this); /* free this again */
-
-  ha1 = malloc(33); /* 32 digits and 1 zero byte */
-  if(!ha1)
-    return CURLE_OUT_OF_MEMORY;
-
-  md5_to_ascii(md5buf, ha1);
-
-  if(d->algo == CURLDIGESTALGO_MD5SESS) {
-    /* nonce and cnonce are OUTSIDE the hash */
-    tmp = aprintf("%s:%s:%s", ha1, d->nonce, d->cnonce);
-    if(!tmp)
-      return CURLE_OUT_OF_MEMORY;
-    CURL_OUTPUT_DIGEST_CONV(data, tmp); /* convert on non-ASCII machines */
-    Curl_md5it(md5buf, (unsigned char *)tmp);
-    free(tmp); /* free this again */
-    md5_to_ascii(md5buf, ha1);
-  }
-
-  /*
-    If the "qop" directive's value is "auth" or is unspecified, then A2 is:
-
-      A2       = Method ":" digest-uri-value
-
-          If the "qop" value is "auth-int", then A2 is:
-
-      A2       = Method ":" digest-uri-value ":" H(entity-body)
-
-    (The "Method" value is the HTTP request method as specified in section
-    5.1.1 of RFC 2616)
-  */
-
-  /* So IE browsers < v7 cut off the URI part at the query part when they
-     evaluate the MD5 and some (IIS?) servers work with them so we may need to
-     do the Digest IE-style. Note that the different ways cause different MD5
-     sums to get sent.
-
-     Apache servers can be set to do the Digest IE-style automatically using
-     the BrowserMatch feature:
-     http://httpd.apache.org/docs/2.2/mod/mod_auth_digest.html#msie
-
-     Further details on Digest implementation differences:
-     http://www.fngtps.com/2006/09/http-authentication
-  */
-  if(authp->iestyle && ((tmp = strchr((char *)uripath, '?')) != NULL)) {
-    md5this = (unsigned char *)aprintf("%s:%.*s", request,
-                                       curlx_sztosi(tmp - (char *)uripath),
-                                       uripath);
-  }
-  else
-    md5this = (unsigned char *)aprintf("%s:%s", request, uripath);
-
-  if(!md5this) {
-    free(ha1);
-    return CURLE_OUT_OF_MEMORY;
-  }
-
-  if(d->qop && Curl_raw_equal(d->qop, "auth-int")) {
-    /* We don't support auth-int at the moment. I can't see a easy way to get
-       entity-body here */
-    /* TODO: Append H(entity-body)*/
-  }
-  CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
-  Curl_md5it(md5buf, md5this);
-  free(md5this); /* free this again */
-  md5_to_ascii(md5buf, ha2);
-
-  if(d->qop) {
-    md5this = (unsigned char *)aprintf("%s:%s:%08x:%s:%s:%s",
-                                       ha1,
-                                       d->nonce,
-                                       d->nc,
-                                       d->cnonce,
-                                       d->qop,
-                                       ha2);
-  }
-  else {
-    md5this = (unsigned char *)aprintf("%s:%s:%s",
-                                       ha1,
-                                       d->nonce,
-                                       ha2);
-  }
-  free(ha1);
-  if(!md5this)
-    return CURLE_OUT_OF_MEMORY;
-
-  CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
-  Curl_md5it(md5buf, md5this);
-  free(md5this); /* free this again */
-  md5_to_ascii(md5buf, request_digest);
-
-  /* for test case 64 (snooped from a Mozilla 1.3a request)
-
-    Authorization: Digest username="testuser", realm="testrealm", \
-    nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
-  */
-
-  if(d->qop) {
-    *allocuserpwd =
-      aprintf( "%sAuthorization: Digest "
-               "username=\"%s\", "
-               "realm=\"%s\", "
-               "nonce=\"%s\", "
-               "uri=\"%s\", "
-               "cnonce=\"%s\", "
-               "nc=%08x, "
-               "qop=%s, "
-               "response=\"%s\"",
-               proxy?"Proxy-":"",
-               userp,
-               d->realm,
-               d->nonce,
-               uripath, /* this is the PATH part of the URL */
-               d->cnonce,
-               d->nc,
-               d->qop,
-               request_digest);
-
-    if(Curl_raw_equal(d->qop, "auth"))
-      d->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0 padded
-                  which tells to the server how many times you are using the
-                  same nonce in the qop=auth mode. */
-  }
-  else {
-    *allocuserpwd =
-      aprintf( "%sAuthorization: Digest "
-               "username=\"%s\", "
-               "realm=\"%s\", "
-               "nonce=\"%s\", "
-               "uri=\"%s\", "
-               "response=\"%s\"",
-               proxy?"Proxy-":"",
-               userp,
-               d->realm,
-               d->nonce,
-               uripath, /* this is the PATH part of the URL */
-               request_digest);
-  }
-  if(!*allocuserpwd)
-    return CURLE_OUT_OF_MEMORY;
-
-  /* Add optional fields */
-  if(d->opaque) {
-    /* append opaque */
-    tmp = aprintf("%s, opaque=\"%s\"", *allocuserpwd, d->opaque);
-    if(!tmp)
-      return CURLE_OUT_OF_MEMORY;
-    free(*allocuserpwd);
-    *allocuserpwd = tmp;
-  }
-
-  if(d->algorithm) {
-    /* append algorithm */
-    tmp = aprintf("%s, algorithm=\"%s\"", *allocuserpwd, d->algorithm);
-    if(!tmp)
-      return CURLE_OUT_OF_MEMORY;
-    free(*allocuserpwd);
-    *allocuserpwd = tmp;
-  }
-
-  /* append CRLF + zero (3 bytes) to the userpwd header */
-  tmp = realloc(*allocuserpwd, strlen(*allocuserpwd) + 3);
-  if(!tmp)
-    return CURLE_OUT_OF_MEMORY;
-  strcat(tmp, "\r\n");
-  *allocuserpwd = tmp;
-
-  return CURLE_OK;
-}
-
-static void digest_cleanup_one(struct digestdata *d)
-{
-  if(d->nonce)
-    free(d->nonce);
-  d->nonce = NULL;
-
-  if(d->cnonce)
-    free(d->cnonce);
-  d->cnonce = NULL;
-
-  if(d->realm)
-    free(d->realm);
-  d->realm = NULL;
-
-  if(d->opaque)
-    free(d->opaque);
-  d->opaque = NULL;
-
-  if(d->qop)
-    free(d->qop);
-  d->qop = NULL;
-
-  if(d->algorithm)
-    free(d->algorithm);
-  d->algorithm = NULL;
-
-  d->nc = 0;
-  d->algo = CURLDIGESTALGO_MD5; /* default algorithm */
-  d->stale = FALSE; /* default means normal, not stale */
-}
-
-
-void Curl_digest_cleanup(struct SessionHandle *data)
-{
-  digest_cleanup_one(&data->state.digest);
-  digest_cleanup_one(&data->state.proxydigest);
-}
-
-#endif
diff --git a/lib/http_negotiate.c b/lib/http_negotiate.c
deleted file mode 100644 (file)
index 446c49b..0000000
+++ /dev/null
@@ -1,373 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef HAVE_GSSAPI
-#ifdef HAVE_OLD_GSSMIT
-#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
-#define NCOMPAT 1
-#endif
-
-#ifndef CURL_DISABLE_HTTP
-
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_gssapi.h"
-#include "curl_rawstr.h"
-#include "curl_base64.h"
-#include "curl_http_negotiate.h"
-#include "curl_memory.h"
-#include "curl_url.h"
-
-#ifdef HAVE_SPNEGO
-#  include <spnegohelp.h>
-#  ifdef USE_SSLEAY
-#    ifdef USE_OPENSSL
-#      include <openssl/objects.h>
-#    else
-#      include <objects.h>
-#    endif
-#  else
-#    error "Can't compile SPNEGO support without OpenSSL."
-#  endif
-#endif
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-static int
-get_gss_name(struct connectdata *conn, bool proxy, gss_name_t *server)
-{
-  struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg:
-    &conn->data->state.negotiate;
-  OM_uint32 major_status, minor_status;
-  gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
-  char name[2048];
-  const char* service;
-
-  /* GSSAPI implementation by Globus (known as GSI) requires the name to be
-     of form "<service>/<fqdn>" instead of <service>@<fqdn> (ie. slash instead
-     of at-sign). Also GSI servers are often identified as 'host' not 'khttp'.
-     Change following lines if you want to use GSI */
-
-  /* IIS uses the <service>@<fqdn> form but uses 'http' as the service name */
-
-  if(neg_ctx->gss)
-    service = "KHTTP";
-  else
-    service = "HTTP";
-
-  token.length = strlen(service) + 1 + strlen(proxy ? conn->proxy.name :
-                                              conn->host.name) + 1;
-  if(token.length + 1 > sizeof(name))
-    return EMSGSIZE;
-
-  snprintf(name, sizeof(name), "%s@%s", service, proxy ? conn->proxy.name :
-           conn->host.name);
-
-  token.value = (void *) name;
-  major_status = gss_import_name(&minor_status,
-                                 &token,
-                                 GSS_C_NT_HOSTBASED_SERVICE,
-                                 server);
-
-  return GSS_ERROR(major_status) ? -1 : 0;
-}
-
-static void
-log_gss_error(struct connectdata *conn, OM_uint32 error_status,
-              const char *prefix)
-{
-  OM_uint32 maj_stat, min_stat;
-  OM_uint32 msg_ctx = 0;
-  gss_buffer_desc status_string;
-  char buf[1024];
-  size_t len;
-
-  snprintf(buf, sizeof(buf), "%s", prefix);
-  len = strlen(buf);
-  do {
-    maj_stat = gss_display_status(&min_stat,
-                                  error_status,
-                                  GSS_C_MECH_CODE,
-                                  GSS_C_NO_OID,
-                                  &msg_ctx,
-                                  &status_string);
-      if(sizeof(buf) > len + status_string.length + 1) {
-        snprintf(buf + len, sizeof(buf) - len,
-                 ": %s", (char*) status_string.value);
-      len += status_string.length;
-    }
-    gss_release_buffer(&min_stat, &status_string);
-  } while(!GSS_ERROR(maj_stat) && msg_ctx != 0);
-
-  infof(conn->data, "%s\n", buf);
-}
-
-/* returning zero (0) means success, everything else is treated as "failure"
-   with no care exactly what the failure was */
-int Curl_input_negotiate(struct connectdata *conn, bool proxy,
-                         const char *header)
-{
-  struct SessionHandle *data = conn->data;
-  struct negotiatedata *neg_ctx = proxy?&data->state.proxyneg:
-    &data->state.negotiate;
-  OM_uint32 major_status, minor_status, minor_status2;
-  gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
-  gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
-  int ret;
-  size_t len;
-  size_t rawlen = 0;
-  bool gss;
-  const char* protocol;
-  CURLcode error;
-
-  while(*header && ISSPACE(*header))
-    header++;
-  if(checkprefix("GSS-Negotiate", header)) {
-    protocol = "GSS-Negotiate";
-    gss = TRUE;
-  }
-  else if(checkprefix("Negotiate", header)) {
-    protocol = "Negotiate";
-    gss = FALSE;
-  }
-  else
-    return -1;
-
-  if(neg_ctx->context) {
-    if(neg_ctx->gss != gss) {
-      return -1;
-    }
-  }
-  else {
-    neg_ctx->protocol = protocol;
-    neg_ctx->gss = gss;
-  }
-
-  if(neg_ctx->context && neg_ctx->status == GSS_S_COMPLETE) {
-    /* We finished successfully our part of authentication, but server
-     * rejected it (since we're again here). Exit with an error since we
-     * can't invent anything better */
-    Curl_cleanup_negotiate(data);
-    return -1;
-  }
-
-  if(neg_ctx->server_name == NULL &&
-      (ret = get_gss_name(conn, proxy, &neg_ctx->server_name)))
-    return ret;
-
-  header += strlen(neg_ctx->protocol);
-  while(*header && ISSPACE(*header))
-    header++;
-
-  len = strlen(header);
-  if(len > 0) {
-    error = Curl_base64_decode(header,
-                               (unsigned char **)&input_token.value, &rawlen);
-    if(error || rawlen == 0)
-      return -1;
-    input_token.length = rawlen;
-
-#ifdef HAVE_SPNEGO /* Handle SPNEGO */
-    if(checkprefix("Negotiate", header)) {
-      ASN1_OBJECT *   object            = NULL;
-      unsigned char * spnegoToken       = NULL;
-      size_t          spnegoTokenLength = 0;
-      unsigned char * mechToken         = NULL;
-      size_t          mechTokenLength   = 0;
-
-      if(input_token.value == NULL)
-        return CURLE_OUT_OF_MEMORY;
-
-      spnegoToken = malloc(input_token.length);
-      if(spnegoToken == NULL)
-        return CURLE_OUT_OF_MEMORY;
-
-      spnegoTokenLength = input_token.length;
-
-      object = OBJ_txt2obj ("1.2.840.113554.1.2.2", 1);
-      if(!parseSpnegoTargetToken(spnegoToken,
-                                 spnegoTokenLength,
-                                 NULL,
-                                 NULL,
-                                 &mechToken,
-                                 &mechTokenLength,
-                                 NULL,
-                                 NULL)) {
-        free(spnegoToken);
-        spnegoToken = NULL;
-        infof(data, "Parse SPNEGO Target Token failed\n");
-      }
-      else {
-        free(input_token.value);
-        input_token.value = malloc(mechTokenLength);
-        if(input_token.value == NULL)
-          return CURLE_OUT_OF_MEMORY;
-
-        memcpy(input_token.value, mechToken,mechTokenLength);
-        input_token.length = mechTokenLength;
-        free(mechToken);
-        mechToken = NULL;
-        infof(data, "Parse SPNEGO Target Token succeeded\n");
-      }
-    }
-#endif
-  }
-
-  major_status = Curl_gss_init_sec_context(data,
-                                           &minor_status,
-                                           &neg_ctx->context,
-                                           neg_ctx->server_name,
-                                           GSS_C_NO_CHANNEL_BINDINGS,
-                                           &input_token,
-                                           &output_token,
-                                           NULL);
-  if(input_token.length > 0)
-    gss_release_buffer(&minor_status2, &input_token);
-  neg_ctx->status = major_status;
-  if(GSS_ERROR(major_status)) {
-    /* Curl_cleanup_negotiate(data) ??? */
-    log_gss_error(conn, minor_status,
-                  "gss_init_sec_context() failed: ");
-    return -1;
-  }
-
-  if(output_token.length == 0) {
-    return -1;
-  }
-
-  neg_ctx->output_token = output_token;
-  /* conn->bits.close = FALSE; */
-
-  return 0;
-}
-
-
-CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy)
-{
-  struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg:
-    &conn->data->state.negotiate;
-  char *encoded = NULL;
-  size_t len = 0;
-  char *userp;
-  CURLcode error;
-
-#ifdef HAVE_SPNEGO /* Handle SPNEGO */
-  if(checkprefix("Negotiate", neg_ctx->protocol)) {
-    ASN1_OBJECT *   object            = NULL;
-    unsigned char * spnegoToken       = NULL;
-    size_t          spnegoTokenLength = 0;
-    unsigned char * responseToken       = NULL;
-    size_t          responseTokenLength = 0;
-
-    responseToken = malloc(neg_ctx->output_token.length);
-    if(responseToken == NULL)
-      return CURLE_OUT_OF_MEMORY;
-    memcpy(responseToken, neg_ctx->output_token.value,
-           neg_ctx->output_token.length);
-    responseTokenLength = neg_ctx->output_token.length;
-
-    object=OBJ_txt2obj ("1.2.840.113554.1.2.2", 1);
-    if(!makeSpnegoInitialToken (object,
-                                 responseToken,
-                                 responseTokenLength,
-                                 &spnegoToken,
-                                 &spnegoTokenLength)) {
-      free(responseToken);
-      responseToken = NULL;
-      infof(conn->data, "Make SPNEGO Initial Token failed\n");
-    }
-    else {
-      free(responseToken);
-      responseToken = NULL;
-      free(neg_ctx->output_token.value);
-      neg_ctx->output_token.value = malloc(spnegoTokenLength);
-      if(neg_ctx->output_token.value == NULL) {
-        free(spnegoToken);
-        spnegoToken = NULL;
-        return CURLE_OUT_OF_MEMORY;
-      }
-      memcpy(neg_ctx->output_token.value, spnegoToken,spnegoTokenLength);
-      neg_ctx->output_token.length = spnegoTokenLength;
-      free(spnegoToken);
-      spnegoToken = NULL;
-      infof(conn->data, "Make SPNEGO Initial Token succeeded\n");
-    }
-  }
-#endif
-  error = Curl_base64_encode(conn->data,
-                             neg_ctx->output_token.value,
-                             neg_ctx->output_token.length,
-                             &encoded, &len);
-  if(error) {
-    Curl_safefree(neg_ctx->output_token.value);
-    neg_ctx->output_token.value = NULL;
-    return error;
-  }
-
-  if(len == 0) {
-    Curl_safefree(neg_ctx->output_token.value);
-    neg_ctx->output_token.value = NULL;
-    return CURLE_REMOTE_ACCESS_DENIED;
-  }
-
-  userp = aprintf("%sAuthorization: %s %s\r\n", proxy ? "Proxy-" : "",
-                  neg_ctx->protocol, encoded);
-
-  if(proxy)
-    conn->allocptr.proxyuserpwd = userp;
-  else
-    conn->allocptr.userpwd = userp;
-  free(encoded);
-  Curl_cleanup_negotiate (conn->data);
-  return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK;
-}
-
-static void cleanup(struct negotiatedata *neg_ctx)
-{
-  OM_uint32 minor_status;
-  if(neg_ctx->context != GSS_C_NO_CONTEXT)
-    gss_delete_sec_context(&minor_status, &neg_ctx->context, GSS_C_NO_BUFFER);
-
-  if(neg_ctx->output_token.length != 0)
-    gss_release_buffer(&minor_status, &neg_ctx->output_token);
-
-  if(neg_ctx->server_name != GSS_C_NO_NAME)
-    gss_release_name(&minor_status, &neg_ctx->server_name);
-
-  memset(neg_ctx, 0, sizeof(*neg_ctx));
-}
-
-void Curl_cleanup_negotiate(struct SessionHandle *data)
-{
-  cleanup(&data->state.negotiate);
-  cleanup(&data->state.proxyneg);
-}
-
-
-#endif
-#endif
diff --git a/lib/http_negotiate_sspi.c b/lib/http_negotiate_sspi.c
deleted file mode 100644 (file)
index d82b271..0000000
+++ /dev/null
@@ -1,308 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef USE_WINDOWS_SSPI
-
-#ifndef CURL_DISABLE_HTTP
-
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_rawstr.h"
-#include "curl_warnless.h"
-#include "curl_base64.h"
-#include "curl_http_negotiate.h"
-#include "curl_memory.h"
-#include "curl_multibyte.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-static int
-get_gss_name(struct connectdata *conn, bool proxy,
-             struct negotiatedata *neg_ctx)
-{
-  const char* service;
-  size_t length;
-
-  if(proxy && !conn->proxy.name)
-    /* proxy auth requested but no given proxy name, error out! */
-    return -1;
-
-  /* GSSAPI implementation by Globus (known as GSI) requires the name to be
-     of form "<service>/<fqdn>" instead of <service>@<fqdn> (ie. slash instead
-     of at-sign). Also GSI servers are often identified as 'host' not 'khttp'.
-     Change following lines if you want to use GSI */
-
-  /* IIS uses the <service>@<fqdn> form but uses 'http' as the service name,
-     and SSPI then generates an NTLM token. When using <service>/<fqdn> a
-     Kerberos token is generated. */
-
-  if(neg_ctx->gss)
-    service = "KHTTP";
-  else
-    service = "HTTP";
-
-  length = strlen(service) + 1 + strlen(proxy ? conn->proxy.name :
-                                        conn->host.name) + 1;
-  if(length + 1 > sizeof(neg_ctx->server_name))
-    return EMSGSIZE;
-
-  snprintf(neg_ctx->server_name, sizeof(neg_ctx->server_name), "%s/%s",
-           service, proxy ? conn->proxy.name : conn->host.name);
-
-  return 0;
-}
-
-/* returning zero (0) means success, everything else is treated as "failure"
-   with no care exactly what the failure was */
-int Curl_input_negotiate(struct connectdata *conn, bool proxy,
-                         const char *header)
-{
-  struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg:
-    &conn->data->state.negotiate;
-  BYTE              *input_token = 0;
-  SecBufferDesc     out_buff_desc;
-  SecBuffer         out_sec_buff;
-  SecBufferDesc     in_buff_desc;
-  SecBuffer         in_sec_buff;
-  unsigned long     context_attributes;
-  TimeStamp         lifetime;
-  TCHAR             *sname;
-  int ret;
-  size_t len = 0, input_token_len = 0;
-  bool gss = FALSE;
-  const char* protocol;
-  CURLcode error;
-
-  while(*header && ISSPACE(*header))
-    header++;
-
-  if(checkprefix("GSS-Negotiate", header)) {
-    protocol = "GSS-Negotiate";
-    gss = TRUE;
-  }
-  else if(checkprefix("Negotiate", header)) {
-    protocol = "Negotiate";
-    gss = FALSE;
-  }
-  else
-    return -1;
-
-  if(neg_ctx->context) {
-    if(neg_ctx->gss != gss) {
-      return -1;
-    }
-  }
-  else {
-    neg_ctx->protocol = protocol;
-    neg_ctx->gss = gss;
-  }
-
-  if(neg_ctx->context && neg_ctx->status == SEC_E_OK) {
-    /* We finished successfully our part of authentication, but server
-     * rejected it (since we're again here). Exit with an error since we
-     * can't invent anything better */
-    Curl_cleanup_negotiate(conn->data);
-    return -1;
-  }
-
-  if(0 == strlen(neg_ctx->server_name)) {
-    ret = get_gss_name(conn, proxy, neg_ctx);
-    if(ret)
-      return ret;
-  }
-
-  if(!neg_ctx->output_token) {
-    PSecPkgInfo SecurityPackage;
-    ret = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT("Negotiate"),
-                                             &SecurityPackage);
-    if(ret != SEC_E_OK)
-      return -1;
-
-    /* Allocate input and output buffers according to the max token size
-       as indicated by the security package */
-    neg_ctx->max_token_length = SecurityPackage->cbMaxToken;
-    neg_ctx->output_token = malloc(neg_ctx->max_token_length);
-    s_pSecFn->FreeContextBuffer(SecurityPackage);
-  }
-
-  /* Obtain the input token, if any */
-  header += strlen(neg_ctx->protocol);
-  while(*header && ISSPACE(*header))
-    header++;
-
-  len = strlen(header);
-  if(!len) {
-    /* first call in a new negotation, we have to acquire credentials,
-       and allocate memory for the context */
-
-    neg_ctx->credentials = malloc(sizeof(CredHandle));
-    neg_ctx->context = malloc(sizeof(CtxtHandle));
-
-    if(!neg_ctx->credentials || !neg_ctx->context)
-      return -1;
-
-    neg_ctx->status =
-      s_pSecFn->AcquireCredentialsHandle(NULL,
-                                         (TCHAR *) TEXT("Negotiate"),
-                                         SECPKG_CRED_OUTBOUND, NULL, NULL,
-                                         NULL, NULL, neg_ctx->credentials,
-                                         &lifetime);
-    if(neg_ctx->status != SEC_E_OK)
-      return -1;
-  }
-  else {
-    input_token = malloc(neg_ctx->max_token_length);
-    if(!input_token)
-      return -1;
-
-    error = Curl_base64_decode(header,
-                               (unsigned char **)&input_token,
-                               &input_token_len);
-    if(error || input_token_len == 0)
-      return -1;
-  }
-
-  /* prepare the output buffers, and input buffers if present */
-  out_buff_desc.ulVersion = 0;
-  out_buff_desc.cBuffers  = 1;
-  out_buff_desc.pBuffers  = &out_sec_buff;
-
-  out_sec_buff.cbBuffer   = curlx_uztoul(neg_ctx->max_token_length);
-  out_sec_buff.BufferType = SECBUFFER_TOKEN;
-  out_sec_buff.pvBuffer   = neg_ctx->output_token;
-
-
-  if(input_token) {
-    in_buff_desc.ulVersion = 0;
-    in_buff_desc.cBuffers  = 1;
-    in_buff_desc.pBuffers  = &in_sec_buff;
-
-    in_sec_buff.cbBuffer   = curlx_uztoul(input_token_len);
-    in_sec_buff.BufferType = SECBUFFER_TOKEN;
-    in_sec_buff.pvBuffer   = input_token;
-  }
-
-  sname = Curl_convert_UTF8_to_tchar(neg_ctx->server_name);
-  if(!sname)
-    return CURLE_OUT_OF_MEMORY;
-
-  neg_ctx->status = s_pSecFn->InitializeSecurityContext(
-    neg_ctx->credentials,
-    input_token ? neg_ctx->context : 0,
-    sname,
-    ISC_REQ_CONFIDENTIALITY,
-    0,
-    SECURITY_NATIVE_DREP,
-    input_token ? &in_buff_desc : 0,
-    0,
-    neg_ctx->context,
-    &out_buff_desc,
-    &context_attributes,
-    &lifetime);
-
-  Curl_unicodefree(sname);
-
-  if(GSS_ERROR(neg_ctx->status))
-    return -1;
-
-  if(neg_ctx->status == SEC_I_COMPLETE_NEEDED ||
-     neg_ctx->status == SEC_I_COMPLETE_AND_CONTINUE) {
-    neg_ctx->status = s_pSecFn->CompleteAuthToken(neg_ctx->context,
-                                                  &out_buff_desc);
-    if(GSS_ERROR(neg_ctx->status))
-      return -1;
-  }
-
-  neg_ctx->output_token_length = out_sec_buff.cbBuffer;
-
-  return 0;
-}
-
-
-CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy)
-{
-  struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg:
-    &conn->data->state.negotiate;
-  char *encoded = NULL;
-  size_t len = 0;
-  char *userp;
-  CURLcode error;
-
-  error = Curl_base64_encode(conn->data,
-                             (const char*)neg_ctx->output_token,
-                             neg_ctx->output_token_length,
-                             &encoded, &len);
-  if(error)
-    return error;
-
-  if(len == 0)
-    return CURLE_REMOTE_ACCESS_DENIED;
-
-  userp = aprintf("%sAuthorization: %s %s\r\n", proxy ? "Proxy-" : "",
-                  neg_ctx->protocol, encoded);
-
-  if(proxy)
-    conn->allocptr.proxyuserpwd = userp;
-  else
-    conn->allocptr.userpwd = userp;
-  free(encoded);
-  Curl_cleanup_negotiate (conn->data);
-  return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK;
-}
-
-static void cleanup(struct negotiatedata *neg_ctx)
-{
-  if(neg_ctx->context) {
-    s_pSecFn->DeleteSecurityContext(neg_ctx->context);
-    free(neg_ctx->context);
-    neg_ctx->context = 0;
-  }
-
-  if(neg_ctx->credentials) {
-    s_pSecFn->FreeCredentialsHandle(neg_ctx->credentials);
-    free(neg_ctx->credentials);
-    neg_ctx->credentials = 0;
-  }
-
-  if(neg_ctx->output_token) {
-    free(neg_ctx->output_token);
-    neg_ctx->output_token = 0;
-  }
-
-  neg_ctx->max_token_length = 0;
-}
-
-void Curl_cleanup_negotiate(struct SessionHandle *data)
-{
-  cleanup(&data->state.negotiate);
-  cleanup(&data->state.proxyneg);
-}
-
-
-#endif
-#endif
diff --git a/lib/http_proxy.c b/lib/http_proxy.c
deleted file mode 100644 (file)
index 14ef9dc..0000000
+++ /dev/null
@@ -1,595 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
-
-#include "curl_urldata.h"
-#include <curl/curl.h>
-#include "curl_http_proxy.h"
-#include "curl_sendf.h"
-#include "curl_http.h"
-#include "curl_url.h"
-#include "curl_select.h"
-#include "curl_rawstr.h"
-#include "curl_progress.h"
-#include "curl_non_ascii.h"
-#include "curl_connect.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curlx.h"
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-CURLcode Curl_proxy_connect(struct connectdata *conn)
-{
-  if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
-#ifndef CURL_DISABLE_PROXY
-    /* for [protocol] tunneled through HTTP proxy */
-    struct HTTP http_proxy;
-    void *prot_save;
-    CURLcode result;
-
-    /* BLOCKING */
-    /* We want "seamless" operations through HTTP proxy tunnel */
-
-    /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the
-     * member conn->proto.http; we want [protocol] through HTTP and we have
-     * to change the member temporarily for connecting to the HTTP
-     * proxy. After Curl_proxyCONNECT we have to set back the member to the
-     * original pointer
-     *
-     * This function might be called several times in the multi interface case
-     * if the proxy's CONNTECT response is not instant.
-     */
-    prot_save = conn->data->state.proto.generic;
-    memset(&http_proxy, 0, sizeof(http_proxy));
-    conn->data->state.proto.http = &http_proxy;
-    conn->bits.close = FALSE;
-    result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
-                               conn->host.name, conn->remote_port);
-    conn->data->state.proto.generic = prot_save;
-    if(CURLE_OK != result)
-      return result;
-#else
-    return CURLE_NOT_BUILT_IN;
-#endif
-  }
-  /* no HTTP tunnel proxy, just return */
-  return CURLE_OK;
-}
-
-/*
- * Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This
- * function will issue the necessary commands to get a seamless tunnel through
- * this proxy. After that, the socket can be used just as a normal socket.
- *
- * This badly needs to be rewritten. CONNECT should be sent and dealt with
- * like any ordinary HTTP request, and not specially crafted like this. This
- * function only remains here like this for now since the rewrite is a bit too
- * much work to do at the moment.
- *
- * This function is BLOCKING which is nasty for all multi interface using apps.
- */
-
-CURLcode Curl_proxyCONNECT(struct connectdata *conn,
-                           int sockindex,
-                           const char *hostname,
-                           unsigned short remote_port)
-{
-  int subversion=0;
-  struct SessionHandle *data=conn->data;
-  struct SingleRequest *k = &data->req;
-  CURLcode result;
-  long timeout =
-    data->set.timeout?data->set.timeout:PROXY_TIMEOUT; /* in milliseconds */
-  curl_socket_t tunnelsocket = conn->sock[sockindex];
-  curl_off_t cl=0;
-  bool closeConnection = FALSE;
-  bool chunked_encoding = FALSE;
-  long check;
-
-#define SELECT_OK      0
-#define SELECT_ERROR   1
-#define SELECT_TIMEOUT 2
-  int error = SELECT_OK;
-
-  if(conn->tunnel_state[sockindex] == TUNNEL_COMPLETE)
-    return CURLE_OK; /* CONNECT is already completed */
-
-  conn->bits.proxy_connect_closed = FALSE;
-
-  do {
-    if(TUNNEL_INIT == conn->tunnel_state[sockindex]) {
-      /* BEGIN CONNECT PHASE */
-      char *host_port;
-      Curl_send_buffer *req_buffer;
-
-      infof(data, "Establish HTTP proxy tunnel to %s:%hu\n",
-            hostname, remote_port);
-
-      if(data->req.newurl) {
-        /* This only happens if we've looped here due to authentication
-           reasons, and we don't really use the newly cloned URL here
-           then. Just free() it. */
-        free(data->req.newurl);
-        data->req.newurl = NULL;
-      }
-
-      /* initialize a dynamic send-buffer */
-      req_buffer = Curl_add_buffer_init();
-
-      if(!req_buffer)
-        return CURLE_OUT_OF_MEMORY;
-
-      host_port = aprintf("%s:%hu", hostname, remote_port);
-      if(!host_port) {
-        free(req_buffer);
-        return CURLE_OUT_OF_MEMORY;
-      }
-
-      /* Setup the proxy-authorization header, if any */
-      result = Curl_http_output_auth(conn, "CONNECT", host_port, TRUE);
-
-      free(host_port);
-
-      if(CURLE_OK == result) {
-        char *host=(char *)"";
-        const char *proxyconn="";
-        const char *useragent="";
-        const char *http = (conn->proxytype == CURLPROXY_HTTP_1_0) ?
-          "1.0" : "1.1";
-        char *hostheader= /* host:port with IPv6 support */
-          aprintf("%s%s%s:%hu", conn->bits.ipv6_ip?"[":"",
-                  hostname, conn->bits.ipv6_ip?"]":"",
-                  remote_port);
-        if(!hostheader) {
-          free(req_buffer);
-          return CURLE_OUT_OF_MEMORY;
-        }
-
-        if(!Curl_checkheaders(data, "Host:")) {
-          host = aprintf("Host: %s\r\n", hostheader);
-          if(!host) {
-            free(hostheader);
-            free(req_buffer);
-            return CURLE_OUT_OF_MEMORY;
-          }
-        }
-        if(!Curl_checkheaders(data, "Proxy-Connection:"))
-          proxyconn = "Proxy-Connection: Keep-Alive\r\n";
-
-        if(!Curl_checkheaders(data, "User-Agent:") &&
-           data->set.str[STRING_USERAGENT])
-          useragent = conn->allocptr.uagent;
-
-        result =
-          Curl_add_bufferf(req_buffer,
-                           "CONNECT %s HTTP/%s\r\n"
-                           "%s"  /* Host: */
-                           "%s"  /* Proxy-Authorization */
-                           "%s"  /* User-Agent */
-                           "%s", /* Proxy-Connection */
-                           hostheader,
-                           http,
-                           host,
-                           conn->allocptr.proxyuserpwd?
-                           conn->allocptr.proxyuserpwd:"",
-                           useragent,
-                           proxyconn);
-
-        if(host && *host)
-          free(host);
-        free(hostheader);
-
-        if(CURLE_OK == result)
-          result = Curl_add_custom_headers(conn, req_buffer);
-
-        if(CURLE_OK == result)
-          /* CRLF terminate the request */
-          result = Curl_add_bufferf(req_buffer, "\r\n");
-
-        if(CURLE_OK == result) {
-          /* Send the connect request to the proxy */
-          /* BLOCKING */
-          result =
-            Curl_add_buffer_send(req_buffer, conn,
-                                 &data->info.request_size, 0, sockindex);
-        }
-        req_buffer = NULL;
-        if(result)
-          failf(data, "Failed sending CONNECT to proxy");
-      }
-
-      Curl_safefree(req_buffer);
-      if(result)
-        return result;
-
-      conn->tunnel_state[sockindex] = TUNNEL_CONNECT;
-    } /* END CONNECT PHASE */
-
-    /* now we've issued the CONNECT and we're waiting to hear back -
-       we try not to block here in multi-mode because that might be a LONG
-       wait if the proxy cannot connect-through to the remote host. */
-
-    /* if timeout is requested, find out how much remaining time we have */
-    check = timeout - /* timeout time */
-      Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
-    if(check <= 0) {
-      failf(data, "Proxy CONNECT aborted due to timeout");
-      return CURLE_RECV_ERROR;
-    }
-
-    /* if we're in multi-mode and we would block, return instead for a retry */
-    if(Curl_if_multi == data->state.used_interface) {
-      if(0 == Curl_socket_ready(tunnelsocket, CURL_SOCKET_BAD, 0))
-        /* return so we'll be called again polling-style */
-        return CURLE_OK;
-      else {
-        DEBUGF(infof(data,
-                     "Multi mode finished polling for response from "
-                     "proxy CONNECT\n"));
-      }
-    }
-    else {
-      DEBUGF(infof(data, "Easy mode waiting response from proxy CONNECT\n"));
-    }
-
-    /* at this point, either:
-       1) we're in easy-mode and so it's okay to block waiting for a CONNECT
-       response
-       2) we're in multi-mode and we didn't block - it's either an error or we
-       now have some data waiting.
-       In any case, the tunnel_connecting phase is over. */
-
-    { /* BEGIN NEGOTIATION PHASE */
-      size_t nread;   /* total size read */
-      int perline; /* count bytes per line */
-      int keepon=TRUE;
-      ssize_t gotbytes;
-      char *ptr;
-      char *line_start;
-
-      ptr=data->state.buffer;
-      line_start = ptr;
-
-      nread=0;
-      perline=0;
-      keepon=TRUE;
-
-      while((nread<BUFSIZE) && (keepon && !error)) {
-
-        /* if timeout is requested, find out how much remaining time we have */
-        check = timeout - /* timeout time */
-          Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
-        if(check <= 0) {
-          failf(data, "Proxy CONNECT aborted due to timeout");
-          error = SELECT_TIMEOUT; /* already too little time */
-          break;
-        }
-
-        /* loop every second at least, less if the timeout is near */
-        switch (Curl_socket_ready(tunnelsocket, CURL_SOCKET_BAD,
-                                  check<1000L?check:1000)) {
-        case -1: /* select() error, stop reading */
-          error = SELECT_ERROR;
-          failf(data, "Proxy CONNECT aborted due to select/poll error");
-          break;
-        case 0: /* timeout */
-          break;
-        default:
-          DEBUGASSERT(ptr+BUFSIZE-nread <= data->state.buffer+BUFSIZE+1);
-          result = Curl_read(conn, tunnelsocket, ptr, BUFSIZE-nread,
-                             &gotbytes);
-          if(result==CURLE_AGAIN)
-            continue; /* go loop yourself */
-          else if(result)
-            keepon = FALSE;
-          else if(gotbytes <= 0) {
-            keepon = FALSE;
-            if(data->set.proxyauth && data->state.authproxy.avail) {
-              /* proxy auth was requested and there was proxy auth available,
-                 then deem this as "mere" proxy disconnect */
-              conn->bits.proxy_connect_closed = TRUE;
-            }
-            else {
-              error = SELECT_ERROR;
-              failf(data, "Proxy CONNECT aborted");
-            }
-          }
-          else {
-            /*
-             * We got a whole chunk of data, which can be anything from one
-             * byte to a set of lines and possibly just a piece of the last
-             * line.
-             */
-            int i;
-
-            nread += gotbytes;
-
-            if(keepon > TRUE) {
-              /* This means we are currently ignoring a response-body */
-
-              nread = 0; /* make next read start over in the read buffer */
-              ptr=data->state.buffer;
-              if(cl) {
-                /* A Content-Length based body: simply count down the counter
-                   and make sure to break out of the loop when we're done! */
-                cl -= gotbytes;
-                if(cl<=0) {
-                  keepon = FALSE;
-                  break;
-                }
-              }
-              else {
-                /* chunked-encoded body, so we need to do the chunked dance
-                   properly to know when the end of the body is reached */
-                CHUNKcode r;
-                ssize_t tookcareof=0;
-
-                /* now parse the chunked piece of data so that we can
-                   properly tell when the stream ends */
-                r = Curl_httpchunk_read(conn, ptr, gotbytes, &tookcareof);
-                if(r == CHUNKE_STOP) {
-                  /* we're done reading chunks! */
-                  infof(data, "chunk reading DONE\n");
-                  keepon = FALSE;
-                  /* we did the full CONNECT treatment, go COMPLETE */
-                  conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
-                }
-                else
-                  infof(data, "Read %zd bytes of chunk, continue\n",
-                        tookcareof);
-              }
-            }
-            else
-              for(i = 0; i < gotbytes; ptr++, i++) {
-                perline++; /* amount of bytes in this line so far */
-                if(*ptr == 0x0a) {
-                  char letter;
-                  int writetype;
-
-                  /* convert from the network encoding */
-                  result = Curl_convert_from_network(data, line_start,
-                                                     perline);
-                  /* Curl_convert_from_network calls failf if unsuccessful */
-                  if(result)
-                    return result;
-
-                  /* output debug if that is requested */
-                  if(data->set.verbose)
-                    Curl_debug(data, CURLINFO_HEADER_IN,
-                               line_start, (size_t)perline, conn);
-
-                  /* send the header to the callback */
-                  writetype = CLIENTWRITE_HEADER;
-                  if(data->set.include_header)
-                    writetype |= CLIENTWRITE_BODY;
-
-                  result = Curl_client_write(conn, writetype, line_start,
-                                             perline);
-                  if(result)
-                    return result;
-
-                  /* Newlines are CRLF, so the CR is ignored as the line isn't
-                     really terminated until the LF comes. Treat a following CR
-                     as end-of-headers as well.*/
-
-                  if(('\r' == line_start[0]) ||
-                     ('\n' == line_start[0])) {
-                    /* end of response-headers from the proxy */
-                    nread = 0; /* make next read start over in the read
-                                  buffer */
-                    ptr=data->state.buffer;
-                    if((407 == k->httpcode) && !data->state.authproblem) {
-                      /* If we get a 407 response code with content length
-                         when we have no auth problem, we must ignore the
-                         whole response-body */
-                      keepon = 2;
-
-                      if(cl) {
-
-                        infof(data, "Ignore %" FORMAT_OFF_T
-                              " bytes of response-body\n", cl);
-                        /* remove the remaining chunk of what we already
-                           read */
-                        cl -= (gotbytes - i);
-
-                        if(cl<=0)
-                          /* if the whole thing was already read, we are done!
-                           */
-                          keepon=FALSE;
-                      }
-                      else if(chunked_encoding) {
-                        CHUNKcode r;
-                        /* We set ignorebody true here since the chunked
-                           decoder function will acknowledge that. Pay
-                           attention so that this is cleared again when this
-                           function returns! */
-                        k->ignorebody = TRUE;
-                        infof(data, "%zd bytes of chunk left\n", gotbytes-i);
-
-                        if(line_start[1] == '\n') {
-                          /* this can only be a LF if the letter at index 0
-                             was a CR */
-                          line_start++;
-                          i++;
-                        }
-
-                        /* now parse the chunked piece of data so that we can
-                           properly tell when the stream ends */
-                        r = Curl_httpchunk_read(conn, line_start+1,
-                                                  gotbytes -i, &gotbytes);
-                        if(r == CHUNKE_STOP) {
-                          /* we're done reading chunks! */
-                          infof(data, "chunk reading DONE\n");
-                          keepon = FALSE;
-                          /* we did the full CONNECT treatment, go to
-                             COMPLETE */
-                          conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
-                        }
-                        else
-                          infof(data, "Read %zd bytes of chunk, continue\n",
-                                gotbytes);
-                      }
-                      else {
-                        /* without content-length or chunked encoding, we
-                           can't keep the connection alive since the close is
-                           the end signal so we bail out at once instead */
-                        keepon=FALSE;
-                      }
-                    }
-                    else {
-                      keepon = FALSE;
-                      if(200 == data->info.httpproxycode) {
-                        if(gotbytes - (i+1))
-                          failf(data, "Proxy CONNECT followed by %zd bytes "
-                                "of opaque data. Data ignored (known bug #39)",
-                                gotbytes - (i+1));
-                      }
-                    }
-                    /* we did the full CONNECT treatment, go to COMPLETE */
-                    conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
-                    break; /* breaks out of for-loop, not switch() */
-                  }
-
-                  /* keep a backup of the position we are about to blank */
-                  letter = line_start[perline];
-                  line_start[perline]=0; /* zero terminate the buffer */
-                  if((checkprefix("WWW-Authenticate:", line_start) &&
-                      (401 == k->httpcode)) ||
-                     (checkprefix("Proxy-authenticate:", line_start) &&
-                      (407 == k->httpcode))) {
-                    result = Curl_http_input_auth(conn, k->httpcode,
-                                                  line_start);
-                    if(result)
-                      return result;
-                  }
-                  else if(checkprefix("Content-Length:", line_start)) {
-                    cl = curlx_strtoofft(line_start +
-                                         strlen("Content-Length:"), NULL, 10);
-                  }
-                  else if(Curl_compareheader(line_start,
-                                             "Connection:", "close"))
-                    closeConnection = TRUE;
-                  else if(Curl_compareheader(line_start,
-                                             "Transfer-Encoding:",
-                                             "chunked")) {
-                    infof(data, "CONNECT responded chunked\n");
-                    chunked_encoding = TRUE;
-                    /* init our chunky engine */
-                    Curl_httpchunk_init(conn);
-                  }
-                  else if(Curl_compareheader(line_start,
-                                             "Proxy-Connection:", "close"))
-                    closeConnection = TRUE;
-                  else if(2 == sscanf(line_start, "HTTP/1.%d %d",
-                                      &subversion,
-                                      &k->httpcode)) {
-                    /* store the HTTP code from the proxy */
-                    data->info.httpproxycode = k->httpcode;
-                  }
-                  /* put back the letter we blanked out before */
-                  line_start[perline]= letter;
-
-                  perline=0; /* line starts over here */
-                  line_start = ptr+1; /* this skips the zero byte we wrote */
-                }
-              }
-          }
-          break;
-        } /* switch */
-        if(Curl_pgrsUpdate(conn))
-          return CURLE_ABORTED_BY_CALLBACK;
-      } /* while there's buffer left and loop is requested */
-
-      if(error)
-        return CURLE_RECV_ERROR;
-
-      if(data->info.httpproxycode != 200) {
-        /* Deal with the possibly already received authenticate
-           headers. 'newurl' is set to a new URL if we must loop. */
-        result = Curl_http_auth_act(conn);
-        if(result)
-          return result;
-
-        if(conn->bits.close)
-          /* the connection has been marked for closure, most likely in the
-             Curl_http_auth_act() function and thus we can kill it at once
-             below
-          */
-          closeConnection = TRUE;
-      }
-
-      if(closeConnection && data->req.newurl) {
-        /* Connection closed by server. Don't use it anymore */
-        Curl_closesocket(conn, conn->sock[sockindex]);
-        conn->sock[sockindex] = CURL_SOCKET_BAD;
-        break;
-      }
-    } /* END NEGOTIATION PHASE */
-
-    /* If we are supposed to continue and request a new URL, which basically
-     * means the HTTP authentication is still going on so if the tunnel
-     * is complete we start over in INIT state */
-    if(data->req.newurl &&
-       (TUNNEL_COMPLETE == conn->tunnel_state[sockindex])) {
-      conn->tunnel_state[sockindex] = TUNNEL_INIT;
-      infof(data, "TUNNEL_STATE switched to: %d\n",
-            conn->tunnel_state[sockindex]);
-    }
-
-  } while(data->req.newurl);
-
-  if(200 != data->req.httpcode) {
-    failf(data, "Received HTTP code %d from proxy after CONNECT",
-          data->req.httpcode);
-
-    if(closeConnection && data->req.newurl)
-      conn->bits.proxy_connect_closed = TRUE;
-
-    /* to back to init state */
-    conn->tunnel_state[sockindex] = TUNNEL_INIT;
-
-    return CURLE_RECV_ERROR;
-  }
-
-  conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
-
-  /* If a proxy-authorization header was used for the proxy, then we should
-     make sure that it isn't accidentally used for the document request
-     after we've connected. So let's free and clear it here. */
-  Curl_safefree(conn->allocptr.proxyuserpwd);
-  conn->allocptr.proxyuserpwd = NULL;
-
-  data->state.authproxy.done = TRUE;
-
-  infof (data, "Proxy replied OK to CONNECT request\n");
-  data->req.ignorebody = FALSE; /* put it (back) to non-ignore state */
-  return CURLE_OK;
-}
-#endif /* CURL_DISABLE_PROXY */
diff --git a/lib/idn_win32.c b/lib/idn_win32.c
deleted file mode 100644 (file)
index eca2254..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
- /*
-  * IDN conversions using Windows kernel32 and normaliz libraries.
-  */
-
-#include "curl_setup.h"
-
-#ifdef USE_WIN32_IDN
-
-#include "curl_multibyte.h"
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-#ifdef WANT_IDN_PROTOTYPES
-WINBASEAPI int WINAPI IdnToAscii(DWORD, const WCHAR *, int, WCHAR *, int);
-WINBASEAPI int WINAPI IdnToUnicode(DWORD, const WCHAR *, int, WCHAR *, int);
-#endif
-
-#define IDN_MAX_LENGTH 255
-
-int curl_win32_idn_to_ascii(const char *in, char **out);
-int curl_win32_ascii_to_idn(const char *in, size_t in_len, char **out_utf8);
-
-int curl_win32_idn_to_ascii(const char *in, char **out)
-{
-  wchar_t *in_w = Curl_convert_UTF8_to_wchar(in);
-  if(in_w) {
-    wchar_t punycode[IDN_MAX_LENGTH];
-    if(IdnToAscii(0, in_w, -1, punycode, IDN_MAX_LENGTH) == 0) {
-      wprintf(L"ERROR %d converting to Punycode\n", GetLastError());
-      free(in_w);
-      return 0;
-    }
-    free(in_w);
-
-    *out = Curl_convert_wchar_to_UTF8(punycode);
-    if(!*out)
-      return 0;
-  }
-  return 1;
-}
-
-int curl_win32_ascii_to_idn(const char *in, size_t in_len, char **out_utf8)
-{
-  (void)in_len; /* unused */
-  if(in) {
-    WCHAR unicode[IDN_MAX_LENGTH];
-
-    if(IdnToUnicode(0, (wchar_t *)in, -1, unicode, IDN_MAX_LENGTH) == 0) {
-      wprintf(L"ERROR %d converting to Punycode\n", GetLastError());
-      return 0;
-    }
-    else {
-      *out_utf8 = Curl_convert_wchar_to_UTF8(unicode);
-      if(!*out_utf8)
-        return 0;
-    }
-  }
-  return 1;
-}
-
-#endif /* USE_WIN32_IDN */
diff --git a/lib/if2ip.c b/lib/if2ip.c
deleted file mode 100644 (file)
index db9c784..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef HAVE_NETINET_IN_H
-#  include <netinet/in.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#  include <arpa/inet.h>
-#endif
-#ifdef HAVE_NET_IF_H
-#  include <net/if.h>
-#endif
-#ifdef HAVE_SYS_IOCTL_H
-#  include <sys/ioctl.h>
-#endif
-#ifdef HAVE_NETDB_H
-#  include <netdb.h>
-#endif
-#ifdef HAVE_SYS_SOCKIO_H
-#  include <sys/sockio.h>
-#endif
-#ifdef HAVE_IFADDRS_H
-#  include <ifaddrs.h>
-#endif
-#ifdef HAVE_STROPTS_H
-#  include <stropts.h>
-#endif
-#ifdef __VMS
-#  include <inet.h>
-#endif
-
-#include "curl_inet_ntop.h"
-#include "curl_strequal.h"
-#include "curl_if2ip.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/* ------------------------------------------------------------------ */
-
-#if defined(HAVE_GETIFADDRS)
-
-bool Curl_if_is_interface_name(const char *interf)
-{
-  bool result = FALSE;
-
-  struct ifaddrs *iface, *head;
-
-  if(getifaddrs(&head) >= 0) {
-    for(iface=head; iface != NULL; iface=iface->ifa_next) {
-      if(curl_strequal(iface->ifa_name, interf)) {
-        result = TRUE;
-        break;
-      }
-    }
-    freeifaddrs(head);
-  }
-  return result;
-}
-
-char *Curl_if2ip(int af, const char *interf, char *buf, int buf_size)
-{
-  struct ifaddrs *iface, *head;
-  char *ip = NULL;
-
-  if(getifaddrs(&head) >= 0) {
-    for(iface=head; iface != NULL; iface=iface->ifa_next) {
-      if((iface->ifa_addr != NULL) &&
-         (iface->ifa_addr->sa_family == af) &&
-         curl_strequal(iface->ifa_name, interf)) {
-        void *addr;
-        char scope[12]="";
-#ifdef ENABLE_IPV6
-        if(af == AF_INET6) {
-          unsigned int scopeid = 0;
-          addr = &((struct sockaddr_in6 *)iface->ifa_addr)->sin6_addr;
-#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
-          /* Include the scope of this interface as part of the address */
-          scopeid = ((struct sockaddr_in6 *)iface->ifa_addr)->sin6_scope_id;
-#endif
-          if(scopeid)
-            snprintf(scope, sizeof(scope), "%%%u", scopeid);
-        }
-        else
-#endif
-          addr = &((struct sockaddr_in *)iface->ifa_addr)->sin_addr;
-        ip = (char *) Curl_inet_ntop(af, addr, buf, buf_size);
-        strlcat(buf, scope, buf_size);
-        break;
-      }
-    }
-    freeifaddrs(head);
-  }
-  return ip;
-}
-
-#elif defined(HAVE_IOCTL_SIOCGIFADDR)
-
-bool Curl_if_is_interface_name(const char *interf)
-{
-  /* This is here just to support the old interfaces */
-  char buf[256];
-
-  char *ip = Curl_if2ip(AF_INET, interf, buf, sizeof(buf));
-
-  return (ip != NULL) ? TRUE : FALSE;
-}
-
-char *Curl_if2ip(int af, const char *interf, char *buf, int buf_size)
-{
-  struct ifreq req;
-  struct in_addr in;
-  struct sockaddr_in *s;
-  curl_socket_t dummy;
-  size_t len;
-  char *ip;
-
-  if(!interf || (af != AF_INET))
-    return NULL;
-
-  len = strlen(interf);
-  if(len >= sizeof(req.ifr_name))
-    return NULL;
-
-  dummy = socket(AF_INET, SOCK_STREAM, 0);
-  if(CURL_SOCKET_BAD == dummy)
-    return NULL;
-
-  memset(&req, 0, sizeof(req));
-  memcpy(req.ifr_name, interf, len+1);
-  req.ifr_addr.sa_family = AF_INET;
-
-  if(ioctl(dummy, SIOCGIFADDR, &req) < 0) {
-    sclose(dummy);
-    return NULL;
-  }
-
-  s = (struct sockaddr_in *)&req.ifr_addr;
-  memcpy(&in, &s->sin_addr, sizeof(in));
-  ip = (char *) Curl_inet_ntop(s->sin_family, &in, buf, buf_size);
-
-  sclose(dummy);
-  return ip;
-}
-
-#else
-
-bool Curl_if_is_interface_name(const char *interf)
-{
-  (void) interf;
-
-  return FALSE;
-}
-
-char *Curl_if2ip(int af, const char *interf, char *buf, int buf_size)
-{
-    (void) af;
-    (void) interf;
-    (void) buf;
-    (void) buf_size;
-    return NULL;
-}
-
-#endif
diff --git a/lib/imap.c b/lib/imap.c
deleted file mode 100644 (file)
index 8175daa..0000000
+++ /dev/null
@@ -1,1139 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * RFC3501 IMAPv4 protocol
- * RFC5092 IMAP URL Scheme
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifndef CURL_DISABLE_IMAP
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef HAVE_UTSNAME_H
-#include <sys/utsname.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef __VMS
-#include <in.h>
-#include <inet.h>
-#endif
-
-#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
-#undef in_addr_t
-#define in_addr_t unsigned long
-#endif
-
-#include <curl/curl.h>
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_if2ip.h"
-#include "curl_hostip.h"
-#include "curl_progress.h"
-#include "curl_transfer.h"
-#include "curl_escape.h"
-#include "curl_http.h" /* for HTTP proxy tunnel stuff */
-#include "curl_socks.h"
-#include "curl_imap.h"
-
-#include "curl_strtoofft.h"
-#include "curl_strequal.h"
-#include "curl_sslgen.h"
-#include "curl_connect.h"
-#include "curl_strerror.h"
-#include "curl_select.h"
-#include "curl_multiif.h"
-#include "curl_url.h"
-#include "curl_rawstr.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/* Local API functions */
-static CURLcode imap_parse_url_path(struct connectdata *conn);
-static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done);
-static CURLcode imap_do(struct connectdata *conn, bool *done);
-static CURLcode imap_done(struct connectdata *conn, CURLcode status,
-                          bool premature);
-static CURLcode imap_connect(struct connectdata *conn, bool *done);
-static CURLcode imap_disconnect(struct connectdata *conn, bool dead);
-static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done);
-static int imap_getsock(struct connectdata *conn,
-                        curl_socket_t *socks,
-                        int numsocks);
-static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done);
-static CURLcode imap_setup_connection(struct connectdata *conn);
-static CURLcode imap_state_upgrade_tls(struct connectdata *conn);
-
-/*
- * IMAP protocol handler.
- */
-
-const struct Curl_handler Curl_handler_imap = {
-  "IMAP",                           /* scheme */
-  imap_setup_connection,            /* setup_connection */
-  imap_do,                          /* do_it */
-  imap_done,                        /* done */
-  ZERO_NULL,                        /* do_more */
-  imap_connect,                     /* connect_it */
-  imap_multi_statemach,             /* connecting */
-  imap_doing,                       /* doing */
-  imap_getsock,                     /* proto_getsock */
-  imap_getsock,                     /* doing_getsock */
-  ZERO_NULL,                        /* domore_getsock */
-  ZERO_NULL,                        /* perform_getsock */
-  imap_disconnect,                  /* disconnect */
-  ZERO_NULL,                        /* readwrite */
-  PORT_IMAP,                        /* defport */
-  CURLPROTO_IMAP,                   /* protocol */
-  PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD
-  | PROTOPT_NOURLQUERY              /* flags */
-};
-
-
-#ifdef USE_SSL
-/*
- * IMAPS protocol handler.
- */
-
-const struct Curl_handler Curl_handler_imaps = {
-  "IMAPS",                          /* scheme */
-  imap_setup_connection,            /* setup_connection */
-  imap_do,                          /* do_it */
-  imap_done,                        /* done */
-  ZERO_NULL,                        /* do_more */
-  imap_connect,                     /* connect_it */
-  imap_multi_statemach,             /* connecting */
-  imap_doing,                       /* doing */
-  imap_getsock,                     /* proto_getsock */
-  imap_getsock,                     /* doing_getsock */
-  ZERO_NULL,                        /* domore_getsock */
-  ZERO_NULL,                        /* perform_getsock */
-  imap_disconnect,                  /* disconnect */
-  ZERO_NULL,                        /* readwrite */
-  PORT_IMAPS,                       /* defport */
-  CURLPROTO_IMAP | CURLPROTO_IMAPS, /* protocol */
-  PROTOPT_CLOSEACTION | PROTOPT_SSL | PROTOPT_NEEDSPWD
-  | PROTOPT_NOURLQUERY              /* flags */
-};
-#endif
-
-#ifndef CURL_DISABLE_HTTP
-/*
- * HTTP-proxyed IMAP protocol handler.
- */
-
-static const struct Curl_handler Curl_handler_imap_proxy = {
-  "IMAP",                               /* scheme */
-  ZERO_NULL,                            /* setup_connection */
-  Curl_http,                            /* do_it */
-  Curl_http_done,                       /* done */
-  ZERO_NULL,                            /* do_more */
-  ZERO_NULL,                            /* connect_it */
-  ZERO_NULL,                            /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
-  ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* readwrite */
-  PORT_IMAP,                            /* defport */
-  CURLPROTO_HTTP,                       /* protocol */
-  PROTOPT_NONE                          /* flags */
-};
-
-
-#ifdef USE_SSL
-/*
- * HTTP-proxyed IMAPS protocol handler.
- */
-
-static const struct Curl_handler Curl_handler_imaps_proxy = {
-  "IMAPS",                              /* scheme */
-  ZERO_NULL,                            /* setup_connection */
-  Curl_http,                            /* do_it */
-  Curl_http_done,                       /* done */
-  ZERO_NULL,                            /* do_more */
-  ZERO_NULL,                            /* connect_it */
-  ZERO_NULL,                            /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
-  ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* readwrite */
-  PORT_IMAPS,                           /* defport */
-  CURLPROTO_HTTP,                       /* protocol */
-  PROTOPT_NONE                          /* flags */
-};
-#endif
-#endif
-
-/***********************************************************************
- *
- * imap_sendf()
- *
- * Sends the formated string as an IMAP command to the server.
- *
- * Designed to never block.
- */
-static CURLcode imap_sendf(struct connectdata *conn,
-                           const char *idstr, /* command id to wait for */
-                           const char *fmt, ...)
-{
-  CURLcode res;
-  struct imap_conn *imapc = &conn->proto.imapc;
-  va_list ap;
-  va_start(ap, fmt);
-
-  imapc->idstr = idstr;
-
-  res = Curl_pp_vsendf(&imapc->pp, fmt, ap);
-
-  va_end(ap);
-
-  return res;
-}
-
-static const char *getcmdid(struct connectdata *conn)
-{
-  static const char * const ids[]= {
-    "A",
-    "B",
-    "C",
-    "D"
-  };
-
-  struct imap_conn *imapc = &conn->proto.imapc;
-
-  /* Get the next id, but wrap at end of table */
-  imapc->cmdid = (int)((imapc->cmdid + 1) % (sizeof(ids) / sizeof(ids[0])));
-
-  return ids[imapc->cmdid];
-}
-
-/***********************************************************************
- *
- * imap_atom()
- *
- * Checks the input string for characters that need escaping and returns an
- * atom ready for sending to the server.
- *
- * The returned string needs to be freed.
- *
- */
-static char* imap_atom(const char* str)
-{
-  const char *p1;
-  char *p2;
-  size_t backsp_count = 0;
-  size_t quote_count = 0;
-  bool space_exists = FALSE;
-  size_t newlen = 0;
-  char *newstr = NULL;
-
-  if(!str)
-    return NULL;
-
-  /* Count any unescapped characters */
-  p1 = str;
-  while(*p1) {
-    if(*p1 == '\\')
-      backsp_count++;
-    else if(*p1 == '"')
-      quote_count++;
-    else if(*p1 == ' ')
-      space_exists = TRUE;
-
-    p1++;
-  }
-
-  /* Does the input contain any unescapped characters? */
-  if(!backsp_count && !quote_count && !space_exists)
-    return strdup(str);
-
-  /* Calculate the new string length */
-  newlen = strlen(str) + backsp_count + quote_count + (space_exists ? 2 : 0);
-
-  /* Allocate the new string */
-  newstr = (char *) malloc((newlen + 1) * sizeof(char));
-  if(!newstr)
-    return NULL;
-
-  /* Surround the string in quotes if necessary */
-  p2 = newstr;
-  if(space_exists) {
-    newstr[0] = '"';
-    newstr[newlen - 1] = '"';
-    p2++;
-  }
-
-  /* Copy the string, escaping backslash and quote characters along the way */
-  p1 = str;
-  while(*p1) {
-    if(*p1 == '\\' || *p1 == '"') {
-      *p2 = '\\';
-      p2++;
-    }
-
-   *p2 = *p1;
-
-    p1++;
-    p2++;
-  }
-
-  /* Terminate the string */
-  newstr[newlen] = '\0';
-
-  return newstr;
-}
-
-/* Function that checks for an ending imap status code at the start of the
-   given string. */
-static int imap_endofresp(struct pingpong *pp, int *resp)
-{
-  char *line = pp->linestart_resp;
-  size_t len = pp->nread_resp;
-  struct imap_conn *imapc = &pp->conn->proto.imapc;
-  const char *id = imapc->idstr;
-  size_t id_len = strlen(id);
-
-  /* Do we have a generic command response? */
-  if(len >= id_len + 3) {
-    if(!memcmp(id, line, id_len) && line[id_len] == ' ') {
-      *resp = line[id_len + 1]; /* O, N or B */
-      return TRUE;
-    }
-  }
-
-  /* Are we processing FETCH command responses? */
-  if(imapc->state == IMAP_FETCH) {
-    /* Do we have a valid response? */
-    if(len >= 2 && !memcmp("* ", line, 2)) {
-      *resp = '*';
-      return TRUE;
-    }
-  }
-
-  return FALSE; /* nothing for us */
-}
-
-/* This is the ONLY way to change IMAP state! */
-static void state(struct connectdata *conn,
-                  imapstate newstate)
-{
-  struct imap_conn *imapc = &conn->proto.imapc;
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
-  /* for debug purposes */
-  static const char * const names[]={
-    "STOP",
-    "SERVERGREET",
-    "STARTTLS",
-    "UPGRADETLS",
-    "LOGIN",
-    "SELECT",
-    "FETCH",
-    "LOGOUT",
-    /* LAST */
-  };
-
-  if(imapc->state != newstate)
-    infof(conn->data, "IMAP %p state change from %s to %s\n",
-          imapc, names[imapc->state], names[newstate]);
-#endif
-
-  imapc->state = newstate;
-}
-
-static CURLcode imap_state_login(struct connectdata *conn)
-{
-  CURLcode result;
-  struct FTP *imap = conn->data->state.proto.imap;
-  const char *str = getcmdid(conn);
-  char *user = imap_atom(imap->user);
-  char *passwd = imap_atom(imap->passwd);
-
-  /* send USER and password */
-  result = imap_sendf(conn, str, "%s LOGIN %s %s", str,
-                      user ? user : "", passwd ? passwd : "");
-
-  Curl_safefree(user);
-  Curl_safefree(passwd);
-
-  if(result)
-    return result;
-
-  state(conn, IMAP_LOGIN);
-
-  return CURLE_OK;
-}
-
-/* For the IMAP "protocol connect" and "doing" phases only */
-static int imap_getsock(struct connectdata *conn,
-                        curl_socket_t *socks,
-                        int numsocks)
-{
-  return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
-}
-
-#ifdef USE_SSL
-static void imap_to_imaps(struct connectdata *conn)
-{
-  conn->handler = &Curl_handler_imaps;
-}
-#else
-#define imap_to_imaps(x) Curl_nop_stmt
-#endif
-
-/* For the initial server greeting */
-static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
-                                            int imapcode,
-                                            imapstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  (void)instate; /* no use for this yet */
-
-  if(imapcode != 'O') {
-    failf(data, "Got unexpected imap-server response");
-    return CURLE_FTP_WEIRD_SERVER_REPLY;
-  }
-
-  if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
-    /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
-       to TLS connection now */
-    const char *str = getcmdid(conn);
-    result = imap_sendf(conn, str, "%s STARTTLS", str);
-    state(conn, IMAP_STARTTLS);
-  }
-  else
-    result = imap_state_login(conn);
-
-  return result;
-}
-
-/* For STARTTLS responses */
-static CURLcode imap_state_starttls_resp(struct connectdata *conn,
-                                         int imapcode,
-                                         imapstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  (void)instate; /* no use for this yet */
-
-  if(imapcode != 'O') {
-    if(data->set.use_ssl != CURLUSESSL_TRY) {
-      failf(data, "STARTTLS denied. %c", imapcode);
-      result = CURLE_USE_SSL_FAILED;
-    }
-    else
-      result = imap_state_login(conn);
-  }
-  else {
-    if(data->state.used_interface == Curl_if_multi) {
-      state(conn, IMAP_UPGRADETLS);
-      result = imap_state_upgrade_tls(conn);
-    }
-    else {
-      result = Curl_ssl_connect(conn, FIRSTSOCKET);
-      if(CURLE_OK == result) {
-        imap_to_imaps(conn);
-        result = imap_state_login(conn);
-      }
-    }
-  }
-
-  return result;
-}
-
-static CURLcode imap_state_upgrade_tls(struct connectdata *conn)
-{
-  struct imap_conn *imapc = &conn->proto.imapc;
-  CURLcode result;
-
-  result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
-
-  if(imapc->ssldone) {
-    imap_to_imaps(conn);
-    result = imap_state_login(conn);
-  }
-
-  return result;
-}
-
-/* For LOGIN responses */
-static CURLcode imap_state_login_resp(struct connectdata *conn,
-                                      int imapcode,
-                                      imapstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  (void)instate; /* no use for this yet */
-
-  if(imapcode != 'O') {
-    failf(data, "Access denied. %c", imapcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else
-    /* End of connect phase */
-    state(conn, IMAP_STOP);
-
-  return result;
-}
-
-/* Start the DO phase */
-static CURLcode imap_select(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-  struct imap_conn *imapc = &conn->proto.imapc;
-  const char *str = getcmdid(conn);
-
-  result = imap_sendf(conn, str, "%s SELECT %s", str,
-                      imapc->mailbox?imapc->mailbox:"");
-  if(result)
-    return result;
-
-  state(conn, IMAP_SELECT);
-
-  return result;
-}
-
-static CURLcode imap_fetch(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-  const char *str = getcmdid(conn);
-
-  /* TODO: make this select the correct mail
-   * Use "1 body[text]" to get the full mail body of mail 1
-   */
-  result = imap_sendf(conn, str, "%s FETCH 1 BODY[TEXT]", str);
-  if(result)
-    return result;
-
-  /*
-   * When issued, the server will respond with a single line similar to
-   * '* 1 FETCH (BODY[TEXT] {2021}'
-   *
-   * Identifying the fetch and how many bytes of contents we can expect. We
-   * must extract that number before continuing to "download as usual".
-   */
-
-  state(conn, IMAP_FETCH);
-
-  return result;
-}
-
-/* For SELECT responses */
-static CURLcode imap_state_select_resp(struct connectdata *conn,
-                                       int imapcode,
-                                       imapstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  (void)instate; /* no use for this yet */
-
-  if(imapcode != 'O') {
-    failf(data, "Select failed");
-    result = CURLE_LOGIN_DENIED;
-  }
-  else
-    result = imap_fetch(conn);
-
-  return result;
-}
-
-/* For the (first line of) FETCH BODY[TEXT] response */
-static CURLcode imap_state_fetch_resp(struct connectdata *conn,
-                                      int imapcode,
-                                      imapstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  struct imap_conn *imapc = &conn->proto.imapc;
-  struct FTP *imap = data->state.proto.imap;
-  struct pingpong *pp = &imapc->pp;
-  const char *ptr = data->state.buffer;
-
-  (void)instate; /* no use for this yet */
-
-  if('*' != imapcode) {
-    Curl_pgrsSetDownloadSize(data, 0);
-    state(conn, IMAP_STOP);
-    return CURLE_OK;
-  }
-
-  /* Something like this comes "* 1 FETCH (BODY[TEXT] {2021}\r" */
-  while(*ptr && (*ptr != '{'))
-    ptr++;
-
-  if(*ptr == '{') {
-    curl_off_t filesize = curlx_strtoofft(ptr + 1, NULL, 10);
-    if(filesize)
-      Curl_pgrsSetDownloadSize(data, filesize);
-
-    infof(data, "Found %" FORMAT_OFF_TU " bytes to download\n", filesize);
-
-    if(pp->cache) {
-      /* At this point there is a bunch of data in the header "cache" that is
-         actually body content, send it as body and then skip it. Do note
-         that there may even be additional "headers" after the body. */
-      size_t chunk = pp->cache_size;
-
-      if(chunk > (size_t)filesize)
-        /* the conversion from curl_off_t to size_t is always fine here */
-        chunk = (size_t)filesize;
-
-      result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
-      if(result)
-        return result;
-
-      filesize -= chunk;
-
-      /* we've now used parts of or the entire cache */
-      if(pp->cache_size > chunk) {
-        /* part of, move the trailing data to the start and reduce the size */
-        memmove(pp->cache, pp->cache+chunk,
-                pp->cache_size - chunk);
-        pp->cache_size -= chunk;
-      }
-      else {
-        /* cache is drained */
-        Curl_safefree(pp->cache);
-        pp->cache = NULL;
-        pp->cache_size = 0;
-      }
-    }
-
-    infof(data, "Filesize left: %" FORMAT_OFF_T "\n", filesize);
-
-    if(!filesize)
-      /* the entire data is already transferred! */
-      Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
-    else
-      /* IMAP download */
-      Curl_setup_transfer(conn, FIRSTSOCKET, filesize, FALSE,
-                          imap->bytecountp, -1, NULL); /* no upload here */
-
-    data->req.maxdownload = filesize;
-  }
-  else
-    /* We don't know how to parse this line */
-    result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
-
-  /* End of do phase */
-  state(conn, IMAP_STOP);
-
-  return result;
-}
-
-static CURLcode imap_statemach_act(struct connectdata *conn)
-{
-  CURLcode result;
-  curl_socket_t sock = conn->sock[FIRSTSOCKET];
-  int imapcode;
-  struct imap_conn *imapc = &conn->proto.imapc;
-  struct pingpong *pp = &imapc->pp;
-  size_t nread = 0;
-
-  /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
-  if(imapc->state == IMAP_UPGRADETLS)
-    return imap_state_upgrade_tls(conn);
-
-  /* Flush any data that needs to be sent */
-  if(pp->sendleft)
-    return Curl_pp_flushsend(pp);
-
-  /* Read the response from the server */
-  result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
-  if(result)
-    return result;
-
-  if(imapcode) {
-    /* We have now received a full IMAP server response */
-    switch(imapc->state) {
-    case IMAP_SERVERGREET:
-      result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
-      break;
-
-    case IMAP_STARTTLS:
-      result = imap_state_starttls_resp(conn, imapcode, imapc->state);
-      break;
-
-    case IMAP_LOGIN:
-      result = imap_state_login_resp(conn, imapcode, imapc->state);
-      break;
-
-    case IMAP_FETCH:
-      result = imap_state_fetch_resp(conn, imapcode, imapc->state);
-      break;
-
-    case IMAP_SELECT:
-      result = imap_state_select_resp(conn, imapcode, imapc->state);
-      break;
-
-    case IMAP_LOGOUT:
-      /* fallthrough, just stop! */
-    default:
-      /* internal error */
-      state(conn, IMAP_STOP);
-      break;
-    }
-  }
-
-  return result;
-}
-
-/* Called repeatedly until done from curl_multi.c */
-static CURLcode imap_multi_statemach(struct connectdata *conn,
-                                         bool *done)
-{
-  struct imap_conn *imapc = &conn->proto.imapc;
-  CURLcode result;
-
-  if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone)
-    result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
-  else
-    result = Curl_pp_multi_statemach(&imapc->pp);
-
-  *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
-
-  return result;
-}
-
-static CURLcode imap_easy_statemach(struct connectdata *conn)
-{
-  struct imap_conn *imapc = &conn->proto.imapc;
-  struct pingpong *pp = &imapc->pp;
-  CURLcode result = CURLE_OK;
-
-  while(imapc->state != IMAP_STOP) {
-    result = Curl_pp_easy_statemach(pp);
-    if(result)
-      break;
-  }
-
-  return result;
-}
-
-/* Allocate and initialize the struct IMAP for the current SessionHandle if
-   required */
-static CURLcode imap_init(struct connectdata *conn)
-{
-  struct SessionHandle *data = conn->data;
-  struct FTP *imap = data->state.proto.imap;
-
-  if(!imap) {
-    imap = data->state.proto.imap = calloc(sizeof(struct FTP), 1);
-    if(!imap)
-      return CURLE_OUT_OF_MEMORY;
-  }
-
-  /* Get some initial data into the imap struct */
-  imap->bytecountp = &data->req.bytecount;
-
-  /* No need to duplicate user+password, the connectdata struct won't change
-     during a session, but we re-init them here since on subsequent inits
-     since the conn struct may have changed or been replaced.
-  */
-  imap->user = conn->user;
-  imap->passwd = conn->passwd;
-
-  return CURLE_OK;
-}
-
-/***********************************************************************
- *
- * imap_connect() should do everything that is to be considered a part of
- * the connection phase.
- *
- * The variable 'done' points to will be TRUE if the protocol-layer connect
- * phase is done when this function returns, or FALSE is not. When called as
- * a part of the easy interface, it will always be TRUE.
- */
-static CURLcode imap_connect(struct connectdata *conn,
-                                 bool *done) /* see description above */
-{
-  CURLcode result;
-  struct imap_conn *imapc = &conn->proto.imapc;
-  struct SessionHandle *data=conn->data;
-  struct pingpong *pp = &imapc->pp;
-
-  *done = FALSE; /* default to not done yet */
-
-  /* If there already is a protocol-specific struct allocated for this
-     sessionhandle, deal with it */
-  Curl_reset_reqproto(conn);
-
-  result = imap_init(conn);
-  if(CURLE_OK != result)
-    return result;
-
-  /* We always support persistent connections on imap */
-  conn->bits.close = FALSE;
-
-  pp->response_time = RESP_TIMEOUT; /* set default response time-out */
-  pp->statemach_act = imap_statemach_act;
-  pp->endofresp = imap_endofresp;
-  pp->conn = conn;
-
-  if((conn->handler->flags & PROTOPT_SSL) &&
-     data->state.used_interface != Curl_if_multi) {
-    /* IMAPS is simply imap with SSL for the control channel */
-    /* so perform the SSL initialization for this socket */
-    result = Curl_ssl_connect(conn, FIRSTSOCKET);
-    if(result)
-      return result;
-  }
-
-  /* Initialise the response reader stuff */
-  Curl_pp_init(pp);
-
-  /* Start off waiting for the server greeting response */
-  state(conn, IMAP_SERVERGREET);
-
-  /* Start off with an id of '*' */
-  imapc->idstr = "*";
-
-  if(data->state.used_interface == Curl_if_multi)
-    result = imap_multi_statemach(conn, done);
-  else {
-    result = imap_easy_statemach(conn);
-    if(!result)
-      *done = TRUE;
-  }
-
-  return result;
-}
-
-/***********************************************************************
- *
- * imap_done()
- *
- * The DONE function. This does what needs to be done after a single DO has
- * performed.
- *
- * Input argument is already checked for validity.
- */
-static CURLcode imap_done(struct connectdata *conn, CURLcode status,
-                          bool premature)
-{
-  struct SessionHandle *data = conn->data;
-  struct FTP *imap = data->state.proto.imap;
-  CURLcode result=CURLE_OK;
-
-  (void)premature;
-
-  if(!imap)
-    /* When the easy handle is removed from the multi while libcurl is still
-     * trying to resolve the host name, it seems that the imap struct is not
-     * yet initialized, but the removal action calls Curl_done() which calls
-     * this function. So we simply return success if no imap pointer is set.
-     */
-    return CURLE_OK;
-
-  if(status) {
-    conn->bits.close = TRUE; /* marked for closure */
-    result = status;         /* use the already set error code */
-  }
-
-  /* Clear the transfer mode for the next connection */
-  imap->transfer = FTPTRANSFER_BODY;
-
-  return result;
-}
-
-/***********************************************************************
- *
- * imap_perform()
- *
- * This is the actual DO function for IMAP. Get a file/directory according to
- * the options previously setup.
- */
-static CURLcode imap_perform(struct connectdata *conn, bool *connected,
-                             bool *dophase_done)
-{
-  /* This is IMAP and no proxy */
-  CURLcode result = CURLE_OK;
-
-  DEBUGF(infof(conn->data, "DO phase starts\n"));
-
-  if(conn->data->set.opt_no_body) {
-    /* Requested no body means no transfer */
-    struct FTP *imap = conn->data->state.proto.imap;
-    imap->transfer = FTPTRANSFER_INFO;
-  }
-
-  *dophase_done = FALSE; /* not done yet */
-
-  /* Start the first command in the DO phase */
-  result = imap_select(conn);
-  if(result)
-    return result;
-
-  /* Run the state-machine */
-  if(conn->data->state.used_interface == Curl_if_multi)
-    result = imap_multi_statemach(conn, dophase_done);
-  else {
-    result = imap_easy_statemach(conn);
-    *dophase_done = TRUE; /* with the easy interface we are done here */
-  }
-  *connected = conn->bits.tcpconnect[FIRSTSOCKET];
-
-  if(*dophase_done)
-    DEBUGF(infof(conn->data, "DO phase is complete\n"));
-
-  return result;
-}
-
-/***********************************************************************
- *
- * imap_do()
- *
- * This function is registered as 'curl_do' function. It decodes the path
- * parts etc as a wrapper to the actual DO function (imap_perform).
- *
- * The input argument is already checked for validity.
- */
-static CURLcode imap_do(struct connectdata *conn, bool *done)
-{
-  CURLcode retcode = CURLE_OK;
-
-  *done = FALSE; /* default to false */
-
-  /*
-    Since connections can be re-used between SessionHandles, this might be a
-    connection already existing but on a fresh SessionHandle struct so we must
-    make sure we have a good 'struct IMAP' to play with. For new connections,
-    the struct IMAP is allocated and setup in the imap_connect() function.
-  */
-  Curl_reset_reqproto(conn);
-  retcode = imap_init(conn);
-  if(retcode)
-    return retcode;
-
-  /* Parse the URL path */
-  retcode = imap_parse_url_path(conn);
-  if(retcode)
-    return retcode;
-
-  retcode = imap_regular_transfer(conn, done);
-
-  return retcode;
-}
-
-/***********************************************************************
- *
- * imap_logout()
- *
- * This should be called before calling sclose().  We should then wait for the
- * response from the server before returning. The calling code should then try
- * to close the connection.
- *
- */
-static CURLcode imap_logout(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-  const char *str = getcmdid(conn);
-
-  result = imap_sendf(conn, str, "%s LOGOUT", str, NULL);
-  if(result)
-    return result;
-
-  state(conn, IMAP_LOGOUT);
-
-  result = imap_easy_statemach(conn);
-
-  return result;
-}
-
-/***********************************************************************
- *
- * imap_disconnect()
- *
- * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
- * resources. BLOCKING.
- */
-static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
-{
-  struct imap_conn *imapc= &conn->proto.imapc;
-
-  /* We cannot send quit unconditionally. If this connection is stale or
-     bad in any way, sending quit and waiting around here will make the
-     disconnect wait in vain and cause more problems than we need to */
-
-  /* The IMAP session may or may not have been allocated/setup at this
-     point! */
-  if(!dead_connection && imapc->pp.conn)
-    (void)imap_logout(conn); /* ignore errors on the LOGOUT */
-
-  /* Disconnect from the server */
-  Curl_pp_disconnect(&imapc->pp);
-
-  /* Cleanup our connection based variables */
-  Curl_safefree(imapc->mailbox);
-
-  return CURLE_OK;
-}
-
-/***********************************************************************
- *
- * imap_parse_url_path()
- *
- * Parse the URL path into separate path components.
- *
- */
-static CURLcode imap_parse_url_path(struct connectdata *conn)
-{
-  /* The imap struct is already inited in imap_connect() */
-  struct imap_conn *imapc = &conn->proto.imapc;
-  struct SessionHandle *data = conn->data;
-  const char *path = data->state.path;
-
-  if(!*path)
-    path = "INBOX";
-
-  /* URL decode the path and use this mailbox */
-  return Curl_urldecode(data, path, 0, &imapc->mailbox, NULL, TRUE);
-}
-
-/* Call this when the DO phase has completed */
-static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
-{
-  struct FTP *imap = conn->data->state.proto.imap;
-
-  (void)connected;
-
-  if(imap->transfer != FTPTRANSFER_BODY)
-    /* no data to transfer */
-    Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
-
-  return CURLE_OK;
-}
-
-/* Called from curl_multi.c while DOing */
-static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
-{
-  CURLcode result = imap_multi_statemach(conn, dophase_done);
-
-  if(result)
-    DEBUGF(infof(conn->data, "DO phase failed\n"));
-  else {
-    if(*dophase_done) {
-      result = imap_dophase_done(conn, FALSE /* not connected */);
-
-      DEBUGF(infof(conn->data, "DO phase is complete\n"));
-    }
-  }
-
-  return result;
-}
-
-/***********************************************************************
- *
- * imap_regular_transfer()
- *
- * The input argument is already checked for validity.
- *
- * Performs all commands done before a regular transfer between a local and a
- * remote host.
- */
-static CURLcode imap_regular_transfer(struct connectdata *conn,
-                                      bool *dophase_done)
-{
-  CURLcode result = CURLE_OK;
-  bool connected = FALSE;
-  struct SessionHandle *data = conn->data;
-
-  /* Make sure size is unknown at this point */
-  data->req.size = -1;
-
-  Curl_pgrsSetUploadCounter(data, 0);
-  Curl_pgrsSetDownloadCounter(data, 0);
-  Curl_pgrsSetUploadSize(data, 0);
-  Curl_pgrsSetDownloadSize(data, 0);
-
-  result = imap_perform(conn, &connected, dophase_done);
-
-  if(CURLE_OK == result) {
-    if(!*dophase_done)
-      /* The DO phase has not completed yet */
-      return CURLE_OK;
-
-    result = imap_dophase_done(conn, connected);
-    if(result)
-      return result;
-  }
-
-  return result;
-}
-
-static CURLcode imap_setup_connection(struct connectdata * conn)
-{
-  struct SessionHandle *data = conn->data;
-
-  if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
-    /* Unless we have asked to tunnel imap operations through the proxy, we
-       switch and use HTTP operations only */
-#ifndef CURL_DISABLE_HTTP
-    if(conn->handler == &Curl_handler_imap)
-      conn->handler = &Curl_handler_imap_proxy;
-    else {
-#ifdef USE_SSL
-      conn->handler = &Curl_handler_imaps_proxy;
-#else
-      failf(data, "IMAPS not supported!");
-      return CURLE_UNSUPPORTED_PROTOCOL;
-#endif
-    }
-
-    /* We explicitly mark this connection as persistent here as we're doing
-       IMAP over HTTP and thus we accidentally avoid setting this value
-       otherwise */
-    conn->bits.close = FALSE;
-#else
-    failf(data, "IMAP over http proxy requires HTTP support built-in!");
-    return CURLE_UNSUPPORTED_PROTOCOL;
-#endif
-  }
-
-  data->state.path++;   /* don't include the initial slash */
-
-  return CURLE_OK;
-}
-
-#endif /* CURL_DISABLE_IMAP */
diff --git a/lib/inet_ntop.c b/lib/inet_ntop.c
deleted file mode 100644 (file)
index b184029..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (C) 1996-2001  Internet Software Consortium.
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
- * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
- * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
- * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
- * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
- * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-/*
- * Original code by Paul Vixie. "curlified" by Gisle Vanem.
- */
-
-#include "curl_setup.h"
-
-#ifndef HAVE_INET_NTOP
-
-#ifdef HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_inet_ntop.h"
-
-#define IN6ADDRSZ       16
-#define INADDRSZ         4
-#define INT16SZ          2
-
-/*
- * Format an IPv4 address, more or less like inet_ntoa().
- *
- * Returns `dst' (as a const)
- * Note:
- *  - uses no statics
- *  - takes a unsigned char* not an in_addr as input
- */
-static char *inet_ntop4 (const unsigned char *src, char *dst, size_t size)
-{
-  char tmp[sizeof "255.255.255.255"];
-  size_t len;
-
-  DEBUGASSERT(size >= 16);
-
-  tmp[0] = '\0';
-  (void)snprintf(tmp, sizeof(tmp), "%d.%d.%d.%d",
-          ((int)((unsigned char)src[0])) & 0xff,
-          ((int)((unsigned char)src[1])) & 0xff,
-          ((int)((unsigned char)src[2])) & 0xff,
-          ((int)((unsigned char)src[3])) & 0xff);
-
-  len = strlen(tmp);
-  if(len == 0 || len >= size) {
-    SET_ERRNO(ENOSPC);
-    return (NULL);
-  }
-  strcpy(dst, tmp);
-  return dst;
-}
-
-#ifdef ENABLE_IPV6
-/*
- * Convert IPv6 binary address into presentation (printable) format.
- */
-static char *inet_ntop6 (const unsigned char *src, char *dst, size_t size)
-{
-  /*
-   * Note that int32_t and int16_t need only be "at least" large enough
-   * to contain a value of the specified size.  On some systems, like
-   * Crays, there is no such thing as an integer variable with 16 bits.
-   * Keep this in mind if you think this function should have been coded
-   * to use pointer overlays.  All the world's not a VAX.
-   */
-  char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
-  char *tp;
-  struct {
-    long base;
-    long len;
-  } best, cur;
-  unsigned long words[IN6ADDRSZ / INT16SZ];
-  int i;
-
-  /* Preprocess:
-   *  Copy the input (bytewise) array into a wordwise array.
-   *  Find the longest run of 0x00's in src[] for :: shorthanding.
-   */
-  memset(words, '\0', sizeof(words));
-  for(i = 0; i < IN6ADDRSZ; i++)
-    words[i/2] |= (src[i] << ((1 - (i % 2)) << 3));
-
-  best.base = -1;
-  cur.base  = -1;
-  best.len = 0;
-  cur.len = 0;
-
-  for(i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
-    if(words[i] == 0) {
-      if(cur.base == -1)
-        cur.base = i, cur.len = 1;
-      else
-        cur.len++;
-    }
-    else if(cur.base != -1) {
-      if(best.base == -1 || cur.len > best.len)
-        best = cur;
-      cur.base = -1;
-    }
-  }
-  if((cur.base != -1) && (best.base == -1 || cur.len > best.len))
-    best = cur;
-  if(best.base != -1 && best.len < 2)
-    best.base = -1;
-  /* Format the result. */
-  tp = tmp;
-  for(i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
-    /* Are we inside the best run of 0x00's? */
-    if(best.base != -1 && i >= best.base && i < (best.base + best.len)) {
-      if(i == best.base)
-        *tp++ = ':';
-      continue;
-    }
-
-    /* Are we following an initial run of 0x00s or any real hex?
-     */
-    if(i != 0)
-      *tp++ = ':';
-
-    /* Is this address an encapsulated IPv4?
-     */
-    if(i == 6 && best.base == 0 &&
-        (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
-      if(!inet_ntop4(src+12, tp, sizeof(tmp) - (tp - tmp))) {
-        SET_ERRNO(ENOSPC);
-        return (NULL);
-      }
-      tp += strlen(tp);
-      break;
-    }
-    tp += snprintf(tp, 5, "%lx", words[i]);
-  }
-
-  /* Was it a trailing run of 0x00's?
-   */
-  if(best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ))
-     *tp++ = ':';
-  *tp++ = '\0';
-
-  /* Check for overflow, copy, and we're done.
-   */
-  if((size_t)(tp - tmp) > size) {
-    SET_ERRNO(ENOSPC);
-    return (NULL);
-  }
-  strcpy(dst, tmp);
-  return dst;
-}
-#endif  /* ENABLE_IPV6 */
-
-/*
- * Convert a network format address to presentation format.
- *
- * Returns pointer to presentation format address (`buf').
- * Returns NULL on error and errno set with the specific
- * error, EAFNOSUPPORT or ENOSPC.
- *
- * On Windows we store the error in the thread errno, not
- * in the winsock error code. This is to avoid losing the
- * actual last winsock error. So use macro ERRNO to fetch the
- * errno this function sets when returning NULL, not SOCKERRNO.
- */
-char *Curl_inet_ntop(int af, const void *src, char *buf, size_t size)
-{
-  switch (af) {
-  case AF_INET:
-    return inet_ntop4((const unsigned char*)src, buf, size);
-#ifdef ENABLE_IPV6
-  case AF_INET6:
-    return inet_ntop6((const unsigned char*)src, buf, size);
-#endif
-  default:
-    SET_ERRNO(EAFNOSUPPORT);
-    return NULL;
-  }
-}
-#endif  /* HAVE_INET_NTOP */
diff --git a/lib/inet_pton.c b/lib/inet_pton.c
deleted file mode 100644 (file)
index 175f938..0000000
+++ /dev/null
@@ -1,234 +0,0 @@
-/* This is from the BIND 4.9.4 release, modified to compile by itself */
-
-/* Copyright (c) 1996 by Internet Software Consortium.
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
- * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
- * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
- * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
- * SOFTWARE.
- */
-
-#include "curl_setup.h"
-
-#ifndef HAVE_INET_PTON
-
-#ifdef HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-
-#include "curl_inet_pton.h"
-
-#define IN6ADDRSZ       16
-#define INADDRSZ         4
-#define INT16SZ          2
-
-/*
- * WARNING: Don't even consider trying to compile this on a system where
- * sizeof(int) < 4.  sizeof(int) > 4 is fine; all the world's not a VAX.
- */
-
-static int      inet_pton4(const char *src, unsigned char *dst);
-#ifdef ENABLE_IPV6
-static int      inet_pton6(const char *src, unsigned char *dst);
-#endif
-
-/* int
- * inet_pton(af, src, dst)
- *      convert from presentation format (which usually means ASCII printable)
- *      to network format (which is usually some kind of binary format).
- * return:
- *      1 if the address was valid for the specified address family
- *      0 if the address wasn't valid (`dst' is untouched in this case)
- *      -1 if some other error occurred (`dst' is untouched in this case, too)
- * notice:
- *      On Windows we store the error in the thread errno, not
- *      in the winsock error code. This is to avoid losing the
- *      actual last winsock error. So use macro ERRNO to fetch the
- *      errno this function sets when returning (-1), not SOCKERRNO.
- * author:
- *      Paul Vixie, 1996.
- */
-int
-Curl_inet_pton(int af, const char *src, void *dst)
-{
-  switch (af) {
-  case AF_INET:
-    return (inet_pton4(src, (unsigned char *)dst));
-#ifdef ENABLE_IPV6
-  case AF_INET6:
-    return (inet_pton6(src, (unsigned char *)dst));
-#endif
-  default:
-    SET_ERRNO(EAFNOSUPPORT);
-    return (-1);
-  }
-  /* NOTREACHED */
-}
-
-/* int
- * inet_pton4(src, dst)
- *      like inet_aton() but without all the hexadecimal and shorthand.
- * return:
- *      1 if `src' is a valid dotted quad, else 0.
- * notice:
- *      does not touch `dst' unless it's returning 1.
- * author:
- *      Paul Vixie, 1996.
- */
-static int
-inet_pton4(const char *src, unsigned char *dst)
-{
-  static const char digits[] = "0123456789";
-  int saw_digit, octets, ch;
-  unsigned char tmp[INADDRSZ], *tp;
-
-  saw_digit = 0;
-  octets = 0;
-  tp = tmp;
-  *tp = 0;
-  while((ch = *src++) != '\0') {
-    const char *pch;
-
-    if((pch = strchr(digits, ch)) != NULL) {
-      unsigned int val = *tp * 10 + (unsigned int)(pch - digits);
-
-      if(saw_digit && *tp == 0)
-        return (0);
-      if(val > 255)
-        return (0);
-      *tp = (unsigned char)val;
-      if(! saw_digit) {
-        if(++octets > 4)
-          return (0);
-        saw_digit = 1;
-      }
-    }
-    else if(ch == '.' && saw_digit) {
-      if(octets == 4)
-        return (0);
-      *++tp = 0;
-      saw_digit = 0;
-    }
-    else
-      return (0);
-  }
-  if(octets < 4)
-    return (0);
-  memcpy(dst, tmp, INADDRSZ);
-  return (1);
-}
-
-#ifdef ENABLE_IPV6
-/* int
- * inet_pton6(src, dst)
- *      convert presentation level address to network order binary form.
- * return:
- *      1 if `src' is a valid [RFC1884 2.2] address, else 0.
- * notice:
- *      (1) does not touch `dst' unless it's returning 1.
- *      (2) :: in a full address is silently ignored.
- * credit:
- *      inspired by Mark Andrews.
- * author:
- *      Paul Vixie, 1996.
- */
-static int
-inet_pton6(const char *src, unsigned char *dst)
-{
-  static const char xdigits_l[] = "0123456789abcdef",
-    xdigits_u[] = "0123456789ABCDEF";
-  unsigned char tmp[IN6ADDRSZ], *tp, *endp, *colonp;
-  const char *xdigits, *curtok;
-  int ch, saw_xdigit;
-  size_t val;
-
-  memset((tp = tmp), 0, IN6ADDRSZ);
-  endp = tp + IN6ADDRSZ;
-  colonp = NULL;
-  /* Leading :: requires some special handling. */
-  if(*src == ':')
-    if(*++src != ':')
-      return (0);
-  curtok = src;
-  saw_xdigit = 0;
-  val = 0;
-  while((ch = *src++) != '\0') {
-    const char *pch;
-
-    if((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
-      pch = strchr((xdigits = xdigits_u), ch);
-    if(pch != NULL) {
-      val <<= 4;
-      val |= (pch - xdigits);
-      if(++saw_xdigit > 4)
-        return (0);
-      continue;
-    }
-    if(ch == ':') {
-      curtok = src;
-      if(!saw_xdigit) {
-        if(colonp)
-          return (0);
-        colonp = tp;
-        continue;
-      }
-      if(tp + INT16SZ > endp)
-        return (0);
-      *tp++ = (unsigned char) (val >> 8) & 0xff;
-      *tp++ = (unsigned char) val & 0xff;
-      saw_xdigit = 0;
-      val = 0;
-      continue;
-    }
-    if(ch == '.' && ((tp + INADDRSZ) <= endp) &&
-        inet_pton4(curtok, tp) > 0) {
-      tp += INADDRSZ;
-      saw_xdigit = 0;
-      break;    /* '\0' was seen by inet_pton4(). */
-    }
-    return (0);
-  }
-  if(saw_xdigit) {
-    if(tp + INT16SZ > endp)
-      return (0);
-    *tp++ = (unsigned char) (val >> 8) & 0xff;
-    *tp++ = (unsigned char) val & 0xff;
-  }
-  if(colonp != NULL) {
-    /*
-     * Since some memmove()'s erroneously fail to handle
-     * overlapping regions, we'll do the shift by hand.
-     */
-    const ssize_t n = tp - colonp;
-    ssize_t i;
-
-    if(tp == endp)
-      return (0);
-    for(i = 1; i <= n; i++) {
-      *(endp - i) = *(colonp + n - i);
-      *(colonp + n - i) = 0;
-    }
-    tp = endp;
-  }
-  if(tp != endp)
-    return (0);
-  memcpy(dst, tmp, IN6ADDRSZ);
-  return (1);
-}
-#endif /* ENABLE_IPV6 */
-
-#endif /* HAVE_INET_PTON */
diff --git a/lib/krb4.c b/lib/krb4.c
deleted file mode 100644 (file)
index 8ba326e..0000000
+++ /dev/null
@@ -1,440 +0,0 @@
-/* This source code was modified by Martin Hedenfalk <mhe@stacken.kth.se> for
- * use in Curl. Martin's latest changes were done 2000-09-18.
- *
- * It has since been patched away like a madman by Daniel Stenberg to make it
- * better applied to curl conditions, and to make it not use globals, pollute
- * name space and more.
- *
- * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
- * (Royal Institute of Technology, Stockholm, Sweden).
- * Copyright (c) 2004 - 2011 Daniel Stenberg
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * 3. Neither the name of the Institute nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- */
-
-#include "curl_setup.h"
-
-#ifndef CURL_DISABLE_FTP
-#ifdef HAVE_KRB4
-
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#include <krb.h>
-#include <des.h>
-
-#include "curl_urldata.h"
-#include "curl_base64.h"
-#include "curl_ftp.h"
-#include "curl_sendf.h"
-#include "curl_krb4.h"
-#include "curl_inet_ntop.h"
-#include "curl_memory.h"
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-#define LOCAL_ADDR (&conn->local_addr)
-#define REMOTE_ADDR conn->ip_addr->ai_addr
-#define myctladdr LOCAL_ADDR
-#define hisctladdr REMOTE_ADDR
-
-struct krb4_data {
-  des_cblock key;
-  des_key_schedule schedule;
-  char name[ANAME_SZ];
-  char instance[INST_SZ];
-  char realm[REALM_SZ];
-};
-
-#ifndef HAVE_STRLCPY
-/* if it ever goes non-static, make it Curl_ prefixed! */
-static size_t
-strlcpy (char *dst, const char *src, size_t dst_sz)
-{
-  size_t n;
-  char *p;
-
-  for(p = dst, n = 0;
-      n + 1 < dst_sz && *src != '\0';
-      ++p, ++src, ++n)
-    *p = *src;
-  *p = '\0';
-  if(*src == '\0')
-    return n;
-  else
-    return n + strlen (src);
-}
-#else
-size_t strlcpy (char *dst, const char *src, size_t dst_sz);
-#endif
-
-static int
-krb4_check_prot(void *app_data, int level)
-{
-  app_data = NULL; /* prevent compiler warning */
-  if(level == PROT_CONFIDENTIAL)
-    return -1;
-  return 0;
-}
-
-static int
-krb4_decode(void *app_data, void *buf, int len, int level,
-            struct connectdata *conn)
-{
-  MSG_DAT m;
-  int e;
-  struct krb4_data *d = app_data;
-
-  if(level == PROT_SAFE)
-    e = krb_rd_safe(buf, len, &d->key,
-                    (struct sockaddr_in *)REMOTE_ADDR,
-                    (struct sockaddr_in *)LOCAL_ADDR, &m);
-  else
-    e = krb_rd_priv(buf, len, d->schedule, &d->key,
-                    (struct sockaddr_in *)REMOTE_ADDR,
-                    (struct sockaddr_in *)LOCAL_ADDR, &m);
-  if(e) {
-    struct SessionHandle *data = conn->data;
-    infof(data, "krb4_decode: %s\n", krb_get_err_text(e));
-    return -1;
-  }
-  memmove(buf, m.app_data, m.app_length);
-  return m.app_length;
-}
-
-static int
-krb4_overhead(void *app_data, int level, int len)
-{
-  /* no arguments are used, just init them to prevent compiler warnings */
-  app_data = NULL;
-  level = 0;
-  len = 0;
-  return 31;
-}
-
-static int
-krb4_encode(void *app_data, const void *from, int length, int level, void **to,
-            struct connectdata *conn)
-{
-  struct krb4_data *d = app_data;
-  *to = malloc(length + 31);
-  if(!*to)
-    return -1;
-  if(level == PROT_SAFE)
-    /* NOTE that the void* cast is safe, krb_mk_safe/priv don't modify the
-     * input buffer
-     */
-    return krb_mk_safe((void*)from, *to, length, &d->key,
-                       (struct sockaddr_in *)LOCAL_ADDR,
-                       (struct sockaddr_in *)REMOTE_ADDR);
-  else if(level == PROT_PRIVATE)
-    return krb_mk_priv((void*)from, *to, length, d->schedule, &d->key,
-                       (struct sockaddr_in *)LOCAL_ADDR,
-                       (struct sockaddr_in *)REMOTE_ADDR);
-  else
-    return -1;
-}
-
-static int
-mk_auth(struct krb4_data *d, KTEXT adat,
-        const char *service, char *host, int checksum)
-{
-  int ret;
-  CREDENTIALS cred;
-  char sname[SNAME_SZ], inst[INST_SZ], realm[REALM_SZ];
-
-  strlcpy(sname, service, sizeof(sname));
-  strlcpy(inst, krb_get_phost(host), sizeof(inst));
-  strlcpy(realm, krb_realmofhost(host), sizeof(realm));
-  ret = krb_mk_req(adat, sname, inst, realm, checksum);
-  if(ret)
-    return ret;
-  strlcpy(sname, service, sizeof(sname));
-  strlcpy(inst, krb_get_phost(host), sizeof(inst));
-  strlcpy(realm, krb_realmofhost(host), sizeof(realm));
-  ret = krb_get_cred(sname, inst, realm, &cred);
-  memmove(&d->key, &cred.session, sizeof(des_cblock));
-  des_key_sched(&d->key, d->schedule);
-  memset(&cred, 0, sizeof(cred));
-  return ret;
-}
-
-#ifdef HAVE_KRB_GET_OUR_IP_FOR_REALM
-int krb_get_our_ip_for_realm(char *, struct in_addr *);
-#endif
-
-static int
-krb4_auth(void *app_data, struct connectdata *conn)
-{
-  int ret;
-  char *p;
-  unsigned char *ptr;
-  size_t len = 0;
-  KTEXT_ST adat;
-  MSG_DAT msg_data;
-  int checksum;
-  u_int32_t cs;
-  struct krb4_data *d = app_data;
-  char *host = conn->host.name;
-  ssize_t nread;
-  int l = sizeof(conn->local_addr);
-  struct SessionHandle *data = conn->data;
-  CURLcode result;
-  size_t base64_sz = 0;
-
-  if(getsockname(conn->sock[FIRSTSOCKET],
-                 (struct sockaddr *)LOCAL_ADDR, &l) < 0)
-    perror("getsockname()");
-
-  checksum = getpid();
-  ret = mk_auth(d, &adat, "ftp", host, checksum);
-  if(ret == KDC_PR_UNKNOWN)
-    ret = mk_auth(d, &adat, "rcmd", host, checksum);
-  if(ret) {
-    infof(data, "%s\n", krb_get_err_text(ret));
-    return AUTH_CONTINUE;
-  }
-
-#ifdef HAVE_KRB_GET_OUR_IP_FOR_REALM
-  if(krb_get_config_bool("nat_in_use")) {
-    struct sockaddr_in *localaddr  = (struct sockaddr_in *)LOCAL_ADDR;
-    struct in_addr natAddr;
-
-    if(krb_get_our_ip_for_realm(krb_realmofhost(host),
-                                 &natAddr) != KSUCCESS
-        && krb_get_our_ip_for_realm(NULL, &natAddr) != KSUCCESS)
-      infof(data, "Can't get address for realm %s\n",
-                 krb_realmofhost(host));
-    else {
-      if(natAddr.s_addr != localaddr->sin_addr.s_addr) {
-        char addr_buf[128];
-        if(Curl_inet_ntop(AF_INET, natAddr, addr_buf, sizeof(addr_buf)))
-          infof(data, "Using NAT IP address (%s) for kerberos 4\n", addr_buf);
-        localaddr->sin_addr = natAddr;
-      }
-    }
-  }
-#endif
-
-  result = Curl_base64_encode(conn->data, (char *)adat.dat, adat.length,
-                              &p, &base64_sz);
-  if(result) {
-    Curl_failf(data, "base64-encoding: %s", curl_easy_strerror(result));
-    return AUTH_CONTINUE;
-  }
-
-  result = Curl_ftpsendf(conn, "ADAT %s", p);
-
-  free(p);
-
-  if(result)
-    return -2;
-
-  if(Curl_GetFTPResponse(&nread, conn, NULL))
-    return -1;
-
-  if(data->state.buffer[0] != '2') {
-    Curl_failf(data, "Server didn't accept auth data");
-    return AUTH_ERROR;
-  }
-
-  p = strstr(data->state.buffer, "ADAT=");
-  if(!p) {
-    Curl_failf(data, "Remote host didn't send adat reply");
-    return AUTH_ERROR;
-  }
-  p += 5;
-  result = Curl_base64_decode(p, &ptr, &len);
-  if(result) {
-    Curl_failf(data, "base64-decoding: %s", curl_easy_strerror(result));
-    return AUTH_ERROR;
-  }
-  if(len > sizeof(adat.dat)-1) {
-    free(ptr);
-    ptr = NULL;
-    len = 0;
-  }
-  if(!len || !ptr) {
-    Curl_failf(data, "Failed to decode base64 from server");
-    return AUTH_ERROR;
-  }
-  memcpy((char *)adat.dat, ptr, len);
-  free(ptr);
-  adat.length = len;
-  ret = krb_rd_safe(adat.dat, adat.length, &d->key,
-                    (struct sockaddr_in *)hisctladdr,
-                    (struct sockaddr_in *)myctladdr, &msg_data);
-  if(ret) {
-    Curl_failf(data, "Error reading reply from server: %s",
-               krb_get_err_text(ret));
-    return AUTH_ERROR;
-  }
-  krb_get_int(msg_data.app_data, &cs, 4, 0);
-  if(cs - checksum != 1) {
-    Curl_failf(data, "Bad checksum returned from server");
-    return AUTH_ERROR;
-  }
-  return AUTH_OK;
-}
-
-struct Curl_sec_client_mech Curl_krb4_client_mech = {
-    "KERBEROS_V4",
-    sizeof(struct krb4_data),
-    NULL, /* init */
-    krb4_auth,
-    NULL, /* end */
-    krb4_check_prot,
-    krb4_overhead,
-    krb4_encode,
-    krb4_decode
-};
-
-static enum protection_level
-krb4_set_command_prot(struct connectdata *conn, enum protection_level level)
-{
-  enum protection_level old = conn->command_prot;
-  DEBUGASSERT(level > PROT_NONE && level < PROT_LAST);
-  conn->command_prot = level;
-  return old;
-}
-
-CURLcode Curl_krb_kauth(struct connectdata *conn)
-{
-  des_cblock key;
-  des_key_schedule schedule;
-  KTEXT_ST tkt, tktcopy;
-  char *name;
-  char *p;
-  char passwd[100];
-  size_t tmp = 0;
-  ssize_t nread;
-  enum protection_level save;
-  CURLcode result;
-  unsigned char *ptr;
-  size_t base64_sz = 0;
-
-  save = krb4_set_command_prot(conn, PROT_PRIVATE);
-
-  result = Curl_ftpsendf(conn, "SITE KAUTH %s", conn->user);
-
-  if(result)
-    return result;
-
-  result = Curl_GetFTPResponse(&nread, conn, NULL);
-  if(result)
-    return result;
-
-  if(conn->data->state.buffer[0] != '3') {
-    krb4_set_command_prot(conn, save);
-    return CURLE_FTP_WEIRD_SERVER_REPLY;
-  }
-
-  p = strstr(conn->data->state.buffer, "T=");
-  if(!p) {
-    Curl_failf(conn->data, "Bad reply from server");
-    krb4_set_command_prot(conn, save);
-    return CURLE_FTP_WEIRD_SERVER_REPLY;
-  }
-
-  p += 2;
-  result = Curl_base64_decode(p, &ptr, &tmp);
-  if(result) {
-    Curl_failf(conn->data, "base64-decoding: %s", curl_easy_strerror(result));
-    return result;
-  }
-  if(tmp >= sizeof(tkt.dat)) {
-    free(ptr);
-    ptr = NULL;
-    tmp = 0;
-  }
-  if(!tmp || !ptr) {
-    Curl_failf(conn->data, "Failed to decode base64 in reply");
-    krb4_set_command_prot(conn, save);
-    return CURLE_FTP_WEIRD_SERVER_REPLY;
-  }
-  memcpy((char *)tkt.dat, ptr, tmp);
-  free(ptr);
-  tkt.length = tmp;
-  tktcopy.length = tkt.length;
-
-  p = strstr(conn->data->state.buffer, "P=");
-  if(!p) {
-    Curl_failf(conn->data, "Bad reply from server");
-    krb4_set_command_prot(conn, save);
-    return CURLE_FTP_WEIRD_SERVER_REPLY;
-  }
-  name = p + 2;
-  for(; *p && *p != ' ' && *p != '\r' && *p != '\n'; p++);
-  *p = 0;
-
-  des_string_to_key (conn->passwd, &key);
-  des_key_sched(&key, schedule);
-
-  des_pcbc_encrypt((void *)tkt.dat, (void *)tktcopy.dat,
-                   tkt.length,
-                   schedule, &key, DES_DECRYPT);
-  if(strcmp ((char*)tktcopy.dat + 8,
-              KRB_TICKET_GRANTING_TICKET) != 0) {
-    afs_string_to_key(passwd,
-                      krb_realmofhost(conn->host.name),
-                      &key);
-    des_key_sched(&key, schedule);
-    des_pcbc_encrypt((void *)tkt.dat, (void *)tktcopy.dat,
-                     tkt.length,
-                     schedule, &key, DES_DECRYPT);
-  }
-  memset(key, 0, sizeof(key));
-  memset(schedule, 0, sizeof(schedule));
-  memset(passwd, 0, sizeof(passwd));
-  result = Curl_base64_encode(conn->data, (char *)tktcopy.dat, tktcopy.length,
-                              &p, &base64_sz);
-  if(result) {
-    Curl_failf(conn->data, "base64-encoding: %s", curl_easy_strerror(result));
-    krb4_set_command_prot(conn, save);
-    return result;
-  }
-  memset (tktcopy.dat, 0, tktcopy.length);
-
-  result = Curl_ftpsendf(conn, "SITE KAUTH %s %s", name, p);
-  free(p);
-  if(result)
-    return result;
-
-  result = Curl_GetFTPResponse(&nread, conn, NULL);
-  if(result)
-    return result;
-  krb4_set_command_prot(conn, save);
-
-  return CURLE_OK;
-}
-
-#endif /* HAVE_KRB4 */
-#endif /* CURL_DISABLE_FTP */
diff --git a/lib/krb5.c b/lib/krb5.c
deleted file mode 100644 (file)
index d793fef..0000000
+++ /dev/null
@@ -1,341 +0,0 @@
-/* GSSAPI/krb5 support for FTP - loosely based on old curl_krb4.c
- *
- * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
- * (Royal Institute of Technology, Stockholm, Sweden).
- * Copyright (c) 2004 - 2013 Daniel Stenberg
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * 3. Neither the name of the Institute nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.  */
-
-#include "curl_setup.h"
-
-#ifndef CURL_DISABLE_FTP
-#ifdef HAVE_GSSAPI
-
-#ifdef HAVE_OLD_GSSMIT
-#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
-#define NCOMPAT 1
-#endif
-
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-
-#include "curl_urldata.h"
-#include "curl_base64.h"
-#include "curl_ftp.h"
-#include "curl_gssapi.h"
-#include "curl_sendf.h"
-#include "curl_krb4.h"
-#include "curl_memory.h"
-#include "curl_warnless.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-#define LOCAL_ADDR (&conn->local_addr)
-#define REMOTE_ADDR conn->ip_addr->ai_addr
-
-static int
-krb5_init(void *app_data)
-{
-  gss_ctx_id_t *context = app_data;
-  /* Make sure our context is initialized for krb5_end. */
-  *context = GSS_C_NO_CONTEXT;
-  return 0;
-}
-
-static int
-krb5_check_prot(void *app_data, int level)
-{
-  (void)app_data; /* unused */
-  if(level == PROT_CONFIDENTIAL)
-    return -1;
-  return 0;
-}
-
-static int
-krb5_decode(void *app_data, void *buf, int len,
-            int level UNUSED_PARAM,
-            struct connectdata *conn UNUSED_PARAM)
-{
-  gss_ctx_id_t *context = app_data;
-  OM_uint32 maj, min;
-  gss_buffer_desc enc, dec;
-
-  (void)level;
-  (void)conn;
-
-  enc.value = buf;
-  enc.length = len;
-  maj = gss_unseal(&min, *context, &enc, &dec, NULL, NULL);
-  if(maj != GSS_S_COMPLETE) {
-    if(len >= 4)
-      strcpy(buf, "599 ");
-    return -1;
-  }
-
-  memcpy(buf, dec.value, dec.length);
-  len = curlx_uztosi(dec.length);
-  gss_release_buffer(&min, &dec);
-
-  return len;
-}
-
-static int
-krb5_overhead(void *app_data, int level, int len)
-{
-  /* no arguments are used */
-  (void)app_data;
-  (void)level;
-  (void)len;
-  return 0;
-}
-
-static int
-krb5_encode(void *app_data, const void *from, int length, int level, void **to,
-            struct connectdata *conn UNUSED_PARAM)
-{
-  gss_ctx_id_t *context = app_data;
-  gss_buffer_desc dec, enc;
-  OM_uint32 maj, min;
-  int state;
-  int len;
-
-  /* shut gcc up */
-  conn = NULL;
-
-  /* NOTE that the cast is safe, neither of the krb5, gnu gss and heimdal
-   * libraries modify the input buffer in gss_seal()
-   */
-  dec.value = (void*)from;
-  dec.length = length;
-  maj = gss_seal(&min, *context,
-                 level == PROT_PRIVATE,
-                 GSS_C_QOP_DEFAULT,
-                 &dec, &state, &enc);
-
-  if(maj != GSS_S_COMPLETE)
-    return -1;
-
-  /* malloc a new buffer, in case gss_release_buffer doesn't work as
-     expected */
-  *to = malloc(enc.length);
-  if(!*to)
-    return -1;
-  memcpy(*to, enc.value, enc.length);
-  len = curlx_uztosi(enc.length);
-  gss_release_buffer(&min, &enc);
-  return len;
-}
-
-static int
-krb5_auth(void *app_data, struct connectdata *conn)
-{
-  int ret = AUTH_OK;
-  char *p;
-  const char *host = conn->host.name;
-  ssize_t nread;
-  curl_socklen_t l = sizeof(conn->local_addr);
-  struct SessionHandle *data = conn->data;
-  CURLcode result;
-  const char *service = "ftp", *srv_host = "host";
-  gss_buffer_desc input_buffer, output_buffer, _gssresp, *gssresp;
-  OM_uint32 maj, min;
-  gss_name_t gssname;
-  gss_ctx_id_t *context = app_data;
-  struct gss_channel_bindings_struct chan;
-  size_t base64_sz = 0;
-
-  if(getsockname(conn->sock[FIRSTSOCKET],
-                 (struct sockaddr *)LOCAL_ADDR, &l) < 0)
-    perror("getsockname()");
-
-  chan.initiator_addrtype = GSS_C_AF_INET;
-  chan.initiator_address.length = l - 4;
-  chan.initiator_address.value =
-    &((struct sockaddr_in *)LOCAL_ADDR)->sin_addr.s_addr;
-  chan.acceptor_addrtype = GSS_C_AF_INET;
-  chan.acceptor_address.length = l - 4;
-  chan.acceptor_address.value =
-    &((struct sockaddr_in *)REMOTE_ADDR)->sin_addr.s_addr;
-  chan.application_data.length = 0;
-  chan.application_data.value = NULL;
-
-  /* this loop will execute twice (once for service, once for host) */
-  for(;;) {
-    /* this really shouldn't be repeated here, but can't help it */
-    if(service == srv_host) {
-      result = Curl_ftpsendf(conn, "AUTH GSSAPI");
-
-      if(result)
-        return -2;
-      if(Curl_GetFTPResponse(&nread, conn, NULL))
-        return -1;
-
-      if(data->state.buffer[0] != '3')
-        return -1;
-    }
-
-    input_buffer.value = data->state.buffer;
-    input_buffer.length = snprintf(input_buffer.value, BUFSIZE, "%s@%s",
-                                   service, host);
-    maj = gss_import_name(&min, &input_buffer, GSS_C_NT_HOSTBASED_SERVICE,
-                          &gssname);
-    if(maj != GSS_S_COMPLETE) {
-      gss_release_name(&min, &gssname);
-      if(service == srv_host) {
-        Curl_failf(data, "Error importing service name %s",
-                   input_buffer.value);
-        return AUTH_ERROR;
-      }
-      service = srv_host;
-      continue;
-    }
-    /* We pass NULL as |output_name_type| to avoid a leak. */
-    gss_display_name(&min, gssname, &output_buffer, NULL);
-    Curl_infof(data, "Trying against %s\n", output_buffer.value);
-    gssresp = GSS_C_NO_BUFFER;
-    *context = GSS_C_NO_CONTEXT;
-
-    do {
-      /* Release the buffer at each iteration to avoid leaking: the first time
-         we are releasing the memory from gss_display_name. The last item is
-         taken care by a final gss_release_buffer. */
-      gss_release_buffer(&min, &output_buffer);
-      ret = AUTH_OK;
-      maj = Curl_gss_init_sec_context(data,
-                                      &min,
-                                      context,
-                                      gssname,
-                                      &chan,
-                                      gssresp,
-                                      &output_buffer,
-                                      NULL);
-
-      if(gssresp) {
-        free(_gssresp.value);
-        gssresp = NULL;
-      }
-
-      if(GSS_ERROR(maj)) {
-        Curl_infof(data, "Error creating security context\n");
-        ret = AUTH_ERROR;
-        break;
-      }
-
-      if(output_buffer.length != 0) {
-        result = Curl_base64_encode(data, (char *)output_buffer.value,
-                                    output_buffer.length, &p, &base64_sz);
-        if(result) {
-          Curl_infof(data,"base64-encoding: %s\n", curl_easy_strerror(result));
-          ret = AUTH_CONTINUE;
-          break;
-        }
-
-        result = Curl_ftpsendf(conn, "ADAT %s", p);
-
-        free(p);
-
-        if(result) {
-          ret = -2;
-          break;
-        }
-
-        if(Curl_GetFTPResponse(&nread, conn, NULL)) {
-          ret = -1;
-          break;
-        }
-
-        if(data->state.buffer[0] != '2' && data->state.buffer[0] != '3') {
-          Curl_infof(data, "Server didn't accept auth data\n");
-          ret = AUTH_ERROR;
-          break;
-        }
-
-        p = data->state.buffer + 4;
-        p = strstr(p, "ADAT=");
-        if(p) {
-          result = Curl_base64_decode(p + 5,
-                                      (unsigned char **)&_gssresp.value,
-                                      &_gssresp.length);
-          if(result) {
-            Curl_failf(data,"base64-decoding: %s", curl_easy_strerror(result));
-            ret = AUTH_CONTINUE;
-            break;
-          }
-        }
-
-        gssresp = &_gssresp;
-      }
-    } while(maj == GSS_S_CONTINUE_NEEDED);
-
-    gss_release_name(&min, &gssname);
-    gss_release_buffer(&min, &output_buffer);
-
-    if(gssresp)
-      free(_gssresp.value);
-
-    if(ret == AUTH_OK || service == srv_host)
-      return ret;
-
-    service = srv_host;
-  }
-  return ret;
-}
-
-static void krb5_end(void *app_data)
-{
-    OM_uint32 min;
-    gss_ctx_id_t *context = app_data;
-    if(*context != GSS_C_NO_CONTEXT) {
-#ifdef DEBUGBUILD
-      OM_uint32 maj =
-#endif
-      gss_delete_sec_context(&min, context, GSS_C_NO_BUFFER);
-      DEBUGASSERT(maj == GSS_S_COMPLETE);
-    }
-}
-
-struct Curl_sec_client_mech Curl_krb5_client_mech = {
-    "GSSAPI",
-    sizeof(gss_ctx_id_t),
-    krb5_init,
-    krb5_auth,
-    krb5_end,
-    krb5_check_prot,
-    krb5_overhead,
-    krb5_encode,
-    krb5_decode
-};
-
-#endif /* HAVE_GSSAPI */
-#endif /* CURL_DISABLE_FTP */
diff --git a/lib/ldap.c b/lib/ldap.c
deleted file mode 100644 (file)
index 59f3b83..0000000
+++ /dev/null
@@ -1,725 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#if !defined(CURL_DISABLE_LDAP) && !defined(USE_OPENLDAP)
-
-/*
- * Notice that USE_OPENLDAP is only a source code selection switch. When
- * libcurl is built with USE_OPENLDAP defined the libcurl source code that
- * gets compiled is the code from curl_openldap.c, otherwise the code that
- * gets compiled is the code from curl_ldap.c.
- *
- * When USE_OPENLDAP is defined a recent version of the OpenLDAP library
- * might be required for compilation and runtime. In order to use ancient
- * OpenLDAP library versions, USE_OPENLDAP shall not be defined.
- */
-
-#ifdef CURL_LDAP_WIN            /* Use Windows LDAP implementation. */
-# include <winldap.h>
-# ifndef LDAP_VENDOR_NAME
-#  error Your Platform SDK is NOT sufficient for LDAP support! \
-         Update your Platform SDK, or disable LDAP support!
-# else
-#  include <winber.h>
-# endif
-#else
-# define LDAP_DEPRECATED 1      /* Be sure ldap_init() is defined. */
-# ifdef HAVE_LBER_H
-#  include <lber.h>
-# endif
-# include <ldap.h>
-# if (defined(HAVE_LDAP_SSL) && defined(HAVE_LDAP_SSL_H))
-#  include <ldap_ssl.h>
-# endif /* HAVE_LDAP_SSL && HAVE_LDAP_SSL_H */
-#endif
-
-#include "curl_urldata.h"
-#include <curl/curl.h>
-#include "curl_sendf.h"
-#include "curl_escape.h"
-#include "curl_progress.h"
-#include "curl_transfer.h"
-#include "curl_strequal.h"
-#include "curl_strtok.h"
-#include "curl_ldap.h"
-#include "curl_memory.h"
-#include "curl_base64.h"
-#include "curl_rawstr.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memdebug.h"
-
-#ifndef HAVE_LDAP_URL_PARSE
-
-/* Use our own implementation. */
-
-typedef struct {
-    char   *lud_host;
-    int     lud_port;
-    char   *lud_dn;
-    char  **lud_attrs;
-    int     lud_scope;
-    char   *lud_filter;
-    char  **lud_exts;
-} CURL_LDAPURLDesc;
-
-#undef LDAPURLDesc
-#define LDAPURLDesc             CURL_LDAPURLDesc
-
-static int  _ldap_url_parse (const struct connectdata *conn,
-                             LDAPURLDesc **ludp);
-static void _ldap_free_urldesc (LDAPURLDesc *ludp);
-
-#undef ldap_free_urldesc
-#define ldap_free_urldesc       _ldap_free_urldesc
-#endif
-
-#ifdef DEBUG_LDAP
-  #define LDAP_TRACE(x)   do { \
-                            _ldap_trace ("%u: ", __LINE__); \
-                            _ldap_trace x; \
-                          } WHILE_FALSE
-
-  static void _ldap_trace (const char *fmt, ...);
-#else
-  #define LDAP_TRACE(x)   Curl_nop_stmt
-#endif
-
-
-static CURLcode Curl_ldap(struct connectdata *conn, bool *done);
-
-/*
- * LDAP protocol handler.
- */
-
-const struct Curl_handler Curl_handler_ldap = {
-  "LDAP",                               /* scheme */
-  ZERO_NULL,                            /* setup_connection */
-  Curl_ldap,                            /* do_it */
-  ZERO_NULL,                            /* done */
-  ZERO_NULL,                            /* do_more */
-  ZERO_NULL,                            /* connect_it */
-  ZERO_NULL,                            /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
-  ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* readwrite */
-  PORT_LDAP,                            /* defport */
-  CURLPROTO_LDAP,                       /* protocol */
-  PROTOPT_NONE                          /* flags */
-};
-
-#ifdef HAVE_LDAP_SSL
-/*
- * LDAPS protocol handler.
- */
-
-const struct Curl_handler Curl_handler_ldaps = {
-  "LDAPS",                              /* scheme */
-  ZERO_NULL,                            /* setup_connection */
-  Curl_ldap,                            /* do_it */
-  ZERO_NULL,                            /* done */
-  ZERO_NULL,                            /* do_more */
-  ZERO_NULL,                            /* connect_it */
-  ZERO_NULL,                            /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
-  ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* readwrite */
-  PORT_LDAPS,                           /* defport */
-  CURLPROTO_LDAP | CURLPROTO_LDAPS,     /* protocol */
-  PROTOPT_SSL                           /* flags */
-};
-#endif
-
-
-static CURLcode Curl_ldap(struct connectdata *conn, bool *done)
-{
-  CURLcode status = CURLE_OK;
-  int rc = 0;
-  LDAP *server = NULL;
-  LDAPURLDesc *ludp = NULL;
-  LDAPMessage *result = NULL;
-  LDAPMessage *entryIterator;
-  int num = 0;
-  struct SessionHandle *data=conn->data;
-  int ldap_proto = LDAP_VERSION3;
-  int ldap_ssl = 0;
-  char *val_b64 = NULL;
-  size_t val_b64_sz = 0;
-  curl_off_t dlsize = 0;
-#ifdef LDAP_OPT_NETWORK_TIMEOUT
-  struct timeval ldap_timeout = {10,0}; /* 10 sec connection/search timeout */
-#endif
-
-  *done = TRUE; /* unconditionally */
-  infof(data, "LDAP local: LDAP Vendor = %s ; LDAP Version = %d\n",
-          LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION);
-  infof(data, "LDAP local: %s\n", data->change.url);
-
-#ifdef HAVE_LDAP_URL_PARSE
-  rc = ldap_url_parse(data->change.url, &ludp);
-#else
-  rc = _ldap_url_parse(conn, &ludp);
-#endif
-  if(rc != 0) {
-    failf(data, "LDAP local: %s", ldap_err2string(rc));
-    status = CURLE_LDAP_INVALID_URL;
-    goto quit;
-  }
-
-  /* Get the URL scheme ( either ldap or ldaps ) */
-  if(conn->given->flags & PROTOPT_SSL)
-    ldap_ssl = 1;
-  infof(data, "LDAP local: trying to establish %s connection\n",
-          ldap_ssl ? "encrypted" : "cleartext");
-
-#ifdef LDAP_OPT_NETWORK_TIMEOUT
-  ldap_set_option(NULL, LDAP_OPT_NETWORK_TIMEOUT, &ldap_timeout);
-#endif
-  ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
-
-  if(ldap_ssl) {
-#ifdef HAVE_LDAP_SSL
-#ifdef CURL_LDAP_WIN
-    /* Win32 LDAP SDK doesn't support insecure mode without CA! */
-    server = ldap_sslinit(conn->host.name, (int)conn->port, 1);
-    ldap_set_option(server, LDAP_OPT_SSL, LDAP_OPT_ON);
-#else
-    int ldap_option;
-    char* ldap_ca = data->set.str[STRING_SSL_CAFILE];
-#if defined(CURL_HAS_NOVELL_LDAPSDK)
-    rc = ldapssl_client_init(NULL, NULL);
-    if(rc != LDAP_SUCCESS) {
-      failf(data, "LDAP local: ldapssl_client_init %s", ldap_err2string(rc));
-      status = CURLE_SSL_CERTPROBLEM;
-      goto quit;
-    }
-    if(data->set.ssl.verifypeer) {
-      /* Novell SDK supports DER or BASE64 files. */
-      int cert_type = LDAPSSL_CERT_FILETYPE_B64;
-      if((data->set.str[STRING_CERT_TYPE]) &&
-         (Curl_raw_equal(data->set.str[STRING_CERT_TYPE], "DER")))
-        cert_type = LDAPSSL_CERT_FILETYPE_DER;
-      if(!ldap_ca) {
-        failf(data, "LDAP local: ERROR %s CA cert not set!",
-              (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"));
-        status = CURLE_SSL_CERTPROBLEM;
-        goto quit;
-      }
-      infof(data, "LDAP local: using %s CA cert '%s'\n",
-              (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"),
-              ldap_ca);
-      rc = ldapssl_add_trusted_cert(ldap_ca, cert_type);
-      if(rc != LDAP_SUCCESS) {
-        failf(data, "LDAP local: ERROR setting %s CA cert: %s",
-                (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"),
-                ldap_err2string(rc));
-        status = CURLE_SSL_CERTPROBLEM;
-        goto quit;
-      }
-      ldap_option = LDAPSSL_VERIFY_SERVER;
-    }
-    else
-      ldap_option = LDAPSSL_VERIFY_NONE;
-    rc = ldapssl_set_verify_mode(ldap_option);
-    if(rc != LDAP_SUCCESS) {
-      failf(data, "LDAP local: ERROR setting cert verify mode: %s",
-              ldap_err2string(rc));
-      status = CURLE_SSL_CERTPROBLEM;
-      goto quit;
-    }
-    server = ldapssl_init(conn->host.name, (int)conn->port, 1);
-    if(server == NULL) {
-      failf(data, "LDAP local: Cannot connect to %s:%hu",
-              conn->host.name, conn->port);
-      status = CURLE_COULDNT_CONNECT;
-      goto quit;
-    }
-#elif defined(LDAP_OPT_X_TLS)
-    if(data->set.ssl.verifypeer) {
-      /* OpenLDAP SDK supports BASE64 files. */
-      if((data->set.str[STRING_CERT_TYPE]) &&
-         (!Curl_raw_equal(data->set.str[STRING_CERT_TYPE], "PEM"))) {
-        failf(data, "LDAP local: ERROR OpenLDAP only supports PEM cert-type!");
-        status = CURLE_SSL_CERTPROBLEM;
-        goto quit;
-      }
-      if(!ldap_ca) {
-        failf(data, "LDAP local: ERROR PEM CA cert not set!");
-        status = CURLE_SSL_CERTPROBLEM;
-        goto quit;
-      }
-      infof(data, "LDAP local: using PEM CA cert: %s\n", ldap_ca);
-      rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, ldap_ca);
-      if(rc != LDAP_SUCCESS) {
-        failf(data, "LDAP local: ERROR setting PEM CA cert: %s",
-                ldap_err2string(rc));
-        status = CURLE_SSL_CERTPROBLEM;
-        goto quit;
-      }
-      ldap_option = LDAP_OPT_X_TLS_DEMAND;
-    }
-    else
-      ldap_option = LDAP_OPT_X_TLS_NEVER;
-
-    rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &ldap_option);
-    if(rc != LDAP_SUCCESS) {
-      failf(data, "LDAP local: ERROR setting cert verify mode: %s",
-              ldap_err2string(rc));
-      status = CURLE_SSL_CERTPROBLEM;
-      goto quit;
-    }
-    server = ldap_init(conn->host.name, (int)conn->port);
-    if(server == NULL) {
-      failf(data, "LDAP local: Cannot connect to %s:%hu",
-              conn->host.name, conn->port);
-      status = CURLE_COULDNT_CONNECT;
-      goto quit;
-    }
-    ldap_option = LDAP_OPT_X_TLS_HARD;
-    rc = ldap_set_option(server, LDAP_OPT_X_TLS, &ldap_option);
-    if(rc != LDAP_SUCCESS) {
-      failf(data, "LDAP local: ERROR setting SSL/TLS mode: %s",
-              ldap_err2string(rc));
-      status = CURLE_SSL_CERTPROBLEM;
-      goto quit;
-    }
-/*
-    rc = ldap_start_tls_s(server, NULL, NULL);
-    if(rc != LDAP_SUCCESS) {
-      failf(data, "LDAP local: ERROR starting SSL/TLS mode: %s",
-              ldap_err2string(rc));
-      status = CURLE_SSL_CERTPROBLEM;
-      goto quit;
-    }
-*/
-#else
-    /* we should probably never come up to here since configure
-       should check in first place if we can support LDAP SSL/TLS */
-    failf(data, "LDAP local: SSL/TLS not supported with this version "
-            "of the OpenLDAP toolkit\n");
-    status = CURLE_SSL_CERTPROBLEM;
-    goto quit;
-#endif
-#endif
-#endif /* CURL_LDAP_USE_SSL */
-  }
-  else {
-    server = ldap_init(conn->host.name, (int)conn->port);
-    if(server == NULL) {
-      failf(data, "LDAP local: Cannot connect to %s:%hu",
-              conn->host.name, conn->port);
-      status = CURLE_COULDNT_CONNECT;
-      goto quit;
-    }
-  }
-#ifdef CURL_LDAP_WIN
-  ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
-#endif
-
-  rc = ldap_simple_bind_s(server,
-                          conn->bits.user_passwd ? conn->user : NULL,
-                          conn->bits.user_passwd ? conn->passwd : NULL);
-  if(!ldap_ssl && rc != 0) {
-    ldap_proto = LDAP_VERSION2;
-    ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
-    rc = ldap_simple_bind_s(server,
-                            conn->bits.user_passwd ? conn->user : NULL,
-                            conn->bits.user_passwd ? conn->passwd : NULL);
-  }
-  if(rc != 0) {
-    failf(data, "LDAP local: ldap_simple_bind_s %s", ldap_err2string(rc));
-    status = CURLE_LDAP_CANNOT_BIND;
-    goto quit;
-  }
-
-  rc = ldap_search_s(server, ludp->lud_dn, ludp->lud_scope,
-                     ludp->lud_filter, ludp->lud_attrs, 0, &result);
-
-  if(rc != 0 && rc != LDAP_SIZELIMIT_EXCEEDED) {
-    failf(data, "LDAP remote: %s", ldap_err2string(rc));
-    status = CURLE_LDAP_SEARCH_FAILED;
-    goto quit;
-  }
-
-  for(num = 0, entryIterator = ldap_first_entry(server, result);
-      entryIterator;
-      entryIterator = ldap_next_entry(server, entryIterator), num++) {
-    BerElement *ber = NULL;
-    char  *attribute;       /*! suspicious that this isn't 'const' */
-    char  *dn = ldap_get_dn(server, entryIterator);
-    int i;
-
-    Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"DN: ", 4);
-    Curl_client_write(conn, CLIENTWRITE_BODY, (char *)dn, 0);
-    Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
-
-    dlsize += strlen(dn)+5;
-
-    for(attribute = ldap_first_attribute(server, entryIterator, &ber);
-        attribute;
-        attribute = ldap_next_attribute(server, entryIterator, ber)) {
-      BerValue **vals = ldap_get_values_len(server, entryIterator, attribute);
-
-      if(vals != NULL) {
-        for(i = 0; (vals[i] != NULL); i++) {
-          Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1);
-          Curl_client_write(conn, CLIENTWRITE_BODY, (char *) attribute, 0);
-          Curl_client_write(conn, CLIENTWRITE_BODY, (char *)": ", 2);
-          dlsize += strlen(attribute)+3;
-
-          if((strlen(attribute) > 7) &&
-              (strcmp(";binary",
-                      (char *)attribute +
-                      (strlen((char *)attribute) - 7)) == 0)) {
-            /* Binary attribute, encode to base64. */
-            CURLcode error = Curl_base64_encode(data,
-                                                vals[i]->bv_val,
-                                                vals[i]->bv_len,
-                                                &val_b64,
-                                                &val_b64_sz);
-            if(error) {
-              ldap_value_free_len(vals);
-              ldap_memfree(attribute);
-              ldap_memfree(dn);
-              if(ber)
-                ber_free(ber, 0);
-              status = error;
-              goto quit;
-            }
-            if(val_b64_sz > 0) {
-              Curl_client_write(conn, CLIENTWRITE_BODY, val_b64, val_b64_sz);
-              free(val_b64);
-              dlsize += val_b64_sz;
-            }
-          }
-          else {
-            Curl_client_write(conn, CLIENTWRITE_BODY, vals[i]->bv_val,
-                              vals[i]->bv_len);
-            dlsize += vals[i]->bv_len;
-          }
-          Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
-          dlsize++;
-        }
-
-        /* Free memory used to store values */
-        ldap_value_free_len(vals);
-      }
-      Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
-      dlsize++;
-      Curl_pgrsSetDownloadCounter(data, dlsize);
-      ldap_memfree(attribute);
-    }
-    ldap_memfree(dn);
-    if(ber)
-       ber_free(ber, 0);
-  }
-
-quit:
-  if(result) {
-    ldap_msgfree(result);
-    LDAP_TRACE (("Received %d entries\n", num));
-  }
-  if(rc == LDAP_SIZELIMIT_EXCEEDED)
-    infof(data, "There are more than %d entries\n", num);
-  if(ludp)
-    ldap_free_urldesc(ludp);
-  if(server)
-    ldap_unbind_s(server);
-#if defined(HAVE_LDAP_SSL) && defined(CURL_HAS_NOVELL_LDAPSDK)
-  if(ldap_ssl)
-    ldapssl_client_deinit();
-#endif /* HAVE_LDAP_SSL && CURL_HAS_NOVELL_LDAPSDK */
-
-  /* no data to transfer */
-  Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
-  conn->bits.close = TRUE;
-
-  return status;
-}
-
-#ifdef DEBUG_LDAP
-static void _ldap_trace (const char *fmt, ...)
-{
-  static int do_trace = -1;
-  va_list args;
-
-  if(do_trace == -1) {
-    const char *env = getenv("CURL_TRACE");
-    do_trace = (env && strtol(env, NULL, 10) > 0);
-  }
-  if(!do_trace)
-    return;
-
-  va_start (args, fmt);
-  vfprintf (stderr, fmt, args);
-  va_end (args);
-}
-#endif
-
-#ifndef HAVE_LDAP_URL_PARSE
-
-/*
- * Return scope-value for a scope-string.
- */
-static int str2scope (const char *p)
-{
-  if(strequal(p, "one"))
-     return LDAP_SCOPE_ONELEVEL;
-  if(strequal(p, "onetree"))
-     return LDAP_SCOPE_ONELEVEL;
-  if(strequal(p, "base"))
-     return LDAP_SCOPE_BASE;
-  if(strequal(p, "sub"))
-     return LDAP_SCOPE_SUBTREE;
-  if(strequal( p, "subtree"))
-     return LDAP_SCOPE_SUBTREE;
-  return (-1);
-}
-
-/*
- * Split 'str' into strings separated by commas.
- * Note: res[] points into 'str'.
- */
-static char **split_str (char *str)
-{
-  char **res, *lasts, *s;
-  int  i;
-
-  for(i = 2, s = strchr(str,','); s; i++)
-    s = strchr(++s,',');
-
-  res = calloc(i, sizeof(char*));
-  if(!res)
-    return NULL;
-
-  for(i = 0, s = strtok_r(str, ",", &lasts); s;
-      s = strtok_r(NULL, ",", &lasts), i++)
-    res[i] = s;
-  return res;
-}
-
-/*
- * Unescape the LDAP-URL components
- */
-static bool unescape_elements (void *data, LDAPURLDesc *ludp)
-{
-  int i;
-
-  if(ludp->lud_filter) {
-    ludp->lud_filter = curl_easy_unescape(data, ludp->lud_filter, 0, NULL);
-    if(!ludp->lud_filter)
-       return (FALSE);
-  }
-
-  for(i = 0; ludp->lud_attrs && ludp->lud_attrs[i]; i++) {
-    ludp->lud_attrs[i] = curl_easy_unescape(data, ludp->lud_attrs[i], 0, NULL);
-    if(!ludp->lud_attrs[i])
-      return (FALSE);
-  }
-
-  for(i = 0; ludp->lud_exts && ludp->lud_exts[i]; i++) {
-    ludp->lud_exts[i] = curl_easy_unescape(data, ludp->lud_exts[i], 0, NULL);
-    if(!ludp->lud_exts[i])
-      return (FALSE);
-  }
-
-  if(ludp->lud_dn) {
-    char *dn = ludp->lud_dn;
-    char *new_dn = curl_easy_unescape(data, dn, 0, NULL);
-
-    free(dn);
-    ludp->lud_dn = new_dn;
-    if(!new_dn)
-       return (FALSE);
-  }
-  return (TRUE);
-}
-
-/*
- * Break apart the pieces of an LDAP URL.
- * Syntax:
- *   ldap://<hostname>:<port>/<base_dn>?<attributes>?<scope>?<filter>?<ext>
- *
- * <hostname> already known from 'conn->host.name'.
- * <port>     already known from 'conn->remote_port'.
- * extract the rest from 'conn->data->state.path+1'. All fields are optional.
- * e.g.
- *   ldap://<hostname>:<port>/?<attributes>?<scope>?<filter>
- * yields ludp->lud_dn = "".
- *
- * Defined in RFC4516 section 2.
- */
-static int _ldap_url_parse2 (const struct connectdata *conn, LDAPURLDesc *ludp)
-{
-  char *p, *q;
-  int i;
-
-  if(!conn->data ||
-      !conn->data->state.path ||
-      conn->data->state.path[0] != '/' ||
-      !checkprefix("LDAP", conn->data->change.url))
-    return LDAP_INVALID_SYNTAX;
-
-  ludp->lud_scope = LDAP_SCOPE_BASE;
-  ludp->lud_port  = conn->remote_port;
-  ludp->lud_host  = conn->host.name;
-
-  /* parse DN (Distinguished Name).
-   */
-  ludp->lud_dn = strdup(conn->data->state.path+1);
-  if(!ludp->lud_dn)
-    return LDAP_NO_MEMORY;
-
-  p = strchr(ludp->lud_dn, '?');
-  LDAP_TRACE (("DN '%.*s'\n", p ? (size_t)(p-ludp->lud_dn) :
-               strlen(ludp->lud_dn), ludp->lud_dn));
-
-  if(!p)
-    goto success;
-
-  *p++ = '\0';
-
-  /* parse attributes. skip "??".
-   */
-  q = strchr(p, '?');
-  if(q)
-    *q++ = '\0';
-
-  if(*p && *p != '?') {
-    ludp->lud_attrs = split_str(p);
-    if(!ludp->lud_attrs)
-      return LDAP_NO_MEMORY;
-
-    for(i = 0; ludp->lud_attrs[i]; i++)
-      LDAP_TRACE (("attr[%d] '%s'\n", i, ludp->lud_attrs[i]));
-  }
-
-  p = q;
-  if(!p)
-    goto success;
-
-  /* parse scope. skip "??"
-   */
-  q = strchr(p, '?');
-  if(q)
-    *q++ = '\0';
-
-  if(*p && *p != '?') {
-    ludp->lud_scope = str2scope(p);
-    if(ludp->lud_scope == -1)
-      return LDAP_INVALID_SYNTAX;
-    LDAP_TRACE (("scope %d\n", ludp->lud_scope));
-  }
-
-  p = q;
-  if(!p)
-    goto success;
-
-  /* parse filter
-   */
-  q = strchr(p, '?');
-  if(q)
-    *q++ = '\0';
-  if(!*p)
-    return LDAP_INVALID_SYNTAX;
-
-  ludp->lud_filter = p;
-  LDAP_TRACE (("filter '%s'\n", ludp->lud_filter));
-
-  p = q;
-  if(!p)
-    goto success;
-
-  /* parse extensions
-   */
-  ludp->lud_exts = split_str(p);
-  if(!ludp->lud_exts)
-    return LDAP_NO_MEMORY;
-
-  for(i = 0; ludp->lud_exts[i]; i++)
-    LDAP_TRACE (("exts[%d] '%s'\n", i, ludp->lud_exts[i]));
-
-  success:
-  if(!unescape_elements(conn->data, ludp))
-    return LDAP_NO_MEMORY;
-  return LDAP_SUCCESS;
-}
-
-static int _ldap_url_parse (const struct connectdata *conn,
-                            LDAPURLDesc **ludpp)
-{
-  LDAPURLDesc *ludp = calloc(1, sizeof(*ludp));
-  int rc;
-
-  *ludpp = NULL;
-  if(!ludp)
-     return LDAP_NO_MEMORY;
-
-  rc = _ldap_url_parse2 (conn, ludp);
-  if(rc != LDAP_SUCCESS) {
-    _ldap_free_urldesc(ludp);
-    ludp = NULL;
-  }
-  *ludpp = ludp;
-  return (rc);
-}
-
-static void _ldap_free_urldesc (LDAPURLDesc *ludp)
-{
-  int i;
-
-  if(!ludp)
-    return;
-
-  if(ludp->lud_dn)
-    free(ludp->lud_dn);
-
-  if(ludp->lud_filter)
-    free(ludp->lud_filter);
-
-  if(ludp->lud_attrs) {
-    for(i = 0; ludp->lud_attrs[i]; i++)
-      free(ludp->lud_attrs[i]);
-    free(ludp->lud_attrs);
-  }
-
-  if(ludp->lud_exts) {
-    for(i = 0; ludp->lud_exts[i]; i++)
-      free(ludp->lud_exts[i]);
-    free(ludp->lud_exts);
-  }
-  free (ludp);
-}
-#endif  /* !HAVE_LDAP_URL_PARSE */
-#endif  /* !CURL_DISABLE_LDAP && !USE_OPENLDAP */
diff --git a/lib/llist.c b/lib/llist.c
deleted file mode 100644 (file)
index 46a8d99..0000000
+++ /dev/null
@@ -1,212 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#include "curl_llist.h"
-#include "curl_memory.h"
-
-/* this must be the last include file */
-#include "curl_memdebug.h"
-
-/*
- * @unittest: 1300
- */
-static void
-llist_init(struct curl_llist *l, curl_llist_dtor dtor)
-{
-  l->size = 0;
-  l->dtor = dtor;
-  l->head = NULL;
-  l->tail = NULL;
-}
-
-struct curl_llist *
-Curl_llist_alloc(curl_llist_dtor dtor)
-{
-  struct curl_llist *list;
-
-  list = malloc(sizeof(struct curl_llist));
-  if(!list)
-    return NULL;
-
-  llist_init(list, dtor);
-
-  return list;
-}
-
-/*
- * Curl_llist_insert_next()
- *
- * Inserts a new list element after the given one 'e'. If the given existing
- * entry is NULL and the list already has elements, the new one will be
- * inserted first in the list.
- *
- * Returns: 1 on success and 0 on failure.
- *
- * @unittest: 1300
- */
-int
-Curl_llist_insert_next(struct curl_llist *list, struct curl_llist_element *e,
-                       const void *p)
-{
-  struct curl_llist_element *ne = malloc(sizeof(struct curl_llist_element));
-  if(!ne)
-    return 0;
-
-  ne->ptr = (void *) p;
-  if(list->size == 0) {
-    list->head = ne;
-    list->head->prev = NULL;
-    list->head->next = NULL;
-    list->tail = ne;
-  }
-  else {
-    /* if 'e' is NULL here, we insert the new element first in the list */
-    ne->next = e?e->next:list->head;
-    ne->prev = e;
-    if(!e) {
-      list->head->prev = ne;
-      list->head = ne;
-    }
-    else if(e->next) {
-      e->next->prev = ne;
-    }
-    else {
-      list->tail = ne;
-    }
-    if(e)
-      e->next = ne;
-  }
-
-  ++list->size;
-
-  return 1;
-}
-
-/*
- * @unittest: 1300
- */
-int
-Curl_llist_remove(struct curl_llist *list, struct curl_llist_element *e,
-                  void *user)
-{
-  if(e == NULL || list->size == 0)
-    return 1;
-
-  if(e == list->head) {
-    list->head = e->next;
-
-    if(list->head == NULL)
-      list->tail = NULL;
-    else
-      e->next->prev = NULL;
-  }
-  else {
-    e->prev->next = e->next;
-    if(!e->next)
-      list->tail = e->prev;
-    else
-      e->next->prev = e->prev;
-  }
-
-  list->dtor(user, e->ptr);
-
-  e->ptr  = NULL;
-  e->prev = NULL;
-  e->next = NULL;
-
-  free(e);
-  --list->size;
-
-  return 1;
-}
-
-void
-Curl_llist_destroy(struct curl_llist *list, void *user)
-{
-  if(list) {
-    while(list->size > 0)
-      Curl_llist_remove(list, list->tail, user);
-
-    free(list);
-  }
-}
-
-size_t
-Curl_llist_count(struct curl_llist *list)
-{
-  return list->size;
-}
-
-/*
- * @unittest: 1300
- */
-int Curl_llist_move(struct curl_llist *list, struct curl_llist_element *e,
-                    struct curl_llist *to_list,
-                    struct curl_llist_element *to_e)
-{
-  /* Remove element from list */
-  if(e == NULL || list->size == 0)
-    return 0;
-
-  if(e == list->head) {
-    list->head = e->next;
-
-    if(list->head == NULL)
-      list->tail = NULL;
-    else
-      e->next->prev = NULL;
-  }
-  else {
-    e->prev->next = e->next;
-    if(!e->next)
-      list->tail = e->prev;
-    else
-      e->next->prev = e->prev;
-  }
-
-  --list->size;
-
-  /* Add element to to_list after to_e */
-  if(to_list->size == 0) {
-    to_list->head = e;
-    to_list->head->prev = NULL;
-    to_list->head->next = NULL;
-    to_list->tail = e;
-  }
-  else {
-    e->next = to_e->next;
-    e->prev = to_e;
-    if(to_e->next) {
-      to_e->next->prev = e;
-    }
-    else {
-      to_list->tail = e;
-    }
-    to_e->next = e;
-  }
-
-  ++to_list->size;
-
-  return 1;
-}
diff --git a/lib/md4.c b/lib/md4.c
deleted file mode 100644 (file)
index d64b472..0000000
--- a/lib/md4.c
+++ /dev/null
@@ -1,282 +0,0 @@
-/*-
-   Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved.
-
-   License to copy and use this software is granted provided that it
-   is identified as the "RSA Data Security, Inc. MD4 Message-Digest
-   Algorithm" in all material mentioning or referencing this software
-   or this function.
-
-   License is also granted to make and use derivative works provided
-   that such works are identified as "derived from the RSA Data
-   Security, Inc. MD4 Message-Digest Algorithm" in all material
-   mentioning or referencing the derived work.
-
-   RSA Data Security, Inc. makes no representations concerning either
-   the merchantability of this software or the suitability of this
-   software for any particular purpose. It is provided "as is"
-   without express or implied warranty of any kind.
-
-   These notices must be retained in any copies of any part of this
-   documentation and/or software.
- */
-
-#include "curl_setup.h"
-
-/* NSS crypto library does not provide the MD4 hash algorithm, so that we have
- * a local implementation of it */
-#ifdef USE_NSS
-
-#include "curl_md4.h"
-#include "curl_warnless.h"
-
-typedef unsigned int UINT4;
-
-typedef struct MD4Context {
-  UINT4 state[4];               /* state (ABCD) */
-  UINT4 count[2];               /* number of bits, modulo 2^64 (lsb first) */
-  unsigned char buffer[64];     /* input buffer */
-} MD4_CTX;
-
-/* Constants for MD4Transform routine.
- */
-#define S11 3
-#define S12 7
-#define S13 11
-#define S14 19
-#define S21 3
-#define S22 5
-#define S23 9
-#define S24 13
-#define S31 3
-#define S32 9
-#define S33 11
-#define S34 15
-
-static void MD4Transform(UINT4 [4], const unsigned char [64]);
-static void Encode(unsigned char *, UINT4 *, unsigned int);
-static void Decode(UINT4 *, const unsigned char *, unsigned int);
-
-static unsigned char PADDING[64] = {
-  0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-};
-
-/* F, G and H are basic MD4 functions.
- */
-#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
-#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
-#define H(x, y, z) ((x) ^ (y) ^ (z))
-
-/* ROTATE_LEFT rotates x left n bits.
- */
-#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
-
-/* FF, GG and HH are transformations for rounds 1, 2 and 3 */
-/* Rotation is separate from addition to prevent recomputation */
-#define FF(a, b, c, d, x, s) { \
-    (a) += F ((b), (c), (d)) + (x); \
-    (a) = ROTATE_LEFT ((a), (s)); \
-  }
-#define GG(a, b, c, d, x, s) { \
-    (a) += G ((b), (c), (d)) + (x) + (UINT4)0x5a827999; \
-    (a) = ROTATE_LEFT ((a), (s)); \
-  }
-#define HH(a, b, c, d, x, s) { \
-    (a) += H ((b), (c), (d)) + (x) + (UINT4)0x6ed9eba1; \
-    (a) = ROTATE_LEFT ((a), (s)); \
-  }
-
-/* MD4 initialization. Begins an MD4 operation, writing a new context.
- */
-static void MD4Init(MD4_CTX *context)
-{
-  context->count[0] = context->count[1] = 0;
-
-  /* Load magic initialization constants.
-   */
-  context->state[0] = 0x67452301;
-  context->state[1] = 0xefcdab89;
-  context->state[2] = 0x98badcfe;
-  context->state[3] = 0x10325476;
-}
-
-/* MD4 block update operation. Continues an MD4 message-digest
-     operation, processing another message block, and updating the
-     context.
- */
-static void MD4Update(MD4_CTX *context, const unsigned char *input,
-                      unsigned int inputLen)
-{
-  unsigned int i, bufindex, partLen;
-
-  /* Compute number of bytes mod 64 */
-  bufindex = (unsigned int)((context->count[0] >> 3) & 0x3F);
-  /* Update number of bits */
-  if((context->count[0] += ((UINT4)inputLen << 3))
-     < ((UINT4)inputLen << 3))
-    context->count[1]++;
-  context->count[1] += ((UINT4)inputLen >> 29);
-
-  partLen = 64 - bufindex;
-  /* Transform as many times as possible.
-   */
-  if(inputLen >= partLen) {
-    memcpy(&context->buffer[bufindex], input, partLen);
-    MD4Transform (context->state, context->buffer);
-
-    for(i = partLen; i + 63 < inputLen; i += 64)
-      MD4Transform (context->state, &input[i]);
-
-    bufindex = 0;
-  }
-  else
-    i = 0;
-
-  /* Buffer remaining input */
-  memcpy(&context->buffer[bufindex], &input[i], inputLen-i);
-}
-
-/* MD4 padding. */
-static void MD4Pad(MD4_CTX *context)
-{
-  unsigned char bits[8];
-  unsigned int bufindex, padLen;
-
-  /* Save number of bits */
-  Encode (bits, context->count, 8);
-
-  /* Pad out to 56 mod 64.
-   */
-  bufindex = (unsigned int)((context->count[0] >> 3) & 0x3f);
-  padLen = (bufindex < 56) ? (56 - bufindex) : (120 - bufindex);
-  MD4Update (context, PADDING, padLen);
-
-  /* Append length (before padding) */
-  MD4Update (context, bits, 8);
-}
-
-/* MD4 finalization. Ends an MD4 message-digest operation, writing the
-     the message digest and zeroizing the context.
- */
-static void MD4Final (unsigned char digest[16], MD4_CTX *context)
-{
-  /* Do padding */
-  MD4Pad (context);
-
-  /* Store state in digest */
-  Encode (digest, context->state, 16);
-
-  /* Zeroize sensitive information.
-   */
-  memset(context, 0, sizeof(*context));
-}
-
-/* MD4 basic transformation. Transforms state based on block.
- */
-static void MD4Transform (UINT4 state[4], const unsigned char block[64])
-{
-  UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
-
-  Decode (x, block, 64);
-
-  /* Round 1 */
-  FF (a, b, c, d, x[ 0], S11); /* 1 */
-  FF (d, a, b, c, x[ 1], S12); /* 2 */
-  FF (c, d, a, b, x[ 2], S13); /* 3 */
-  FF (b, c, d, a, x[ 3], S14); /* 4 */
-  FF (a, b, c, d, x[ 4], S11); /* 5 */
-  FF (d, a, b, c, x[ 5], S12); /* 6 */
-  FF (c, d, a, b, x[ 6], S13); /* 7 */
-  FF (b, c, d, a, x[ 7], S14); /* 8 */
-  FF (a, b, c, d, x[ 8], S11); /* 9 */
-  FF (d, a, b, c, x[ 9], S12); /* 10 */
-  FF (c, d, a, b, x[10], S13); /* 11 */
-  FF (b, c, d, a, x[11], S14); /* 12 */
-  FF (a, b, c, d, x[12], S11); /* 13 */
-  FF (d, a, b, c, x[13], S12); /* 14 */
-  FF (c, d, a, b, x[14], S13); /* 15 */
-  FF (b, c, d, a, x[15], S14); /* 16 */
-
-  /* Round 2 */
-  GG (a, b, c, d, x[ 0], S21); /* 17 */
-  GG (d, a, b, c, x[ 4], S22); /* 18 */
-  GG (c, d, a, b, x[ 8], S23); /* 19 */
-  GG (b, c, d, a, x[12], S24); /* 20 */
-  GG (a, b, c, d, x[ 1], S21); /* 21 */
-  GG (d, a, b, c, x[ 5], S22); /* 22 */
-  GG (c, d, a, b, x[ 9], S23); /* 23 */
-  GG (b, c, d, a, x[13], S24); /* 24 */
-  GG (a, b, c, d, x[ 2], S21); /* 25 */
-  GG (d, a, b, c, x[ 6], S22); /* 26 */
-  GG (c, d, a, b, x[10], S23); /* 27 */
-  GG (b, c, d, a, x[14], S24); /* 28 */
-  GG (a, b, c, d, x[ 3], S21); /* 29 */
-  GG (d, a, b, c, x[ 7], S22); /* 30 */
-  GG (c, d, a, b, x[11], S23); /* 31 */
-  GG (b, c, d, a, x[15], S24); /* 32 */
-
-  /* Round 3 */
-  HH (a, b, c, d, x[ 0], S31); /* 33 */
-  HH (d, a, b, c, x[ 8], S32); /* 34 */
-  HH (c, d, a, b, x[ 4], S33); /* 35 */
-  HH (b, c, d, a, x[12], S34); /* 36 */
-  HH (a, b, c, d, x[ 2], S31); /* 37 */
-  HH (d, a, b, c, x[10], S32); /* 38 */
-  HH (c, d, a, b, x[ 6], S33); /* 39 */
-  HH (b, c, d, a, x[14], S34); /* 40 */
-  HH (a, b, c, d, x[ 1], S31); /* 41 */
-  HH (d, a, b, c, x[ 9], S32); /* 42 */
-  HH (c, d, a, b, x[ 5], S33); /* 43 */
-  HH (b, c, d, a, x[13], S34); /* 44 */
-  HH (a, b, c, d, x[ 3], S31); /* 45 */
-  HH (d, a, b, c, x[11], S32); /* 46 */
-  HH (c, d, a, b, x[ 7], S33); /* 47 */
-  HH (b, c, d, a, x[15], S34); /* 48 */
-
-  state[0] += a;
-  state[1] += b;
-  state[2] += c;
-  state[3] += d;
-
-  /* Zeroize sensitive information.
-   */
-  memset(x, 0, sizeof(x));
-}
-
-/* Encodes input (UINT4) into output (unsigned char). Assumes len is
-     a multiple of 4.
- */
-static void Encode(unsigned char *output, UINT4 *input, unsigned int len)
-{
-  unsigned int i, j;
-
-  for(i = 0, j = 0; j < len; i++, j += 4) {
-    output[j] = (unsigned char)(input[i] & 0xff);
-    output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
-    output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
-    output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
-  }
-}
-
-/* Decodes input (unsigned char) into output (UINT4). Assumes len is
-     a multiple of 4.
- */
-static void Decode (UINT4 *output, const unsigned char *input,
-                    unsigned int len)
-{
-  unsigned int i, j;
-
-  for(i = 0, j = 0; j < len; i++, j += 4)
-    output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
-      (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
-}
-
-void Curl_md4it(unsigned char *output, const unsigned char *input, size_t len)
-{
-  MD4_CTX ctx;
-  MD4Init(&ctx);
-  MD4Update(&ctx, input, curlx_uztoui(len));
-  MD4Final(output, &ctx);
-}
-#endif /* USE_NSS */
diff --git a/lib/md5.c b/lib/md5.c
deleted file mode 100644 (file)
index 74f53f6..0000000
--- a/lib/md5.c
+++ /dev/null
@@ -1,521 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifndef CURL_DISABLE_CRYPTO_AUTH
-
-#include "curl_md5.h"
-#include "curl_hmac.h"
-#include "curl_warnless.h"
-
-#include "curl_memory.h"
-
-#if defined(USE_GNUTLS_NETTLE)
-
-#include <nettle/md5.h>
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-typedef struct md5_ctx MD5_CTX;
-
-static void MD5_Init(MD5_CTX * ctx)
-{
-  md5_init(ctx);
-}
-
-static void MD5_Update(MD5_CTX * ctx,
-                       const unsigned char * input,
-                       unsigned int inputLen)
-{
-  md5_update(ctx, inputLen, input);
-}
-
-static void MD5_Final(unsigned char digest[16], MD5_CTX * ctx)
-{
-  md5_digest(ctx, 16, digest);
-}
-
-#elif defined(USE_GNUTLS)
-
-#include <gcrypt.h>
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-typedef gcry_md_hd_t MD5_CTX;
-
-static void MD5_Init(MD5_CTX * ctx)
-{
-  gcry_md_open(ctx, GCRY_MD_MD5, 0);
-}
-
-static void MD5_Update(MD5_CTX * ctx,
-                       const unsigned char * input,
-                       unsigned int inputLen)
-{
-  gcry_md_write(*ctx, input, inputLen);
-}
-
-static void MD5_Final(unsigned char digest[16], MD5_CTX * ctx)
-{
-  memcpy(digest, gcry_md_read(*ctx, 0), 16);
-  gcry_md_close(*ctx);
-}
-
-#elif defined(USE_SSLEAY)
-/* When OpenSSL is available we use the MD5-function from OpenSSL */
-
-#  ifdef USE_OPENSSL
-#    include <openssl/md5.h>
-#  else
-#    include <md5.h>
-#  endif
-
-#elif defined(__MAC_10_4) || defined(__IPHONE_5_0)
-
-/* For Apple operating systems: CommonCrypto has the functions we need.
-   The library's headers are even backward-compatible with OpenSSL's
-   headers as long as we define COMMON_DIGEST_FOR_OPENSSL first.
-
-   These functions are available on Tiger and later, as well as iOS 5.0
-   and later. If you're building for an older cat, well, sorry. */
-#  define COMMON_DIGEST_FOR_OPENSSL
-#  include <CommonCrypto/CommonDigest.h>
-
-#elif defined(_WIN32)
-
-#include <wincrypt.h>
-
-typedef struct {
-  HCRYPTPROV hCryptProv;
-  HCRYPTHASH hHash;
-} MD5_CTX;
-
-static void MD5_Init(MD5_CTX *ctx)
-{
-  if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL,
-                         PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
-    CryptCreateHash(ctx->hCryptProv, CALG_MD5, 0, 0, &ctx->hHash);
-  }
-}
-
-static void MD5_Update(MD5_CTX *ctx,
-                       const unsigned char *input,
-                       unsigned int inputLen)
-{
-  CryptHashData(ctx->hHash, (unsigned char *)input, inputLen, 0);
-}
-
-static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx)
-{
-  unsigned long length;
-  CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0);
-  if(length == 16)
-    CryptGetHashParam(ctx->hHash, HP_HASHVAL, digest, &length, 0);
-  if(ctx->hHash)
-    CryptDestroyHash(ctx->hHash);
-  if(ctx->hCryptProv)
-    CryptReleaseContext(ctx->hCryptProv, 0);
-}
-
-#else
-/* When no other crypto library is available we use this code segment */
-
-/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
-rights reserved.
-
-License to copy and use this software is granted provided that it
-is identified as the "RSA Data Security, Inc. MD5 Message-Digest
-Algorithm" in all material mentioning or referencing this software
-or this function.
-
-License is also granted to make and use derivative works provided
-that such works are identified as "derived from the RSA Data
-Security, Inc. MD5 Message-Digest Algorithm" in all material
-mentioning or referencing the derived work.
-
-RSA Data Security, Inc. makes no representations concerning either
-the merchantability of this software or the suitability of this
-software for any particular purpose. It is provided "as is"
-without express or implied warranty of any kind.
-
-These notices must be retained in any copies of any part of this
-documentation and/or software.
- */
-
-/* UINT4 defines a four byte word */
-typedef unsigned int UINT4;
-
-/* MD5 context. */
-struct md5_ctx {
-  UINT4 state[4];                                   /* state (ABCD) */
-  UINT4 count[2];        /* number of bits, modulo 2^64 (lsb first) */
-  unsigned char buffer[64];                         /* input buffer */
-};
-
-typedef struct md5_ctx MD5_CTX;
-
-static void MD5_Init(struct md5_ctx *);
-static void MD5_Update(struct md5_ctx *, const unsigned char *, unsigned int);
-static void MD5_Final(unsigned char [16], struct md5_ctx *);
-
-/* Constants for MD5Transform routine.
- */
-
-#define S11 7
-#define S12 12
-#define S13 17
-#define S14 22
-#define S21 5
-#define S22 9
-#define S23 14
-#define S24 20
-#define S31 4
-#define S32 11
-#define S33 16
-#define S34 23
-#define S41 6
-#define S42 10
-#define S43 15
-#define S44 21
-
-static void MD5Transform(UINT4 [4], const unsigned char [64]);
-static void Encode(unsigned char *, UINT4 *, unsigned int);
-static void Decode(UINT4 *, const unsigned char *, unsigned int);
-
-static const unsigned char PADDING[64] = {
-  0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-};
-
-/* F, G, H and I are basic MD5 functions.
- */
-#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
-#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
-#define H(x, y, z) ((x) ^ (y) ^ (z))
-#define I(x, y, z) ((y) ^ ((x) | (~z)))
-
-/* ROTATE_LEFT rotates x left n bits.
- */
-#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
-
-/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
-Rotation is separate from addition to prevent recomputation.
- */
-#define FF(a, b, c, d, x, s, ac) { \
- (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
- (a) = ROTATE_LEFT ((a), (s)); \
- (a) += (b); \
-  }
-#define GG(a, b, c, d, x, s, ac) { \
- (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
- (a) = ROTATE_LEFT ((a), (s)); \
- (a) += (b); \
-  }
-#define HH(a, b, c, d, x, s, ac) { \
- (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
- (a) = ROTATE_LEFT ((a), (s)); \
- (a) += (b); \
-  }
-#define II(a, b, c, d, x, s, ac) { \
- (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
- (a) = ROTATE_LEFT ((a), (s)); \
- (a) += (b); \
-  }
-
-/* MD5 initialization. Begins an MD5 operation, writing a new context.
- */
-static void MD5_Init(struct md5_ctx *context)
-{
-  context->count[0] = context->count[1] = 0;
-  /* Load magic initialization constants. */
-  context->state[0] = 0x67452301;
-  context->state[1] = 0xefcdab89;
-  context->state[2] = 0x98badcfe;
-  context->state[3] = 0x10325476;
-}
-
-/* MD5 block update operation. Continues an MD5 message-digest
-  operation, processing another message block, and updating the
-  context.
- */
-static void MD5_Update (struct md5_ctx *context,    /* context */
-                        const unsigned char *input, /* input block */
-                        unsigned int inputLen)      /* length of input block */
-{
-  unsigned int i, bufindex, partLen;
-
-  /* Compute number of bytes mod 64 */
-  bufindex = (unsigned int)((context->count[0] >> 3) & 0x3F);
-
-  /* Update number of bits */
-  if((context->count[0] += ((UINT4)inputLen << 3))
-      < ((UINT4)inputLen << 3))
-    context->count[1]++;
-  context->count[1] += ((UINT4)inputLen >> 29);
-
-  partLen = 64 - bufindex;
-
-  /* Transform as many times as possible. */
-  if(inputLen >= partLen) {
-    memcpy(&context->buffer[bufindex], input, partLen);
-    MD5Transform(context->state, context->buffer);
-
-    for(i = partLen; i + 63 < inputLen; i += 64)
-      MD5Transform(context->state, &input[i]);
-
-    bufindex = 0;
-  }
-  else
-    i = 0;
-
-  /* Buffer remaining input */
-  memcpy(&context->buffer[bufindex], &input[i], inputLen-i);
-}
-
-/* MD5 finalization. Ends an MD5 message-digest operation, writing the
-   the message digest and zeroizing the context.
-*/
-static void MD5_Final(unsigned char digest[16], /* message digest */
-                      struct md5_ctx *context) /* context */
-{
-  unsigned char bits[8];
-  unsigned int count, padLen;
-
-  /* Save number of bits */
-  Encode (bits, context->count, 8);
-
-  /* Pad out to 56 mod 64. */
-  count = (unsigned int)((context->count[0] >> 3) & 0x3f);
-  padLen = (count < 56) ? (56 - count) : (120 - count);
-  MD5_Update (context, PADDING, padLen);
-
-  /* Append length (before padding) */
-  MD5_Update (context, bits, 8);
-
-  /* Store state in digest */
-  Encode (digest, context->state, 16);
-
-  /* Zeroize sensitive information. */
-  memset ((void *)context, 0, sizeof (*context));
-}
-
-/* MD5 basic transformation. Transforms state based on block. */
-static void MD5Transform(UINT4 state[4],
-                         const unsigned char block[64])
-{
-  UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
-
-  Decode (x, block, 64);
-
-  /* Round 1 */
-  FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
-  FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
-  FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
-  FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
-  FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
-  FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
-  FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
-  FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
-  FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
-  FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
-  FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
-  FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
-  FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
-  FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
-  FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
-  FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
-
- /* Round 2 */
-  GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
-  GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
-  GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
-  GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
-  GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
-  GG (d, a, b, c, x[10], S22,  0x2441453); /* 22 */
-  GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
-  GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
-  GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
-  GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
-  GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
-  GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
-  GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
-  GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
-  GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
-  GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
-
-  /* Round 3 */
-  HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
-  HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
-  HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
-  HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
-  HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
-  HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
-  HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
-  HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
-  HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
-  HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
-  HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
-  HH (b, c, d, a, x[ 6], S34,  0x4881d05); /* 44 */
-  HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
-  HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
-  HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
-  HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
-
-  /* Round 4 */
-  II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
-  II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
-  II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
-  II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
-  II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
-  II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
-  II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
-  II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
-  II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
-  II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
-  II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
-  II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
-  II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
-  II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
-  II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
-  II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
-
-  state[0] += a;
-  state[1] += b;
-  state[2] += c;
-  state[3] += d;
-
-  /* Zeroize sensitive information. */
-  memset((void *)x, 0, sizeof (x));
-}
-
-/* Encodes input (UINT4) into output (unsigned char). Assumes len is
-  a multiple of 4.
- */
-static void Encode (unsigned char *output,
-                    UINT4 *input,
-                    unsigned int len)
-{
-  unsigned int i, j;
-
-  for(i = 0, j = 0; j < len; i++, j += 4) {
-    output[j] = (unsigned char)(input[i] & 0xff);
-    output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
-    output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
-    output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
-  }
-}
-
-/* Decodes input (unsigned char) into output (UINT4). Assumes len is
-   a multiple of 4.
-*/
-static void Decode (UINT4 *output,
-                    const unsigned char *input,
-                    unsigned int len)
-{
-  unsigned int i, j;
-
-  for(i = 0, j = 0; j < len; i++, j += 4)
-    output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
-      (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
-}
-
-#endif /* CRYPTO LIBS */
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-const HMAC_params Curl_HMAC_MD5[] = {
-  {
-    (HMAC_hinit_func) MD5_Init,           /* Hash initialization function. */
-    (HMAC_hupdate_func) MD5_Update,       /* Hash update function. */
-    (HMAC_hfinal_func) MD5_Final,         /* Hash computation end function. */
-    sizeof(MD5_CTX),                      /* Size of hash context structure. */
-    64,                                   /* Maximum key length. */
-    16                                    /* Result size. */
-  }
-};
-
-const MD5_params Curl_DIGEST_MD5[] = {
-  {
-    (Curl_MD5_init_func) MD5_Init,      /* Digest initialization function */
-    (Curl_MD5_update_func) MD5_Update,  /* Digest update function */
-    (Curl_MD5_final_func) MD5_Final,    /* Digest computation end function */
-    sizeof(MD5_CTX),                    /* Size of digest context struct */
-    16                                  /* Result size */
-  }
-};
-
-void Curl_md5it(unsigned char *outbuffer, /* 16 bytes */
-                const unsigned char *input)
-{
-  MD5_CTX ctx;
-  MD5_Init(&ctx);
-  MD5_Update(&ctx, input, curlx_uztoui(strlen((char *)input)));
-  MD5_Final(outbuffer, &ctx);
-}
-
-MD5_context *Curl_MD5_init(const MD5_params *md5params)
-{
-  MD5_context *ctxt;
-
-  /* Create MD5 context */
-  ctxt = malloc(sizeof *ctxt);
-
-  if(!ctxt)
-    return ctxt;
-
-  ctxt->md5_hashctx = malloc(md5params->md5_ctxtsize);
-
-  if(!ctxt->md5_hashctx) {
-    free(ctxt);
-    return NULL;
-  }
-
-  ctxt->md5_hash = md5params;
-
-  (*md5params->md5_init_func)(ctxt->md5_hashctx);
-
-  return ctxt;
-}
-
-int Curl_MD5_update(MD5_context *context,
-                    const unsigned char *data,
-                    unsigned int len)
-{
-  (*context->md5_hash->md5_update_func)(context->md5_hashctx, data, len);
-
-  return 0;
-}
-
-int Curl_MD5_final(MD5_context *context, unsigned char *result)
-{
-  (*context->md5_hash->md5_final_func)(result, context->md5_hashctx);
-
-  free(context->md5_hashctx);
-  free(context);
-
-  return 0;
-}
-
-#endif /* CURL_DISABLE_CRYPTO_AUTH */
diff --git a/lib/memdebug.c b/lib/memdebug.c
deleted file mode 100644 (file)
index e756126..0000000
+++ /dev/null
@@ -1,445 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef CURLDEBUG
-
-#include <curl/curl.h>
-
-#define _MPRINTF_REPLACE
-#include <curl/mprintf.h>
-#include "curl_urldata.h"
-
-#define MEMDEBUG_NODEFINES /* don't redefine the standard functions */
-#include "curl_memory.h"
-#include "curl_memdebug.h"
-
-#ifndef HAVE_ASSERT_H
-#  define assert(x) Curl_nop_stmt
-#endif
-
-/*
- * Until 2011-08-17 libcurl's Memory Tracking feature also performed
- * automatic malloc and free filling operations using 0xA5 and 0x13
- * values. Our own preinitialization of dynamically allocated memory
- * might be useful when not using third party memory debuggers, but
- * on the other hand this would fool memory debuggers into thinking
- * that all dynamically allocated memory is properly initialized.
- *
- * As a default setting, libcurl's Memory Tracking feature no longer
- * performs preinitialization of dynamically allocated memory on its
- * own. If you know what you are doing, and really want to retain old
- * behavior, you can achieve this compiling with preprocessor symbols
- * CURL_MT_MALLOC_FILL and CURL_MT_FREE_FILL defined with appropriate
- * values.
- */
-
-#ifdef CURL_MT_MALLOC_FILL
-# if (CURL_MT_MALLOC_FILL < 0) || (CURL_MT_MALLOC_FILL > 0xff)
-#   error "invalid CURL_MT_MALLOC_FILL or out of range"
-# endif
-#endif
-
-#ifdef CURL_MT_FREE_FILL
-# if (CURL_MT_FREE_FILL < 0) || (CURL_MT_FREE_FILL > 0xff)
-#   error "invalid CURL_MT_FREE_FILL or out of range"
-# endif
-#endif
-
-#if defined(CURL_MT_MALLOC_FILL) && defined(CURL_MT_FREE_FILL)
-# if (CURL_MT_MALLOC_FILL == CURL_MT_FREE_FILL)
-#   error "CURL_MT_MALLOC_FILL same as CURL_MT_FREE_FILL"
-# endif
-#endif
-
-#ifdef CURL_MT_MALLOC_FILL
-#  define mt_malloc_fill(buf,len) memset((buf), CURL_MT_MALLOC_FILL, (len))
-#else
-#  define mt_malloc_fill(buf,len) Curl_nop_stmt
-#endif
-
-#ifdef CURL_MT_FREE_FILL
-#  define mt_free_fill(buf,len) memset((buf), CURL_MT_FREE_FILL, (len))
-#else
-#  define mt_free_fill(buf,len) Curl_nop_stmt
-#endif
-
-struct memdebug {
-  size_t size;
-  union {
-    curl_off_t o;
-    double d;
-    void * p;
-  } mem[1];
-  /* I'm hoping this is the thing with the strictest alignment
-   * requirements.  That also means we waste some space :-( */
-};
-
-/*
- * Note that these debug functions are very simple and they are meant to
- * remain so. For advanced analysis, record a log file and write perl scripts
- * to analyze them!
- *
- * Don't use these with multithreaded test programs!
- */
-
-#define logfile curl_debuglogfile
-FILE *curl_debuglogfile = NULL;
-static bool memlimit = FALSE; /* enable memory limit */
-static long memsize = 0;  /* set number of mallocs allowed */
-
-/* this sets the log file name */
-void curl_memdebug(const char *logname)
-{
-  if(!logfile) {
-    if(logname && *logname)
-      logfile = fopen(logname, "w");
-    else
-      logfile = stderr;
-#ifdef MEMDEBUG_LOG_SYNC
-    /* Flush the log file after every line so the log isn't lost in a crash */
-    setvbuf(logfile, (char *)NULL, _IOLBF, 0);
-#endif
-  }
-}
-
-/* This function sets the number of malloc() calls that should return
-   successfully! */
-void curl_memlimit(long limit)
-{
-  if(!memlimit) {
-    memlimit = TRUE;
-    memsize = limit;
-  }
-}
-
-/* returns TRUE if this isn't allowed! */
-static bool countcheck(const char *func, int line, const char *source)
-{
-  /* if source is NULL, then the call is made internally and this check
-     should not be made */
-  if(memlimit && source) {
-    if(!memsize) {
-      if(source) {
-        /* log to file */
-        curl_memlog("LIMIT %s:%d %s reached memlimit\n",
-                    source, line, func);
-        /* log to stderr also */
-        fprintf(stderr, "LIMIT %s:%d %s reached memlimit\n",
-                source, line, func);
-      }
-      SET_ERRNO(ENOMEM);
-      return TRUE; /* RETURN ERROR! */
-    }
-    else
-      memsize--; /* countdown */
-
-    /* log the countdown */
-    if(source)
-      curl_memlog("LIMIT %s:%d %ld ALLOCS left\n",
-                  source, line, memsize);
-
-  }
-
-  return FALSE; /* allow this */
-}
-
-void *curl_domalloc(size_t wantedsize, int line, const char *source)
-{
-  struct memdebug *mem;
-  size_t size;
-
-  assert(wantedsize != 0);
-
-  if(countcheck("malloc", line, source))
-    return NULL;
-
-  /* alloc at least 64 bytes */
-  size = sizeof(struct memdebug)+wantedsize;
-
-  mem = (Curl_cmalloc)(size);
-  if(mem) {
-    /* fill memory with junk */
-    mt_malloc_fill(mem->mem, wantedsize);
-    mem->size = wantedsize;
-  }
-
-  if(source)
-    curl_memlog("MEM %s:%d malloc(%zd) = %p\n",
-                source, line, wantedsize, mem ? mem->mem : 0);
-  return (mem ? mem->mem : NULL);
-}
-
-void *curl_docalloc(size_t wanted_elements, size_t wanted_size,
-                    int line, const char *source)
-{
-  struct memdebug *mem;
-  size_t size, user_size;
-
-  assert(wanted_elements != 0);
-  assert(wanted_size != 0);
-
-  if(countcheck("calloc", line, source))
-    return NULL;
-
-  /* alloc at least 64 bytes */
-  user_size = wanted_size * wanted_elements;
-  size = sizeof(struct memdebug) + user_size;
-
-  mem = (Curl_ccalloc)(1, size);
-  if(mem)
-    mem->size = user_size;
-
-  if(source)
-    curl_memlog("MEM %s:%d calloc(%zu,%zu) = %p\n",
-                source, line, wanted_elements, wanted_size, mem?mem->mem:0);
-  return (mem ? mem->mem : NULL);
-}
-
-char *curl_dostrdup(const char *str, int line, const char *source)
-{
-  char *mem;
-  size_t len;
-
-  assert(str != NULL);
-
-  if(countcheck("strdup", line, source))
-    return NULL;
-
-  len=strlen(str)+1;
-
-  mem=curl_domalloc(len, 0, NULL); /* NULL prevents logging */
-  if(mem)
-    memcpy(mem, str, len);
-
-  if(source)
-    curl_memlog("MEM %s:%d strdup(%p) (%zu) = %p\n",
-                source, line, str, len, mem);
-
-  return mem;
-}
-
-/* We provide a realloc() that accepts a NULL as pointer, which then
-   performs a malloc(). In order to work with ares. */
-void *curl_dorealloc(void *ptr, size_t wantedsize,
-                     int line, const char *source)
-{
-  struct memdebug *mem=NULL;
-
-  size_t size = sizeof(struct memdebug)+wantedsize;
-
-  assert(wantedsize != 0);
-
-  if(countcheck("realloc", line, source))
-    return NULL;
-
-#ifdef __INTEL_COMPILER
-#  pragma warning(push)
-#  pragma warning(disable:1684)
-   /* 1684: conversion from pointer to same-sized integral type */
-#endif
-
-  if(ptr)
-    mem = (void *)((char *)ptr - offsetof(struct memdebug, mem));
-
-#ifdef __INTEL_COMPILER
-#  pragma warning(pop)
-#endif
-
-  mem = (Curl_crealloc)(mem, size);
-  if(source)
-    curl_memlog("MEM %s:%d realloc(%p, %zu) = %p\n",
-                source, line, ptr, wantedsize, mem?mem->mem:NULL);
-
-  if(mem) {
-    mem->size = wantedsize;
-    return mem->mem;
-  }
-
-  return NULL;
-}
-
-void curl_dofree(void *ptr, int line, const char *source)
-{
-  struct memdebug *mem;
-
-  assert(ptr != NULL);
-
-#ifdef __INTEL_COMPILER
-#  pragma warning(push)
-#  pragma warning(disable:1684)
-   /* 1684: conversion from pointer to same-sized integral type */
-#endif
-
-  mem = (void *)((char *)ptr - offsetof(struct memdebug, mem));
-
-#ifdef __INTEL_COMPILER
-#  pragma warning(pop)
-#endif
-
-  /* destroy */
-  mt_free_fill(mem->mem, mem->size);
-
-  /* free for real */
-  (Curl_cfree)(mem);
-
-  if(source)
-    curl_memlog("MEM %s:%d free(%p)\n", source, line, ptr);
-}
-
-curl_socket_t curl_socket(int domain, int type, int protocol,
-                          int line, const char *source)
-{
-  const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
-                    "FD %s:%d socket() = %d\n" :
-                    (sizeof(curl_socket_t) == sizeof(long)) ?
-                    "FD %s:%d socket() = %ld\n" :
-                    "FD %s:%d socket() = %zd\n" ;
-
-  curl_socket_t sockfd = socket(domain, type, protocol);
-  if(source && (sockfd != CURL_SOCKET_BAD))
-    curl_memlog(fmt, source, line, sockfd);
-  return sockfd;
-}
-
-#ifdef HAVE_SOCKETPAIR
-int curl_socketpair(int domain, int type, int protocol,
-                    curl_socket_t socket_vector[2],
-                    int line, const char *source)
-{
-  const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
-                    "FD %s:%d socketpair() = %d %d\n" :
-                    (sizeof(curl_socket_t) == sizeof(long)) ?
-                    "FD %s:%d socketpair() = %ld %ld\n" :
-                    "FD %s:%d socketpair() = %zd %zd\n" ;
-
-  int res = socketpair(domain, type, protocol, socket_vector);
-  if(source && (0 == res))
-    curl_memlog(fmt, source, line, socket_vector[0], socket_vector[1]);
-  return res;
-}
-#endif
-
-curl_socket_t curl_accept(curl_socket_t s, void *saddr, void *saddrlen,
-                          int line, const char *source)
-{
-  const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
-                    "FD %s:%d accept() = %d\n" :
-                    (sizeof(curl_socket_t) == sizeof(long)) ?
-                    "FD %s:%d accept() = %ld\n" :
-                    "FD %s:%d accept() = %zd\n" ;
-
-  struct sockaddr *addr = (struct sockaddr *)saddr;
-  curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen;
-  curl_socket_t sockfd = accept(s, addr, addrlen);
-  if(source && (sockfd != CURL_SOCKET_BAD))
-    curl_memlog(fmt, source, line, sockfd);
-  return sockfd;
-}
-
-/* separate function to allow libcurl to mark a "faked" close */
-void curl_mark_sclose(curl_socket_t sockfd, int line, const char *source)
-{
-  const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
-                    "FD %s:%d sclose(%d)\n" :
-                    (sizeof(curl_socket_t) == sizeof(long)) ?
-                    "FD %s:%d sclose(%ld)\n" :
-                    "FD %s:%d sclose(%zd)\n" ;
-
-  if(source)
-    curl_memlog(fmt, source, line, sockfd);
-}
-
-/* this is our own defined way to close sockets on *ALL* platforms */
-int curl_sclose(curl_socket_t sockfd, int line, const char *source)
-{
-  int res=sclose(sockfd);
-  curl_mark_sclose(sockfd, line, source);
-  return res;
-}
-
-FILE *curl_fopen(const char *file, const char *mode,
-                 int line, const char *source)
-{
-  FILE *res=fopen(file, mode);
-  if(source)
-    curl_memlog("FILE %s:%d fopen(\"%s\",\"%s\") = %p\n",
-                source, line, file, mode, res);
-  return res;
-}
-
-#ifdef HAVE_FDOPEN
-FILE *curl_fdopen(int filedes, const char *mode,
-                  int line, const char *source)
-{
-  FILE *res=fdopen(filedes, mode);
-  if(source)
-    curl_memlog("FILE %s:%d fdopen(\"%d\",\"%s\") = %p\n",
-                source, line, filedes, mode, res);
-  return res;
-}
-#endif
-
-int curl_fclose(FILE *file, int line, const char *source)
-{
-  int res;
-
-  assert(file != NULL);
-
-  res=fclose(file);
-  if(source)
-    curl_memlog("FILE %s:%d fclose(%p)\n",
-                source, line, file);
-  return res;
-}
-
-#define LOGLINE_BUFSIZE  1024
-
-/* this does the writting to the memory tracking log file */
-void curl_memlog(const char *format, ...)
-{
-  char *buf;
-  int nchars;
-  va_list ap;
-
-  if(!logfile)
-    return;
-
-  buf = (Curl_cmalloc)(LOGLINE_BUFSIZE);
-  if(!buf)
-    return;
-
-  va_start(ap, format);
-  nchars = vsnprintf(buf, LOGLINE_BUFSIZE, format, ap);
-  va_end(ap);
-
-  if(nchars > LOGLINE_BUFSIZE - 1)
-    nchars = LOGLINE_BUFSIZE - 1;
-
-  if(nchars > 0)
-    fwrite(buf, 1, nchars, logfile);
-
-  (Curl_cfree)(buf);
-}
-
-#endif /* CURLDEBUG */
diff --git a/lib/mprintf.c b/lib/mprintf.c
deleted file mode 100644 (file)
index 35b9f64..0000000
+++ /dev/null
@@ -1,1197 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1999 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- *
- * Purpose:
- *  A merge of Bjorn Reese's format() function and Daniel's dsprintf()
- *  1.0. A full blooded printf() clone with full support for <num>$
- *  everywhere (parameters, widths and precisions) including variabled
- *  sized parameters (like doubles, long longs, long doubles and even
- *  void * in 64-bit architectures).
- *
- * Current restrictions:
- * - Max 128 parameters
- * - No 'long double' support.
- *
- * If you ever want truly portable and good *printf() clones, the project that
- * took on from here is named 'Trio' and you find more details on the trio web
- * page at http://daniel.haxx.se/trio/
- */
-
-#include "curl_setup.h"
-
-#if defined(DJGPP) && (DJGPP_MINOR < 4)
-#undef _MPRINTF_REPLACE /* don't use x_was_used() here */
-#endif
-
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-#ifndef SIZEOF_LONG_DOUBLE
-#define SIZEOF_LONG_DOUBLE 0
-#endif
-
-/*
- * If SIZEOF_SIZE_T has not been defined, default to the size of long.
- */
-
-#ifndef SIZEOF_SIZE_T
-#  define SIZEOF_SIZE_T CURL_SIZEOF_LONG
-#endif
-
-#ifdef HAVE_LONGLONG
-#  define LONG_LONG_TYPE long long
-#  define HAVE_LONG_LONG_TYPE
-#else
-#  if defined(_MSC_VER) && (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)
-#    define LONG_LONG_TYPE __int64
-#    define HAVE_LONG_LONG_TYPE
-#  else
-#    undef LONG_LONG_TYPE
-#    undef HAVE_LONG_LONG_TYPE
-#  endif
-#endif
-
-/*
- * Max integer data types that curl_mprintf.c is capable
- */
-
-#ifdef HAVE_LONG_LONG_TYPE
-#  define mp_intmax_t LONG_LONG_TYPE
-#  define mp_uintmax_t unsigned LONG_LONG_TYPE
-#else
-#  define mp_intmax_t long
-#  define mp_uintmax_t unsigned long
-#endif
-
-#define BUFFSIZE 256 /* buffer for long-to-str and float-to-str calcs */
-#define MAX_PARAMETERS 128 /* lame static limit */
-
-#ifdef __AMIGA__
-# undef FORMAT_INT
-#endif
-
-/* Lower-case digits.  */
-static const char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
-
-/* Upper-case digits.  */
-static const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-
-#define OUTCHAR(x) \
-  do{ \
-    if(stream((unsigned char)(x), (FILE *)data) != -1) \
-      done++; \
-    else \
-     return done; /* return immediately on failure */ \
-  } WHILE_FALSE
-
-/* Data type to read from the arglist */
-typedef enum  {
-  FORMAT_UNKNOWN = 0,
-  FORMAT_STRING,
-  FORMAT_PTR,
-  FORMAT_INT,
-  FORMAT_INTPTR,
-  FORMAT_LONG,
-  FORMAT_LONGLONG,
-  FORMAT_DOUBLE,
-  FORMAT_LONGDOUBLE,
-  FORMAT_WIDTH /* For internal use */
-} FormatType;
-
-/* conversion and display flags */
-enum {
-  FLAGS_NEW        = 0,
-  FLAGS_SPACE      = 1<<0,
-  FLAGS_SHOWSIGN   = 1<<1,
-  FLAGS_LEFT       = 1<<2,
-  FLAGS_ALT        = 1<<3,
-  FLAGS_SHORT      = 1<<4,
-  FLAGS_LONG       = 1<<5,
-  FLAGS_LONGLONG   = 1<<6,
-  FLAGS_LONGDOUBLE = 1<<7,
-  FLAGS_PAD_NIL    = 1<<8,
-  FLAGS_UNSIGNED   = 1<<9,
-  FLAGS_OCTAL      = 1<<10,
-  FLAGS_HEX        = 1<<11,
-  FLAGS_UPPER      = 1<<12,
-  FLAGS_WIDTH      = 1<<13, /* '*' or '*<num>$' used */
-  FLAGS_WIDTHPARAM = 1<<14, /* width PARAMETER was specified */
-  FLAGS_PREC       = 1<<15, /* precision was specified */
-  FLAGS_PRECPARAM  = 1<<16, /* precision PARAMETER was specified */
-  FLAGS_CHAR       = 1<<17, /* %c story */
-  FLAGS_FLOATE     = 1<<18, /* %e or %E */
-  FLAGS_FLOATG     = 1<<19  /* %g or %G */
-};
-
-typedef struct {
-  FormatType type;
-  int flags;
-  long width;     /* width OR width parameter number */
-  long precision; /* precision OR precision parameter number */
-  union {
-    char *str;
-    void *ptr;
-    union {
-      mp_intmax_t as_signed;
-      mp_uintmax_t as_unsigned;
-    } num;
-    double dnum;
-  } data;
-} va_stack_t;
-
-struct nsprintf {
-  char *buffer;
-  size_t length;
-  size_t max;
-};
-
-struct asprintf {
-  char *buffer; /* allocated buffer */
-  size_t len;   /* length of string */
-  size_t alloc; /* length of alloc */
-  int fail;     /* (!= 0) if an alloc has failed and thus
-                   the output is not the complete data */
-};
-
-static long dprintf_DollarString(char *input, char **end)
-{
-  int number=0;
-  while(ISDIGIT(*input)) {
-    number *= 10;
-    number += *input-'0';
-    input++;
-  }
-  if(number && ('$'==*input++)) {
-    *end = input;
-    return number;
-  }
-  return 0;
-}
-
-static int dprintf_IsQualifierNoDollar(char c)
-{
-  switch (c) {
-  case '-': case '+': case ' ': case '#': case '.':
-  case '0': case '1': case '2': case '3': case '4':
-  case '5': case '6': case '7': case '8': case '9':
-  case 'h': case 'l': case 'L': case 'z': case 'q':
-  case '*': case 'O':
-    return 1; /* true */
-  default:
-    return 0; /* false */
-  }
-}
-
-#ifdef DPRINTF_DEBUG2
-static void dprintf_Pass1Report(va_stack_t *vto, int max)
-{
-  int i;
-  char buffer[256];
-  int bit;
-  int flags;
-
-  for(i=0; i<max; i++) {
-    char *type;
-    switch(vto[i].type) {
-    case FORMAT_UNKNOWN:
-      type = "unknown";
-      break;
-    case FORMAT_STRING:
-      type ="string";
-      break;
-    case FORMAT_PTR:
-      type ="pointer";
-      break;
-    case FORMAT_INT:
-      type = "int";
-      break;
-    case FORMAT_INTPTR:
-      type = "intptr";
-      break;
-    case FORMAT_LONG:
-      type = "long";
-      break;
-    case FORMAT_LONGLONG:
-      type = "long long";
-      break;
-    case FORMAT_DOUBLE:
-      type = "double";
-      break;
-    case FORMAT_LONGDOUBLE:
-      type = "long double";
-      break;
-    }
-
-
-    buffer[0]=0;
-
-    for(bit=0; bit<31; bit++) {
-      flags = vto[i].flags & (1<<bit);
-
-      if(flags & FLAGS_SPACE)
-        strcat(buffer, "space ");
-      else if(flags & FLAGS_SHOWSIGN)
-        strcat(buffer, "plus ");
-      else if(flags & FLAGS_LEFT)
-        strcat(buffer, "left ");
-      else if(flags & FLAGS_ALT)
-        strcat(buffer, "alt ");
-      else if(flags & FLAGS_SHORT)
-        strcat(buffer, "short ");
-      else if(flags & FLAGS_LONG)
-        strcat(buffer, "long ");
-      else if(flags & FLAGS_LONGLONG)
-        strcat(buffer, "longlong ");
-      else if(flags & FLAGS_LONGDOUBLE)
-        strcat(buffer, "longdouble ");
-      else if(flags & FLAGS_PAD_NIL)
-        strcat(buffer, "padnil ");
-      else if(flags & FLAGS_UNSIGNED)
-        strcat(buffer, "unsigned ");
-      else if(flags & FLAGS_OCTAL)
-        strcat(buffer, "octal ");
-      else if(flags & FLAGS_HEX)
-        strcat(buffer, "hex ");
-      else if(flags & FLAGS_UPPER)
-        strcat(buffer, "upper ");
-      else if(flags & FLAGS_WIDTH)
-        strcat(buffer, "width ");
-      else if(flags & FLAGS_WIDTHPARAM)
-        strcat(buffer, "widthparam ");
-      else if(flags & FLAGS_PREC)
-        strcat(buffer, "precision ");
-      else if(flags & FLAGS_PRECPARAM)
-        strcat(buffer, "precparam ");
-      else if(flags & FLAGS_CHAR)
-        strcat(buffer, "char ");
-      else if(flags & FLAGS_FLOATE)
-        strcat(buffer, "floate ");
-      else if(flags & FLAGS_FLOATG)
-        strcat(buffer, "floatg ");
-    }
-    printf("REPORT: %d. %s [%s]\n", i, type, buffer);
-
-  }
-
-
-}
-#endif
-
-/******************************************************************
- *
- * Pass 1:
- * Create an index with the type of each parameter entry and its
- * value (may vary in size)
- *
- ******************************************************************/
-
-static long dprintf_Pass1(const char *format, va_stack_t *vto, char **endpos,
-                          va_list arglist)
-{
-  char *fmt = (char *)format;
-  int param_num = 0;
-  long this_param;
-  long width;
-  long precision;
-  int flags;
-  long max_param=0;
-  long i;
-
-  while(*fmt) {
-    if(*fmt++ == '%') {
-      if(*fmt == '%') {
-        fmt++;
-        continue; /* while */
-      }
-
-      flags = FLAGS_NEW;
-
-      /* Handle the positional case (N$) */
-
-      param_num++;
-
-      this_param = dprintf_DollarString(fmt, &fmt);
-      if(0 == this_param)
-        /* we got no positional, get the next counter */
-        this_param = param_num;
-
-      if(this_param > max_param)
-        max_param = this_param;
-
-      /*
-       * The parameter with number 'i' should be used. Next, we need
-       * to get SIZE and TYPE of the parameter. Add the information
-       * to our array.
-       */
-
-      width = 0;
-      precision = 0;
-
-      /* Handle the flags */
-
-      while(dprintf_IsQualifierNoDollar(*fmt)) {
-        switch (*fmt++) {
-        case ' ':
-          flags |= FLAGS_SPACE;
-          break;
-        case '+':
-          flags |= FLAGS_SHOWSIGN;
-          break;
-        case '-':
-          flags |= FLAGS_LEFT;
-          flags &= ~FLAGS_PAD_NIL;
-          break;
-        case '#':
-          flags |= FLAGS_ALT;
-          break;
-        case '.':
-          flags |= FLAGS_PREC;
-          if('*' == *fmt) {
-            /* The precision is picked from a specified parameter */
-
-            flags |= FLAGS_PRECPARAM;
-            fmt++;
-            param_num++;
-
-            i = dprintf_DollarString(fmt, &fmt);
-            if(i)
-              precision = i;
-            else
-              precision = param_num;
-
-            if(precision > max_param)
-              max_param = precision;
-          }
-          else {
-            flags |= FLAGS_PREC;
-            precision = strtol(fmt, &fmt, 10);
-          }
-          break;
-        case 'h':
-          flags |= FLAGS_SHORT;
-          break;
-        case 'l':
-          if(flags & FLAGS_LONG)
-            flags |= FLAGS_LONGLONG;
-          else
-            flags |= FLAGS_LONG;
-          break;
-        case 'L':
-          flags |= FLAGS_LONGDOUBLE;
-          break;
-        case 'q':
-          flags |= FLAGS_LONGLONG;
-          break;
-        case 'z':
-          /* the code below generates a warning if -Wunreachable-code is
-             used */
-#if (SIZEOF_SIZE_T > CURL_SIZEOF_LONG)
-          flags |= FLAGS_LONGLONG;
-#else
-          flags |= FLAGS_LONG;
-#endif
-          break;
-        case 'O':
-#if (CURL_SIZEOF_CURL_OFF_T > CURL_SIZEOF_LONG)
-          flags |= FLAGS_LONGLONG;
-#else
-          flags |= FLAGS_LONG;
-#endif
-          break;
-        case '0':
-          if(!(flags & FLAGS_LEFT))
-            flags |= FLAGS_PAD_NIL;
-          /* FALLTHROUGH */
-        case '1': case '2': case '3': case '4':
-        case '5': case '6': case '7': case '8': case '9':
-          flags |= FLAGS_WIDTH;
-          width = strtol(fmt-1, &fmt, 10);
-          break;
-        case '*':  /* Special case */
-          flags |= FLAGS_WIDTHPARAM;
-          param_num++;
-
-          i = dprintf_DollarString(fmt, &fmt);
-          if(i)
-            width = i;
-          else
-            width = param_num;
-          if(width > max_param)
-            max_param=width;
-          break;
-        default:
-          break;
-        }
-      } /* switch */
-
-      /* Handle the specifier */
-
-      i = this_param - 1;
-
-      switch (*fmt) {
-      case 'S':
-        flags |= FLAGS_ALT;
-        /* FALLTHROUGH */
-      case 's':
-        vto[i].type = FORMAT_STRING;
-        break;
-      case 'n':
-        vto[i].type = FORMAT_INTPTR;
-        break;
-      case 'p':
-        vto[i].type = FORMAT_PTR;
-        break;
-      case 'd': case 'i':
-        vto[i].type = FORMAT_INT;
-        break;
-      case 'u':
-        vto[i].type = FORMAT_INT;
-        flags |= FLAGS_UNSIGNED;
-        break;
-      case 'o':
-        vto[i].type = FORMAT_INT;
-        flags |= FLAGS_OCTAL;
-        break;
-      case 'x':
-        vto[i].type = FORMAT_INT;
-        flags |= FLAGS_HEX;
-        break;
-      case 'X':
-        vto[i].type = FORMAT_INT;
-        flags |= FLAGS_HEX|FLAGS_UPPER;
-        break;
-      case 'c':
-        vto[i].type = FORMAT_INT;
-        flags |= FLAGS_CHAR;
-        break;
-      case 'f':
-        vto[i].type = FORMAT_DOUBLE;
-        break;
-      case 'e':
-        vto[i].type = FORMAT_DOUBLE;
-        flags |= FLAGS_FLOATE;
-        break;
-      case 'E':
-        vto[i].type = FORMAT_DOUBLE;
-        flags |= FLAGS_FLOATE|FLAGS_UPPER;
-        break;
-      case 'g':
-        vto[i].type = FORMAT_DOUBLE;
-        flags |= FLAGS_FLOATG;
-        break;
-      case 'G':
-        vto[i].type = FORMAT_DOUBLE;
-        flags |= FLAGS_FLOATG|FLAGS_UPPER;
-        break;
-      default:
-        vto[i].type = FORMAT_UNKNOWN;
-        break;
-      } /* switch */
-
-      vto[i].flags = flags;
-      vto[i].width = width;
-      vto[i].precision = precision;
-
-      if(flags & FLAGS_WIDTHPARAM) {
-        /* we have the width specified from a parameter, so we make that
-           parameter's info setup properly */
-        vto[i].width = width - 1;
-        i = width - 1;
-        vto[i].type = FORMAT_WIDTH;
-        vto[i].flags = FLAGS_NEW;
-        vto[i].precision = vto[i].width = 0; /* can't use width or precision
-                                                of width! */
-      }
-      if(flags & FLAGS_PRECPARAM) {
-        /* we have the precision specified from a parameter, so we make that
-           parameter's info setup properly */
-        vto[i].precision = precision - 1;
-        i = precision - 1;
-        vto[i].type = FORMAT_WIDTH;
-        vto[i].flags = FLAGS_NEW;
-        vto[i].precision = vto[i].width = 0; /* can't use width or precision
-                                                of width! */
-      }
-      *endpos++ = fmt + 1; /* end of this sequence */
-    }
-  }
-
-#ifdef DPRINTF_DEBUG2
-  dprintf_Pass1Report(vto, max_param);
-#endif
-
-  /* Read the arg list parameters into our data list */
-  for(i=0; i<max_param; i++) {
-    if((i + 1 < max_param) && (vto[i + 1].type == FORMAT_WIDTH)) {
-      /* Width/precision arguments must be read before the main argument
-       * they are attached to
-       */
-      vto[i + 1].data.num.as_signed = (mp_intmax_t)va_arg(arglist, int);
-    }
-
-    switch (vto[i].type) {
-    case FORMAT_STRING:
-      vto[i].data.str = va_arg(arglist, char *);
-      break;
-
-    case FORMAT_INTPTR:
-    case FORMAT_UNKNOWN:
-    case FORMAT_PTR:
-      vto[i].data.ptr = va_arg(arglist, void *);
-      break;
-
-    case FORMAT_INT:
-#ifdef HAVE_LONG_LONG_TYPE
-      if((vto[i].flags & FLAGS_LONGLONG) && (vto[i].flags & FLAGS_UNSIGNED))
-        vto[i].data.num.as_unsigned =
-          (mp_uintmax_t)va_arg(arglist, mp_uintmax_t);
-      else if(vto[i].flags & FLAGS_LONGLONG)
-        vto[i].data.num.as_signed =
-          (mp_intmax_t)va_arg(arglist, mp_intmax_t);
-      else
-#endif
-      {
-        if((vto[i].flags & FLAGS_LONG) && (vto[i].flags & FLAGS_UNSIGNED))
-          vto[i].data.num.as_unsigned =
-            (mp_uintmax_t)va_arg(arglist, unsigned long);
-        else if(vto[i].flags & FLAGS_LONG)
-          vto[i].data.num.as_signed =
-            (mp_intmax_t)va_arg(arglist, long);
-        else if(vto[i].flags & FLAGS_UNSIGNED)
-          vto[i].data.num.as_unsigned =
-            (mp_uintmax_t)va_arg(arglist, unsigned int);
-        else
-          vto[i].data.num.as_signed =
-            (mp_intmax_t)va_arg(arglist, int);
-      }
-      break;
-
-    case FORMAT_DOUBLE:
-      vto[i].data.dnum = va_arg(arglist, double);
-      break;
-
-    case FORMAT_WIDTH:
-      /* Argument has been read. Silently convert it into an integer
-       * for later use
-       */
-      vto[i].type = FORMAT_INT;
-      break;
-
-    default:
-      break;
-    }
-  }
-
-  return max_param;
-
-}
-
-static int dprintf_formatf(
-  void *data, /* untouched by format(), just sent to the stream() function in
-                 the second argument */
-  /* function pointer called for each output character */
-  int (*stream)(int, FILE *),
-  const char *format,    /* %-formatted string */
-  va_list ap_save) /* list of parameters */
-{
-  /* Base-36 digits for numbers.  */
-  const char *digits = lower_digits;
-
-  /* Pointer into the format string.  */
-  char *f;
-
-  /* Number of characters written.  */
-  int done = 0;
-
-  long param; /* current parameter to read */
-  long param_num=0; /* parameter counter */
-
-  va_stack_t vto[MAX_PARAMETERS];
-  char *endpos[MAX_PARAMETERS];
-  char **end;
-
-  char work[BUFFSIZE];
-
-  va_stack_t *p;
-
-  /* Do the actual %-code parsing */
-  dprintf_Pass1(format, vto, endpos, ap_save);
-
-  end = &endpos[0]; /* the initial end-position from the list dprintf_Pass1()
-                       created for us */
-
-  f = (char *)format;
-  while(*f != '\0') {
-    /* Format spec modifiers.  */
-    int is_alt;
-
-    /* Width of a field.  */
-    long width;
-
-    /* Precision of a field.  */
-    long prec;
-
-    /* Decimal integer is negative.  */
-    int is_neg;
-
-    /* Base of a number to be written.  */
-    long base;
-
-    /* Integral values to be written.  */
-    mp_uintmax_t num;
-
-    /* Used to convert negative in positive.  */
-    mp_intmax_t signed_num;
-
-    if(*f != '%') {
-      /* This isn't a format spec, so write everything out until the next one
-         OR end of string is reached.  */
-      do {
-        OUTCHAR(*f);
-      } while(*++f && ('%' != *f));
-      continue;
-    }
-
-    ++f;
-
-    /* Check for "%%".  Note that although the ANSI standard lists
-       '%' as a conversion specifier, it says "The complete format
-       specification shall be `%%'," so we can avoid all the width
-       and precision processing.  */
-    if(*f == '%') {
-      ++f;
-      OUTCHAR('%');
-      continue;
-    }
-
-    /* If this is a positional parameter, the position must follow immediately
-       after the %, thus create a %<num>$ sequence */
-    param=dprintf_DollarString(f, &f);
-
-    if(!param)
-      param = param_num;
-    else
-      --param;
-
-    param_num++; /* increase this always to allow "%2$s %1$s %s" and then the
-                    third %s will pick the 3rd argument */
-
-    p = &vto[param];
-
-    /* pick up the specified width */
-    if(p->flags & FLAGS_WIDTHPARAM)
-      width = (long)vto[p->width].data.num.as_signed;
-    else
-      width = p->width;
-
-    /* pick up the specified precision */
-    if(p->flags & FLAGS_PRECPARAM) {
-      prec = (long)vto[p->precision].data.num.as_signed;
-      param_num++; /* since the precision is extraced from a parameter, we
-                      must skip that to get to the next one properly */
-    }
-    else if(p->flags & FLAGS_PREC)
-      prec = p->precision;
-    else
-      prec = -1;
-
-    is_alt = (p->flags & FLAGS_ALT) ? 1 : 0;
-
-    switch (p->type) {
-    case FORMAT_INT:
-      num = p->data.num.as_unsigned;
-      if(p->flags & FLAGS_CHAR) {
-        /* Character.  */
-        if(!(p->flags & FLAGS_LEFT))
-          while(--width > 0)
-            OUTCHAR(' ');
-        OUTCHAR((char) num);
-        if(p->flags & FLAGS_LEFT)
-          while(--width > 0)
-            OUTCHAR(' ');
-        break;
-      }
-      if(p->flags & FLAGS_UNSIGNED) {
-        /* Decimal unsigned integer.  */
-        base = 10;
-        goto unsigned_number;
-      }
-      if(p->flags & FLAGS_OCTAL) {
-        /* Octal unsigned integer.  */
-        base = 8;
-        goto unsigned_number;
-      }
-      if(p->flags & FLAGS_HEX) {
-        /* Hexadecimal unsigned integer.  */
-
-        digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
-        base = 16;
-        goto unsigned_number;
-      }
-
-      /* Decimal integer.  */
-      base = 10;
-
-      is_neg = (p->data.num.as_signed < (mp_intmax_t)0) ? 1 : 0;
-      if(is_neg) {
-        /* signed_num might fail to hold absolute negative minimum by 1 */
-        signed_num = p->data.num.as_signed + (mp_intmax_t)1;
-        signed_num = -signed_num;
-        num = (mp_uintmax_t)signed_num;
-        num += (mp_uintmax_t)1;
-      }
-
-      goto number;
-
-      unsigned_number:
-      /* Unsigned number of base BASE.  */
-      is_neg = 0;
-
-      number:
-      /* Number of base BASE.  */
-      {
-        char *workend = &work[sizeof(work) - 1];
-        char *w;
-
-        /* Supply a default precision if none was given.  */
-        if(prec == -1)
-          prec = 1;
-
-        /* Put the number in WORK.  */
-        w = workend;
-        while(num > 0) {
-          *w-- = digits[num % base];
-          num /= base;
-        }
-        width -= (long)(workend - w);
-        prec -= (long)(workend - w);
-
-        if(is_alt && base == 8 && prec <= 0) {
-          *w-- = '0';
-          --width;
-        }
-
-        if(prec > 0) {
-          width -= prec;
-          while(prec-- > 0)
-            *w-- = '0';
-        }
-
-        if(is_alt && base == 16)
-          width -= 2;
-
-        if(is_neg || (p->flags & FLAGS_SHOWSIGN) || (p->flags & FLAGS_SPACE))
-          --width;
-
-        if(!(p->flags & FLAGS_LEFT) && !(p->flags & FLAGS_PAD_NIL))
-          while(width-- > 0)
-            OUTCHAR(' ');
-
-        if(is_neg)
-          OUTCHAR('-');
-        else if(p->flags & FLAGS_SHOWSIGN)
-          OUTCHAR('+');
-        else if(p->flags & FLAGS_SPACE)
-          OUTCHAR(' ');
-
-        if(is_alt && base == 16) {
-          OUTCHAR('0');
-          if(p->flags & FLAGS_UPPER)
-            OUTCHAR('X');
-          else
-            OUTCHAR('x');
-        }
-
-        if(!(p->flags & FLAGS_LEFT) && (p->flags & FLAGS_PAD_NIL))
-          while(width-- > 0)
-            OUTCHAR('0');
-
-        /* Write the number.  */
-        while(++w <= workend) {
-          OUTCHAR(*w);
-        }
-
-        if(p->flags & FLAGS_LEFT)
-          while(width-- > 0)
-            OUTCHAR(' ');
-      }
-      break;
-
-    case FORMAT_STRING:
-            /* String.  */
-      {
-        static const char null[] = "(nil)";
-        const char *str;
-        size_t len;
-
-        str = (char *) p->data.str;
-        if(str == NULL) {
-          /* Write null[] if there's space.  */
-          if(prec == -1 || prec >= (long) sizeof(null) - 1) {
-            str = null;
-            len = sizeof(null) - 1;
-            /* Disable quotes around (nil) */
-            p->flags &= (~FLAGS_ALT);
-          }
-          else {
-            str = "";
-            len = 0;
-          }
-        }
-        else
-          len = strlen(str);
-
-        if(prec != -1 && (size_t) prec < len)
-          len = (size_t)prec;
-        width -= (long)len;
-
-        if(p->flags & FLAGS_ALT)
-          OUTCHAR('"');
-
-        if(!(p->flags&FLAGS_LEFT))
-          while(width-- > 0)
-            OUTCHAR(' ');
-
-        while(len-- > 0)
-          OUTCHAR(*str++);
-        if(p->flags&FLAGS_LEFT)
-          while(width-- > 0)
-            OUTCHAR(' ');
-
-        if(p->flags & FLAGS_ALT)
-          OUTCHAR('"');
-      }
-      break;
-
-    case FORMAT_PTR:
-      /* Generic pointer.  */
-      {
-        void *ptr;
-        ptr = (void *) p->data.ptr;
-        if(ptr != NULL) {
-          /* If the pointer is not NULL, write it as a %#x spec.  */
-          base = 16;
-          digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
-          is_alt = 1;
-          num = (size_t) ptr;
-          is_neg = 0;
-          goto number;
-        }
-        else {
-          /* Write "(nil)" for a nil pointer.  */
-          static const char strnil[] = "(nil)";
-          const char *point;
-
-          width -= (long)(sizeof(strnil) - 1);
-          if(p->flags & FLAGS_LEFT)
-            while(width-- > 0)
-              OUTCHAR(' ');
-          for(point = strnil; *point != '\0'; ++point)
-            OUTCHAR(*point);
-          if(! (p->flags & FLAGS_LEFT))
-            while(width-- > 0)
-              OUTCHAR(' ');
-        }
-      }
-      break;
-
-    case FORMAT_DOUBLE:
-      {
-        char formatbuf[32]="%";
-        char *fptr;
-        size_t left = sizeof(formatbuf)-strlen(formatbuf);
-        int len;
-
-        width = -1;
-        if(p->flags & FLAGS_WIDTH)
-          width = p->width;
-        else if(p->flags & FLAGS_WIDTHPARAM)
-          width = (long)vto[p->width].data.num.as_signed;
-
-        prec = -1;
-        if(p->flags & FLAGS_PREC)
-          prec = p->precision;
-        else if(p->flags & FLAGS_PRECPARAM)
-          prec = (long)vto[p->precision].data.num.as_signed;
-
-        if(p->flags & FLAGS_LEFT)
-          strcat(formatbuf, "-");
-        if(p->flags & FLAGS_SHOWSIGN)
-          strcat(formatbuf, "+");
-        if(p->flags & FLAGS_SPACE)
-          strcat(formatbuf, " ");
-        if(p->flags & FLAGS_ALT)
-          strcat(formatbuf, "#");
-
-        fptr=&formatbuf[strlen(formatbuf)];
-
-        if(width >= 0) {
-          /* RECURSIVE USAGE */
-          len = curl_msnprintf(fptr, left, "%ld", width);
-          fptr += len;
-          left -= len;
-        }
-        if(prec >= 0) {
-          /* RECURSIVE USAGE */
-          len = curl_msnprintf(fptr, left, ".%ld", prec);
-          fptr += len;
-        }
-        if(p->flags & FLAGS_LONG)
-          *fptr++ = 'l';
-
-        if(p->flags & FLAGS_FLOATE)
-          *fptr++ = (char)((p->flags & FLAGS_UPPER) ? 'E':'e');
-        else if(p->flags & FLAGS_FLOATG)
-          *fptr++ = (char)((p->flags & FLAGS_UPPER) ? 'G' : 'g');
-        else
-          *fptr++ = 'f';
-
-        *fptr = 0; /* and a final zero termination */
-
-        /* NOTE NOTE NOTE!! Not all sprintf() implementations returns number
-           of output characters */
-        (sprintf)(work, formatbuf, p->data.dnum);
-
-        for(fptr=work; *fptr; fptr++)
-          OUTCHAR(*fptr);
-      }
-      break;
-
-    case FORMAT_INTPTR:
-      /* Answer the count of characters written.  */
-#ifdef HAVE_LONG_LONG_TYPE
-      if(p->flags & FLAGS_LONGLONG)
-        *(LONG_LONG_TYPE *) p->data.ptr = (LONG_LONG_TYPE)done;
-      else
-#endif
-        if(p->flags & FLAGS_LONG)
-          *(long *) p->data.ptr = (long)done;
-      else if(!(p->flags & FLAGS_SHORT))
-        *(int *) p->data.ptr = (int)done;
-      else
-        *(short *) p->data.ptr = (short)done;
-      break;
-
-    default:
-      break;
-    }
-    f = *end++; /* goto end of %-code */
-
-  }
-  return done;
-}
-
-/* fputc() look-alike */
-static int addbyter(int output, FILE *data)
-{
-  struct nsprintf *infop=(struct nsprintf *)data;
-  unsigned char outc = (unsigned char)output;
-
-  if(infop->length < infop->max) {
-    /* only do this if we haven't reached max length yet */
-    infop->buffer[0] = outc; /* store */
-    infop->buffer++; /* increase pointer */
-    infop->length++; /* we are now one byte larger */
-    return outc;     /* fputc() returns like this on success */
-  }
-  return -1;
-}
-
-int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format,
-                    va_list ap_save)
-{
-  int retcode;
-  struct nsprintf info;
-
-  info.buffer = buffer;
-  info.length = 0;
-  info.max = maxlength;
-
-  retcode = dprintf_formatf(&info, addbyter, format, ap_save);
-  if(info.max) {
-    /* we terminate this with a zero byte */
-    if(info.max == info.length)
-      /* we're at maximum, scrap the last letter */
-      info.buffer[-1] = 0;
-    else
-      info.buffer[0] = 0;
-  }
-  return retcode;
-}
-
-int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...)
-{
-  int retcode;
-  va_list ap_save; /* argument pointer */
-  va_start(ap_save, format);
-  retcode = curl_mvsnprintf(buffer, maxlength, format, ap_save);
-  va_end(ap_save);
-  return retcode;
-}
-
-/* fputc() look-alike */
-static int alloc_addbyter(int output, FILE *data)
-{
-  struct asprintf *infop=(struct asprintf *)data;
-  unsigned char outc = (unsigned char)output;
-
-  if(!infop->buffer) {
-    infop->buffer = malloc(32);
-    if(!infop->buffer) {
-      infop->fail = 1;
-      return -1; /* fail */
-    }
-    infop->alloc = 32;
-    infop->len =0;
-  }
-  else if(infop->len+1 >= infop->alloc) {
-    char *newptr;
-
-    newptr = realloc(infop->buffer, infop->alloc*2);
-
-    if(!newptr) {
-      infop->fail = 1;
-      return -1; /* fail */
-    }
-    infop->buffer = newptr;
-    infop->alloc *= 2;
-  }
-
-  infop->buffer[ infop->len ] = outc;
-
-  infop->len++;
-
-  return outc; /* fputc() returns like this on success */
-}
-
-char *curl_maprintf(const char *format, ...)
-{
-  va_list ap_save; /* argument pointer */
-  int retcode;
-  struct asprintf info;
-
-  info.buffer = NULL;
-  info.len = 0;
-  info.alloc = 0;
-  info.fail = 0;
-
-  va_start(ap_save, format);
-  retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save);
-  va_end(ap_save);
-  if((-1 == retcode) || info.fail) {
-    if(info.alloc)
-      free(info.buffer);
-    return NULL;
-  }
-  if(info.alloc) {
-    info.buffer[info.len] = 0; /* we terminate this with a zero byte */
-    return info.buffer;
-  }
-  else
-    return strdup("");
-}
-
-char *curl_mvaprintf(const char *format, va_list ap_save)
-{
-  int retcode;
-  struct asprintf info;
-
-  info.buffer = NULL;
-  info.len = 0;
-  info.alloc = 0;
-  info.fail = 0;
-
-  retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save);
-  if((-1 == retcode) || info.fail) {
-    if(info.alloc)
-      free(info.buffer);
-    return NULL;
-  }
-
-  if(info.alloc) {
-    info.buffer[info.len] = 0; /* we terminate this with a zero byte */
-    return info.buffer;
-  }
-  else
-    return strdup("");
-}
-
-static int storebuffer(int output, FILE *data)
-{
-  char **buffer = (char **)data;
-  unsigned char outc = (unsigned char)output;
-  **buffer = outc;
-  (*buffer)++;
-  return outc; /* act like fputc() ! */
-}
-
-int curl_msprintf(char *buffer, const char *format, ...)
-{
-  va_list ap_save; /* argument pointer */
-  int retcode;
-  va_start(ap_save, format);
-  retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
-  va_end(ap_save);
-  *buffer=0; /* we terminate this with a zero byte */
-  return retcode;
-}
-
-int curl_mprintf(const char *format, ...)
-{
-  int retcode;
-  va_list ap_save; /* argument pointer */
-  va_start(ap_save, format);
-
-  retcode = dprintf_formatf(stdout, fputc, format, ap_save);
-  va_end(ap_save);
-  return retcode;
-}
-
-int curl_mfprintf(FILE *whereto, const char *format, ...)
-{
-  int retcode;
-  va_list ap_save; /* argument pointer */
-  va_start(ap_save, format);
-  retcode = dprintf_formatf(whereto, fputc, format, ap_save);
-  va_end(ap_save);
-  return retcode;
-}
-
-int curl_mvsprintf(char *buffer, const char *format, va_list ap_save)
-{
-  int retcode;
-  retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
-  *buffer=0; /* we terminate this with a zero byte */
-  return retcode;
-}
-
-int curl_mvprintf(const char *format, va_list ap_save)
-{
-  return dprintf_formatf(stdout, fputc, format, ap_save);
-}
-
-int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save)
-{
-  return dprintf_formatf(whereto, fputc, format, ap_save);
-}
diff --git a/lib/multi.c b/lib/multi.c
deleted file mode 100644 (file)
index 9553883..0000000
+++ /dev/null
@@ -1,2814 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#include <curl/curl.h>
-
-#include "curl_urldata.h"
-#include "curl_transfer.h"
-#include "curl_url.h"
-#include "curl_connect.h"
-#include "curl_progress.h"
-#include "curl_easyif.h"
-#include "curl_multiif.h"
-#include "curl_sendf.h"
-#include "curl_timeval.h"
-#include "curl_http.h"
-#include "curl_select.h"
-#include "curl_warnless.h"
-#include "curl_speedcheck.h"
-#include "curl_conncache.h"
-#include "curl_bundles.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/*
-  CURL_SOCKET_HASH_TABLE_SIZE should be a prime number. Increasing it from 97
-  to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes.  Still, every
-  CURL handle takes 45-50 K memory, therefore this 3K are not significant.
-*/
-#ifndef CURL_SOCKET_HASH_TABLE_SIZE
-#define CURL_SOCKET_HASH_TABLE_SIZE 911
-#endif
-
-struct Curl_message {
-  /* the 'CURLMsg' is the part that is visible to the external user */
-  struct CURLMsg extmsg;
-};
-
-/* NOTE: if you add a state here, add the name to the statename[] array as
-   well!
-*/
-typedef enum {
-  CURLM_STATE_INIT,        /* 0 - start in this state */
-  CURLM_STATE_CONNECT,     /* 1 - resolve/connect has been sent off */
-  CURLM_STATE_WAITRESOLVE, /* 2 - awaiting the resolve to finalize */
-  CURLM_STATE_WAITCONNECT, /* 3 - awaiting the connect to finalize */
-  CURLM_STATE_WAITPROXYCONNECT, /* 4 - awaiting proxy CONNECT to finalize */
-  CURLM_STATE_PROTOCONNECT, /* 5 - completing the protocol-specific connect
-                               phase */
-  CURLM_STATE_WAITDO,      /* 6 - wait for our turn to send the request */
-  CURLM_STATE_DO,          /* 7 - start send off the request (part 1) */
-  CURLM_STATE_DOING,       /* 8 - sending off the request (part 1) */
-  CURLM_STATE_DO_MORE,     /* 9 - send off the request (part 2) */
-  CURLM_STATE_DO_DONE,     /* 10 - done sending off request */
-  CURLM_STATE_WAITPERFORM, /* 11 - wait for our turn to read the response */
-  CURLM_STATE_PERFORM,     /* 12 - transfer data */
-  CURLM_STATE_TOOFAST,     /* 13 - wait because limit-rate exceeded */
-  CURLM_STATE_DONE,        /* 14 - post data transfer operation */
-  CURLM_STATE_COMPLETED,   /* 15 - operation complete */
-  CURLM_STATE_MSGSENT,     /* 16 - the operation complete message is sent */
-  CURLM_STATE_LAST         /* 17 - not a true state, never use this */
-} CURLMstate;
-
-/* we support N sockets per easy handle. Set the corresponding bit to what
-   action we should wait for */
-#define MAX_SOCKSPEREASYHANDLE 5
-#define GETSOCK_READABLE (0x00ff)
-#define GETSOCK_WRITABLE (0xff00)
-
-struct Curl_one_easy {
-  /* first, two fields for the linked list of these */
-  struct Curl_one_easy *next;
-  struct Curl_one_easy *prev;
-
-  struct SessionHandle *easy_handle; /* the easy handle for this unit */
-  struct connectdata *easy_conn;     /* the "unit's" connection */
-
-  CURLMstate state;  /* the handle's state */
-  CURLcode result;   /* previous result */
-
-  struct Curl_message msg; /* A single posted message. */
-
-  /* Array with the plain socket numbers this handle takes care of, in no
-     particular order. Note that all sockets are added to the sockhash, where
-     the state etc are also kept. This array is mostly used to detect when a
-     socket is to be removed from the hash. See singlesocket(). */
-  curl_socket_t sockets[MAX_SOCKSPEREASYHANDLE];
-  int numsocks;
-};
-
-#define CURL_MULTI_HANDLE 0x000bab1e
-
-#define GOOD_MULTI_HANDLE(x) \
-  ((x) && (((struct Curl_multi *)(x))->type == CURL_MULTI_HANDLE))
-#define GOOD_EASY_HANDLE(x) \
-  ((x) && (((struct SessionHandle *)(x))->magic == CURLEASY_MAGIC_NUMBER))
-
-/* This is the struct known as CURLM on the outside */
-struct Curl_multi {
-  /* First a simple identifier to easier detect if a user mix up
-     this multi handle with an easy handle. Set this to CURL_MULTI_HANDLE. */
-  long type;
-
-  /* We have a doubly-linked circular list with easy handles */
-  struct Curl_one_easy easy;
-
-  int num_easy; /* amount of entries in the linked list above. */
-  int num_alive; /* amount of easy handles that are added but have not yet
-                    reached COMPLETE state */
-
-  struct curl_llist *msglist; /* a list of messages from completed transfers */
-
-  /* callback function and user data pointer for the *socket() API */
-  curl_socket_callback socket_cb;
-  void *socket_userp;
-
-  /* Hostname cache */
-  struct curl_hash *hostcache;
-
-  /* timetree points to the splay-tree of time nodes to figure out expire
-     times of all currently set timers */
-  struct Curl_tree *timetree;
-
-  /* 'sockhash' is the lookup hash for socket descriptor => easy handles (note
-     the pluralis form, there can be more than one easy handle waiting on the
-     same actual socket) */
-  struct curl_hash *sockhash;
-
-  /* Whether pipelining is enabled for this multi handle */
-  bool pipelining_enabled;
-
-  /* Shared connection cache (bundles)*/
-  struct conncache *conn_cache;
-
-  /* This handle will be used for closing the cached connections in
-     curl_multi_cleanup() */
-  struct SessionHandle *closure_handle;
-
-  long maxconnects; /* if >0, a fixed limit of the maximum number of entries
-                       we're allowed to grow the connection cache to */
-
-  /* timer callback and user data pointer for the *socket() API */
-  curl_multi_timer_callback timer_cb;
-  void *timer_userp;
-  struct timeval timer_lastcall; /* the fixed time for the timeout for the
-                                    previous callback */
-};
-
-static void singlesocket(struct Curl_multi *multi,
-                         struct Curl_one_easy *easy);
-static int update_timer(struct Curl_multi *multi);
-
-static CURLcode addHandleToSendOrPendPipeline(struct SessionHandle *handle,
-                                              struct connectdata *conn);
-static int checkPendPipeline(struct connectdata *conn);
-static void moveHandleFromSendToRecvPipeline(struct SessionHandle *handle,
-                                             struct connectdata *conn);
-static void moveHandleFromRecvToDonePipeline(struct SessionHandle *handle,
-                                             struct connectdata *conn);
-static bool isHandleAtHead(struct SessionHandle *handle,
-                           struct curl_llist *pipeline);
-static CURLMcode add_next_timeout(struct timeval now,
-                                  struct Curl_multi *multi,
-                                  struct SessionHandle *d);
-
-#ifdef DEBUGBUILD
-static const char * const statename[]={
-  "INIT",
-  "CONNECT",
-  "WAITRESOLVE",
-  "WAITCONNECT",
-  "WAITPROXYCONNECT",
-  "PROTOCONNECT",
-  "WAITDO",
-  "DO",
-  "DOING",
-  "DO_MORE",
-  "DO_DONE",
-  "WAITPERFORM",
-  "PERFORM",
-  "TOOFAST",
-  "DONE",
-  "COMPLETED",
-  "MSGSENT",
-};
-#endif
-
-static void multi_freetimeout(void *a, void *b);
-
-/* always use this function to change state, to make debugging easier */
-static void multistate(struct Curl_one_easy *easy, CURLMstate state)
-{
-#ifdef DEBUGBUILD
-  long connection_id = -5000;
-#endif
-  CURLMstate oldstate = easy->state;
-
-  if(oldstate == state)
-    /* don't bother when the new state is the same as the old state */
-    return;
-
-  easy->state = state;
-
-#ifdef DEBUGBUILD
-  if(easy->easy_conn) {
-    if(easy->state > CURLM_STATE_CONNECT &&
-       easy->state < CURLM_STATE_COMPLETED)
-      connection_id = easy->easy_conn->connection_id;
-
-    infof(easy->easy_handle,
-          "STATE: %s => %s handle %p; (connection #%ld) \n",
-          statename[oldstate], statename[easy->state],
-          (char *)easy, connection_id);
-  }
-#endif
-  if(state == CURLM_STATE_COMPLETED)
-    /* changing to COMPLETED means there's one less easy handle 'alive' */
-    easy->easy_handle->multi->num_alive--;
-}
-
-/*
- * We add one of these structs to the sockhash for a particular socket
- */
-
-struct Curl_sh_entry {
-  struct SessionHandle *easy;
-  time_t timestamp;
-  int action;  /* what action READ/WRITE this socket waits for */
-  curl_socket_t socket; /* mainly to ease debugging */
-  void *socketp; /* settable by users with curl_multi_assign() */
-};
-/* bits for 'action' having no bits means this socket is not expecting any
-   action */
-#define SH_READ  1
-#define SH_WRITE 2
-
-/* make sure this socket is present in the hash for this handle */
-static struct Curl_sh_entry *sh_addentry(struct curl_hash *sh,
-                                         curl_socket_t s,
-                                         struct SessionHandle *data)
-{
-  struct Curl_sh_entry *there =
-    Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t));
-  struct Curl_sh_entry *check;
-
-  if(there)
-    /* it is present, return fine */
-    return there;
-
-  /* not present, add it */
-  check = calloc(1, sizeof(struct Curl_sh_entry));
-  if(!check)
-    return NULL; /* major failure */
-  check->easy = data;
-  check->socket = s;
-
-  /* make/add new hash entry */
-  if(NULL == Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) {
-    free(check);
-    return NULL; /* major failure */
-  }
-
-  return check; /* things are good in sockhash land */
-}
-
-
-/* delete the given socket + handle from the hash */
-static void sh_delentry(struct curl_hash *sh, curl_socket_t s)
-{
-  struct Curl_sh_entry *there =
-    Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t));
-
-  if(there) {
-    /* this socket is in the hash */
-    /* We remove the hash entry. (This'll end up in a call to
-       sh_freeentry().) */
-    Curl_hash_delete(sh, (char *)&s, sizeof(curl_socket_t));
-  }
-}
-
-/*
- * free a sockhash entry
- */
-static void sh_freeentry(void *freethis)
-{
-  struct Curl_sh_entry *p = (struct Curl_sh_entry *) freethis;
-
-  if(p)
-    free(p);
-}
-
-static size_t fd_key_compare(void*k1, size_t k1_len, void*k2, size_t k2_len)
-{
-  (void) k1_len; (void) k2_len;
-
-  return (*((int* ) k1)) == (*((int* ) k2));
-}
-
-static size_t hash_fd(void* key, size_t key_length, size_t slots_num)
-{
-  int fd = * ((int* ) key);
-  (void) key_length;
-
-  return (fd % (int)slots_num);
-}
-
-/*
- * sh_init() creates a new socket hash and returns the handle for it.
- *
- * Quote from README.multi_socket:
- *
- * "Some tests at 7000 and 9000 connections showed that the socket hash lookup
- * is somewhat of a bottle neck. Its current implementation may be a bit too
- * limiting. It simply has a fixed-size array, and on each entry in the array
- * it has a linked list with entries. So the hash only checks which list to
- * scan through. The code I had used so for used a list with merely 7 slots
- * (as that is what the DNS hash uses) but with 7000 connections that would
- * make an average of 1000 nodes in each list to run through. I upped that to
- * 97 slots (I believe a prime is suitable) and noticed a significant speed
- * increase.  I need to reconsider the hash implementation or use a rather
- * large default value like this. At 9000 connections I was still below 10us
- * per call."
- *
- */
-static struct curl_hash *sh_init(void)
-{
-  return Curl_hash_alloc(CURL_SOCKET_HASH_TABLE_SIZE, hash_fd, fd_key_compare,
-                         sh_freeentry);
-}
-
-/*
- * multi_addmsg()
- *
- * Called when a transfer is completed. Adds the given msg pointer to
- * the list kept in the multi handle.
- */
-static CURLMcode multi_addmsg(struct Curl_multi *multi,
-                              struct Curl_message *msg)
-{
-  if(!Curl_llist_insert_next(multi->msglist, multi->msglist->tail, msg))
-    return CURLM_OUT_OF_MEMORY;
-
-  return CURLM_OK;
-}
-
-/*
- * multi_freeamsg()
- *
- * Callback used by the llist system when a single list entry is destroyed.
- */
-static void multi_freeamsg(void *a, void *b)
-{
-  (void)a;
-  (void)b;
-}
-
-CURLM *curl_multi_init(void)
-{
-  struct Curl_multi *multi = calloc(1, sizeof(struct Curl_multi));
-
-  if(!multi)
-    return NULL;
-
-  multi->type = CURL_MULTI_HANDLE;
-
-  multi->hostcache = Curl_mk_dnscache();
-  if(!multi->hostcache)
-    goto error;
-
-  multi->sockhash = sh_init();
-  if(!multi->sockhash)
-    goto error;
-
-  multi->conn_cache = Curl_conncache_init(CONNCACHE_MULTI);
-  if(!multi->conn_cache)
-    goto error;
-
-  multi->msglist = Curl_llist_alloc(multi_freeamsg);
-  if(!multi->msglist)
-    goto error;
-
-  /* Let's make the doubly-linked list a circular list.  This makes
-     the linked list code simpler and allows inserting at the end
-     with less work (we didn't keep a tail pointer before). */
-  multi->easy.next = &multi->easy;
-  multi->easy.prev = &multi->easy;
-
-  return (CURLM *) multi;
-
-  error:
-
-  Curl_hash_destroy(multi->sockhash);
-  multi->sockhash = NULL;
-  Curl_hash_destroy(multi->hostcache);
-  multi->hostcache = NULL;
-  Curl_conncache_destroy(multi->conn_cache);
-  multi->conn_cache = NULL;
-
-  free(multi);
-  return NULL;
-}
-
-CURLMcode curl_multi_add_handle(CURLM *multi_handle,
-                                CURL *easy_handle)
-{
-  struct curl_llist *timeoutlist;
-  struct Curl_one_easy *easy;
-  struct Curl_multi *multi = (struct Curl_multi *)multi_handle;
-  struct SessionHandle *data = (struct SessionHandle *)easy_handle;
-  struct SessionHandle *new_closure = NULL;
-  struct curl_hash *hostcache = NULL;
-
-  /* First, make some basic checks that the CURLM handle is a good handle */
-  if(!GOOD_MULTI_HANDLE(multi))
-    return CURLM_BAD_HANDLE;
-
-  /* Verify that we got a somewhat good easy handle too */
-  if(!GOOD_EASY_HANDLE(easy_handle))
-    return CURLM_BAD_EASY_HANDLE;
-
-  /* Prevent users from adding same easy handle more than
-     once and prevent adding to more than one multi stack */
-  if(data->multi)
-    /* possibly we should create a new unique error code for this condition */
-    return CURLM_BAD_EASY_HANDLE;
-
-  /* Allocate and initialize timeout list for easy handle */
-  timeoutlist = Curl_llist_alloc(multi_freetimeout);
-  if(!timeoutlist)
-    return CURLM_OUT_OF_MEMORY;
-
-  /* Allocate new node for the doubly-linked circular list of
-     Curl_one_easy structs that holds pointers to easy handles */
-  easy = calloc(1, sizeof(struct Curl_one_easy));
-  if(!easy) {
-    Curl_llist_destroy(timeoutlist, NULL);
-    return CURLM_OUT_OF_MEMORY;
-  }
-
-  /* In case multi handle has no hostcache yet, allocate one */
-  if(!multi->hostcache) {
-    hostcache = Curl_mk_dnscache();
-    if(!hostcache) {
-      free(easy);
-      Curl_llist_destroy(timeoutlist, NULL);
-      return CURLM_OUT_OF_MEMORY;
-    }
-  }
-
-  /* In case multi handle has no closure_handle yet, allocate
-     a new easy handle to use when closing cached connections */
-  if(!multi->closure_handle) {
-    new_closure = (struct SessionHandle *)curl_easy_init();
-    if(!new_closure) {
-      Curl_hash_destroy(hostcache);
-      free(easy);
-      Curl_llist_destroy(timeoutlist, NULL);
-      return CURLM_OUT_OF_MEMORY;
-    }
-  }
-
-  /*
-  ** No failure allowed in this function beyond this point. And
-  ** no modification of easy nor multi handle allowed before this
-  ** except for potential multi's connection cache growing which
-  ** won't be undone in this function no matter what.
-  */
-
-  /* In case a new closure handle has been initialized above, it
-     is associated now with the multi handle which lacked one. */
-  if(new_closure) {
-    multi->closure_handle = new_closure;
-    Curl_easy_addmulti(multi->closure_handle, multi_handle);
-    multi->closure_handle->state.conn_cache = multi->conn_cache;
-  }
-
-  /* In case hostcache has been allocated above,
-     it is associated now with the multi handle. */
-  if(hostcache)
-    multi->hostcache = hostcache;
-
-  /* Make easy handle use timeout list initialized above */
-  data->state.timeoutlist = timeoutlist;
-  timeoutlist = NULL;
-
-  /* set the easy handle */
-  easy->easy_handle = data;
-  multistate(easy, CURLM_STATE_INIT);
-
-  /* set the back pointer to one_easy to assist in removal */
-  easy->easy_handle->multi_pos =  easy;
-
-  /* for multi interface connections, we share DNS cache automatically if the
-     easy handle's one is currently private. */
-  if(easy->easy_handle->dns.hostcache &&
-     (easy->easy_handle->dns.hostcachetype == HCACHE_PRIVATE)) {
-    Curl_hash_destroy(easy->easy_handle->dns.hostcache);
-    easy->easy_handle->dns.hostcache = NULL;
-    easy->easy_handle->dns.hostcachetype = HCACHE_NONE;
-  }
-
-  if(!easy->easy_handle->dns.hostcache ||
-     (easy->easy_handle->dns.hostcachetype == HCACHE_NONE)) {
-    easy->easy_handle->dns.hostcache = multi->hostcache;
-    easy->easy_handle->dns.hostcachetype = HCACHE_MULTI;
-  }
-
-  /* On a multi stack the connection cache, owned by the multi handle,
-     is shared between all easy handles within the multi handle.
-     Therefore we free the private connection cache if there is one */
-  if(easy->easy_handle->state.conn_cache &&
-     easy->easy_handle->state.conn_cache->type == CONNCACHE_PRIVATE) {
-    Curl_conncache_destroy(easy->easy_handle->state.conn_cache);
-  }
-
-  /* Point now to this multi's connection cache */
-  easy->easy_handle->state.conn_cache = multi->conn_cache;
-
-  /* This adds the new entry at the 'end' of the doubly-linked circular
-     list of Curl_one_easy structs to try and maintain a FIFO queue so
-     the pipelined requests are in order. */
-
-  /* We add this new entry last in the list. We make our 'next' point to the
-     'first' struct and our 'prev' point to the previous 'prev' */
-  easy->next = &multi->easy;
-  easy->prev = multi->easy.prev;
-
-  /* make 'easy' the last node in the chain */
-  multi->easy.prev = easy;
-
-  /* if there was a prev node, make sure its 'next' pointer links to
-     the new node */
-  easy->prev->next = easy;
-
-  /* make the SessionHandle refer back to this multi handle */
-  Curl_easy_addmulti(easy_handle, multi_handle);
-
-  /* make the SessionHandle struct refer back to this struct */
-  easy->easy_handle->set.one_easy = easy;
-
-  /* Set the timeout for this handle to expire really soon so that it will
-     be taken care of even when this handle is added in the midst of operation
-     when only the curl_multi_socket() API is used. During that flow, only
-     sockets that time-out or have actions will be dealt with. Since this
-     handle has no action yet, we make sure it times out to get things to
-     happen. */
-  Curl_expire(easy->easy_handle, 1);
-
-  /* increase the node-counter */
-  multi->num_easy++;
-
-  /* increase the alive-counter */
-  multi->num_alive++;
-
-  /* A somewhat crude work-around for a little glitch in update_timer() that
-     happens if the lastcall time is set to the same time when the handle is
-     removed as when the next handle is added, as then the check in
-     update_timer() that prevents calling the application multiple times with
-     the same timer infor will not trigger and then the new handle's timeout
-     will not be notified to the app.
-
-     The work-around is thus simply to clear the 'lastcall' variable to force
-     update_timer() to always trigger a callback to the app when a new easy
-     handle is added */
-  memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall));
-
-  update_timer(multi);
-  return CURLM_OK;
-}
-
-#if 0
-/* Debug-function, used like this:
- *
- * Curl_hash_print(multi->sockhash, debug_print_sock_hash);
- *
- * Enable the hash print function first by editing curl_hash.c
- */
-static void debug_print_sock_hash(void *p)
-{
-  struct Curl_sh_entry *sh = (struct Curl_sh_entry *)p;
-
-  fprintf(stderr, " [easy %p/magic %x/socket %d]",
-          (void *)sh->easy, sh->easy->magic, (int)sh->socket);
-}
-#endif
-
-CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
-                                   CURL *curl_handle)
-{
-  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
-  struct Curl_one_easy *easy;
-  struct SessionHandle *data = curl_handle;
-
-  /* First, make some basic checks that the CURLM handle is a good handle */
-  if(!GOOD_MULTI_HANDLE(multi))
-    return CURLM_BAD_HANDLE;
-
-  /* Verify that we got a somewhat good easy handle too */
-  if(!GOOD_EASY_HANDLE(curl_handle))
-    return CURLM_BAD_EASY_HANDLE;
-
-  /* pick-up from the 'curl_handle' the kept position in the list */
-  easy = data->multi_pos;
-
-  if(easy) {
-    bool premature = (easy->state < CURLM_STATE_COMPLETED) ? TRUE : FALSE;
-    bool easy_owns_conn = (easy->easy_conn &&
-                           (easy->easy_conn->data == easy->easy_handle)) ?
-                           TRUE : FALSE;
-
-    /* If the 'state' is not INIT or COMPLETED, we might need to do something
-       nice to put the easy_handle in a good known state when this returns. */
-    if(premature)
-      /* this handle is "alive" so we need to count down the total number of
-         alive connections when this is removed */
-      multi->num_alive--;
-
-    if(easy->easy_conn &&
-       (easy->easy_conn->send_pipe->size +
-        easy->easy_conn->recv_pipe->size > 1) &&
-       easy->state > CURLM_STATE_WAITDO &&
-       easy->state < CURLM_STATE_COMPLETED) {
-      /* If the handle is in a pipeline and has started sending off its
-         request but not received its response yet, we need to close
-         connection. */
-      easy->easy_conn->bits.close = TRUE;
-      /* Set connection owner so that Curl_done() closes it.
-         We can sefely do this here since connection is killed. */
-      easy->easy_conn->data = easy->easy_handle;
-    }
-
-    /* The timer must be shut down before easy->multi is set to NULL,
-       else the timenode will remain in the splay tree after
-       curl_easy_cleanup is called. */
-    Curl_expire(easy->easy_handle, 0);
-
-    /* destroy the timeout list that is held in the easy handle */
-    if(data->state.timeoutlist) {
-      Curl_llist_destroy(data->state.timeoutlist, NULL);
-      data->state.timeoutlist = NULL;
-    }
-
-    if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) {
-      if(multi->num_easy == 1) {
-        if(easy_owns_conn) {
-          Curl_resolver_cancel(easy->easy_conn);
-          if(easy->easy_conn->dns_entry) {
-            Curl_resolv_unlock(easy->easy_handle, easy->easy_conn->dns_entry);
-            easy->easy_conn->dns_entry = NULL;
-          }
-        }
-        Curl_hostcache_destroy(easy->easy_handle);
-        multi->hostcache = NULL;
-      }
-      /* clear out the usage of the shared DNS cache */
-      easy->easy_handle->dns.hostcache = NULL;
-      easy->easy_handle->dns.hostcachetype = HCACHE_NONE;
-    }
-
-    if(easy->easy_conn) {
-
-      /* we must call Curl_done() here (if we still "own it") so that we don't
-         leave a half-baked one around */
-      if(easy_owns_conn) {
-
-        /* Curl_done() clears the conn->data field to lose the association
-           between the easy handle and the connection
-
-           Note that this ignores the return code simply because there's
-           nothing really useful to do with it anyway! */
-        (void)Curl_done(&easy->easy_conn, easy->result, premature);
-      }
-      else
-        /* Clear connection pipelines, if Curl_done above was not called */
-        Curl_getoff_all_pipelines(easy->easy_handle, easy->easy_conn);
-    }
-
-    if(easy->easy_handle->state.conn_cache->type == CONNCACHE_MULTI) {
-      /* if this was using the shared connection cache we clear the pointer
-         to that since we're not part of that handle anymore */
-      easy->easy_handle->state.conn_cache = NULL;
-      easy->easy_handle->state.lastconnect = NULL;
-    }
-
-    /* change state without using multistate(), only to make singlesocket() do
-       what we want */
-    easy->state = CURLM_STATE_COMPLETED;
-    singlesocket(multi, easy); /* to let the application know what sockets
-                                  that vanish with this handle */
-
-    /* Remove the association between the connection and the handle */
-    if(easy->easy_conn) {
-      easy->easy_conn->data = NULL;
-      easy->easy_conn = NULL;
-    }
-
-    Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association
-                                                    to this multi handle */
-
-    {
-      /* make sure there's no pending message in the queue sent from this easy
-         handle */
-      struct curl_llist_element *e;
-
-      for(e = multi->msglist->head; e; e = e->next) {
-        struct Curl_message *msg = e->ptr;
-
-        if(msg->extmsg.easy_handle == easy->easy_handle) {
-          Curl_llist_remove(multi->msglist, e, NULL);
-          /* there can only be one from this specific handle */
-          break;
-        }
-      }
-    }
-
-    /* make the previous node point to our next */
-    if(easy->prev)
-      easy->prev->next = easy->next;
-    /* make our next point to our previous node */
-    if(easy->next)
-      easy->next->prev = easy->prev;
-
-    easy->easy_handle->set.one_easy = NULL; /* detached */
-
-    /* Null the position in the controlling structure */
-    easy->easy_handle->multi_pos = NULL;
-
-    /* NOTE NOTE NOTE
-       We do not touch the easy handle here! */
-    free(easy);
-
-    multi->num_easy--; /* one less to care about now */
-
-    update_timer(multi);
-    return CURLM_OK;
-  }
-  else
-    return CURLM_BAD_EASY_HANDLE; /* twasn't found */
-}
-
-bool Curl_multi_canPipeline(const struct Curl_multi* multi)
-{
-  return multi->pipelining_enabled;
-}
-
-void Curl_multi_handlePipeBreak(struct SessionHandle *data)
-{
-  struct Curl_one_easy *one_easy = data->set.one_easy;
-
-  if(one_easy)
-    one_easy->easy_conn = NULL;
-}
-
-static int waitconnect_getsock(struct connectdata *conn,
-                               curl_socket_t *sock,
-                               int numsocks)
-{
-  if(!numsocks)
-    return GETSOCK_BLANK;
-
-  sock[0] = conn->sock[FIRSTSOCKET];
-
-  /* when we've sent a CONNECT to a proxy, we should rather wait for the
-     socket to become readable to be able to get the response headers */
-  if(conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
-    return GETSOCK_READSOCK(0);
-
-  return GETSOCK_WRITESOCK(0);
-}
-
-static int domore_getsock(struct connectdata *conn,
-                          curl_socket_t *socks,
-                          int numsocks)
-{
-  if(conn && conn->handler->domore_getsock)
-    return conn->handler->domore_getsock(conn, socks, numsocks);
-  return GETSOCK_BLANK;
-}
-
-/* returns bitmapped flags for this handle and its sockets */
-static int multi_getsock(struct Curl_one_easy *easy,
-                         curl_socket_t *socks, /* points to numsocks number
-                                                  of sockets */
-                         int numsocks)
-{
-  /* If the pipe broke, or if there's no connection left for this easy handle,
-     then we MUST bail out now with no bitmask set. The no connection case can
-     happen when this is called from curl_multi_remove_handle() =>
-     singlesocket() => multi_getsock().
-  */
-  if(easy->easy_handle->state.pipe_broke || !easy->easy_conn)
-    return 0;
-
-  if(easy->state > CURLM_STATE_CONNECT &&
-     easy->state < CURLM_STATE_COMPLETED) {
-    /* Set up ownership correctly */
-    easy->easy_conn->data = easy->easy_handle;
-  }
-
-  switch(easy->state) {
-  default:
-#if 0 /* switch back on these cases to get the compiler to check for all enums
-         to be present */
-  case CURLM_STATE_TOOFAST:  /* returns 0, so will not select. */
-  case CURLM_STATE_COMPLETED:
-  case CURLM_STATE_MSGSENT:
-  case CURLM_STATE_INIT:
-  case CURLM_STATE_CONNECT:
-  case CURLM_STATE_WAITDO:
-  case CURLM_STATE_DONE:
-  case CURLM_STATE_LAST:
-    /* this will get called with CURLM_STATE_COMPLETED when a handle is
-       removed */
-#endif
-    return 0;
-
-  case CURLM_STATE_WAITRESOLVE:
-    return Curl_resolver_getsock(easy->easy_conn, socks, numsocks);
-
-  case CURLM_STATE_PROTOCONNECT:
-    return Curl_protocol_getsock(easy->easy_conn, socks, numsocks);
-
-  case CURLM_STATE_DO:
-  case CURLM_STATE_DOING:
-    return Curl_doing_getsock(easy->easy_conn, socks, numsocks);
-
-  case CURLM_STATE_WAITPROXYCONNECT:
-  case CURLM_STATE_WAITCONNECT:
-    return waitconnect_getsock(easy->easy_conn, socks, numsocks);
-
-  case CURLM_STATE_DO_MORE:
-    return domore_getsock(easy->easy_conn, socks, numsocks);
-
-  case CURLM_STATE_DO_DONE: /* since is set after DO is completed, we switch
-                               to waiting for the same as the *PERFORM
-                               states */
-  case CURLM_STATE_PERFORM:
-  case CURLM_STATE_WAITPERFORM:
-    return Curl_single_getsock(easy->easy_conn, socks, numsocks);
-  }
-
-}
-
-CURLMcode curl_multi_fdset(CURLM *multi_handle,
-                           fd_set *read_fd_set, fd_set *write_fd_set,
-                           fd_set *exc_fd_set, int *max_fd)
-{
-  /* Scan through all the easy handles to get the file descriptors set.
-     Some easy handles may not have connected to the remote host yet,
-     and then we must make sure that is done. */
-  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
-  struct Curl_one_easy *easy;
-  int this_max_fd=-1;
-  curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE];
-  int bitmap;
-  int i;
-  (void)exc_fd_set; /* not used */
-
-  if(!GOOD_MULTI_HANDLE(multi))
-    return CURLM_BAD_HANDLE;
-
-  easy=multi->easy.next;
-  while(easy != &multi->easy) {
-    bitmap = multi_getsock(easy, sockbunch, MAX_SOCKSPEREASYHANDLE);
-
-    for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) {
-      curl_socket_t s = CURL_SOCKET_BAD;
-
-      if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK((sockbunch[i]))) {
-        FD_SET(sockbunch[i], read_fd_set);
-        s = sockbunch[i];
-      }
-      if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK((sockbunch[i]))) {
-        FD_SET(sockbunch[i], write_fd_set);
-        s = sockbunch[i];
-      }
-      if(s == CURL_SOCKET_BAD)
-        /* this socket is unused, break out of loop */
-        break;
-      else {
-        if((int)s > this_max_fd)
-          this_max_fd = (int)s;
-      }
-    }
-
-    easy = easy->next; /* check next handle */
-  }
-
-  *max_fd = this_max_fd;
-
-  return CURLM_OK;
-}
-
-CURLMcode curl_multi_wait(CURLM *multi_handle,
-                          struct curl_waitfd extra_fds[],
-                          unsigned int extra_nfds,
-                          int timeout_ms,
-                          int *ret)
-{
-  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
-  struct Curl_one_easy *easy;
-  curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE];
-  int bitmap;
-  unsigned int i;
-  unsigned int nfds = extra_nfds;
-  struct pollfd *ufds = NULL;
-
-  if(!GOOD_MULTI_HANDLE(multi))
-    return CURLM_BAD_HANDLE;
-
-  /* Count up how many fds we have from the multi handle */
-  easy=multi->easy.next;
-  while(easy != &multi->easy) {
-    bitmap = multi_getsock(easy, sockbunch, MAX_SOCKSPEREASYHANDLE);
-
-    for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) {
-      curl_socket_t s = CURL_SOCKET_BAD;
-
-      if(bitmap & GETSOCK_READSOCK(i)) {
-        ++nfds;
-        s = sockbunch[i];
-      }
-      if(bitmap & GETSOCK_WRITESOCK(i)) {
-        ++nfds;
-        s = sockbunch[i];
-      }
-      if(s == CURL_SOCKET_BAD) {
-        break;
-      }
-    }
-
-    easy = easy->next; /* check next handle */
-  }
-
-  if(nfds) {
-    ufds = malloc(nfds * sizeof(struct pollfd));
-    if(!ufds)
-      return CURLM_OUT_OF_MEMORY;
-  }
-  nfds = 0;
-
-  /* Add the curl handles to our pollfds first */
-  easy=multi->easy.next;
-  while(easy != &multi->easy) {
-    bitmap = multi_getsock(easy, sockbunch, MAX_SOCKSPEREASYHANDLE);
-
-    for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) {
-      curl_socket_t s = CURL_SOCKET_BAD;
-
-      if(bitmap & GETSOCK_READSOCK(i)) {
-        ufds[nfds].fd = sockbunch[i];
-        ufds[nfds].events = POLLIN;
-        ++nfds;
-        s = sockbunch[i];
-      }
-      if(bitmap & GETSOCK_WRITESOCK(i)) {
-        ufds[nfds].fd = sockbunch[i];
-        ufds[nfds].events = POLLOUT;
-        ++nfds;
-        s = sockbunch[i];
-      }
-      if(s == CURL_SOCKET_BAD) {
-        break;
-      }
-    }
-
-    easy = easy->next; /* check next handle */
-  }
-
-  /* Add external file descriptions from poll-like struct curl_waitfd */
-  for(i = 0; i < extra_nfds; i++) {
-    ufds[nfds].fd = extra_fds[i].fd;
-    ufds[nfds].events = 0;
-    if(extra_fds[i].events & CURL_WAIT_POLLIN)
-      ufds[nfds].events |= POLLIN;
-    if(extra_fds[i].events & CURL_WAIT_POLLPRI)
-      ufds[nfds].events |= POLLPRI;
-    if(extra_fds[i].events & CURL_WAIT_POLLOUT)
-      ufds[nfds].events |= POLLOUT;
-    ++nfds;
-  }
-
-  if(nfds)
-    /* wait... */
-    i = Curl_poll(ufds, nfds, timeout_ms);
-  else
-    i = 0;
-
-  Curl_safefree(ufds);
-  if(ret)
-    *ret = i;
-  return CURLM_OK;
-}
-
-static CURLMcode multi_runsingle(struct Curl_multi *multi,
-                                 struct timeval now,
-                                 struct Curl_one_easy *easy)
-{
-  struct Curl_message *msg = NULL;
-  bool connected;
-  bool async;
-  bool protocol_connect = FALSE;
-  bool dophase_done;
-  bool done = FALSE;
-  CURLMcode result = CURLM_OK;
-  struct SingleRequest *k;
-  struct SessionHandle *data;
-  long timeout_ms;
-
-  if(!GOOD_EASY_HANDLE(easy->easy_handle))
-    return CURLM_BAD_EASY_HANDLE;
-
-  data = easy->easy_handle;
-
-  do {
-    /* this is a single-iteration do-while loop just to allow a
-       break to skip to the end of it */
-    bool disconnect_conn = FALSE;
-
-    /* Handle the case when the pipe breaks, i.e., the connection
-       we're using gets cleaned up and we're left with nothing. */
-    if(data->state.pipe_broke) {
-      infof(data, "Pipe broke: handle 0x%p, url = %s\n",
-            easy, data->state.path);
-
-      if(easy->state < CURLM_STATE_COMPLETED) {
-        /* Head back to the CONNECT state */
-        multistate(easy, CURLM_STATE_CONNECT);
-        result = CURLM_CALL_MULTI_PERFORM;
-        easy->result = CURLE_OK;
-      }
-
-      data->state.pipe_broke = FALSE;
-      easy->easy_conn = NULL;
-      break;
-    }
-
-    if(!easy->easy_conn &&
-       easy->state > CURLM_STATE_CONNECT &&
-       easy->state < CURLM_STATE_DONE) {
-      /* In all these states, the code will blindly access 'easy->easy_conn'
-         so this is precaution that it isn't NULL. And it silences static
-         analyzers. */
-      failf(data, "In state %d with no easy_conn, bail out!\n", easy->state);
-      return CURLM_INTERNAL_ERROR;
-    }
-
-    if(easy->easy_conn && easy->state > CURLM_STATE_CONNECT &&
-       easy->state < CURLM_STATE_COMPLETED)
-      /* Make sure we set the connection's current owner */
-      easy->easy_conn->data = data;
-
-    if(easy->easy_conn &&
-       (easy->state >= CURLM_STATE_CONNECT) &&
-       (easy->state < CURLM_STATE_COMPLETED)) {
-      /* we need to wait for the connect state as only then is the start time
-         stored, but we must not check already completed handles */
-
-      timeout_ms = Curl_timeleft(data, &now,
-                                 (easy->state <= CURLM_STATE_WAITDO)?
-                                 TRUE:FALSE);
-
-      if(timeout_ms < 0) {
-        /* Handle timed out */
-        if(easy->state == CURLM_STATE_WAITRESOLVE)
-          failf(data, "Resolving timed out after %ld milliseconds",
-                Curl_tvdiff(now, data->progress.t_startsingle));
-        else if(easy->state == CURLM_STATE_WAITCONNECT)
-          failf(data, "Connection timed out after %ld milliseconds",
-                Curl_tvdiff(now, data->progress.t_startsingle));
-        else {
-          k = &data->req;
-          failf(data, "Operation timed out after %ld milliseconds with %"
-                FORMAT_OFF_T " out of %" FORMAT_OFF_T " bytes received",
-                Curl_tvdiff(now, data->progress.t_startsingle), k->bytecount,
-                k->size);
-        }
-
-        /* Force the connection closed because the server could continue to
-           send us stuff at any time. (The disconnect_conn logic used below
-           doesn't work at this point). */
-        easy->easy_conn->bits.close = TRUE;
-        easy->result = CURLE_OPERATION_TIMEDOUT;
-        multistate(easy, CURLM_STATE_COMPLETED);
-        break;
-      }
-    }
-
-    switch(easy->state) {
-    case CURLM_STATE_INIT:
-      /* init this transfer. */
-      easy->result=Curl_pretransfer(data);
-
-      if(CURLE_OK == easy->result) {
-        /* after init, go CONNECT */
-        multistate(easy, CURLM_STATE_CONNECT);
-        result = CURLM_CALL_MULTI_PERFORM;
-
-        data->state.used_interface = Curl_if_multi;
-      }
-      break;
-
-    case CURLM_STATE_CONNECT:
-      /* Connect. We get a connection identifier filled in. */
-      Curl_pgrsTime(data, TIMER_STARTSINGLE);
-      easy->result = Curl_connect(data, &easy->easy_conn,
-                                  &async, &protocol_connect);
-
-      if(CURLE_OK == easy->result) {
-        /* Add this handle to the send or pend pipeline */
-        easy->result = addHandleToSendOrPendPipeline(data,
-                                                     easy->easy_conn);
-        if(CURLE_OK != easy->result)
-          disconnect_conn = TRUE;
-        else {
-          if(async)
-            /* We're now waiting for an asynchronous name lookup */
-            multistate(easy, CURLM_STATE_WAITRESOLVE);
-          else {
-            /* after the connect has been sent off, go WAITCONNECT unless the
-               protocol connect is already done and we can go directly to
-               WAITDO or DO! */
-            result = CURLM_CALL_MULTI_PERFORM;
-
-            if(protocol_connect)
-              multistate(easy, multi->pipelining_enabled?
-                         CURLM_STATE_WAITDO:CURLM_STATE_DO);
-            else {
-#ifndef CURL_DISABLE_HTTP
-              if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
-                multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
-              else
-#endif
-                multistate(easy, CURLM_STATE_WAITCONNECT);
-            }
-          }
-        }
-      }
-      break;
-
-    case CURLM_STATE_WAITRESOLVE:
-      /* awaiting an asynch name resolve to complete */
-    {
-      struct Curl_dns_entry *dns = NULL;
-
-      /* check if we have the name resolved by now */
-      easy->result = Curl_resolver_is_resolved(easy->easy_conn, &dns);
-
-      /* Update sockets here, because the socket(s) may have been
-         closed and the application thus needs to be told, even if it
-         is likely that the same socket(s) will again be used further
-         down.  If the name has not yet been resolved, it is likely
-         that new sockets have been opened in an attempt to contact
-         another resolver. */
-      singlesocket(multi, easy);
-
-      if(dns) {
-        /* Perform the next step in the connection phase, and then move on
-           to the WAITCONNECT state */
-        easy->result = Curl_async_resolved(easy->easy_conn,
-                                           &protocol_connect);
-
-        if(CURLE_OK != easy->result)
-          /* if Curl_async_resolved() returns failure, the connection struct
-             is already freed and gone */
-          easy->easy_conn = NULL;           /* no more connection */
-        else {
-          /* call again please so that we get the next socket setup */
-          result = CURLM_CALL_MULTI_PERFORM;
-          if(protocol_connect)
-            multistate(easy, multi->pipelining_enabled?
-                       CURLM_STATE_WAITDO:CURLM_STATE_DO);
-          else {
-#ifndef CURL_DISABLE_HTTP
-            if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
-              multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
-            else
-#endif
-              multistate(easy, CURLM_STATE_WAITCONNECT);
-          }
-        }
-      }
-
-      if(CURLE_OK != easy->result) {
-        /* failure detected */
-        disconnect_conn = TRUE;
-        break;
-      }
-    }
-    break;
-
-#ifndef CURL_DISABLE_HTTP
-    case CURLM_STATE_WAITPROXYCONNECT:
-      /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */
-      easy->result = Curl_http_connect(easy->easy_conn, &protocol_connect);
-
-      if(easy->easy_conn->bits.proxy_connect_closed) {
-        /* reset the error buffer */
-        if(data->set.errorbuffer)
-          data->set.errorbuffer[0] = '\0';
-        data->state.errorbuf = FALSE;
-
-        easy->result = CURLE_OK;
-        result = CURLM_CALL_MULTI_PERFORM;
-        multistate(easy, CURLM_STATE_CONNECT);
-      }
-      else if(CURLE_OK == easy->result) {
-        if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_COMPLETE)
-          multistate(easy, CURLM_STATE_WAITCONNECT);
-      }
-      break;
-#endif
-
-    case CURLM_STATE_WAITCONNECT:
-      /* awaiting a completion of an asynch connect */
-      easy->result = Curl_is_connected(easy->easy_conn,
-                                       FIRSTSOCKET,
-                                       &connected);
-      if(connected) {
-
-        if(!easy->result)
-          /* if everything is still fine we do the protocol-specific connect
-             setup */
-          easy->result = Curl_protocol_connect(easy->easy_conn,
-                                               &protocol_connect);
-      }
-
-      if(CURLE_OK != easy->result) {
-        /* failure detected */
-        /* Just break, the cleaning up is handled all in one place */
-        disconnect_conn = TRUE;
-        break;
-      }
-
-      if(connected) {
-        if(!protocol_connect) {
-          /* We have a TCP connection, but 'protocol_connect' may be false
-             and then we continue to 'STATE_PROTOCONNECT'. If protocol
-             connect is TRUE, we move on to STATE_DO.
-             BUT if we are using a proxy we must change to WAITPROXYCONNECT
-          */
-#ifndef CURL_DISABLE_HTTP
-          if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
-            multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
-          else
-#endif
-            multistate(easy, CURLM_STATE_PROTOCONNECT);
-
-        }
-        else
-          /* after the connect has completed, go WAITDO or DO */
-          multistate(easy, multi->pipelining_enabled?
-                     CURLM_STATE_WAITDO:CURLM_STATE_DO);
-
-        result = CURLM_CALL_MULTI_PERFORM;
-      }
-      break;
-
-    case CURLM_STATE_PROTOCONNECT:
-      /* protocol-specific connect phase */
-      easy->result = Curl_protocol_connecting(easy->easy_conn,
-                                              &protocol_connect);
-      if((easy->result == CURLE_OK) && protocol_connect) {
-        /* after the connect has completed, go WAITDO or DO */
-        multistate(easy, multi->pipelining_enabled?
-                   CURLM_STATE_WAITDO:CURLM_STATE_DO);
-        result = CURLM_CALL_MULTI_PERFORM;
-      }
-      else if(easy->result) {
-        /* failure detected */
-        Curl_posttransfer(data);
-        Curl_done(&easy->easy_conn, easy->result, TRUE);
-        disconnect_conn = TRUE;
-      }
-      break;
-
-    case CURLM_STATE_WAITDO:
-      /* Wait for our turn to DO when we're pipelining requests */
-#ifdef DEBUGBUILD
-      infof(data, "WAITDO: Conn %ld send pipe %zu inuse %d athead %d\n",
-            easy->easy_conn->connection_id,
-            easy->easy_conn->send_pipe->size,
-            easy->easy_conn->writechannel_inuse?1:0,
-            isHandleAtHead(data,
-                           easy->easy_conn->send_pipe)?1:0);
-#endif
-      if(!easy->easy_conn->writechannel_inuse &&
-         isHandleAtHead(data,
-                        easy->easy_conn->send_pipe)) {
-        /* Grab the channel */
-        easy->easy_conn->writechannel_inuse = TRUE;
-        multistate(easy, CURLM_STATE_DO);
-        result = CURLM_CALL_MULTI_PERFORM;
-      }
-      break;
-
-    case CURLM_STATE_DO:
-      if(data->set.connect_only) {
-        /* keep connection open for application to use the socket */
-        easy->easy_conn->bits.close = FALSE;
-        multistate(easy, CURLM_STATE_DONE);
-        easy->result = CURLE_OK;
-        result = CURLM_CALL_MULTI_PERFORM;
-      }
-      else {
-        /* Perform the protocol's DO action */
-        easy->result = Curl_do(&easy->easy_conn,
-                               &dophase_done);
-
-        if(CURLE_OK == easy->result) {
-          if(!dophase_done) {
-            /* some steps needed for wildcard matching */
-            if(data->set.wildcardmatch) {
-              struct WildcardData *wc = &data->wildcard;
-              if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) {
-                /* skip some states if it is important */
-                Curl_done(&easy->easy_conn, CURLE_OK, FALSE);
-                multistate(easy, CURLM_STATE_DONE);
-                result = CURLM_CALL_MULTI_PERFORM;
-                break;
-              }
-            }
-            /* DO was not completed in one function call, we must continue
-               DOING... */
-            multistate(easy, CURLM_STATE_DOING);
-            result = CURLM_OK;
-          }
-
-          /* after DO, go DO_DONE... or DO_MORE */
-          else if(easy->easy_conn->bits.do_more) {
-            /* we're supposed to do more, but we need to sit down, relax
-               and wait a little while first */
-            multistate(easy, CURLM_STATE_DO_MORE);
-            result = CURLM_OK;
-          }
-          else {
-            /* we're done with the DO, now DO_DONE */
-            multistate(easy, CURLM_STATE_DO_DONE);
-            result = CURLM_CALL_MULTI_PERFORM;
-          }
-        }
-        else if((CURLE_SEND_ERROR == easy->result) &&
-                easy->easy_conn->bits.reuse) {
-          /*
-           * In this situation, a connection that we were trying to use
-           * may have unexpectedly died.  If possible, send the connection
-           * back to the CONNECT phase so we can try again.
-           */
-          char *newurl = NULL;
-          followtype follow=FOLLOW_NONE;
-          CURLcode drc;
-          bool retry = FALSE;
-
-          drc = Curl_retry_request(easy->easy_conn, &newurl);
-          if(drc) {
-            /* a failure here pretty much implies an out of memory */
-            easy->result = drc;
-            disconnect_conn = TRUE;
-          }
-          else
-            retry = (newurl)?TRUE:FALSE;
-
-          Curl_posttransfer(data);
-          drc = Curl_done(&easy->easy_conn, easy->result, FALSE);
-
-          /* When set to retry the connection, we must to go back to
-           * the CONNECT state */
-          if(retry) {
-            if((drc == CURLE_OK) || (drc == CURLE_SEND_ERROR)) {
-              follow = FOLLOW_RETRY;
-              drc = Curl_follow(data, newurl, follow);
-              if(drc == CURLE_OK) {
-                multistate(easy, CURLM_STATE_CONNECT);
-                result = CURLM_CALL_MULTI_PERFORM;
-                easy->result = CURLE_OK;
-              }
-              else {
-                /* Follow failed */
-                easy->result = drc;
-                free(newurl);
-              }
-            }
-            else {
-              /* done didn't return OK or SEND_ERROR */
-              easy->result = drc;
-              free(newurl);
-            }
-          }
-          else {
-            /* Have error handler disconnect conn if we can't retry */
-            disconnect_conn = TRUE;
-          }
-        }
-        else {
-          /* failure detected */
-          Curl_posttransfer(data);
-          Curl_done(&easy->easy_conn, easy->result, FALSE);
-          disconnect_conn = TRUE;
-        }
-      }
-      break;
-
-    case CURLM_STATE_DOING:
-      /* we continue DOING until the DO phase is complete */
-      easy->result = Curl_protocol_doing(easy->easy_conn,
-                                         &dophase_done);
-      if(CURLE_OK == easy->result) {
-        if(dophase_done) {
-          /* after DO, go DO_DONE or DO_MORE */
-          multistate(easy, easy->easy_conn->bits.do_more?
-                     CURLM_STATE_DO_MORE:
-                     CURLM_STATE_DO_DONE);
-          result = CURLM_CALL_MULTI_PERFORM;
-        } /* dophase_done */
-      }
-      else {
-        /* failure detected */
-        Curl_posttransfer(data);
-        Curl_done(&easy->easy_conn, easy->result, FALSE);
-        disconnect_conn = TRUE;
-      }
-      break;
-
-    case CURLM_STATE_DO_MORE:
-      /*
-       * When we are connected, DO MORE and then go DO_DONE
-       */
-      easy->result = Curl_do_more(easy->easy_conn, &dophase_done);
-
-      /* No need to remove this handle from the send pipeline here since that
-         is done in Curl_done() */
-      if(CURLE_OK == easy->result) {
-        if(dophase_done) {
-          multistate(easy, CURLM_STATE_DO_DONE);
-          result = CURLM_CALL_MULTI_PERFORM;
-        }
-        else
-          /* stay in DO_MORE */
-          result = CURLM_OK;
-      }
-      else {
-        /* failure detected */
-        Curl_posttransfer(data);
-        Curl_done(&easy->easy_conn, easy->result, FALSE);
-        disconnect_conn = TRUE;
-      }
-      break;
-
-    case CURLM_STATE_DO_DONE:
-      /* Move ourselves from the send to recv pipeline */
-      moveHandleFromSendToRecvPipeline(data, easy->easy_conn);
-      /* Check if we can move pending requests to send pipe */
-      checkPendPipeline(easy->easy_conn);
-      multistate(easy, CURLM_STATE_WAITPERFORM);
-      result = CURLM_CALL_MULTI_PERFORM;
-      break;
-
-    case CURLM_STATE_WAITPERFORM:
-      /* Wait for our turn to PERFORM */
-      if(!easy->easy_conn->readchannel_inuse &&
-         isHandleAtHead(data,
-                        easy->easy_conn->recv_pipe)) {
-        /* Grab the channel */
-        easy->easy_conn->readchannel_inuse = TRUE;
-        multistate(easy, CURLM_STATE_PERFORM);
-        result = CURLM_CALL_MULTI_PERFORM;
-      }
-#ifdef DEBUGBUILD
-      else {
-        infof(data, "WAITPERFORM: Conn %ld recv pipe %zu inuse %d athead %d\n",
-              easy->easy_conn->connection_id,
-              easy->easy_conn->recv_pipe->size,
-              easy->easy_conn->readchannel_inuse?1:0,
-              isHandleAtHead(data,
-                             easy->easy_conn->recv_pipe)?1:0);
-      }
-#endif
-      break;
-
-    case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */
-      /* if both rates are within spec, resume transfer */
-      if(Curl_pgrsUpdate(easy->easy_conn))
-        easy->result = CURLE_ABORTED_BY_CALLBACK;
-      else
-        easy->result = Curl_speedcheck(data, now);
-
-      if(( (data->set.max_send_speed == 0) ||
-           (data->progress.ulspeed < data->set.max_send_speed ))  &&
-         ( (data->set.max_recv_speed == 0) ||
-           (data->progress.dlspeed < data->set.max_recv_speed)))
-        multistate(easy, CURLM_STATE_PERFORM);
-      break;
-
-    case CURLM_STATE_PERFORM:
-      {
-      char *newurl = NULL;
-      bool retry = FALSE;
-
-      /* check if over send speed */
-      if((data->set.max_send_speed > 0) &&
-         (data->progress.ulspeed > data->set.max_send_speed)) {
-        int buffersize;
-
-        multistate(easy, CURLM_STATE_TOOFAST);
-
-        /* calculate upload rate-limitation timeout. */
-        buffersize = (int)(data->set.buffer_size ?
-                           data->set.buffer_size : BUFSIZE);
-        timeout_ms = Curl_sleep_time(data->set.max_send_speed,
-                                     data->progress.ulspeed, buffersize);
-        Curl_expire(data, timeout_ms);
-        break;
-      }
-
-      /* check if over recv speed */
-      if((data->set.max_recv_speed > 0) &&
-         (data->progress.dlspeed > data->set.max_recv_speed)) {
-        int buffersize;
-
-        multistate(easy, CURLM_STATE_TOOFAST);
-
-         /* Calculate download rate-limitation timeout. */
-        buffersize = (int)(data->set.buffer_size ?
-                           data->set.buffer_size : BUFSIZE);
-        timeout_ms = Curl_sleep_time(data->set.max_recv_speed,
-                                     data->progress.dlspeed, buffersize);
-        Curl_expire(data, timeout_ms);
-        break;
-      }
-
-      /* read/write data if it is ready to do so */
-      easy->result = Curl_readwrite(easy->easy_conn, &done);
-
-      k = &data->req;
-
-      if(!(k->keepon & KEEP_RECV)) {
-        /* We're done receiving */
-        easy->easy_conn->readchannel_inuse = FALSE;
-      }
-
-      if(!(k->keepon & KEEP_SEND)) {
-        /* We're done sending */
-        easy->easy_conn->writechannel_inuse = FALSE;
-      }
-
-      if(done || (easy->result == CURLE_RECV_ERROR)) {
-        /* If CURLE_RECV_ERROR happens early enough, we assume it was a race
-         * condition and the server closed the re-used connection exactly when
-         * we wanted to use it, so figure out if that is indeed the case.
-         */
-        CURLcode ret = Curl_retry_request(easy->easy_conn, &newurl);
-        if(!ret)
-          retry = (newurl)?TRUE:FALSE;
-
-        if(retry)
-          /* if we are to retry, set the result to OK */
-          easy->result = CURLE_OK;
-      }
-
-      if(easy->result) {
-        /*
-         * The transfer phase returned error, we mark the connection to get
-         * closed to prevent being re-used. This is because we can't possibly
-         * know if the connection is in a good shape or not now.  Unless it is
-         * a protocol which uses two "channels" like FTP, as then the error
-         * happened in the data connection.
-         */
-
-        if(!(easy->easy_conn->handler->flags & PROTOPT_DUAL))
-          easy->easy_conn->bits.close = TRUE;
-
-        Curl_posttransfer(data);
-        Curl_done(&easy->easy_conn, easy->result, FALSE);
-      }
-      else if(done) {
-        followtype follow=FOLLOW_NONE;
-
-        /* call this even if the readwrite function returned error */
-        Curl_posttransfer(data);
-
-        /* we're no longer receiving */
-        moveHandleFromRecvToDonePipeline(data,
-                                         easy->easy_conn);
-
-        /* expire the new receiving pipeline head */
-        if(easy->easy_conn->recv_pipe->head)
-          Curl_expire(easy->easy_conn->recv_pipe->head->ptr, 1);
-
-        /* Check if we can move pending requests to send pipe */
-        checkPendPipeline(easy->easy_conn);
-
-        /* When we follow redirects or is set to retry the connection, we must
-           to go back to the CONNECT state */
-        if(data->req.newurl || retry) {
-          if(!retry) {
-            /* if the URL is a follow-location and not just a retried request
-               then figure out the URL here */
-            newurl = data->req.newurl;
-            data->req.newurl = NULL;
-            follow = FOLLOW_REDIR;
-          }
-          else
-            follow = FOLLOW_RETRY;
-          easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE);
-          if(easy->result == CURLE_OK)
-            easy->result = Curl_follow(data, newurl, follow);
-          if(CURLE_OK == easy->result) {
-            multistate(easy, CURLM_STATE_CONNECT);
-            result = CURLM_CALL_MULTI_PERFORM;
-            newurl = NULL; /* handed over the memory ownership to
-                              Curl_follow(), make sure we don't free() it
-                              here */
-          }
-        }
-        else {
-          /* after the transfer is done, go DONE */
-
-          /* but first check to see if we got a location info even though we're
-             not following redirects */
-          if(data->req.location) {
-            if(newurl)
-              free(newurl);
-            newurl = data->req.location;
-            data->req.location = NULL;
-            easy->result = Curl_follow(data, newurl, FOLLOW_FAKE);
-            if(CURLE_OK == easy->result)
-              newurl = NULL; /* allocation was handed over Curl_follow() */
-            else
-              disconnect_conn = TRUE;
-          }
-
-          multistate(easy, CURLM_STATE_DONE);
-          result = CURLM_CALL_MULTI_PERFORM;
-        }
-      }
-
-      if(newurl)
-        free(newurl);
-      break;
-      }
-
-    case CURLM_STATE_DONE:
-
-      if(easy->easy_conn) {
-        /* Remove ourselves from the receive and done pipelines. Handle
-           should be on one of these lists, depending upon how we got here. */
-        Curl_removeHandleFromPipeline(data,
-                                      easy->easy_conn->recv_pipe);
-        Curl_removeHandleFromPipeline(data,
-                                      easy->easy_conn->done_pipe);
-        /* Check if we can move pending requests to send pipe */
-        checkPendPipeline(easy->easy_conn);
-
-        if(easy->easy_conn->bits.stream_was_rewound) {
-          /* This request read past its response boundary so we quickly let
-             the other requests consume those bytes since there is no
-             guarantee that the socket will become active again */
-          result = CURLM_CALL_MULTI_PERFORM;
-        }
-
-        /* post-transfer command */
-        easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE);
-        /*
-         * If there are other handles on the pipeline, Curl_done won't set
-         * easy_conn to NULL.  In such a case, curl_multi_remove_handle() can
-         * access free'd data, if the connection is free'd and the handle
-         * removed before we perform the processing in CURLM_STATE_COMPLETED
-         */
-        if(easy->easy_conn)
-          easy->easy_conn = NULL;
-      }
-
-      if(data->set.wildcardmatch) {
-        if(data->wildcard.state != CURLWC_DONE) {
-          /* if a wildcard is set and we are not ending -> lets start again
-             with CURLM_STATE_INIT */
-          result = CURLM_CALL_MULTI_PERFORM;
-          multistate(easy, CURLM_STATE_INIT);
-          break;
-        }
-      }
-
-      /* after we have DONE what we're supposed to do, go COMPLETED, and
-         it doesn't matter what the Curl_done() returned! */
-      multistate(easy, CURLM_STATE_COMPLETED);
-
-      break;
-
-    case CURLM_STATE_COMPLETED:
-      /* this is a completed transfer, it is likely to still be connected */
-
-      /* This node should be delinked from the list now and we should post
-         an information message that we are complete. */
-
-      /* Important: reset the conn pointer so that we don't point to memory
-         that could be freed anytime */
-      easy->easy_conn = NULL;
-
-      Curl_expire(data, 0); /* stop all timers */
-      break;
-
-    case CURLM_STATE_MSGSENT:
-      return CURLM_OK; /* do nothing */
-
-    default:
-      return CURLM_INTERNAL_ERROR;
-    }
-
-    if(easy->state < CURLM_STATE_COMPLETED) {
-      if(CURLE_OK != easy->result) {
-        /*
-         * If an error was returned, and we aren't in completed state now,
-         * then we go to completed and consider this transfer aborted.
-         */
-
-        /* NOTE: no attempt to disconnect connections must be made
-           in the case blocks above - cleanup happens only here */
-
-        data->state.pipe_broke = FALSE;
-
-        if(easy->easy_conn) {
-          /* if this has a connection, unsubscribe from the pipelines */
-          easy->easy_conn->writechannel_inuse = FALSE;
-          easy->easy_conn->readchannel_inuse = FALSE;
-          Curl_removeHandleFromPipeline(data,
-                                        easy->easy_conn->send_pipe);
-          Curl_removeHandleFromPipeline(data,
-                                        easy->easy_conn->recv_pipe);
-          Curl_removeHandleFromPipeline(data,
-                                        easy->easy_conn->done_pipe);
-          /* Check if we can move pending requests to send pipe */
-          checkPendPipeline(easy->easy_conn);
-
-          if(disconnect_conn) {
-            /* disconnect properly */
-            Curl_disconnect(easy->easy_conn, /* dead_connection */ FALSE);
-
-            /* This is where we make sure that the easy_conn pointer is reset.
-               We don't have to do this in every case block above where a
-               failure is detected */
-            easy->easy_conn = NULL;
-          }
-        }
-        else if(easy->state == CURLM_STATE_CONNECT) {
-          /* Curl_connect() failed */
-          (void)Curl_posttransfer(data);
-        }
-
-        multistate(easy, CURLM_STATE_COMPLETED);
-      }
-      /* if there's still a connection to use, call the progress function */
-      else if(easy->easy_conn && Curl_pgrsUpdate(easy->easy_conn)) {
-        /* aborted due to progress callback return code must close the
-           connection */
-        easy->easy_conn->bits.close = TRUE;
-
-        /* if not yet in DONE state, go there, otherwise COMPLETED */
-        multistate(easy, (easy->state < CURLM_STATE_DONE)?
-                   CURLM_STATE_DONE: CURLM_STATE_COMPLETED);
-        result = CURLM_CALL_MULTI_PERFORM;
-      }
-    }
-  } WHILE_FALSE; /* just to break out from! */
-
-  if(CURLM_STATE_COMPLETED == easy->state) {
-    /* now fill in the Curl_message with this info */
-    msg = &easy->msg;
-
-    msg->extmsg.msg = CURLMSG_DONE;
-    msg->extmsg.easy_handle = data;
-    msg->extmsg.data.result = easy->result;
-
-    result = multi_addmsg(multi, msg);
-
-    multistate(easy, CURLM_STATE_MSGSENT);
-  }
-
-  return result;
-}
-
-
-CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
-{
-  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
-  struct Curl_one_easy *easy;
-  CURLMcode returncode=CURLM_OK;
-  struct Curl_tree *t;
-  struct timeval now = Curl_tvnow();
-
-  if(!GOOD_MULTI_HANDLE(multi))
-    return CURLM_BAD_HANDLE;
-
-  easy=multi->easy.next;
-  while(easy != &multi->easy) {
-    CURLMcode result;
-    struct WildcardData *wc = &easy->easy_handle->wildcard;
-
-    if(easy->easy_handle->set.wildcardmatch) {
-      if(!wc->filelist) {
-        CURLcode ret = Curl_wildcard_init(wc); /* init wildcard structures */
-        if(ret)
-          return CURLM_OUT_OF_MEMORY;
-      }
-    }
-
-    do
-      result = multi_runsingle(multi, now, easy);
-    while(CURLM_CALL_MULTI_PERFORM == result);
-
-    if(easy->easy_handle->set.wildcardmatch) {
-      /* destruct wildcard structures if it is needed */
-      if(wc->state == CURLWC_DONE || result)
-        Curl_wildcard_dtor(wc);
-    }
-
-    if(result)
-      returncode = result;
-
-    easy = easy->next; /* operate on next handle */
-  }
-
-  /*
-   * Simply remove all expired timers from the splay since handles are dealt
-   * with unconditionally by this function and curl_multi_timeout() requires
-   * that already passed/handled expire times are removed from the splay.
-   *
-   * It is important that the 'now' value is set at the entry of this function
-   * and not for the current time as it may have ticked a little while since
-   * then and then we risk this loop to remove timers that actually have not
-   * been handled!
-   */
-  do {
-    multi->timetree = Curl_splaygetbest(now, multi->timetree, &t);
-    if(t)
-      /* the removed may have another timeout in queue */
-      (void)add_next_timeout(now, multi, t->payload);
-
-  } while(t);
-
-  *running_handles = multi->num_alive;
-
-  if(CURLM_OK >= returncode)
-    update_timer(multi);
-
-  return returncode;
-}
-
-static void close_all_connections(struct Curl_multi *multi)
-{
-  struct connectdata *conn;
-
-  conn = Curl_conncache_find_first_connection(multi->conn_cache);
-  while(conn) {
-    conn->data = multi->closure_handle;
-
-    /* This will remove the connection from the cache */
-    (void)Curl_disconnect(conn, FALSE);
-
-    conn = Curl_conncache_find_first_connection(multi->conn_cache);
-  }
-}
-
-CURLMcode curl_multi_cleanup(CURLM *multi_handle)
-{
-  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
-  struct Curl_one_easy *easy;
-  struct Curl_one_easy *nexteasy;
-
-  if(GOOD_MULTI_HANDLE(multi)) {
-    multi->type = 0; /* not good anymore */
-
-    /* Close all the connections in the connection cache */
-    close_all_connections(multi);
-
-    Curl_close(multi->closure_handle);
-    multi->closure_handle = NULL;
-
-    Curl_hash_destroy(multi->sockhash);
-    multi->sockhash = NULL;
-
-    Curl_conncache_destroy(multi->conn_cache);
-    multi->conn_cache = NULL;
-
-    /* remove the pending list of messages */
-    Curl_llist_destroy(multi->msglist, NULL);
-    multi->msglist = NULL;
-
-    /* remove all easy handles */
-    easy = multi->easy.next;
-    while(easy != &multi->easy) {
-      nexteasy=easy->next;
-      if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) {
-        /* clear out the usage of the shared DNS cache */
-        Curl_hostcache_clean(easy->easy_handle);
-        easy->easy_handle->dns.hostcache = NULL;
-        easy->easy_handle->dns.hostcachetype = HCACHE_NONE;
-      }
-
-      /* Clear the pointer to the connection cache */
-      easy->easy_handle->state.conn_cache = NULL;
-
-      Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association */
-
-      free(easy);
-      easy = nexteasy;
-    }
-
-    Curl_hash_destroy(multi->hostcache);
-    multi->hostcache = NULL;
-
-    free(multi);
-
-    return CURLM_OK;
-  }
-  else
-    return CURLM_BAD_HANDLE;
-}
-
-/*
- * curl_multi_info_read()
- *
- * This function is the primary way for a multi/multi_socket application to
- * figure out if a transfer has ended. We MUST make this function as fast as
- * possible as it will be polled frequently and we MUST NOT scan any lists in
- * here to figure out things. We must scale fine to thousands of handles and
- * beyond. The current design is fully O(1).
- */
-
-CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue)
-{
-  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
-  struct Curl_message *msg;
-
-  *msgs_in_queue = 0; /* default to none */
-
-  if(GOOD_MULTI_HANDLE(multi) && Curl_llist_count(multi->msglist)) {
-    /* there is one or more messages in the list */
-    struct curl_llist_element *e;
-
-    /* extract the head of the list to return */
-    e = multi->msglist->head;
-
-    msg = e->ptr;
-
-    /* remove the extracted entry */
-    Curl_llist_remove(multi->msglist, e, NULL);
-
-    *msgs_in_queue = curlx_uztosi(Curl_llist_count(multi->msglist));
-
-    return &msg->extmsg;
-  }
-  else
-    return NULL;
-}
-
-/*
- * singlesocket() checks what sockets we deal with and their "action state"
- * and if we have a different state in any of those sockets from last time we
- * call the callback accordingly.
- */
-static void singlesocket(struct Curl_multi *multi,
-                         struct Curl_one_easy *easy)
-{
-  curl_socket_t socks[MAX_SOCKSPEREASYHANDLE];
-  int i;
-  struct Curl_sh_entry *entry;
-  curl_socket_t s;
-  int num;
-  unsigned int curraction;
-  struct Curl_one_easy *easy_by_hash;
-  bool remove_sock_from_hash;
-
-  for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++)
-    socks[i] = CURL_SOCKET_BAD;
-
-  /* Fill in the 'current' struct with the state as it is now: what sockets to
-     supervise and for what actions */
-  curraction = multi_getsock(easy, socks, MAX_SOCKSPEREASYHANDLE);
-
-  /* We have 0 .. N sockets already and we get to know about the 0 .. M
-     sockets we should have from now on. Detect the differences, remove no
-     longer supervised ones and add new ones */
-
-  /* walk over the sockets we got right now */
-  for(i=0; (i< MAX_SOCKSPEREASYHANDLE) &&
-        (curraction & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i)));
-      i++) {
-    int action = CURL_POLL_NONE;
-
-    s = socks[i];
-
-    /* get it from the hash */
-    entry = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));
-
-    if(curraction & GETSOCK_READSOCK(i))
-      action |= CURL_POLL_IN;
-    if(curraction & GETSOCK_WRITESOCK(i))
-      action |= CURL_POLL_OUT;
-
-    if(entry) {
-      /* yeps, already present so check if it has the same action set */
-      if(entry->action == action)
-        /* same, continue */
-        continue;
-    }
-    else {
-      /* this is a socket we didn't have before, add it! */
-      entry = sh_addentry(multi->sockhash, s, easy->easy_handle);
-      if(!entry)
-        /* fatal */
-        return;
-    }
-
-    /* we know (entry != NULL) at this point, see the logic above */
-    if(multi->socket_cb)
-      multi->socket_cb(easy->easy_handle,
-                       s,
-                       action,
-                       multi->socket_userp,
-                       entry->socketp);
-
-    entry->action = action; /* store the current action state */
-  }
-
-  num = i; /* number of sockets */
-
-  /* when we've walked over all the sockets we should have right now, we must
-     make sure to detect sockets that are removed */
-  for(i=0; i< easy->numsocks; i++) {
-    int j;
-    s = easy->sockets[i];
-    for(j=0; j<num; j++) {
-      if(s == socks[j]) {
-        /* this is still supervised */
-        s = CURL_SOCKET_BAD;
-        break;
-      }
-    }
-    if(s != CURL_SOCKET_BAD) {
-
-      /* this socket has been removed. Tell the app to remove it */
-      remove_sock_from_hash = TRUE;
-
-      entry = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));
-      if(entry) {
-        /* check if the socket to be removed serves a connection which has
-           other easy-s in a pipeline. In this case the socket should not be
-           removed. */
-        struct connectdata *easy_conn;
-
-        easy_by_hash = entry->easy->multi_pos;
-        easy_conn = easy_by_hash->easy_conn;
-        if(easy_conn) {
-          if(easy_conn->recv_pipe && easy_conn->recv_pipe->size > 1) {
-            /* the handle should not be removed from the pipe yet */
-            remove_sock_from_hash = FALSE;
-
-            /* Update the sockhash entry to instead point to the next in line
-               for the recv_pipe, or the first (in case this particular easy
-               isn't already) */
-            if(entry->easy == easy->easy_handle) {
-              if(isHandleAtHead(easy->easy_handle, easy_conn->recv_pipe))
-                entry->easy = easy_conn->recv_pipe->head->next->ptr;
-              else
-                entry->easy = easy_conn->recv_pipe->head->ptr;
-            }
-          }
-          if(easy_conn->send_pipe  && easy_conn->send_pipe->size > 1) {
-            /* the handle should not be removed from the pipe yet */
-            remove_sock_from_hash = FALSE;
-
-            /* Update the sockhash entry to instead point to the next in line
-               for the send_pipe, or the first (in case this particular easy
-               isn't already) */
-            if(entry->easy == easy->easy_handle) {
-              if(isHandleAtHead(easy->easy_handle, easy_conn->send_pipe))
-                entry->easy = easy_conn->send_pipe->head->next->ptr;
-              else
-                entry->easy = easy_conn->send_pipe->head->ptr;
-            }
-          }
-          /* Don't worry about overwriting recv_pipe head with send_pipe_head,
-             when action will be asked on the socket (see multi_socket()), the
-             head of the correct pipe will be taken according to the
-             action. */
-        }
-      }
-      else
-        /* just a precaution, this socket really SHOULD be in the hash already
-           but in case it isn't, we don't have to tell the app to remove it
-           either since it never got to know about it */
-        remove_sock_from_hash = FALSE;
-
-      if(remove_sock_from_hash) {
-        /* in this case 'entry' is always non-NULL */
-        if(multi->socket_cb)
-          multi->socket_cb(easy->easy_handle,
-                           s,
-                           CURL_POLL_REMOVE,
-                           multi->socket_userp,
-                           entry->socketp);
-        sh_delentry(multi->sockhash, s);
-      }
-
-    }
-  }
-
-  memcpy(easy->sockets, socks, num*sizeof(curl_socket_t));
-  easy->numsocks = num;
-}
-
-/*
- * add_next_timeout()
- *
- * Each SessionHandle has a list of timeouts. The add_next_timeout() is called
- * when it has just been removed from the splay tree because the timeout has
- * expired. This function is then to advance in the list to pick the next
- * timeout to use (skip the already expired ones) and add this node back to
- * the splay tree again.
- *
- * The splay tree only has each sessionhandle as a single node and the nearest
- * timeout is used to sort it on.
- */
-static CURLMcode add_next_timeout(struct timeval now,
-                                  struct Curl_multi *multi,
-                                  struct SessionHandle *d)
-{
-  struct timeval *tv = &d->state.expiretime;
-  struct curl_llist *list = d->state.timeoutlist;
-  struct curl_llist_element *e;
-
-  /* move over the timeout list for this specific handle and remove all
-     timeouts that are now passed tense and store the next pending
-     timeout in *tv */
-  for(e = list->head; e; ) {
-    struct curl_llist_element *n = e->next;
-    long diff = curlx_tvdiff(*(struct timeval *)e->ptr, now);
-    if(diff <= 0)
-      /* remove outdated entry */
-      Curl_llist_remove(list, e, NULL);
-    else
-      /* the list is sorted so get out on the first mismatch */
-      break;
-    e = n;
-  }
-  e = list->head;
-  if(!e) {
-    /* clear the expire times within the handles that we remove from the
-       splay tree */
-    tv->tv_sec = 0;
-    tv->tv_usec = 0;
-  }
-  else {
-    /* copy the first entry to 'tv' */
-    memcpy(tv, e->ptr, sizeof(*tv));
-
-    /* remove first entry from list */
-    Curl_llist_remove(list, e, NULL);
-
-    /* insert this node again into the splay */
-    multi->timetree = Curl_splayinsert(*tv, multi->timetree,
-                                       &d->state.timenode);
-  }
-  return CURLM_OK;
-}
-
-
-static CURLMcode multi_socket(struct Curl_multi *multi,
-                              bool checkall,
-                              curl_socket_t s,
-                              int ev_bitmask,
-                              int *running_handles)
-{
-  CURLMcode result = CURLM_OK;
-  struct SessionHandle *data = NULL;
-  struct Curl_tree *t;
-  struct timeval now = Curl_tvnow();
-
-  if(checkall) {
-    struct Curl_one_easy *easyp;
-    /* *perform() deals with running_handles on its own */
-    result = curl_multi_perform(multi, running_handles);
-
-    /* walk through each easy handle and do the socket state change magic
-       and callbacks */
-    easyp=multi->easy.next;
-    while(easyp != &multi->easy) {
-      singlesocket(multi, easyp);
-      easyp = easyp->next;
-    }
-
-    /* or should we fall-through and do the timer-based stuff? */
-    return result;
-  }
-  else if(s != CURL_SOCKET_TIMEOUT) {
-
-    struct Curl_sh_entry *entry =
-      Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));
-
-    if(!entry)
-      /* Unmatched socket, we can't act on it but we ignore this fact.  In
-         real-world tests it has been proved that libevent can in fact give
-         the application actions even though the socket was just previously
-         asked to get removed, so thus we better survive stray socket actions
-         and just move on. */
-      ;
-    else {
-      data = entry->easy;
-
-      if(data->magic != CURLEASY_MAGIC_NUMBER)
-        /* bad bad bad bad bad bad bad */
-        return CURLM_INTERNAL_ERROR;
-
-      /* If the pipeline is enabled, take the handle which is in the head of
-         the pipeline. If we should write into the socket, take the send_pipe
-         head.  If we should read from the socket, take the recv_pipe head. */
-      if(data->set.one_easy->easy_conn) {
-        if((ev_bitmask & CURL_POLL_OUT) &&
-           data->set.one_easy->easy_conn->send_pipe &&
-           data->set.one_easy->easy_conn->send_pipe->head)
-          data = data->set.one_easy->easy_conn->send_pipe->head->ptr;
-        else if((ev_bitmask & CURL_POLL_IN) &&
-                data->set.one_easy->easy_conn->recv_pipe &&
-                data->set.one_easy->easy_conn->recv_pipe->head)
-          data = data->set.one_easy->easy_conn->recv_pipe->head->ptr;
-      }
-
-      if(data->set.one_easy->easy_conn &&
-         !(data->set.one_easy->easy_conn->handler->flags & PROTOPT_DIRLOCK))
-        /* set socket event bitmask if they're not locked */
-        data->set.one_easy->easy_conn->cselect_bits = ev_bitmask;
-
-      do
-        result = multi_runsingle(multi, now, data->set.one_easy);
-      while(CURLM_CALL_MULTI_PERFORM == result);
-
-      if(data->set.one_easy->easy_conn &&
-         !(data->set.one_easy->easy_conn->handler->flags & PROTOPT_DIRLOCK))
-        /* clear the bitmask only if not locked */
-        data->set.one_easy->easy_conn->cselect_bits = 0;
-
-      if(CURLM_OK >= result)
-        /* get the socket(s) and check if the state has been changed since
-           last */
-        singlesocket(multi, data->set.one_easy);
-
-      /* Now we fall-through and do the timer-based stuff, since we don't want
-         to force the user to have to deal with timeouts as long as at least
-         one connection in fact has traffic. */
-
-      data = NULL; /* set data to NULL again to avoid calling
-                      multi_runsingle() in case there's no need to */
-    }
-  }
-
-  now.tv_usec += 40000; /* compensate for bad precision timers that might've
-                           triggered too early */
-  if(now.tv_usec >= 1000000) {
-    now.tv_sec++;
-    now.tv_usec -= 1000000;
-  }
-
-  /*
-   * The loop following here will go on as long as there are expire-times left
-   * to process in the splay and 'data' will be re-assigned for every expired
-   * handle we deal with.
-   */
-  do {
-    /* the first loop lap 'data' can be NULL */
-    if(data) {
-      do
-        result = multi_runsingle(multi, now, data->set.one_easy);
-      while(CURLM_CALL_MULTI_PERFORM == result);
-
-      if(CURLM_OK >= result)
-        /* get the socket(s) and check if the state has been changed since
-           last */
-        singlesocket(multi, data->set.one_easy);
-    }
-
-    /* Check if there's one (more) expired timer to deal with! This function
-       extracts a matching node if there is one */
-
-    multi->timetree = Curl_splaygetbest(now, multi->timetree, &t);
-    if(t) {
-      data = t->payload; /* assign this for next loop */
-      (void)add_next_timeout(now, multi, t->payload);
-    }
-
-  } while(t);
-
-  *running_handles = multi->num_alive;
-  return result;
-}
-
-#undef curl_multi_setopt
-CURLMcode curl_multi_setopt(CURLM *multi_handle,
-                            CURLMoption option, ...)
-{
-  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
-  CURLMcode res = CURLM_OK;
-  va_list param;
-
-  if(!GOOD_MULTI_HANDLE(multi))
-    return CURLM_BAD_HANDLE;
-
-  va_start(param, option);
-
-  switch(option) {
-  case CURLMOPT_SOCKETFUNCTION:
-    multi->socket_cb = va_arg(param, curl_socket_callback);
-    break;
-  case CURLMOPT_SOCKETDATA:
-    multi->socket_userp = va_arg(param, void *);
-    break;
-  case CURLMOPT_PIPELINING:
-    multi->pipelining_enabled = (0 != va_arg(param, long)) ? TRUE : FALSE;
-    break;
-  case CURLMOPT_TIMERFUNCTION:
-    multi->timer_cb = va_arg(param, curl_multi_timer_callback);
-    break;
-  case CURLMOPT_TIMERDATA:
-    multi->timer_userp = va_arg(param, void *);
-    break;
-  case CURLMOPT_MAXCONNECTS:
-    multi->maxconnects = va_arg(param, long);
-    break;
-  default:
-    res = CURLM_UNKNOWN_OPTION;
-    break;
-  }
-  va_end(param);
-  return res;
-}
-
-/* we define curl_multi_socket() in the public multi.h header */
-#undef curl_multi_socket
-
-CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s,
-                            int *running_handles)
-{
-  CURLMcode result = multi_socket((struct Curl_multi *)multi_handle, FALSE, s,
-                                  0, running_handles);
-  if(CURLM_OK >= result)
-    update_timer((struct Curl_multi *)multi_handle);
-  return result;
-}
-
-CURLMcode curl_multi_socket_action(CURLM *multi_handle, curl_socket_t s,
-                                   int ev_bitmask, int *running_handles)
-{
-  CURLMcode result = multi_socket((struct Curl_multi *)multi_handle, FALSE, s,
-                                  ev_bitmask, running_handles);
-  if(CURLM_OK >= result)
-    update_timer((struct Curl_multi *)multi_handle);
-  return result;
-}
-
-CURLMcode curl_multi_socket_all(CURLM *multi_handle, int *running_handles)
-
-{
-  CURLMcode result = multi_socket((struct Curl_multi *)multi_handle,
-                                  TRUE, CURL_SOCKET_BAD, 0, running_handles);
-  if(CURLM_OK >= result)
-    update_timer((struct Curl_multi *)multi_handle);
-  return result;
-}
-
-static CURLMcode multi_timeout(struct Curl_multi *multi,
-                               long *timeout_ms)
-{
-  static struct timeval tv_zero = {0,0};
-
-  if(multi->timetree) {
-    /* we have a tree of expire times */
-    struct timeval now = Curl_tvnow();
-
-    /* splay the lowest to the bottom */
-    multi->timetree = Curl_splay(tv_zero, multi->timetree);
-
-    if(Curl_splaycomparekeys(multi->timetree->key, now) > 0) {
-      /* some time left before expiration */
-      *timeout_ms = curlx_tvdiff(multi->timetree->key, now);
-      if(!*timeout_ms)
-        /*
-         * Since we only provide millisecond resolution on the returned value
-         * and the diff might be less than one millisecond here, we don't
-         * return zero as that may cause short bursts of busyloops on fast
-         * processors while the diff is still present but less than one
-         * millisecond! instead we return 1 until the time is ripe.
-         */
-        *timeout_ms=1;
-    }
-    else
-      /* 0 means immediately */
-      *timeout_ms = 0;
-  }
-  else
-    *timeout_ms = -1;
-
-  return CURLM_OK;
-}
-
-CURLMcode curl_multi_timeout(CURLM *multi_handle,
-                             long *timeout_ms)
-{
-  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
-
-  /* First, make some basic checks that the CURLM handle is a good handle */
-  if(!GOOD_MULTI_HANDLE(multi))
-    return CURLM_BAD_HANDLE;
-
-  return multi_timeout(multi, timeout_ms);
-}
-
-/*
- * Tell the application it should update its timers, if it subscribes to the
- * update timer callback.
- */
-static int update_timer(struct Curl_multi *multi)
-{
-  long timeout_ms;
-
-  if(!multi->timer_cb)
-    return 0;
-  if(multi_timeout(multi, &timeout_ms)) {
-    return -1;
-  }
-  if(timeout_ms < 0) {
-    static const struct timeval none={0,0};
-    if(Curl_splaycomparekeys(none, multi->timer_lastcall)) {
-      multi->timer_lastcall = none;
-      /* there's no timeout now but there was one previously, tell the app to
-         disable it */
-      return multi->timer_cb((CURLM*)multi, -1, multi->timer_userp);
-    }
-    return 0;
-  }
-
-  /* When multi_timeout() is done, multi->timetree points to the node with the
-   * timeout we got the (relative) time-out time for. We can thus easily check
-   * if this is the same (fixed) time as we got in a previous call and then
-   * avoid calling the callback again. */
-  if(Curl_splaycomparekeys(multi->timetree->key, multi->timer_lastcall) == 0)
-    return 0;
-
-  multi->timer_lastcall = multi->timetree->key;
-
-  return multi->timer_cb((CURLM*)multi, timeout_ms, multi->timer_userp);
-}
-
-static CURLcode addHandleToSendOrPendPipeline(struct SessionHandle *handle,
-                                              struct connectdata *conn)
-{
-  size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size;
-  struct curl_llist_element *sendhead = conn->send_pipe->head;
-  struct curl_llist *pipeline;
-  CURLcode rc;
-
-  if(!Curl_isPipeliningEnabled(handle) ||
-     pipeLen == 0)
-    pipeline = conn->send_pipe;
-  else {
-    if(conn->server_supports_pipelining &&
-       pipeLen < MAX_PIPELINE_LENGTH)
-      pipeline = conn->send_pipe;
-    else
-      pipeline = conn->pend_pipe;
-  }
-
-  rc = Curl_addHandleToPipeline(handle, pipeline);
-
-  if(pipeline == conn->send_pipe && sendhead != conn->send_pipe->head) {
-    /* this is a new one as head, expire it */
-    conn->writechannel_inuse = FALSE; /* not in use yet */
-#ifdef DEBUGBUILD
-    infof(conn->data, "%p is at send pipe head!\n",
-          conn->send_pipe->head->ptr);
-#endif
-    Curl_expire(conn->send_pipe->head->ptr, 1);
-  }
-
-  return rc;
-}
-
-static int checkPendPipeline(struct connectdata *conn)
-{
-  int result = 0;
-  struct curl_llist_element *sendhead = conn->send_pipe->head;
-
-  size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size;
-  if(conn->server_supports_pipelining || pipeLen == 0) {
-    struct curl_llist_element *curr = conn->pend_pipe->head;
-    const size_t maxPipeLen =
-      conn->server_supports_pipelining ? MAX_PIPELINE_LENGTH : 1;
-
-    while(pipeLen < maxPipeLen && curr) {
-      Curl_llist_move(conn->pend_pipe, curr,
-                      conn->send_pipe, conn->send_pipe->tail);
-      Curl_pgrsTime(curr->ptr, TIMER_PRETRANSFER);
-      ++result; /* count how many handles we moved */
-      curr = conn->pend_pipe->head;
-      ++pipeLen;
-    }
-  }
-
-  if(result) {
-    conn->now = Curl_tvnow();
-    /* something moved, check for a new send pipeline leader */
-    if(sendhead != conn->send_pipe->head) {
-      /* this is a new one as head, expire it */
-      conn->writechannel_inuse = FALSE; /* not in use yet */
-#ifdef DEBUGBUILD
-      infof(conn->data, "%p is at send pipe head!\n",
-            conn->send_pipe->head->ptr);
-#endif
-      Curl_expire(conn->send_pipe->head->ptr, 1);
-    }
-  }
-
-  return result;
-}
-
-/* Move this transfer from the sending list to the receiving list.
-
-   Pay special attention to the new sending list "leader" as it needs to get
-   checked to update what sockets it acts on.
-
-*/
-static void moveHandleFromSendToRecvPipeline(struct SessionHandle *handle,
-                                             struct connectdata *conn)
-{
-  struct curl_llist_element *curr;
-
-  curr = conn->send_pipe->head;
-  while(curr) {
-    if(curr->ptr == handle) {
-      Curl_llist_move(conn->send_pipe, curr,
-                      conn->recv_pipe, conn->recv_pipe->tail);
-
-      if(conn->send_pipe->head) {
-        /* Since there's a new easy handle at the start of the send pipeline,
-           set its timeout value to 1ms to make it trigger instantly */
-        conn->writechannel_inuse = FALSE; /* not used now */
-#ifdef DEBUGBUILD
-        infof(conn->data, "%p is at send pipe head B!\n",
-              conn->send_pipe->head->ptr);
-#endif
-        Curl_expire(conn->send_pipe->head->ptr, 1);
-      }
-
-      /* The receiver's list is not really interesting here since either this
-         handle is now first in the list and we'll deal with it soon, or
-         another handle is already first and thus is already taken care of */
-
-      break; /* we're done! */
-    }
-    curr = curr->next;
-  }
-}
-
-static void moveHandleFromRecvToDonePipeline(struct SessionHandle *handle,
-                                            struct connectdata *conn)
-{
-  struct curl_llist_element *curr;
-
-  curr = conn->recv_pipe->head;
-  while(curr) {
-    if(curr->ptr == handle) {
-      Curl_llist_move(conn->recv_pipe, curr,
-                      conn->done_pipe, conn->done_pipe->tail);
-      break;
-    }
-    curr = curr->next;
-  }
-}
-static bool isHandleAtHead(struct SessionHandle *handle,
-                           struct curl_llist *pipeline)
-{
-  struct curl_llist_element *curr = pipeline->head;
-  if(curr)
-    return (curr->ptr == handle) ? TRUE : FALSE;
-
-  return FALSE;
-}
-
-/*
- * multi_freetimeout()
- *
- * Callback used by the llist system when a single timeout list entry is
- * destroyed.
- */
-static void multi_freetimeout(void *user, void *entryptr)
-{
-  (void)user;
-
-  /* the entry was plain malloc()'ed */
-  free(entryptr);
-}
-
-/*
- * multi_addtimeout()
- *
- * Add a timestamp to the list of timeouts. Keep the list sorted so that head
- * of list is always the timeout nearest in time.
- *
- */
-static CURLMcode
-multi_addtimeout(struct curl_llist *timeoutlist,
-                 struct timeval *stamp)
-{
-  struct curl_llist_element *e;
-  struct timeval *timedup;
-  struct curl_llist_element *prev = NULL;
-
-  timedup = malloc(sizeof(*timedup));
-  if(!timedup)
-    return CURLM_OUT_OF_MEMORY;
-
-  /* copy the timestamp */
-  memcpy(timedup, stamp, sizeof(*timedup));
-
-  if(Curl_llist_count(timeoutlist)) {
-    /* find the correct spot in the list */
-    for(e = timeoutlist->head; e; e = e->next) {
-      struct timeval *checktime = e->ptr;
-      long diff = curlx_tvdiff(*checktime, *timedup);
-      if(diff > 0)
-        break;
-      prev = e;
-    }
-
-  }
-  /* else
-     this is the first timeout on the list */
-
-  if(!Curl_llist_insert_next(timeoutlist, prev, timedup)) {
-    free(timedup);
-    return CURLM_OUT_OF_MEMORY;
-  }
-
-  return CURLM_OK;
-}
-
-/*
- * Curl_expire()
- *
- * given a number of milliseconds from now to use to set the 'act before
- * this'-time for the transfer, to be extracted by curl_multi_timeout()
- *
- * Note that the timeout will be added to a queue of timeouts if it defines a
- * moment in time that is later than the current head of queue.
- *
- * Pass zero to clear all timeout values for this handle.
-*/
-void Curl_expire(struct SessionHandle *data, long milli)
-{
-  struct Curl_multi *multi = data->multi;
-  struct timeval *nowp = &data->state.expiretime;
-  int rc;
-
-  /* this is only interesting for multi-interface using libcurl, and only
-     while there is still a multi interface struct remaining! */
-  if(!multi)
-    return;
-
-  if(!milli) {
-    /* No timeout, clear the time data. */
-    if(nowp->tv_sec || nowp->tv_usec) {
-      /* Since this is an cleared time, we must remove the previous entry from
-         the splay tree */
-      struct curl_llist *list = data->state.timeoutlist;
-
-      rc = Curl_splayremovebyaddr(multi->timetree,
-                                  &data->state.timenode,
-                                  &multi->timetree);
-      if(rc)
-        infof(data, "Internal error clearing splay node = %d\n", rc);
-
-      /* flush the timeout list too */
-      while(list->size > 0)
-        Curl_llist_remove(list, list->tail, NULL);
-
-#ifdef DEBUGBUILD
-      infof(data, "Expire cleared\n");
-#endif
-      nowp->tv_sec = 0;
-      nowp->tv_usec = 0;
-    }
-  }
-  else {
-    struct timeval set;
-
-    set = Curl_tvnow();
-    set.tv_sec += milli/1000;
-    set.tv_usec += (milli%1000)*1000;
-
-    if(set.tv_usec >= 1000000) {
-      set.tv_sec++;
-      set.tv_usec -= 1000000;
-    }
-
-    if(nowp->tv_sec || nowp->tv_usec) {
-      /* This means that the struct is added as a node in the splay tree.
-         Compare if the new time is earlier, and only remove-old/add-new if it
-         is. */
-      long diff = curlx_tvdiff(set, *nowp);
-      if(diff > 0) {
-        /* the new expire time was later so just add it to the queue
-           and get out */
-        multi_addtimeout(data->state.timeoutlist, &set);
-        return;
-      }
-
-      /* the new time is newer than the presently set one, so add the current
-         to the queue and update the head */
-      multi_addtimeout(data->state.timeoutlist, nowp);
-
-      /* Since this is an updated time, we must remove the previous entry from
-         the splay tree first and then re-add the new value */
-      rc = Curl_splayremovebyaddr(multi->timetree,
-                                  &data->state.timenode,
-                                  &multi->timetree);
-      if(rc)
-        infof(data, "Internal error removing splay node = %d\n", rc);
-    }
-
-    *nowp = set;
-    data->state.timenode.payload = data;
-    multi->timetree = Curl_splayinsert(*nowp,
-                                       multi->timetree,
-                                       &data->state.timenode);
-  }
-#if 0
-  Curl_splayprint(multi->timetree, 0, TRUE);
-#endif
-}
-
-CURLMcode curl_multi_assign(CURLM *multi_handle,
-                            curl_socket_t s, void *hashp)
-{
-  struct Curl_sh_entry *there = NULL;
-  struct Curl_multi *multi = (struct Curl_multi *)multi_handle;
-
-  if(s != CURL_SOCKET_BAD)
-    there = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(curl_socket_t));
-
-  if(!there)
-    return CURLM_BAD_SOCKET;
-
-  there->socketp = hashp;
-
-  return CURLM_OK;
-}
-
-#ifdef DEBUGBUILD
-void Curl_multi_dump(const struct Curl_multi *multi_handle)
-{
-  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
-  struct Curl_one_easy *easy;
-  int i;
-  fprintf(stderr, "* Multi status: %d handles, %d alive\n",
-          multi->num_easy, multi->num_alive);
-  for(easy=multi->easy.next; easy != &multi->easy; easy = easy->next) {
-    if(easy->state < CURLM_STATE_COMPLETED) {
-      /* only display handles that are not completed */
-      fprintf(stderr, "handle %p, state %s, %d sockets\n",
-              (void *)easy->easy_handle,
-              statename[easy->state], easy->numsocks);
-      for(i=0; i < easy->numsocks; i++) {
-        curl_socket_t s = easy->sockets[i];
-        struct Curl_sh_entry *entry =
-          Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));
-
-        fprintf(stderr, "%d ", (int)s);
-        if(!entry) {
-          fprintf(stderr, "INTERNAL CONFUSION\n");
-          continue;
-        }
-        fprintf(stderr, "[%s %s] ",
-                entry->action&CURL_POLL_IN?"RECVING":"",
-                entry->action&CURL_POLL_OUT?"SENDING":"");
-      }
-      if(easy->numsocks)
-        fprintf(stderr, "\n");
-    }
-  }
-}
-#endif
diff --git a/lib/netrc.c b/lib/netrc.c
deleted file mode 100644 (file)
index 10853d3..0000000
+++ /dev/null
@@ -1,186 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef HAVE_PWD_H
-#include <pwd.h>
-#endif
-#ifdef __VMS
-#include <unixlib.h>
-#endif
-
-#include <curl/curl.h>
-#include "curl_netrc.h"
-
-#include "curl_strequal.h"
-#include "curl_strtok.h"
-#include "curl_memory.h"
-#include "curl_rawstr.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/* Get user and password from .netrc when given a machine name */
-
-enum host_lookup_state {
-  NOTHING,
-  HOSTFOUND,    /* the 'machine' keyword was found */
-  HOSTVALID     /* this is "our" machine! */
-};
-
-/*
- * @unittest: 1304
- */
-int Curl_parsenetrc(const char *host,
-                    char *login,
-                    char *password,
-                    char *netrcfile)
-{
-  FILE *file;
-  int retcode=1;
-  int specific_login = (login[0] != 0);
-  char *home = NULL;
-  bool home_alloc = FALSE;
-  bool netrc_alloc = FALSE;
-  enum host_lookup_state state=NOTHING;
-
-  char state_login=0;      /* Found a login keyword */
-  char state_password=0;   /* Found a password keyword */
-  int state_our_login=FALSE;  /* With specific_login, found *our* login name */
-
-#define NETRC DOT_CHAR "netrc"
-
-  if(!netrcfile) {
-    home = curl_getenv("HOME"); /* portable environment reader */
-    if(home) {
-      home_alloc = TRUE;
-#if defined(HAVE_GETPWUID) && defined(HAVE_GETEUID)
-    }
-    else {
-      struct passwd *pw;
-      pw= getpwuid(geteuid());
-      if(pw) {
-#ifdef __VMS
-        home = decc_translate_vms(pw->pw_dir);
-#else
-        home = pw->pw_dir;
-#endif
-      }
-#endif
-    }
-
-    if(!home)
-      return -1;
-
-    netrcfile = curl_maprintf("%s%s%s", home, DIR_CHAR, NETRC);
-    if(!netrcfile) {
-      if(home_alloc)
-        free(home);
-      return -1;
-    }
-    netrc_alloc = TRUE;
-  }
-
-  file = fopen(netrcfile, "r");
-  if(file) {
-    char *tok;
-    char *tok_buf;
-    bool done=FALSE;
-    char netrcbuffer[256];
-    int  netrcbuffsize = (int)sizeof(netrcbuffer);
-
-    while(!done && fgets(netrcbuffer, netrcbuffsize, file)) {
-      tok=strtok_r(netrcbuffer, " \t\n", &tok_buf);
-      while(!done && tok) {
-
-        if(login[0] && password[0]) {
-          done=TRUE;
-          break;
-        }
-
-        switch(state) {
-        case NOTHING:
-          if(Curl_raw_equal("machine", tok)) {
-            /* the next tok is the machine name, this is in itself the
-               delimiter that starts the stuff entered for this machine,
-               after this we need to search for 'login' and
-               'password'. */
-            state=HOSTFOUND;
-          }
-          break;
-        case HOSTFOUND:
-          if(Curl_raw_equal(host, tok)) {
-            /* and yes, this is our host! */
-            state=HOSTVALID;
-            retcode=0; /* we did find our host */
-          }
-          else
-            /* not our host */
-            state=NOTHING;
-          break;
-        case HOSTVALID:
-          /* we are now parsing sub-keywords concerning "our" host */
-          if(state_login) {
-            if(specific_login) {
-              state_our_login = Curl_raw_equal(login, tok);
-            }
-            else {
-              strncpy(login, tok, LOGINSIZE-1);
-            }
-            state_login=0;
-          }
-          else if(state_password) {
-            if(state_our_login || !specific_login) {
-              strncpy(password, tok, PASSWORDSIZE-1);
-            }
-            state_password=0;
-          }
-          else if(Curl_raw_equal("login", tok))
-            state_login=1;
-          else if(Curl_raw_equal("password", tok))
-            state_password=1;
-          else if(Curl_raw_equal("machine", tok)) {
-            /* ok, there's machine here go => */
-            state = HOSTFOUND;
-            state_our_login = FALSE;
-          }
-          break;
-        } /* switch (state) */
-
-        tok = strtok_r(NULL, " \t\n", &tok_buf);
-      } /* while(tok) */
-    } /* while fgets() */
-
-    fclose(file);
-  }
-
-  if(home_alloc)
-    free(home);
-  if(netrc_alloc)
-    free(netrcfile);
-
-  return retcode;
-}
diff --git a/lib/non-ascii.c b/lib/non-ascii.c
deleted file mode 100644 (file)
index 68b33a9..0000000
+++ /dev/null
@@ -1,343 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef CURL_DOES_CONVERSIONS
-
-#include <curl/curl.h>
-
-#include "curl_non_ascii.h"
-#include "curl_formdata.h"
-#include "curl_sendf.h"
-#include "curl_urldata.h"
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-#ifdef HAVE_ICONV
-#include <iconv.h>
-/* set default codesets for iconv */
-#ifndef CURL_ICONV_CODESET_OF_NETWORK
-#define CURL_ICONV_CODESET_OF_NETWORK "ISO8859-1"
-#endif
-#ifndef CURL_ICONV_CODESET_FOR_UTF8
-#define CURL_ICONV_CODESET_FOR_UTF8   "UTF-8"
-#endif
-#define ICONV_ERROR  (size_t)-1
-#endif /* HAVE_ICONV */
-
-/*
- * Curl_convert_clone() returns a malloced copy of the source string (if
- * returning CURLE_OK), with the data converted to network format.
- */
-CURLcode Curl_convert_clone(struct SessionHandle *data,
-                           const char *indata,
-                           size_t insize,
-                           char **outbuf)
-{
-  char *convbuf;
-  CURLcode result;
-
-  convbuf = malloc(insize);
-  if(!convbuf)
-    return CURLE_OUT_OF_MEMORY;
-
-  memcpy(convbuf, indata, insize);
-  result = Curl_convert_to_network(data, convbuf, insize);
-  if(result) {
-    free(convbuf);
-    return result;
-  }
-
-  *outbuf = convbuf; /* return the converted buffer */
-
-  return CURLE_OK;
-}
-
-/*
- * Curl_convert_to_network() is an internal function for performing ASCII
- * conversions on non-ASCII platforms. It convers the buffer _in place_.
- */
-CURLcode Curl_convert_to_network(struct SessionHandle *data,
-                                 char *buffer, size_t length)
-{
-  CURLcode rc;
-
-  if(data->set.convtonetwork) {
-    /* use translation callback */
-    rc = data->set.convtonetwork(buffer, length);
-    if(rc != CURLE_OK) {
-      failf(data,
-            "CURLOPT_CONV_TO_NETWORK_FUNCTION callback returned %d: %s",
-            (int)rc, curl_easy_strerror(rc));
-    }
-    return rc;
-  }
-  else {
-#ifdef HAVE_ICONV
-    /* do the translation ourselves */
-    char *input_ptr, *output_ptr;
-    size_t in_bytes, out_bytes, rc;
-    int error;
-
-    /* open an iconv conversion descriptor if necessary */
-    if(data->outbound_cd == (iconv_t)-1) {
-      data->outbound_cd = iconv_open(CURL_ICONV_CODESET_OF_NETWORK,
-                                     CURL_ICONV_CODESET_OF_HOST);
-      if(data->outbound_cd == (iconv_t)-1) {
-        error = ERRNO;
-        failf(data,
-              "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s",
-              CURL_ICONV_CODESET_OF_NETWORK,
-              CURL_ICONV_CODESET_OF_HOST,
-              error, strerror(error));
-        return CURLE_CONV_FAILED;
-      }
-    }
-    /* call iconv */
-    input_ptr = output_ptr = buffer;
-    in_bytes = out_bytes = length;
-    rc = iconv(data->outbound_cd, (const char**)&input_ptr, &in_bytes,
-               &output_ptr, &out_bytes);
-    if((rc == ICONV_ERROR) || (in_bytes != 0)) {
-      error = ERRNO;
-      failf(data,
-            "The Curl_convert_to_network iconv call failed with errno %i: %s",
-            error, strerror(error));
-      return CURLE_CONV_FAILED;
-    }
-#else
-    failf(data, "CURLOPT_CONV_TO_NETWORK_FUNCTION callback required");
-    return CURLE_CONV_REQD;
-#endif /* HAVE_ICONV */
-  }
-
-  return CURLE_OK;
-}
-
-/*
- * Curl_convert_from_network() is an internal function for performing ASCII
- * conversions on non-ASCII platforms. It convers the buffer _in place_.
- */
-CURLcode Curl_convert_from_network(struct SessionHandle *data,
-                                   char *buffer, size_t length)
-{
-  CURLcode rc;
-
-  if(data->set.convfromnetwork) {
-    /* use translation callback */
-    rc = data->set.convfromnetwork(buffer, length);
-    if(rc != CURLE_OK) {
-      failf(data,
-            "CURLOPT_CONV_FROM_NETWORK_FUNCTION callback returned %d: %s",
-            (int)rc, curl_easy_strerror(rc));
-    }
-    return rc;
-  }
-  else {
-#ifdef HAVE_ICONV
-    /* do the translation ourselves */
-    char *input_ptr, *output_ptr;
-    size_t in_bytes, out_bytes, rc;
-    int error;
-
-    /* open an iconv conversion descriptor if necessary */
-    if(data->inbound_cd == (iconv_t)-1) {
-      data->inbound_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
-                                    CURL_ICONV_CODESET_OF_NETWORK);
-      if(data->inbound_cd == (iconv_t)-1) {
-        error = ERRNO;
-        failf(data,
-              "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s",
-              CURL_ICONV_CODESET_OF_HOST,
-              CURL_ICONV_CODESET_OF_NETWORK,
-              error, strerror(error));
-        return CURLE_CONV_FAILED;
-      }
-    }
-    /* call iconv */
-    input_ptr = output_ptr = buffer;
-    in_bytes = out_bytes = length;
-    rc = iconv(data->inbound_cd, (const char **)&input_ptr, &in_bytes,
-               &output_ptr, &out_bytes);
-    if((rc == ICONV_ERROR) || (in_bytes != 0)) {
-      error = ERRNO;
-      failf(data,
-            "Curl_convert_from_network iconv call failed with errno %i: %s",
-            error, strerror(error));
-      return CURLE_CONV_FAILED;
-    }
-#else
-    failf(data, "CURLOPT_CONV_FROM_NETWORK_FUNCTION callback required");
-    return CURLE_CONV_REQD;
-#endif /* HAVE_ICONV */
-  }
-
-  return CURLE_OK;
-}
-
-/*
- * Curl_convert_from_utf8() is an internal function for performing UTF-8
- * conversions on non-ASCII platforms.
- */
-CURLcode Curl_convert_from_utf8(struct SessionHandle *data,
-                                char *buffer, size_t length)
-{
-  CURLcode rc;
-
-  if(data->set.convfromutf8) {
-    /* use translation callback */
-    rc = data->set.convfromutf8(buffer, length);
-    if(rc != CURLE_OK) {
-      failf(data,
-            "CURLOPT_CONV_FROM_UTF8_FUNCTION callback returned %d: %s",
-            (int)rc, curl_easy_strerror(rc));
-    }
-    return rc;
-  }
-  else {
-#ifdef HAVE_ICONV
-    /* do the translation ourselves */
-    const char *input_ptr;
-    char *output_ptr;
-    size_t in_bytes, out_bytes, rc;
-    int error;
-
-    /* open an iconv conversion descriptor if necessary */
-    if(data->utf8_cd == (iconv_t)-1) {
-      data->utf8_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
-                                 CURL_ICONV_CODESET_FOR_UTF8);
-      if(data->utf8_cd == (iconv_t)-1) {
-        error = ERRNO;
-        failf(data,
-              "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s",
-              CURL_ICONV_CODESET_OF_HOST,
-              CURL_ICONV_CODESET_FOR_UTF8,
-              error, strerror(error));
-        return CURLE_CONV_FAILED;
-      }
-    }
-    /* call iconv */
-    input_ptr = output_ptr = buffer;
-    in_bytes = out_bytes = length;
-    rc = iconv(data->utf8_cd, &input_ptr, &in_bytes,
-               &output_ptr, &out_bytes);
-    if((rc == ICONV_ERROR) || (in_bytes != 0)) {
-      error = ERRNO;
-      failf(data,
-            "The Curl_convert_from_utf8 iconv call failed with errno %i: %s",
-            error, strerror(error));
-      return CURLE_CONV_FAILED;
-    }
-    if(output_ptr < input_ptr) {
-      /* null terminate the now shorter output string */
-      *output_ptr = 0x00;
-    }
-#else
-    failf(data, "CURLOPT_CONV_FROM_UTF8_FUNCTION callback required");
-    return CURLE_CONV_REQD;
-#endif /* HAVE_ICONV */
-  }
-
-  return CURLE_OK;
-}
-
-/*
- * Init conversion stuff for a SessionHandle
- */
-void Curl_convert_init(struct SessionHandle *data)
-{
-#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
-  /* conversion descriptors for iconv calls */
-  data->outbound_cd = (iconv_t)-1;
-  data->inbound_cd  = (iconv_t)-1;
-  data->utf8_cd     = (iconv_t)-1;
-#else
-  (void)data;
-#endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */
-}
-
-/*
- * Setup conversion stuff for a SessionHandle
- */
-void Curl_convert_setup(struct SessionHandle *data)
-{
-  data->inbound_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
-                                CURL_ICONV_CODESET_OF_NETWORK);
-  data->outbound_cd = iconv_open(CURL_ICONV_CODESET_OF_NETWORK,
-                                 CURL_ICONV_CODESET_OF_HOST);
-  data->utf8_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
-                             CURL_ICONV_CODESET_FOR_UTF8);
-}
-
-/*
- * Close conversion stuff for a SessionHandle
- */
-
-void Curl_convert_close(struct SessionHandle *data)
-{
-#ifdef HAVE_ICONV
-  /* close iconv conversion descriptors */
-  if(data->inbound_cd != (iconv_t)-1) {
-    iconv_close(data->inbound_cd);
-  }
-  if(data->outbound_cd != (iconv_t)-1) {
-    iconv_close(data->outbound_cd);
-  }
-  if(data->utf8_cd != (iconv_t)-1) {
-    iconv_close(data->utf8_cd);
-  }
-#else
-  (void)data;
-#endif /* HAVE_ICONV */
-}
-
-/*
- * Curl_convert_form() is used from curl_http.c, this converts any form items
- * that need to be sent in the network encoding.  Returns CURLE_OK on success.
- */
-CURLcode Curl_convert_form(struct SessionHandle *data, struct FormData *form)
-{
-  struct FormData *next;
-  CURLcode rc;
-
-  if(!form)
-    return CURLE_OK;
-
-  if(!data)
-    return CURLE_BAD_FUNCTION_ARGUMENT;
-
-  do {
-    next=form->next;  /* the following form line */
-    if(form->type == FORM_DATA) {
-      rc = Curl_convert_to_network(data, form->line, form->length);
-      /* Curl_convert_to_network calls failf if unsuccessful */
-      if(rc != CURLE_OK)
-        return rc;
-    }
-  } while((form = next) != NULL); /* continue */
-  return CURLE_OK;
-}
-
-#endif /* CURL_DOES_CONVERSIONS */
diff --git a/lib/nonblock.c b/lib/nonblock.c
deleted file mode 100644 (file)
index 6e6d128..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
-
-#if (defined(HAVE_IOCTL_FIONBIO) && defined(NETWARE))
-#include <sys/filio.h>
-#endif
-#ifdef __VMS
-#include <in.h>
-#include <inet.h>
-#endif
-
-#include "curl_nonblock.h"
-
-/*
- * curlx_nonblock() set the given socket to either blocking or non-blocking
- * mode based on the 'nonblock' boolean argument. This function is highly
- * portable.
- */
-int curlx_nonblock(curl_socket_t sockfd,    /* operate on this */
-                   int nonblock   /* TRUE or FALSE */)
-{
-#if defined(USE_BLOCKING_SOCKETS)
-
-  return 0; /* returns success */
-
-#elif defined(HAVE_FCNTL_O_NONBLOCK)
-
-  /* most recent unix versions */
-  int flags;
-  flags = sfcntl(sockfd, F_GETFL, 0);
-  if(nonblock)
-    return sfcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
-  else
-    return sfcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK));
-
-#elif defined(HAVE_IOCTL_FIONBIO)
-
-  /* older unix versions */
-  int flags = nonblock ? 1 : 0;
-  return ioctl(sockfd, FIONBIO, &flags);
-
-#elif defined(HAVE_IOCTLSOCKET_FIONBIO)
-
-  /* Windows */
-  unsigned long flags = nonblock ? 1UL : 0UL;
-  return ioctlsocket(sockfd, FIONBIO, &flags);
-
-#elif defined(HAVE_IOCTLSOCKET_CAMEL_FIONBIO)
-
-  /* Amiga */
-  long flags = nonblock ? 1L : 0L;
-  return IoctlSocket(sockfd, FIONBIO, flags);
-
-#elif defined(HAVE_SETSOCKOPT_SO_NONBLOCK)
-
-  /* BeOS */
-  long b = nonblock ? 1L : 0L;
-  return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b));
-
-#else
-#  error "no non-blocking method was found/used/set"
-#endif
-}
diff --git a/lib/nss.c b/lib/nss.c
deleted file mode 100644 (file)
index 15e92a7..0000000
--- a/lib/nss.c
+++ /dev/null
@@ -1,1572 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-/*
- * Source file for all NSS-specific code for the TLS/SSL layer. No code
- * but curl_sslgen.c should ever call or use these functions.
- */
-
-#include "curl_setup.h"
-
-#ifdef USE_NSS
-
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_formdata.h" /* for the boundary function */
-#include "curl_url.h" /* for the ssl config check function */
-#include "curl_connect.h"
-#include "curl_strequal.h"
-#include "curl_select.h"
-#include "curl_sslgen.h"
-#include "curl_llist.h"
-
-#define _MPRINTF_REPLACE /* use the internal *printf() functions */
-#include <curl/mprintf.h>
-
-#include "curl_nssg.h"
-#include <nspr.h>
-#include <nss.h>
-#include <ssl.h>
-#include <sslerr.h>
-#include <secerr.h>
-#include <secmod.h>
-#include <sslproto.h>
-#include <prtypes.h>
-#include <pk11pub.h>
-#include <prio.h>
-#include <secitem.h>
-#include <secport.h>
-#include <certdb.h>
-#include <base64.h>
-#include <cert.h>
-#include <prerror.h>
-
-#include "curl_memory.h"
-#include "curl_rawstr.h"
-#include "curl_warnless.h"
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-#define SSL_DIR "/etc/pki/nssdb"
-
-/* enough to fit the string "PEM Token #[0|1]" */
-#define SLOTSIZE 13
-
-PRFileDesc *PR_ImportTCPSocket(PRInt32 osfd);
-
-PRLock * nss_initlock = NULL;
-PRLock * nss_crllock = NULL;
-#ifdef HAVE_NSS_INITCONTEXT
-NSSInitContext * nss_context = NULL;
-#endif
-
-volatile int initialized = 0;
-
-typedef struct {
-  const char *name;
-  int num;
-} cipher_s;
-
-#define PK11_SETATTRS(_attr, _idx, _type, _val, _len) do {  \
-  CK_ATTRIBUTE *ptr = (_attr) + ((_idx)++);                 \
-  ptr->type = (_type);                                      \
-  ptr->pValue = (_val);                                     \
-  ptr->ulValueLen = (_len);                                 \
-} WHILE_FALSE
-
-#define CERT_NewTempCertificate __CERT_NewTempCertificate
-
-#define NUM_OF_CIPHERS sizeof(cipherlist)/sizeof(cipherlist[0])
-static const cipher_s cipherlist[] = {
-  /* SSL2 cipher suites */
-  {"rc4",                        SSL_EN_RC4_128_WITH_MD5},
-  {"rc4-md5",                    SSL_EN_RC4_128_WITH_MD5},
-  {"rc4export",                  SSL_EN_RC4_128_EXPORT40_WITH_MD5},
-  {"rc2",                        SSL_EN_RC2_128_CBC_WITH_MD5},
-  {"rc2export",                  SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5},
-  {"des",                        SSL_EN_DES_64_CBC_WITH_MD5},
-  {"desede3",                    SSL_EN_DES_192_EDE3_CBC_WITH_MD5},
-  /* SSL3/TLS cipher suites */
-  {"rsa_rc4_128_md5",            SSL_RSA_WITH_RC4_128_MD5},
-  {"rsa_rc4_128_sha",            SSL_RSA_WITH_RC4_128_SHA},
-  {"rsa_3des_sha",               SSL_RSA_WITH_3DES_EDE_CBC_SHA},
-  {"rsa_des_sha",                SSL_RSA_WITH_DES_CBC_SHA},
-  {"rsa_rc4_40_md5",             SSL_RSA_EXPORT_WITH_RC4_40_MD5},
-  {"rsa_rc2_40_md5",             SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5},
-  {"rsa_null_md5",               SSL_RSA_WITH_NULL_MD5},
-  {"rsa_null_sha",               SSL_RSA_WITH_NULL_SHA},
-  {"fips_3des_sha",              SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA},
-  {"fips_des_sha",               SSL_RSA_FIPS_WITH_DES_CBC_SHA},
-  {"fortezza",                   SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA},
-  {"fortezza_rc4_128_sha",       SSL_FORTEZZA_DMS_WITH_RC4_128_SHA},
-  {"fortezza_null",              SSL_FORTEZZA_DMS_WITH_NULL_SHA},
-  /* TLS 1.0: Exportable 56-bit Cipher Suites. */
-  {"rsa_des_56_sha",             TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA},
-  {"rsa_rc4_56_sha",             TLS_RSA_EXPORT1024_WITH_RC4_56_SHA},
-  /* AES ciphers. */
-  {"rsa_aes_128_sha",            TLS_RSA_WITH_AES_128_CBC_SHA},
-  {"rsa_aes_256_sha",            TLS_RSA_WITH_AES_256_CBC_SHA},
-#ifdef NSS_ENABLE_ECC
-  /* ECC ciphers. */
-  {"ecdh_ecdsa_null_sha",        TLS_ECDH_ECDSA_WITH_NULL_SHA},
-  {"ecdh_ecdsa_rc4_128_sha",     TLS_ECDH_ECDSA_WITH_RC4_128_SHA},
-  {"ecdh_ecdsa_3des_sha",        TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA},
-  {"ecdh_ecdsa_aes_128_sha",     TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA},
-  {"ecdh_ecdsa_aes_256_sha",     TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA},
-  {"ecdhe_ecdsa_null_sha",       TLS_ECDHE_ECDSA_WITH_NULL_SHA},
-  {"ecdhe_ecdsa_rc4_128_sha",    TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
-  {"ecdhe_ecdsa_3des_sha",       TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA},
-  {"ecdhe_ecdsa_aes_128_sha",    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
-  {"ecdhe_ecdsa_aes_256_sha",    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
-  {"ecdh_rsa_null_sha",          TLS_ECDH_RSA_WITH_NULL_SHA},
-  {"ecdh_rsa_128_sha",           TLS_ECDH_RSA_WITH_RC4_128_SHA},
-  {"ecdh_rsa_3des_sha",          TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA},
-  {"ecdh_rsa_aes_128_sha",       TLS_ECDH_RSA_WITH_AES_128_CBC_SHA},
-  {"ecdh_rsa_aes_256_sha",       TLS_ECDH_RSA_WITH_AES_256_CBC_SHA},
-  {"echde_rsa_null",             TLS_ECDHE_RSA_WITH_NULL_SHA},
-  {"ecdhe_rsa_rc4_128_sha",      TLS_ECDHE_RSA_WITH_RC4_128_SHA},
-  {"ecdhe_rsa_3des_sha",         TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA},
-  {"ecdhe_rsa_aes_128_sha",      TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
-  {"ecdhe_rsa_aes_256_sha",      TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
-  {"ecdh_anon_null_sha",         TLS_ECDH_anon_WITH_NULL_SHA},
-  {"ecdh_anon_rc4_128sha",       TLS_ECDH_anon_WITH_RC4_128_SHA},
-  {"ecdh_anon_3des_sha",         TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA},
-  {"ecdh_anon_aes_128_sha",      TLS_ECDH_anon_WITH_AES_128_CBC_SHA},
-  {"ecdh_anon_aes_256_sha",      TLS_ECDH_anon_WITH_AES_256_CBC_SHA},
-#endif
-};
-
-/* following ciphers are new in NSS 3.4 and not enabled by default, therefore
-   they are enabled explicitly */
-static const int enable_ciphers_by_default[] = {
-  TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
-  TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
-  TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
-  TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
-  TLS_RSA_WITH_AES_128_CBC_SHA,
-  TLS_RSA_WITH_AES_256_CBC_SHA,
-  SSL_NULL_WITH_NULL_NULL
-};
-
-static const char* pem_library = "libnsspem.so";
-SECMODModule* mod = NULL;
-
-static const char* nss_error_to_name(PRErrorCode code)
-{
-  const char *name = PR_ErrorToName(code);
-  if(name)
-    return name;
-
-  return "unknown error";
-}
-
-static void nss_print_error_message(struct SessionHandle *data, PRUint32 err)
-{
-  failf(data, "%s", PR_ErrorToString(err, PR_LANGUAGE_I_DEFAULT));
-}
-
-static SECStatus set_ciphers(struct SessionHandle *data, PRFileDesc * model,
-                             char *cipher_list)
-{
-  unsigned int i;
-  PRBool cipher_state[NUM_OF_CIPHERS];
-  PRBool found;
-  char *cipher;
-  SECStatus rv;
-
-  /* First disable all ciphers. This uses a different max value in case
-   * NSS adds more ciphers later we don't want them available by
-   * accident
-   */
-  for(i=0; i<SSL_NumImplementedCiphers; i++) {
-    SSL_CipherPrefSet(model, SSL_ImplementedCiphers[i], SSL_NOT_ALLOWED);
-  }
-
-  /* Set every entry in our list to false */
-  for(i=0; i<NUM_OF_CIPHERS; i++) {
-    cipher_state[i] = PR_FALSE;
-  }
-
-  cipher = cipher_list;
-
-  while(cipher_list && (cipher_list[0])) {
-    while((*cipher) && (ISSPACE(*cipher)))
-      ++cipher;
-
-    if((cipher_list = strchr(cipher, ','))) {
-      *cipher_list++ = '\0';
-    }
-
-    found = PR_FALSE;
-
-    for(i=0; i<NUM_OF_CIPHERS; i++) {
-      if(Curl_raw_equal(cipher, cipherlist[i].name)) {
-        cipher_state[i] = PR_TRUE;
-        found = PR_TRUE;
-        break;
-      }
-    }
-
-    if(found == PR_FALSE) {
-      failf(data, "Unknown cipher in list: %s", cipher);
-      return SECFailure;
-    }
-
-    if(cipher_list) {
-      cipher = cipher_list;
-    }
-  }
-
-  /* Finally actually enable the selected ciphers */
-  for(i=0; i<NUM_OF_CIPHERS; i++) {
-    rv = SSL_CipherPrefSet(model, cipherlist[i].num, cipher_state[i]);
-    if(rv != SECSuccess) {
-      failf(data, "cipher-suite not supported by NSS: %s", cipherlist[i].name);
-      return SECFailure;
-    }
-  }
-
-  return SECSuccess;
-}
-
-/*
- * Get the number of ciphers that are enabled. We use this to determine
- * if we need to call NSS_SetDomesticPolicy() to enable the default ciphers.
- */
-static int num_enabled_ciphers(void)
-{
-  PRInt32 policy = 0;
-  int count = 0;
-  unsigned int i;
-
-  for(i=0; i<NUM_OF_CIPHERS; i++) {
-    SSL_CipherPolicyGet(cipherlist[i].num, &policy);
-    if(policy)
-      count++;
-  }
-  return count;
-}
-
-/*
- * Determine whether the nickname passed in is a filename that needs to
- * be loaded as a PEM or a regular NSS nickname.
- *
- * returns 1 for a file
- * returns 0 for not a file (NSS nickname)
- */
-static int is_file(const char *filename)
-{
-  struct_stat st;
-
-  if(filename == NULL)
-    return 0;
-
-  if(stat(filename, &st) == 0)
-    if(S_ISREG(st.st_mode))
-      return 1;
-
-  return 0;
-}
-
-/* Check if the given string is filename or nickname of a certificate.  If the
- * given string is recognized as filename, return NULL.  If the given string is
- * recognized as nickname, return a duplicated string.  The returned string
- * should be later deallocated using free().  If the OOM failure occurs, we
- * return NULL, too.
- */
-static char* dup_nickname(struct SessionHandle *data, enum dupstring cert_kind)
-{
-  const char *str = data->set.str[cert_kind];
-  const char *n;
-
-  if(!is_file(str))
-    /* no such file exists, use the string as nickname */
-    return strdup(str);
-
-  /* search the last slash; we require at least one slash in a file name */
-  n = strrchr(str, '/');
-  if(!n) {
-    infof(data, "warning: certificate file name \"%s\" handled as nickname; "
-          "please use \"./%s\" to force file name\n", str, str);
-    return strdup(str);
-  }
-
-  /* we'll use the PEM reader to read the certificate from file */
-  return NULL;
-}
-
-/* Call PK11_CreateGenericObject() with the given obj_class and filename.  If
- * the call succeeds, append the object handle to the list of objects so that
- * the object can be destroyed in Curl_nss_close(). */
-static CURLcode nss_create_object(struct ssl_connect_data *ssl,
-                                  CK_OBJECT_CLASS obj_class,
-                                  const char *filename, bool cacert)
-{
-  PK11SlotInfo *slot;
-  PK11GenericObject *obj;
-  CK_BBOOL cktrue = CK_TRUE;
-  CK_BBOOL ckfalse = CK_FALSE;
-  CK_ATTRIBUTE attrs[/* max count of attributes */ 4];
-  int attr_cnt = 0;
-  CURLcode err = (cacert)
-    ? CURLE_SSL_CACERT_BADFILE
-    : CURLE_SSL_CERTPROBLEM;
-
-  const int slot_id = (cacert) ? 0 : 1;
-  char *slot_name = aprintf("PEM Token #%d", slot_id);
-  if(!slot_name)
-    return CURLE_OUT_OF_MEMORY;
-
-  slot = PK11_FindSlotByName(slot_name);
-  free(slot_name);
-  if(!slot)
-    return err;
-
-  PK11_SETATTRS(attrs, attr_cnt, CKA_CLASS, &obj_class, sizeof(obj_class));
-  PK11_SETATTRS(attrs, attr_cnt, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL));
-  PK11_SETATTRS(attrs, attr_cnt, CKA_LABEL, (unsigned char *)filename,
-                strlen(filename) + 1);
-
-  if(CKO_CERTIFICATE == obj_class) {
-    CK_BBOOL *pval = (cacert) ? (&cktrue) : (&ckfalse);
-    PK11_SETATTRS(attrs, attr_cnt, CKA_TRUST, pval, sizeof(*pval));
-  }
-
-  obj = PK11_CreateGenericObject(slot, attrs, attr_cnt, PR_FALSE);
-  PK11_FreeSlot(slot);
-  if(!obj)
-    return err;
-
-  if(!Curl_llist_insert_next(ssl->obj_list, ssl->obj_list->tail, obj)) {
-    PK11_DestroyGenericObject(obj);
-    return CURLE_OUT_OF_MEMORY;
-  }
-
-  if(!cacert && CKO_CERTIFICATE == obj_class)
-    /* store reference to a client certificate */
-    ssl->obj_clicert = obj;
-
-  return CURLE_OK;
-}
-
-/* Destroy the NSS object whose handle is given by ptr.  This function is
- * a callback of Curl_llist_alloc() used by Curl_llist_destroy() to destroy
- * NSS objects in Curl_nss_close() */
-static void nss_destroy_object(void *user, void *ptr)
-{
-  PK11GenericObject *obj = (PK11GenericObject *)ptr;
-  (void) user;
-  PK11_DestroyGenericObject(obj);
-}
-
-static CURLcode nss_load_cert(struct ssl_connect_data *ssl,
-                              const char *filename, PRBool cacert)
-{
-  CURLcode err = (cacert)
-    ? CURLE_SSL_CACERT_BADFILE
-    : CURLE_SSL_CERTPROBLEM;
-
-  /* libnsspem.so leaks memory if the requested file does not exist.  For more
-   * details, go to <https://bugzilla.redhat.com/734760>. */
-  if(is_file(filename))
-    err = nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert);
-
-  if(CURLE_OK == err && !cacert) {
-    /* we have successfully loaded a client certificate */
-    CERTCertificate *cert;
-    char *nickname = NULL;
-    char *n = strrchr(filename, '/');
-    if(n)
-      n++;
-
-    /* The following undocumented magic helps to avoid a SIGSEGV on call
-     * of PK11_ReadRawAttribute() from SelectClientCert() when using an
-     * immature version of libnsspem.so.  For more details, go to
-     * <https://bugzilla.redhat.com/733685>. */
-    nickname = aprintf("PEM Token #1:%s", n);
-    if(nickname) {
-      cert = PK11_FindCertFromNickname(nickname, NULL);
-      if(cert)
-        CERT_DestroyCertificate(cert);
-
-      free(nickname);
-    }
-  }
-
-  return err;
-}
-
-/* add given CRL to cache if it is not already there */
-static SECStatus nss_cache_crl(SECItem *crlDER)
-{
-  CERTCertDBHandle *db = CERT_GetDefaultCertDB();
-  CERTSignedCrl *crl = SEC_FindCrlByDERCert(db, crlDER, 0);
-  if(crl) {
-    /* CRL already cached */
-    SEC_DestroyCrl(crl);
-    SECITEM_FreeItem(crlDER, PR_FALSE);
-    return SECSuccess;
-  }
-
-  /* acquire lock before call of CERT_CacheCRL() */
-  PR_Lock(nss_crllock);
-  if(SECSuccess != CERT_CacheCRL(db, crlDER)) {
-    /* unable to cache CRL */
-    PR_Unlock(nss_crllock);
-    SECITEM_FreeItem(crlDER, PR_FALSE);
-    return SECFailure;
-  }
-
-  /* we need to clear session cache, so that the CRL could take effect */
-  SSL_ClearSessionCache();
-  PR_Unlock(nss_crllock);
-  return SECSuccess;
-}
-
-static SECStatus nss_load_crl(const char* crlfilename)
-{
-  PRFileDesc *infile;
-  PRFileInfo  info;
-  SECItem filedata = { 0, NULL, 0 };
-  SECItem crlDER = { 0, NULL, 0 };
-  char *body;
-
-  infile = PR_Open(crlfilename, PR_RDONLY, 0);
-  if(!infile)
-    return SECFailure;
-
-  if(PR_SUCCESS != PR_GetOpenFileInfo(infile, &info))
-    goto fail;
-
-  if(!SECITEM_AllocItem(NULL, &filedata, info.size + /* zero ended */ 1))
-    goto fail;
-
-  if(info.size != PR_Read(infile, filedata.data, info.size))
-    goto fail;
-
-  /* place a trailing zero right after the visible data */
-  body = (char*)filedata.data;
-  body[--filedata.len] = '\0';
-
-  body = strstr(body, "-----BEGIN");
-  if(body) {
-    /* assume ASCII */
-    char *trailer;
-    char *begin = PORT_Strchr(body, '\n');
-    if(!begin)
-      begin = PORT_Strchr(body, '\r');
-    if(!begin)
-      goto fail;
-
-    trailer = strstr(++begin, "-----END");
-    if(!trailer)
-      goto fail;
-
-    /* retrieve DER from ASCII */
-    *trailer = '\0';
-    if(ATOB_ConvertAsciiToItem(&crlDER, begin))
-      goto fail;
-
-    SECITEM_FreeItem(&filedata, PR_FALSE);
-  }
-  else
-    /* assume DER */
-    crlDER = filedata;
-
-  PR_Close(infile);
-  return nss_cache_crl(&crlDER);
-
-fail:
-  PR_Close(infile);
-  SECITEM_FreeItem(&filedata, PR_FALSE);
-  return SECFailure;
-}
-
-static CURLcode nss_load_key(struct connectdata *conn, int sockindex,
-                             char *key_file)
-{
-  PK11SlotInfo *slot;
-  SECStatus status;
-  CURLcode rv;
-  struct ssl_connect_data *ssl = conn->ssl;
-  (void)sockindex; /* unused */
-
-  rv = nss_create_object(ssl, CKO_PRIVATE_KEY, key_file, FALSE);
-  if(CURLE_OK != rv) {
-    PR_SetError(SEC_ERROR_BAD_KEY, 0);
-    return rv;
-  }
-
-  slot = PK11_FindSlotByName("PEM Token #1");
-  if(!slot)
-    return CURLE_SSL_CERTPROBLEM;
-
-  /* This will force the token to be seen as re-inserted */
-  SECMOD_WaitForAnyTokenEvent(mod, 0, 0);
-  PK11_IsPresent(slot);
-
-  status = PK11_Authenticate(slot, PR_TRUE,
-                             conn->data->set.str[STRING_KEY_PASSWD]);
-  PK11_FreeSlot(slot);
-  return (SECSuccess == status)
-    ? CURLE_OK
-    : CURLE_SSL_CERTPROBLEM;
-}
-
-static int display_error(struct connectdata *conn, PRInt32 err,
-                         const char *filename)
-{
-  switch(err) {
-  case SEC_ERROR_BAD_PASSWORD:
-    failf(conn->data, "Unable to load client key: Incorrect password");
-    return 1;
-  case SEC_ERROR_UNKNOWN_CERT:
-    failf(conn->data, "Unable to load certificate %s", filename);
-    return 1;
-  default:
-    break;
-  }
-  return 0; /* The caller will print a generic error */
-}
-
-static CURLcode cert_stuff(struct connectdata *conn, int sockindex,
-                           char *cert_file, char *key_file)
-{
-  struct SessionHandle *data = conn->data;
-  CURLcode rv;
-
-  if(cert_file) {
-    rv = nss_load_cert(&conn->ssl[sockindex], cert_file, PR_FALSE);
-    if(CURLE_OK != rv) {
-      const PRErrorCode err = PR_GetError();
-      if(!display_error(conn, err, cert_file)) {
-        const char *err_name = nss_error_to_name(err);
-        failf(data, "unable to load client cert: %d (%s)", err, err_name);
-      }
-
-      return rv;
-    }
-  }
-
-  if(key_file || (is_file(cert_file))) {
-    if(key_file)
-      rv = nss_load_key(conn, sockindex, key_file);
-    else
-      /* In case the cert file also has the key */
-      rv = nss_load_key(conn, sockindex, cert_file);
-    if(CURLE_OK != rv) {
-      const PRErrorCode err = PR_GetError();
-      if(!display_error(conn, err, key_file)) {
-        const char *err_name = nss_error_to_name(err);
-        failf(data, "unable to load client key: %d (%s)", err, err_name);
-      }
-
-      return rv;
-    }
-  }
-
-  return CURLE_OK;
-}
-
-static char * nss_get_password(PK11SlotInfo * slot, PRBool retry, void *arg)
-{
-  (void)slot; /* unused */
-  if(retry || NULL == arg)
-    return NULL;
-  else
-    return (char *)PORT_Strdup((char *)arg);
-}
-
-/* bypass the default SSL_AuthCertificate() hook in case we do not want to
- * verify peer */
-static SECStatus nss_auth_cert_hook(void *arg, PRFileDesc *fd, PRBool checksig,
-                                    PRBool isServer)
-{
-  struct connectdata *conn = (struct connectdata *)arg;
-  if(!conn->data->set.ssl.verifypeer) {
-    infof(conn->data, "skipping SSL peer certificate verification\n");
-    return SECSuccess;
-  }
-
-  return SSL_AuthCertificate(CERT_GetDefaultCertDB(), fd, checksig, isServer);
-}
-
-/**
- * Inform the application that the handshake is complete.
- */
-static void HandshakeCallback(PRFileDesc *sock, void *arg)
-{
-  (void)sock;
-  (void)arg;
-}
-
-static void display_cert_info(struct SessionHandle *data,
-                              CERTCertificate *cert)
-{
-  char *subject, *issuer, *common_name;
-  PRExplodedTime printableTime;
-  char timeString[256];
-  PRTime notBefore, notAfter;
-
-  subject = CERT_NameToAscii(&cert->subject);
-  issuer = CERT_NameToAscii(&cert->issuer);
-  common_name = CERT_GetCommonName(&cert->subject);
-  infof(data, "\tsubject: %s\n", subject);
-
-  CERT_GetCertTimes(cert, &notBefore, &notAfter);
-  PR_ExplodeTime(notBefore, PR_GMTParameters, &printableTime);
-  PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime);
-  infof(data, "\tstart date: %s\n", timeString);
-  PR_ExplodeTime(notAfter, PR_GMTParameters, &printableTime);
-  PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime);
-  infof(data, "\texpire date: %s\n", timeString);
-  infof(data, "\tcommon name: %s\n", common_name);
-  infof(data, "\tissuer: %s\n", issuer);
-
-  PR_Free(subject);
-  PR_Free(issuer);
-  PR_Free(common_name);
-}
-
-static void display_conn_info(struct connectdata *conn, PRFileDesc *sock)
-{
-  SSLChannelInfo channel;
-  SSLCipherSuiteInfo suite;
-  CERTCertificate *cert;
-
-  if(SSL_GetChannelInfo(sock, &channel, sizeof channel) ==
-     SECSuccess && channel.length == sizeof channel &&
-     channel.cipherSuite) {
-    if(SSL_GetCipherSuiteInfo(channel.cipherSuite,
-                              &suite, sizeof suite) == SECSuccess) {
-      infof(conn->data, "SSL connection using %s\n", suite.cipherSuiteName);
-    }
-  }
-
-  infof(conn->data, "Server certificate:\n");
-
-  cert = SSL_PeerCertificate(sock);
-  display_cert_info(conn->data, cert);
-  CERT_DestroyCertificate(cert);
-
-  return;
-}
-
-static SECStatus BadCertHandler(void *arg, PRFileDesc *sock)
-{
-  struct connectdata *conn = (struct connectdata *)arg;
-  struct SessionHandle *data = conn->data;
-  PRErrorCode err = PR_GetError();
-  CERTCertificate *cert;
-
-  /* remember the cert verification result */
-  data->set.ssl.certverifyresult = err;
-
-  if(err == SSL_ERROR_BAD_CERT_DOMAIN && !data->set.ssl.verifyhost)
-    /* we are asked not to verify the host name */
-    return SECSuccess;
-
-  /* print only info about the cert, the error is printed off the callback */
-  cert = SSL_PeerCertificate(sock);
-  if(cert) {
-    infof(data, "Server certificate:\n");
-    display_cert_info(data, cert);
-    CERT_DestroyCertificate(cert);
-  }
-
-  return SECFailure;
-}
-
-/**
- *
- * Check that the Peer certificate's issuer certificate matches the one found
- * by issuer_nickname.  This is not exactly the way OpenSSL and GNU TLS do the
- * issuer check, so we provide comments that mimic the OpenSSL
- * X509_check_issued function (in x509v3/v3_purp.c)
- */
-static SECStatus check_issuer_cert(PRFileDesc *sock,
-                                   char *issuer_nickname)
-{
-  CERTCertificate *cert,*cert_issuer,*issuer;
-  SECStatus res=SECSuccess;
-  void *proto_win = NULL;
-
-  /*
-    PRArenaPool   *tmpArena = NULL;
-    CERTAuthKeyID *authorityKeyID = NULL;
-    SECITEM       *caname = NULL;
-  */
-
-  cert = SSL_PeerCertificate(sock);
-  cert_issuer = CERT_FindCertIssuer(cert,PR_Now(),certUsageObjectSigner);
-
-  proto_win = SSL_RevealPinArg(sock);
-  issuer = PK11_FindCertFromNickname(issuer_nickname, proto_win);
-
-  if((!cert_issuer) || (!issuer))
-    res = SECFailure;
-  else if(SECITEM_CompareItem(&cert_issuer->derCert,
-                              &issuer->derCert)!=SECEqual)
-    res = SECFailure;
-
-  CERT_DestroyCertificate(cert);
-  CERT_DestroyCertificate(issuer);
-  CERT_DestroyCertificate(cert_issuer);
-  return res;
-}
-
-/**
- *
- * Callback to pick the SSL client certificate.
- */
-static SECStatus SelectClientCert(void *arg, PRFileDesc *sock,
-                                  struct CERTDistNamesStr *caNames,
-                                  struct CERTCertificateStr **pRetCert,
-                                  struct SECKEYPrivateKeyStr **pRetKey)
-{
-  struct ssl_connect_data *connssl = (struct ssl_connect_data *)arg;
-  struct SessionHandle *data = connssl->data;
-  const char *nickname = connssl->client_nickname;
-
-  if(connssl->obj_clicert) {
-    /* use the cert/key provided by PEM reader */
-    static const char pem_slotname[] = "PEM Token #1";
-    SECItem cert_der = { 0, NULL, 0 };
-    void *proto_win = SSL_RevealPinArg(sock);
-    struct CERTCertificateStr *cert;
-    struct SECKEYPrivateKeyStr *key;
-
-    PK11SlotInfo *slot = PK11_FindSlotByName(pem_slotname);
-    if(NULL == slot) {
-      failf(data, "NSS: PK11 slot not found: %s", pem_slotname);
-      return SECFailure;
-    }
-
-    if(PK11_ReadRawAttribute(PK11_TypeGeneric, connssl->obj_clicert, CKA_VALUE,
-                             &cert_der) != SECSuccess) {
-      failf(data, "NSS: CKA_VALUE not found in PK11 generic object");
-      PK11_FreeSlot(slot);
-      return SECFailure;
-    }
-
-    cert = PK11_FindCertFromDERCertItem(slot, &cert_der, proto_win);
-    SECITEM_FreeItem(&cert_der, PR_FALSE);
-    if(NULL == cert) {
-      failf(data, "NSS: client certificate from file not found");
-      PK11_FreeSlot(slot);
-      return SECFailure;
-    }
-
-    key = PK11_FindPrivateKeyFromCert(slot, cert, NULL);
-    PK11_FreeSlot(slot);
-    if(NULL == key) {
-      failf(data, "NSS: private key from file not found");
-      CERT_DestroyCertificate(cert);
-      return SECFailure;
-    }
-
-    infof(data, "NSS: client certificate from file\n");
-    display_cert_info(data, cert);
-
-    *pRetCert = cert;
-    *pRetKey = key;
-    return SECSuccess;
-  }
-
-  /* use the default NSS hook */
-  if(SECSuccess != NSS_GetClientAuthData((void *)nickname, sock, caNames,
-                                          pRetCert, pRetKey)
-      || NULL == *pRetCert) {
-
-    if(NULL == nickname)
-      failf(data, "NSS: client certificate not found (nickname not "
-            "specified)");
-    else
-      failf(data, "NSS: client certificate not found: %s", nickname);
-
-    return SECFailure;
-  }
-
-  /* get certificate nickname if any */
-  nickname = (*pRetCert)->nickname;
-  if(NULL == nickname)
-    nickname = "[unknown]";
-
-  if(NULL == *pRetKey) {
-    failf(data, "NSS: private key not found for certificate: %s", nickname);
-    return SECFailure;
-  }
-
-  infof(data, "NSS: using client certificate: %s\n", nickname);
-  display_cert_info(data, *pRetCert);
-  return SECSuccess;
-}
-
-/* This function is supposed to decide, which error codes should be used
- * to conclude server is TLS intolerant.
- *
- * taken from xulrunner - nsNSSIOLayer.cpp
- */
-static PRBool
-isTLSIntoleranceError(PRInt32 err)
-{
-  switch (err) {
-  case SSL_ERROR_BAD_MAC_ALERT:
-  case SSL_ERROR_BAD_MAC_READ:
-  case SSL_ERROR_HANDSHAKE_FAILURE_ALERT:
-  case SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT:
-  case SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE:
-  case SSL_ERROR_ILLEGAL_PARAMETER_ALERT:
-  case SSL_ERROR_NO_CYPHER_OVERLAP:
-  case SSL_ERROR_BAD_SERVER:
-  case SSL_ERROR_BAD_BLOCK_PADDING:
-  case SSL_ERROR_UNSUPPORTED_VERSION:
-  case SSL_ERROR_PROTOCOL_VERSION_ALERT:
-  case SSL_ERROR_RX_MALFORMED_FINISHED:
-  case SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE:
-  case SSL_ERROR_DECODE_ERROR_ALERT:
-  case SSL_ERROR_RX_UNKNOWN_ALERT:
-    return PR_TRUE;
-  default:
-    return PR_FALSE;
-  }
-}
-
-static CURLcode nss_init_core(struct SessionHandle *data, const char *cert_dir)
-{
-#ifdef HAVE_NSS_INITCONTEXT
-  NSSInitParameters initparams;
-
-  if(nss_context != NULL)
-    return CURLE_OK;
-
-  memset((void *) &initparams, '\0', sizeof(initparams));
-  initparams.length = sizeof(initparams);
-#else /* HAVE_NSS_INITCONTEXT */
-  SECStatus rv;
-
-  if(NSS_IsInitialized())
-    return CURLE_OK;
-#endif
-
-  if(cert_dir) {
-    const bool use_sql = NSS_VersionCheck("3.12.0");
-    char *certpath = aprintf("%s%s", use_sql ? "sql:" : "", cert_dir);
-    if(!certpath)
-      return CURLE_OUT_OF_MEMORY;
-
-    infof(data, "Initializing NSS with certpath: %s\n", certpath);
-#ifdef HAVE_NSS_INITCONTEXT
-    nss_context = NSS_InitContext(certpath, "", "", "", &initparams,
-            NSS_INIT_READONLY | NSS_INIT_PK11RELOAD);
-    free(certpath);
-
-    if(nss_context != NULL)
-      return CURLE_OK;
-#else /* HAVE_NSS_INITCONTEXT */
-    rv = NSS_Initialize(certpath, "", "", "", NSS_INIT_READONLY);
-    free(certpath);
-
-    if(rv == SECSuccess)
-      return CURLE_OK;
-#endif
-
-    infof(data, "Unable to initialize NSS database\n");
-  }
-
-  infof(data, "Initializing NSS with certpath: none\n");
-#ifdef HAVE_NSS_INITCONTEXT
-  nss_context = NSS_InitContext("", "", "", "", &initparams, NSS_INIT_READONLY
-         | NSS_INIT_NOCERTDB   | NSS_INIT_NOMODDB       | NSS_INIT_FORCEOPEN
-         | NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD);
-  if(nss_context != NULL)
-    return CURLE_OK;
-#else /* HAVE_NSS_INITCONTEXT */
-  if(NSS_NoDB_Init(NULL) == SECSuccess)
-    return CURLE_OK;
-#endif
-
-  infof(data, "Unable to initialize NSS\n");
-  return CURLE_SSL_CACERT_BADFILE;
-}
-
-static CURLcode nss_init(struct SessionHandle *data)
-{
-  char *cert_dir;
-  struct_stat st;
-  CURLcode rv;
-
-  if(initialized)
-    return CURLE_OK;
-
-  /* First we check if $SSL_DIR points to a valid dir */
-  cert_dir = getenv("SSL_DIR");
-  if(cert_dir) {
-    if((stat(cert_dir, &st) != 0) ||
-        (!S_ISDIR(st.st_mode))) {
-      cert_dir = NULL;
-    }
-  }
-
-  /* Now we check if the default location is a valid dir */
-  if(!cert_dir) {
-    if((stat(SSL_DIR, &st) == 0) &&
-        (S_ISDIR(st.st_mode))) {
-      cert_dir = (char *)SSL_DIR;
-    }
-  }
-
-  rv = nss_init_core(data, cert_dir);
-  if(rv)
-    return rv;
-
-  if(num_enabled_ciphers() == 0)
-    NSS_SetDomesticPolicy();
-
-  initialized = 1;
-  return CURLE_OK;
-}
-
-/**
- * Global SSL init
- *
- * @retval 0 error initializing SSL
- * @retval 1 SSL initialized successfully
- */
-int Curl_nss_init(void)
-{
-  /* curl_global_init() is not thread-safe so this test is ok */
-  if(nss_initlock == NULL) {
-    PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 256);
-    nss_initlock = PR_NewLock();
-    nss_crllock = PR_NewLock();
-  }
-
-  /* We will actually initialize NSS later */
-
-  return 1;
-}
-
-CURLcode Curl_nss_force_init(struct SessionHandle *data)
-{
-  CURLcode rv;
-  if(!nss_initlock) {
-    failf(data,
-          "unable to initialize NSS, curl_global_init() should have been "
-          "called with CURL_GLOBAL_SSL or CURL_GLOBAL_ALL");
-    return CURLE_FAILED_INIT;
-  }
-
-  PR_Lock(nss_initlock);
-  rv = nss_init(data);
-  PR_Unlock(nss_initlock);
-  return rv;
-}
-
-/* Global cleanup */
-void Curl_nss_cleanup(void)
-{
-  /* This function isn't required to be threadsafe and this is only done
-   * as a safety feature.
-   */
-  PR_Lock(nss_initlock);
-  if(initialized) {
-    /* Free references to client certificates held in the SSL session cache.
-     * Omitting this hampers destruction of the security module owning
-     * the certificates. */
-    SSL_ClearSessionCache();
-
-    if(mod && SECSuccess == SECMOD_UnloadUserModule(mod)) {
-      SECMOD_DestroyModule(mod);
-      mod = NULL;
-    }
-#ifdef HAVE_NSS_INITCONTEXT
-    NSS_ShutdownContext(nss_context);
-    nss_context = NULL;
-#else /* HAVE_NSS_INITCONTEXT */
-    NSS_Shutdown();
-#endif
-  }
-  PR_Unlock(nss_initlock);
-
-  PR_DestroyLock(nss_initlock);
-  PR_DestroyLock(nss_crllock);
-  nss_initlock = NULL;
-
-  initialized = 0;
-}
-
-/*
- * This function uses SSL_peek to determine connection status.
- *
- * Return codes:
- *     1 means the connection is still in place
- *     0 means the connection has been closed
- *    -1 means the connection status is unknown
- */
-int
-Curl_nss_check_cxn(struct connectdata *conn)
-{
-  int rc;
-  char buf;
-
-  rc =
-    PR_Recv(conn->ssl[FIRSTSOCKET].handle, (void *)&buf, 1, PR_MSG_PEEK,
-            PR_SecondsToInterval(1));
-  if(rc > 0)
-    return 1; /* connection still in place */
-
-  if(rc == 0)
-    return 0; /* connection has been closed */
-
-  return -1;  /* connection status unknown */
-}
-
-/*
- * This function is called when an SSL connection is closed.
- */
-void Curl_nss_close(struct connectdata *conn, int sockindex)
-{
-  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-
-  if(connssl->handle) {
-    /* NSS closes the socket we previously handed to it, so we must mark it
-       as closed to avoid double close */
-    fake_sclose(conn->sock[sockindex]);
-    conn->sock[sockindex] = CURL_SOCKET_BAD;
-    if(connssl->client_nickname != NULL) {
-      free(connssl->client_nickname);
-      connssl->client_nickname = NULL;
-
-      /* force NSS to ask again for a client cert when connecting
-       * next time to the same server */
-      SSL_InvalidateSession(connssl->handle);
-    }
-    /* destroy all NSS objects in order to avoid failure of NSS shutdown */
-    Curl_llist_destroy(connssl->obj_list, NULL);
-    connssl->obj_list = NULL;
-    connssl->obj_clicert = NULL;
-
-    PR_Close(connssl->handle);
-    connssl->handle = NULL;
-  }
-}
-
-/*
- * This function is called when the 'data' struct is going away. Close
- * down everything and free all resources!
- */
-int Curl_nss_close_all(struct SessionHandle *data)
-{
-  (void)data;
-  return 0;
-}
-
-/* return true if NSS can provide error code (and possibly msg) for the
-   error */
-static bool is_nss_error(CURLcode err)
-{
-  switch(err) {
-  case CURLE_PEER_FAILED_VERIFICATION:
-  case CURLE_SSL_CACERT:
-  case CURLE_SSL_CACERT_BADFILE:
-  case CURLE_SSL_CERTPROBLEM:
-  case CURLE_SSL_CONNECT_ERROR:
-  case CURLE_SSL_CRL_BADFILE:
-  case CURLE_SSL_ISSUER_ERROR:
-    return true;
-
-  default:
-    return false;
-  }
-}
-
-/* return true if the given error code is related to a client certificate */
-static bool is_cc_error(PRInt32 err)
-{
-  switch(err) {
-  case SSL_ERROR_BAD_CERT_ALERT:
-  case SSL_ERROR_EXPIRED_CERT_ALERT:
-  case SSL_ERROR_REVOKED_CERT_ALERT:
-    return true;
-
-  default:
-    return false;
-  }
-}
-
-static Curl_recv nss_recv;
-static Curl_send nss_send;
-
-static CURLcode nss_load_ca_certificates(struct connectdata *conn,
-                                         int sockindex)
-{
-  struct SessionHandle *data = conn->data;
-  const char *cafile = data->set.ssl.CAfile;
-  const char *capath = data->set.ssl.CApath;
-
-  if(cafile) {
-    CURLcode rv = nss_load_cert(&conn->ssl[sockindex], cafile, PR_TRUE);
-    if(CURLE_OK != rv)
-      return rv;
-  }
-
-  if(capath) {
-    struct_stat st;
-    if(stat(capath, &st) == -1)
-      return CURLE_SSL_CACERT_BADFILE;
-
-    if(S_ISDIR(st.st_mode)) {
-      PRDirEntry *entry;
-      PRDir *dir = PR_OpenDir(capath);
-      if(!dir)
-        return CURLE_SSL_CACERT_BADFILE;
-
-      while((entry = PR_ReadDir(dir, PR_SKIP_BOTH | PR_SKIP_HIDDEN))) {
-        char *fullpath = aprintf("%s/%s", capath, entry->name);
-        if(!fullpath) {
-          PR_CloseDir(dir);
-          return CURLE_OUT_OF_MEMORY;
-        }
-
-        if(CURLE_OK != nss_load_cert(&conn->ssl[sockindex], fullpath, PR_TRUE))
-          /* This is purposefully tolerant of errors so non-PEM files can
-           * be in the same directory */
-          infof(data, "failed to load '%s' from CURLOPT_CAPATH\n", fullpath);
-
-        free(fullpath);
-      }
-
-      PR_CloseDir(dir);
-    }
-    else
-      infof(data, "warning: CURLOPT_CAPATH not a directory (%s)\n", capath);
-  }
-
-  infof(data, "  CAfile: %s\n  CApath: %s\n",
-      cafile ? cafile : "none",
-      capath ? capath : "none");
-
-  return CURLE_OK;
-}
-
-CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
-{
-  PRErrorCode err = 0;
-  PRFileDesc *model = NULL;
-  PRBool ssl2 = PR_FALSE;
-  PRBool ssl3 = PR_FALSE;
-  PRBool tlsv1 = PR_FALSE;
-  PRBool ssl_no_cache;
-  PRBool ssl_cbc_random_iv;
-  struct SessionHandle *data = conn->data;
-  curl_socket_t sockfd = conn->sock[sockindex];
-  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-  CURLcode curlerr;
-  const int *cipher_to_enable;
-  PRSocketOptionData sock_opt;
-  long time_left;
-  PRUint32 timeout;
-
-  if(connssl->state == ssl_connection_complete)
-    return CURLE_OK;
-
-  connssl->data = data;
-
-  /* list of all NSS objects we need to destroy in Curl_nss_close() */
-  connssl->obj_list = Curl_llist_alloc(nss_destroy_object);
-  if(!connssl->obj_list)
-    return CURLE_OUT_OF_MEMORY;
-
-  /* FIXME. NSS doesn't support multiple databases open at the same time. */
-  PR_Lock(nss_initlock);
-  curlerr = nss_init(conn->data);
-  if(CURLE_OK != curlerr) {
-    PR_Unlock(nss_initlock);
-    goto error;
-  }
-
-  curlerr = CURLE_SSL_CONNECT_ERROR;
-
-  if(!mod) {
-    char *configstring = aprintf("library=%s name=PEM", pem_library);
-    if(!configstring) {
-      PR_Unlock(nss_initlock);
-      goto error;
-    }
-    mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE);
-    free(configstring);
-
-    if(!mod || !mod->loaded) {
-      if(mod) {
-        SECMOD_DestroyModule(mod);
-        mod = NULL;
-      }
-      infof(data, "WARNING: failed to load NSS PEM library %s. Using "
-            "OpenSSL PEM certificates will not work.\n", pem_library);
-    }
-  }
-
-  PK11_SetPasswordFunc(nss_get_password);
-  PR_Unlock(nss_initlock);
-
-  model = PR_NewTCPSocket();
-  if(!model)
-    goto error;
-  model = SSL_ImportFD(NULL, model);
-
-  /* make the socket nonblocking */
-  sock_opt.option = PR_SockOpt_Nonblocking;
-  sock_opt.value.non_blocking = PR_TRUE;
-  if(PR_SetSocketOption(model, &sock_opt) != PR_SUCCESS)
-    goto error;
-
-  if(SSL_OptionSet(model, SSL_SECURITY, PR_TRUE) != SECSuccess)
-    goto error;
-  if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != SECSuccess)
-    goto error;
-  if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != SECSuccess)
-    goto error;
-
-  /* do not use SSL cache if we are not going to verify peer */
-  ssl_no_cache = (data->set.ssl.verifypeer) ? PR_FALSE : PR_TRUE;
-  if(SSL_OptionSet(model, SSL_NO_CACHE, ssl_no_cache) != SECSuccess)
-    goto error;
-
-  switch (data->set.ssl.version) {
-  default:
-  case CURL_SSLVERSION_DEFAULT:
-    ssl3 = PR_TRUE;
-    if(data->state.ssl_connect_retry)
-      infof(data, "TLS disabled due to previous handshake failure\n");
-    else
-      tlsv1 = PR_TRUE;
-    break;
-  case CURL_SSLVERSION_TLSv1:
-    tlsv1 = PR_TRUE;
-    break;
-  case CURL_SSLVERSION_SSLv2:
-    ssl2 = PR_TRUE;
-    break;
-  case CURL_SSLVERSION_SSLv3:
-    ssl3 = PR_TRUE;
-    break;
-  }
-
-  if(SSL_OptionSet(model, SSL_ENABLE_SSL2, ssl2) != SECSuccess)
-    goto error;
-  if(SSL_OptionSet(model, SSL_ENABLE_SSL3, ssl3) != SECSuccess)
-    goto error;
-  if(SSL_OptionSet(model, SSL_ENABLE_TLS, tlsv1) != SECSuccess)
-    goto error;
-
-  if(SSL_OptionSet(model, SSL_V2_COMPATIBLE_HELLO, ssl2) != SECSuccess)
-    goto error;
-
-  ssl_cbc_random_iv = !data->set.ssl_enable_beast;
-#ifdef SSL_CBC_RANDOM_IV
-  /* unless the user explicitly asks to allow the protocol vulnerability, we
-     use the work-around */
-  if(SSL_OptionSet(model, SSL_CBC_RANDOM_IV, ssl_cbc_random_iv) != SECSuccess)
-    infof(data, "warning: failed to set SSL_CBC_RANDOM_IV = %d\n",
-          ssl_cbc_random_iv);
-#else
-  if(ssl_cbc_random_iv)
-    infof(data, "warning: support for SSL_CBC_RANDOM_IV not compiled in\n");
-#endif
-
-  /* reset the flag to avoid an infinite loop */
-  data->state.ssl_connect_retry = FALSE;
-
-  /* enable all ciphers from enable_ciphers_by_default */
-  cipher_to_enable = enable_ciphers_by_default;
-  while(SSL_NULL_WITH_NULL_NULL != *cipher_to_enable) {
-    if(SSL_CipherPrefSet(model, *cipher_to_enable, PR_TRUE) != SECSuccess) {
-      curlerr = CURLE_SSL_CIPHER;
-      goto error;
-    }
-    cipher_to_enable++;
-  }
-
-  if(data->set.ssl.cipher_list) {
-    if(set_ciphers(data, model, data->set.ssl.cipher_list) != SECSuccess) {
-      curlerr = CURLE_SSL_CIPHER;
-      goto error;
-    }
-  }
-
-  if(!data->set.ssl.verifypeer && data->set.ssl.verifyhost)
-    infof(data, "warning: ignoring value of ssl.verifyhost\n");
-
-  /* bypass the default SSL_AuthCertificate() hook in case we do not want to
-   * verify peer */
-  if(SSL_AuthCertificateHook(model, nss_auth_cert_hook, conn) != SECSuccess)
-    goto error;
-
-  data->set.ssl.certverifyresult=0; /* not checked yet */
-  if(SSL_BadCertHook(model, BadCertHandler, conn) != SECSuccess)
-    goto error;
-
-  if(SSL_HandshakeCallback(model, HandshakeCallback, NULL) != SECSuccess)
-    goto error;
-
-  if(data->set.ssl.verifypeer) {
-    const CURLcode rv = nss_load_ca_certificates(conn, sockindex);
-    if(CURLE_OK != rv) {
-      curlerr = rv;
-      goto error;
-    }
-  }
-
-  if(data->set.ssl.CRLfile) {
-    if(SECSuccess != nss_load_crl(data->set.ssl.CRLfile)) {
-      curlerr = CURLE_SSL_CRL_BADFILE;
-      goto error;
-    }
-    infof(data,
-          "  CRLfile: %s\n",
-          data->set.ssl.CRLfile ? data->set.ssl.CRLfile : "none");
-  }
-
-  if(data->set.str[STRING_CERT]) {
-    char *nickname = dup_nickname(data, STRING_CERT);
-    if(nickname) {
-      /* we are not going to use libnsspem.so to read the client cert */
-      connssl->obj_clicert = NULL;
-    }
-    else {
-      CURLcode rv = cert_stuff(conn, sockindex, data->set.str[STRING_CERT],
-                               data->set.str[STRING_KEY]);
-      if(CURLE_OK != rv) {
-        /* failf() is already done in cert_stuff() */
-        curlerr = rv;
-        goto error;
-      }
-    }
-
-    /* store the nickname for SelectClientCert() called during handshake */
-    connssl->client_nickname = nickname;
-  }
-  else
-    connssl->client_nickname = NULL;
-
-  if(SSL_GetClientAuthDataHook(model, SelectClientCert,
-                               (void *)connssl) != SECSuccess) {
-    curlerr = CURLE_SSL_CERTPROBLEM;
-    goto error;
-  }
-
-  /* Import our model socket  onto the existing file descriptor */
-  connssl->handle = PR_ImportTCPSocket(sockfd);
-  connssl->handle = SSL_ImportFD(model, connssl->handle);
-  if(!connssl->handle)
-    goto error;
-
-  PR_Close(model); /* We don't need this any more */
-  model = NULL;
-
-  /* This is the password associated with the cert that we're using */
-  if(data->set.str[STRING_KEY_PASSWD]) {
-    SSL_SetPKCS11PinArg(connssl->handle, data->set.str[STRING_KEY_PASSWD]);
-  }
-
-  /* Force handshake on next I/O */
-  SSL_ResetHandshake(connssl->handle, /* asServer */ PR_FALSE);
-
-  SSL_SetURL(connssl->handle, conn->host.name);
-
-  /* check timeout situation */
-  time_left = Curl_timeleft(data, NULL, TRUE);
-  if(time_left < 0L) {
-    failf(data, "timed out before SSL handshake");
-    curlerr = CURLE_OPERATION_TIMEDOUT;
-    goto error;
-  }
-  timeout = PR_MillisecondsToInterval((PRUint32) time_left);
-
-  /* Force the handshake now */
-  if(SSL_ForceHandshakeWithTimeout(connssl->handle, timeout) != SECSuccess) {
-    if(conn->data->set.ssl.certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN)
-      curlerr = CURLE_PEER_FAILED_VERIFICATION;
-    else if(conn->data->set.ssl.certverifyresult!=0)
-      curlerr = CURLE_SSL_CACERT;
-    goto error;
-  }
-
-  connssl->state = ssl_connection_complete;
-  conn->recv[sockindex] = nss_recv;
-  conn->send[sockindex] = nss_send;
-
-  display_conn_info(conn, connssl->handle);
-
-  if(data->set.str[STRING_SSL_ISSUERCERT]) {
-    SECStatus ret = SECFailure;
-    char *nickname = dup_nickname(data, STRING_SSL_ISSUERCERT);
-    if(nickname) {
-      /* we support only nicknames in case of STRING_SSL_ISSUERCERT for now */
-      ret = check_issuer_cert(connssl->handle, nickname);
-      free(nickname);
-    }
-
-    if(SECFailure == ret) {
-      infof(data,"SSL certificate issuer check failed\n");
-      curlerr = CURLE_SSL_ISSUER_ERROR;
-      goto error;
-    }
-    else {
-      infof(data, "SSL certificate issuer check ok\n");
-    }
-  }
-
-  return CURLE_OK;
-
-  error:
-  /* reset the flag to avoid an infinite loop */
-  data->state.ssl_connect_retry = FALSE;
-
-  if(is_nss_error(curlerr)) {
-    /* read NSPR error code */
-    err = PR_GetError();
-    if(is_cc_error(err))
-      curlerr = CURLE_SSL_CERTPROBLEM;
-
-    /* print the error number and error string */
-    infof(data, "NSS error %d (%s)\n", err, nss_error_to_name(err));
-
-    /* print a human-readable message describing the error if available */
-    nss_print_error_message(data, err);
-  }
-
-  if(model)
-    PR_Close(model);
-
-    /* cleanup on connection failure */
-    Curl_llist_destroy(connssl->obj_list, NULL);
-    connssl->obj_list = NULL;
-
-  if(ssl3 && tlsv1 && isTLSIntoleranceError(err)) {
-    /* schedule reconnect through Curl_retry_request() */
-    data->state.ssl_connect_retry = TRUE;
-    infof(data, "Error in TLS handshake, trying SSLv3...\n");
-    return CURLE_OK;
-  }
-
-  return curlerr;
-}
-
-static ssize_t nss_send(struct connectdata *conn,  /* connection data */
-                        int sockindex,             /* socketindex */
-                        const void *mem,           /* send this data */
-                        size_t len,                /* amount to write */
-                        CURLcode *curlcode)
-{
-  int rc;
-
-  rc = PR_Send(conn->ssl[sockindex].handle, mem, (int)len, 0, -1);
-
-  if(rc < 0) {
-    PRInt32 err = PR_GetError();
-    if(err == PR_WOULD_BLOCK_ERROR)
-      *curlcode = CURLE_AGAIN;
-    else {
-      /* print the error number and error string */
-      const char *err_name = nss_error_to_name(err);
-      infof(conn->data, "SSL write: error %d (%s)\n", err, err_name);
-
-      /* print a human-readable message describing the error if available */
-      nss_print_error_message(conn->data, err);
-
-      *curlcode = (is_cc_error(err))
-        ? CURLE_SSL_CERTPROBLEM
-        : CURLE_SEND_ERROR;
-    }
-    return -1;
-  }
-  return rc; /* number of bytes */
-}
-
-static ssize_t nss_recv(struct connectdata * conn, /* connection data */
-                        int num,                   /* socketindex */
-                        char *buf,                 /* store read data here */
-                        size_t buffersize,         /* max amount to read */
-                        CURLcode *curlcode)
-{
-  ssize_t nread;
-
-  nread = PR_Recv(conn->ssl[num].handle, buf, (int)buffersize, 0, -1);
-  if(nread < 0) {
-    /* failed SSL read */
-    PRInt32 err = PR_GetError();
-
-    if(err == PR_WOULD_BLOCK_ERROR)
-      *curlcode = CURLE_AGAIN;
-    else {
-      /* print the error number and error string */
-      const char *err_name = nss_error_to_name(err);
-      infof(conn->data, "SSL read: errno %d (%s)\n", err, err_name);
-
-      /* print a human-readable message describing the error if available */
-      nss_print_error_message(conn->data, err);
-
-      *curlcode = (is_cc_error(err))
-        ? CURLE_SSL_CERTPROBLEM
-        : CURLE_RECV_ERROR;
-    }
-    return -1;
-  }
-  return nread;
-}
-
-size_t Curl_nss_version(char *buffer, size_t size)
-{
-  return snprintf(buffer, size, "NSS/%s", NSS_VERSION);
-}
-
-int Curl_nss_seed(struct SessionHandle *data)
-{
-  /* TODO: implement? */
-  (void) data;
-  return 0;
-}
-
-void Curl_nss_random(struct SessionHandle *data,
-                     unsigned char *entropy,
-                     size_t length)
-{
-  Curl_nss_seed(data);  /* Initiate the seed if not already done */
-  PK11_GenerateRandom(entropy, curlx_uztosi(length));
-}
-
-void Curl_nss_md5sum(unsigned char *tmp, /* input */
-                     size_t tmplen,
-                     unsigned char *md5sum, /* output */
-                     size_t md5len)
-{
-  PK11Context *MD5pw = PK11_CreateDigestContext(SEC_OID_MD5);
-  unsigned int MD5out;
-  PK11_DigestOp(MD5pw, tmp, curlx_uztoui(tmplen));
-  PK11_DigestFinal(MD5pw, md5sum, &MD5out, curlx_uztoui(md5len));
-  PK11_DestroyContext(MD5pw, PR_TRUE);
-}
-
-#endif /* USE_NSS */
diff --git a/lib/nwlib.c b/lib/nwlib.c
deleted file mode 100644 (file)
index f63f16b..0000000
+++ /dev/null
@@ -1,329 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef NETWARE /* Novell NetWare */
-
-#ifdef __NOVELL_LIBC__
-/* For native LibC-based NLM we need to register as a real lib. */
-#include <library.h>
-#include <netware.h>
-#include <screen.h>
-#include <nks/thread.h>
-#include <nks/synch.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-typedef struct
-{
-  int     _errno;
-  void    *twentybytes;
-} libthreaddata_t;
-
-typedef struct
-{
-  int         x;
-  int         y;
-  int         z;
-  void        *tenbytes;
-  NXKey_t     perthreadkey;   /* if -1, no key obtained... */
-  NXMutex_t   *lock;
-} libdata_t;
-
-int         gLibId      = -1;
-void        *gLibHandle = (void *) NULL;
-rtag_t      gAllocTag   = (rtag_t) NULL;
-NXMutex_t   *gLibLock   = (NXMutex_t *) NULL;
-
-/* internal library function prototypes... */
-int  DisposeLibraryData( void * );
-void DisposeThreadData( void * );
-int  GetOrSetUpData( int id, libdata_t **data, libthreaddata_t **threaddata );
-
-
-int _NonAppStart( void        *NLMHandle,
-                  void        *errorScreen,
-                  const char  *cmdLine,
-                  const char  *loadDirPath,
-                  size_t      uninitializedDataLength,
-                  void        *NLMFileHandle,
-                  int         (*readRoutineP)( int conn,
-                                               void *fileHandle, size_t offset,
-                                               size_t nbytes,
-                                               size_t *bytesRead,
-                                               void *buffer ),
-                  size_t      customDataOffset,
-                  size_t      customDataSize,
-                  int         messageCount,
-                  const char  **messages )
-{
-  NX_LOCK_INFO_ALLOC(liblock, "Per-Application Data Lock", 0);
-
-#ifndef __GNUC__
-#pragma unused(cmdLine)
-#pragma unused(loadDirPath)
-#pragma unused(uninitializedDataLength)
-#pragma unused(NLMFileHandle)
-#pragma unused(readRoutineP)
-#pragma unused(customDataOffset)
-#pragma unused(customDataSize)
-#pragma unused(messageCount)
-#pragma unused(messages)
-#endif
-
-  /*
-   * Here we process our command line, post errors (to the error screen),
-   * perform initializations and anything else we need to do before being able
-   * to accept calls into us. If we succeed, we return non-zero and the NetWare
-   * Loader will leave us up, otherwise we fail to load and get dumped.
-   */
-  gAllocTag = AllocateResourceTag(NLMHandle,
-                                  "<library-name> memory allocations",
-                                  AllocSignature);
-
-  if(!gAllocTag) {
-    OutputToScreen(errorScreen, "Unable to allocate resource tag for "
-                   "library memory allocations.\n");
-    return -1;
-  }
-
-  gLibId = register_library(DisposeLibraryData);
-
-  if(gLibId < -1) {
-    OutputToScreen(errorScreen, "Unable to register library with kernel.\n");
-    return -1;
-  }
-
-  gLibHandle = NLMHandle;
-
-  gLibLock = NXMutexAlloc(0, 0, &liblock);
-
-  if(!gLibLock) {
-    OutputToScreen(errorScreen, "Unable to allocate library data lock.\n");
-    return -1;
-  }
-
-  return 0;
-}
-
-/*
- * Here we clean up any resources we allocated. Resource tags is a big part
- * of what we created, but NetWare doesn't ask us to free those.
- */
-void _NonAppStop( void )
-{
-  (void) unregister_library(gLibId);
-  NXMutexFree(gLibLock);
-}
-
-/*
- * This function cannot be the first in the file for if the file is linked
- * first, then the check-unload function's offset will be nlmname.nlm+0
- * which is how to tell that there isn't one. When the check function is
- * first in the linked objects, it is ambiguous. For this reason, we will
- * put it inside this file after the stop function.
- *
- * Here we check to see if it's alright to ourselves to be unloaded. If not,
- * we return a non-zero value. Right now, there isn't any reason not to allow
- * it.
- */
-int _NonAppCheckUnload( void )
-{
-    return 0;
-}
-
-int GetOrSetUpData(int id, libdata_t **appData,
-                   libthreaddata_t **threadData )
-{
-  int                 err;
-  libdata_t           *app_data;
-  libthreaddata_t *thread_data;
-  NXKey_t             key;
-  NX_LOCK_INFO_ALLOC(liblock, "Application Data Lock", 0);
-
-  err         = 0;
-  thread_data = (libthreaddata_t *) NULL;
-
-  /*
-   * Attempt to get our data for the application calling us. This is where we
-   * store whatever application-specific information we need to carry in
-   * support of calling applications.
-   */
-  app_data = (libdata_t *) get_app_data(id);
-
-  if(!app_data) {
-    /*
-     * This application hasn't called us before; set up application AND
-     * per-thread data. Of course, just in case a thread from this same
-     * application is calling us simultaneously, we better lock our application
-     * data-creation mutex. We also need to recheck for data after we acquire
-     * the lock because WE might be that other thread that was too late to
-     * create the data and the first thread in will have created it.
-     */
-    NXLock(gLibLock);
-
-    if(!(app_data = (libdata_t *) get_app_data(id))) {
-      app_data = malloc(sizeof(libdata_t));
-
-      if(app_data) {
-        memset(app_data, 0, sizeof(libdata_t));
-
-        app_data->tenbytes = malloc(10);
-        app_data->lock     = NXMutexAlloc(0, 0, &liblock);
-
-        if(!app_data->tenbytes || !app_data->lock) {
-          if(app_data->lock)
-            NXMutexFree(app_data->lock);
-
-          free(app_data);
-          app_data = (libdata_t *) NULL;
-          err      = ENOMEM;
-        }
-
-        if(app_data) {
-          /*
-           * Here we burn in the application data that we were trying to get
-           * by calling get_app_data(). Next time we call the first function,
-           * we'll get this data we're just now setting. We also go on here to
-           * establish the per-thread data for the calling thread, something
-           * we'll have to do on each application thread the first time
-           * it calls us.
-           */
-          err = set_app_data(gLibId, app_data);
-
-          if(err) {
-            free(app_data);
-            app_data = (libdata_t *) NULL;
-            err      = ENOMEM;
-          }
-          else {
-            /* create key for thread-specific data... */
-            err = NXKeyCreate(DisposeThreadData, (void *) NULL, &key);
-
-            if(err)                /* (no more keys left?) */
-              key = -1;
-
-            app_data->perthreadkey = key;
-          }
-        }
-      }
-    }
-
-    NXUnlock(gLibLock);
-  }
-
-  if(app_data) {
-    key = app_data->perthreadkey;
-
-    if(key != -1 /* couldn't create a key? no thread data */
-        && !(err = NXKeyGetValue(key, (void **) &thread_data))
-        && !thread_data) {
-      /*
-       * Allocate the per-thread data for the calling thread. Regardless of
-       * whether there was already application data or not, this may be the
-       * first call by a new thread. The fact that we allocation 20 bytes on
-       * a pointer is not very important, this just helps to demonstrate that
-       * we can have arbitrarily complex per-thread data.
-       */
-      thread_data = malloc(sizeof(libthreaddata_t));
-
-      if(thread_data) {
-        thread_data->_errno      = 0;
-        thread_data->twentybytes = malloc(20);
-
-        if(!thread_data->twentybytes) {
-          free(thread_data);
-          thread_data = (libthreaddata_t *) NULL;
-          err         = ENOMEM;
-        }
-
-        if((err = NXKeySetValue(key, thread_data))) {
-          free(thread_data->twentybytes);
-          free(thread_data);
-          thread_data = (libthreaddata_t *) NULL;
-        }
-      }
-    }
-  }
-
-  if(appData)
-    *appData = app_data;
-
-  if(threadData)
-    *threadData = thread_data;
-
-  return err;
-}
-
-int DisposeLibraryData( void *data )
-{
-  if(data) {
-    void *tenbytes = ((libdata_t *) data)->tenbytes;
-
-    if(tenbytes)
-      free(tenbytes);
-
-    free(data);
-  }
-
-  return 0;
-}
-
-void DisposeThreadData( void *data )
-{
-  if(data) {
-    void *twentybytes = ((libthreaddata_t *) data)->twentybytes;
-
-    if(twentybytes)
-      free(twentybytes);
-
-    free(data);
-  }
-}
-
-#else /* __NOVELL_LIBC__ */
-/* For native CLib-based NLM seems we can do a bit more simple. */
-#include <nwthread.h>
-
-int main ( void )
-{
-  /* initialize any globals here... */
-
-  /* do this if any global initializing was done
-  SynchronizeStart();
-  */
-  ExitThread (TSR_THREAD, 0);
-  return 0;
-}
-
-#endif /* __NOVELL_LIBC__ */
-
-#else /* NETWARE */
-
-#ifdef __POCC__
-#  pragma warn(disable:2024)  /* Disable warning #2024: Empty input file */
-#endif
-
-#endif /* NETWARE */
diff --git a/lib/nwos.c b/lib/nwos.c
deleted file mode 100644 (file)
index 23ff2a7..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef NETWARE /* Novell NetWare */
-
-#ifdef __NOVELL_LIBC__
-/* For native LibC-based NLM we need to do nothing. */
-int netware_init ( void )
-{
-  return 0;
-}
-
-#else /* __NOVELL_LIBC__ */
-
-/* For native CLib-based NLM we need to initialize the LONG namespace. */
-#include <nwnspace.h>
-#include <nwthread.h>
-#include <nwadv.h>
-/* Make the CLIB Ctx stuff link */
-#include <netdb.h>
-NETDB_DEFINE_CONTEXT
-/* Make the CLIB Inet stuff link */
-#include <netinet/in.h>
-#include <arpa/inet.h>
-NETINET_DEFINE_CONTEXT
-
-int netware_init ( void )
-{
-  int rc = 0;
-  unsigned int myHandle = GetNLMHandle();
-  /* import UnAugmentAsterisk dynamically for NW4.x compatibility */
-  void (*pUnAugmentAsterisk)(int) = (void(*)(int))
-          ImportSymbol(myHandle, "UnAugmentAsterisk");
-  /* import UseAccurateCaseForPaths dynamically for NW3.x compatibility */
-  void (*pUseAccurateCaseForPaths)(int) = (void(*)(int))
-          ImportSymbol(myHandle, "UseAccurateCaseForPaths");
-  if(pUnAugmentAsterisk)
-    pUnAugmentAsterisk(1);
-  if(pUseAccurateCaseForPaths)
-    pUseAccurateCaseForPaths(1);
-  UnimportSymbol(myHandle, "UnAugmentAsterisk");
-  UnimportSymbol(myHandle, "UseAccurateCaseForPaths");
-  /* set long name space */
-  if((SetCurrentNameSpace(4) == 255)) {
-    rc = 1;
-  }
-  if((SetTargetNameSpace(4) == 255)) {
-    rc = rc + 2;
-  }
-  return rc;
-}
-
-/* dummy function to satisfy newer prelude */
-int __init_environment ( void )
-{
-  return 0;
-}
-
-/* dummy function to satisfy newer prelude */
-int __deinit_environment ( void )
-{
-  return 0;
-}
-
-#endif /* __NOVELL_LIBC__ */
-
-#endif /* NETWARE */
diff --git a/lib/openldap.c b/lib/openldap.c
deleted file mode 100644 (file)
index b10d31e..0000000
+++ /dev/null
@@ -1,652 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 2010, Howard Chu, <hyc@openldap.org>
- * Copyright (C) 2011 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#if !defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)
-
-/*
- * Notice that USE_OPENLDAP is only a source code selection switch. When
- * libcurl is built with USE_OPENLDAP defined the libcurl source code that
- * gets compiled is the code from curl_openldap.c, otherwise the code that
- * gets compiled is the code from curl_ldap.c.
- *
- * When USE_OPENLDAP is defined a recent version of the OpenLDAP library
- * might be required for compilation and runtime. In order to use ancient
- * OpenLDAP library versions, USE_OPENLDAP shall not be defined.
- */
-
-#include <ldap.h>
-
-#include "curl_urldata.h"
-#include <curl/curl.h>
-#include "curl_sendf.h"
-#include "curl_sslgen.h"
-#include "curl_transfer.h"
-#include "curl_ldap.h"
-#include "curl_memory.h"
-#include "curl_base64.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memdebug.h"
-
-#ifndef _LDAP_PVT_H
-extern int ldap_pvt_url_scheme2proto(const char *);
-extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url,
-                        LDAP **ld);
-#endif
-
-static CURLcode ldap_setup(struct connectdata *conn);
-static CURLcode ldap_do(struct connectdata *conn, bool *done);
-static CURLcode ldap_done(struct connectdata *conn, CURLcode, bool);
-static CURLcode ldap_connect(struct connectdata *conn, bool *done);
-static CURLcode ldap_connecting(struct connectdata *conn, bool *done);
-static CURLcode ldap_disconnect(struct connectdata *conn, bool dead);
-
-static Curl_recv ldap_recv;
-
-/*
- * LDAP protocol handler.
- */
-
-const struct Curl_handler Curl_handler_ldap = {
-  "LDAP",                               /* scheme */
-  ldap_setup,                           /* setup_connection */
-  ldap_do,                              /* do_it */
-  ldap_done,                            /* done */
-  ZERO_NULL,                            /* do_more */
-  ldap_connect,                         /* connect_it */
-  ldap_connecting,                      /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
-  ldap_disconnect,                      /* disconnect */
-  ZERO_NULL,                            /* readwrite */
-  PORT_LDAP,                            /* defport */
-  CURLPROTO_LDAP,                       /* protocol */
-  PROTOPT_NONE                          /* flags */
-};
-
-#ifdef USE_SSL
-/*
- * LDAPS protocol handler.
- */
-
-const struct Curl_handler Curl_handler_ldaps = {
-  "LDAPS",                              /* scheme */
-  ldap_setup,                           /* setup_connection */
-  ldap_do,                              /* do_it */
-  ldap_done,                            /* done */
-  ZERO_NULL,                            /* do_more */
-  ldap_connect,                         /* connect_it */
-  ldap_connecting,                      /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
-  ldap_disconnect,                      /* disconnect */
-  ZERO_NULL,                            /* readwrite */
-  PORT_LDAPS,                           /* defport */
-  CURLPROTO_LDAP,                       /* protocol */
-  PROTOPT_SSL                           /* flags */
-};
-#endif
-
-static const char *url_errs[] = {
-  "success",
-  "out of memory",
-  "bad parameter",
-  "unrecognized scheme",
-  "unbalanced delimiter",
-  "bad URL",
-  "bad host or port",
-  "bad or missing attributes",
-  "bad or missing scope",
-  "bad or missing filter",
-  "bad or missing extensions"
-};
-
-typedef struct ldapconninfo {
-  LDAP *ld;
-  Curl_recv *recv;  /* for stacking SSL handler */
-  Curl_send *send;
-  int proto;
-  int msgid;
-  bool ssldone;
-  bool sslinst;
-  bool didbind;
-} ldapconninfo;
-
-typedef struct ldapreqinfo {
-  int msgid;
-  int nument;
-} ldapreqinfo;
-
-static CURLcode ldap_setup(struct connectdata *conn)
-{
-  ldapconninfo *li;
-  LDAPURLDesc *lud;
-  struct SessionHandle *data=conn->data;
-  int rc, proto;
-  CURLcode status;
-
-  rc = ldap_url_parse(data->change.url, &lud);
-  if(rc != LDAP_URL_SUCCESS) {
-    const char *msg = "url parsing problem";
-    status = CURLE_URL_MALFORMAT;
-    if(rc > LDAP_URL_SUCCESS && rc <= LDAP_URL_ERR_BADEXTS) {
-      if(rc == LDAP_URL_ERR_MEM)
-        status = CURLE_OUT_OF_MEMORY;
-      msg = url_errs[rc];
-    }
-    failf(conn->data, "LDAP local: %s", msg);
-    return status;
-  }
-  proto = ldap_pvt_url_scheme2proto(lud->lud_scheme);
-  ldap_free_urldesc(lud);
-
-  li = calloc(1, sizeof(ldapconninfo));
-  if(!li)
-    return CURLE_OUT_OF_MEMORY;
-  li->proto = proto;
-  conn->proto.generic = li;
-  conn->bits.close = FALSE;
-  /* TODO:
-   * - provide option to choose SASL Binds instead of Simple
-   */
-  return CURLE_OK;
-}
-
-#ifdef USE_SSL
-static Sockbuf_IO ldapsb_tls;
-#endif
-
-static CURLcode ldap_connect(struct connectdata *conn, bool *done)
-{
-  ldapconninfo *li = conn->proto.generic;
-  struct SessionHandle *data=conn->data;
-  int rc, proto = LDAP_VERSION3;
-  char hosturl[1024], *ptr;
-
-  strcpy(hosturl, "ldap");
-  ptr = hosturl+4;
-  if(conn->handler->flags & PROTOPT_SSL)
-    *ptr++ = 's';
-  snprintf(ptr, sizeof(hosturl)-(ptr-hosturl), "://%s:%d",
-    conn->host.name, conn->remote_port);
-
-  rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld);
-  if(rc) {
-    failf(data, "LDAP local: Cannot connect to %s, %s",
-          hosturl, ldap_err2string(rc));
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
-
-#ifdef USE_SSL
-  if(conn->handler->flags & PROTOPT_SSL) {
-    CURLcode res;
-    if(data->state.used_interface == Curl_if_easy) {
-      res = Curl_ssl_connect(conn, FIRSTSOCKET);
-      if(res)
-        return res;
-      li->ssldone = TRUE;
-    }
-    else {
-      res = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &li->ssldone);
-      if(res)
-        return res;
-    }
-  }
-#endif
-
-  if(data->state.used_interface == Curl_if_easy)
-    return ldap_connecting(conn, done);
-
-  return CURLE_OK;
-}
-
-static CURLcode ldap_connecting(struct connectdata *conn, bool *done)
-{
-  ldapconninfo *li = conn->proto.generic;
-  struct SessionHandle *data=conn->data;
-  LDAPMessage *result = NULL;
-  struct timeval tv = {0,1}, *tvp;
-  int rc, err;
-  char *info = NULL;
-
-#ifdef USE_SSL
-  if(conn->handler->flags & PROTOPT_SSL) {
-    /* Is the SSL handshake complete yet? */
-    if(!li->ssldone) {
-      CURLcode res = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET,
-                                                  &li->ssldone);
-      if(res || !li->ssldone)
-        return res;
-    }
-    /* Have we installed the libcurl SSL handlers into the sockbuf yet? */
-    if(!li->sslinst) {
-      Sockbuf *sb;
-      ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
-      ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, conn);
-      li->sslinst = TRUE;
-      li->recv = conn->recv[FIRSTSOCKET];
-      li->send = conn->send[FIRSTSOCKET];
-    }
-  }
-#endif
-
-  if(data->state.used_interface == Curl_if_easy)
-    tvp = NULL;    /* let ldap_result block indefinitely */
-  else
-    tvp = &tv;
-
-retry:
-  if(!li->didbind) {
-    char *binddn;
-    struct berval passwd;
-
-    if(conn->bits.user_passwd) {
-      binddn = conn->user;
-      passwd.bv_val = conn->passwd;
-      passwd.bv_len = strlen(passwd.bv_val);
-    }
-    else {
-      binddn = NULL;
-      passwd.bv_val = NULL;
-      passwd.bv_len = 0;
-    }
-    rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd,
-                        NULL, NULL, &li->msgid);
-    if(rc)
-      return CURLE_LDAP_CANNOT_BIND;
-    li->didbind = TRUE;
-    if(tvp)
-      return CURLE_OK;
-  }
-
-  rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, tvp, &result);
-  if(rc < 0) {
-    failf(data, "LDAP local: bind ldap_result %s", ldap_err2string(rc));
-    return CURLE_LDAP_CANNOT_BIND;
-  }
-  if(rc == 0) {
-    /* timed out */
-    return CURLE_OK;
-  }
-  rc = ldap_parse_result(li->ld, result, &err, NULL, &info, NULL, NULL, 1);
-  if(rc) {
-    failf(data, "LDAP local: bind ldap_parse_result %s", ldap_err2string(rc));
-    return CURLE_LDAP_CANNOT_BIND;
-  }
-  /* Try to fallback to LDAPv2? */
-  if(err == LDAP_PROTOCOL_ERROR) {
-    int proto;
-    ldap_get_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
-    if(proto == LDAP_VERSION3) {
-      if(info) {
-        ldap_memfree(info);
-        info = NULL;
-      }
-      proto = LDAP_VERSION2;
-      ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
-      li->didbind = FALSE;
-      goto retry;
-    }
-  }
-
-  if(err) {
-    failf(data, "LDAP remote: bind failed %s %s", ldap_err2string(rc),
-          info ? info : "");
-    if(info)
-      ldap_memfree(info);
-    return CURLE_LOGIN_DENIED;
-  }
-
-  if(info)
-    ldap_memfree(info);
-  conn->recv[FIRSTSOCKET] = ldap_recv;
-  *done = TRUE;
-  return CURLE_OK;
-}
-
-static CURLcode ldap_disconnect(struct connectdata *conn, bool dead_connection)
-{
-  ldapconninfo *li = conn->proto.generic;
-  (void) dead_connection;
-
-  if(li) {
-    if(li->ld) {
-      ldap_unbind_ext(li->ld, NULL, NULL);
-      li->ld = NULL;
-    }
-    conn->proto.generic = NULL;
-    free(li);
-  }
-  return CURLE_OK;
-}
-
-static CURLcode ldap_do(struct connectdata *conn, bool *done)
-{
-  ldapconninfo *li = conn->proto.generic;
-  ldapreqinfo *lr;
-  CURLcode status = CURLE_OK;
-  int rc = 0;
-  LDAPURLDesc *ludp = NULL;
-  int msgid;
-  struct SessionHandle *data=conn->data;
-
-  conn->bits.close = FALSE;
-
-  infof(data, "LDAP local: %s\n", data->change.url);
-
-  rc = ldap_url_parse(data->change.url, &ludp);
-  if(rc != LDAP_URL_SUCCESS) {
-    const char *msg = "url parsing problem";
-    status = CURLE_URL_MALFORMAT;
-    if(rc > LDAP_URL_SUCCESS && rc <= LDAP_URL_ERR_BADEXTS) {
-      if(rc == LDAP_URL_ERR_MEM)
-        status = CURLE_OUT_OF_MEMORY;
-      msg = url_errs[rc];
-    }
-    failf(conn->data, "LDAP local: %s", msg);
-    return status;
-  }
-
-  rc = ldap_search_ext(li->ld, ludp->lud_dn, ludp->lud_scope,
-                       ludp->lud_filter, ludp->lud_attrs, 0,
-                       NULL, NULL, NULL, 0, &msgid);
-  ldap_free_urldesc(ludp);
-  if(rc != LDAP_SUCCESS) {
-    failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
-    return CURLE_LDAP_SEARCH_FAILED;
-  }
-  lr = calloc(1,sizeof(ldapreqinfo));
-  if(!lr)
-    return CURLE_OUT_OF_MEMORY;
-  lr->msgid = msgid;
-  data->state.proto.generic = lr;
-  Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
-  *done = TRUE;
-  return CURLE_OK;
-}
-
-static CURLcode ldap_done(struct connectdata *conn, CURLcode res,
-                          bool premature)
-{
-  ldapreqinfo *lr = conn->data->state.proto.generic;
-  (void)res;
-  (void)premature;
-
-  if(lr) {
-    /* if there was a search in progress, abandon it */
-    if(lr->msgid) {
-      ldapconninfo *li = conn->proto.generic;
-      ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
-      lr->msgid = 0;
-    }
-    conn->data->state.proto.generic = NULL;
-    free(lr);
-  }
-  return CURLE_OK;
-}
-
-static ssize_t ldap_recv(struct connectdata *conn, int sockindex, char *buf,
-                         size_t len, CURLcode *err)
-{
-  ldapconninfo *li = conn->proto.generic;
-  struct SessionHandle *data=conn->data;
-  ldapreqinfo *lr = data->state.proto.generic;
-  int rc, ret;
-  LDAPMessage *result = NULL;
-  LDAPMessage *ent;
-  BerElement *ber = NULL;
-  struct timeval tv = {0,1};
-  (void)len;
-  (void)buf;
-  (void)sockindex;
-
-  rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_RECEIVED, &tv, &result);
-  if(rc < 0) {
-    failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc));
-    *err = CURLE_RECV_ERROR;
-    return -1;
-  }
-
-  *err = CURLE_AGAIN;
-  ret = -1;
-
-  /* timed out */
-  if(result == NULL)
-    return ret;
-
-  for(ent = ldap_first_message(li->ld, result); ent;
-    ent = ldap_next_message(li->ld, ent)) {
-    struct berval bv, *bvals, **bvp = &bvals;
-    int binary = 0, msgtype;
-
-    msgtype = ldap_msgtype(ent);
-    if(msgtype == LDAP_RES_SEARCH_RESULT) {
-      int code;
-      char *info = NULL;
-      rc = ldap_parse_result(li->ld, ent, &code, NULL, &info, NULL, NULL, 0);
-      if(rc) {
-        failf(data, "LDAP local: search ldap_parse_result %s",
-              ldap_err2string(rc));
-        *err = CURLE_LDAP_SEARCH_FAILED;
-      }
-      else if(code && code != LDAP_SIZELIMIT_EXCEEDED) {
-        failf(data, "LDAP remote: search failed %s %s", ldap_err2string(rc),
-              info ? info : "");
-        *err = CURLE_LDAP_SEARCH_FAILED;
-      }
-      else {
-        /* successful */
-        if(code == LDAP_SIZELIMIT_EXCEEDED)
-          infof(data, "There are more than %d entries\n", lr->nument);
-        data->req.size = data->req.bytecount;
-        *err = CURLE_OK;
-        ret = 0;
-      }
-      lr->msgid = 0;
-      ldap_memfree(info);
-      break;
-    }
-    else if(msgtype != LDAP_RES_SEARCH_ENTRY)
-      continue;
-
-    lr->nument++;
-    rc = ldap_get_dn_ber(li->ld, ent, &ber, &bv);
-    if(rc < 0) {
-      /* TODO: verify that this is really how this return code should be
-         handled */
-      *err = CURLE_RECV_ERROR;
-      return -1;
-    }
-    Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"DN: ", 4);
-    Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val, bv.bv_len);
-    Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1);
-    data->req.bytecount += bv.bv_len + 5;
-
-    for(rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, bvp);
-      rc == LDAP_SUCCESS;
-      rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, bvp)) {
-      int i;
-
-      if(bv.bv_val == NULL) break;
-
-      if(bv.bv_len > 7 && !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7))
-        binary = 1;
-      else
-        binary = 0;
-
-      for(i=0; bvals[i].bv_val != NULL; i++) {
-        int binval = 0;
-        Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1);
-        Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val,
-                          bv.bv_len);
-        Curl_client_write(conn, CLIENTWRITE_BODY, (char *)":", 1);
-        data->req.bytecount += bv.bv_len + 2;
-
-        if(!binary) {
-          /* check for leading or trailing whitespace */
-          if(ISSPACE(bvals[i].bv_val[0]) ||
-              ISSPACE(bvals[i].bv_val[bvals[i].bv_len-1]))
-            binval = 1;
-          else {
-            /* check for unprintable characters */
-            unsigned int j;
-            for(j=0; j<bvals[i].bv_len; j++)
-              if(!ISPRINT(bvals[i].bv_val[j])) {
-                binval = 1;
-                break;
-              }
-          }
-        }
-        if(binary || binval) {
-          char *val_b64 = NULL;
-          size_t val_b64_sz = 0;
-          /* Binary value, encode to base64. */
-          CURLcode error = Curl_base64_encode(data,
-                                              bvals[i].bv_val,
-                                              bvals[i].bv_len,
-                                              &val_b64,
-                                              &val_b64_sz);
-          if(error) {
-            ber_memfree(bvals);
-            ber_free(ber, 0);
-            ldap_msgfree(result);
-            *err = error;
-            return -1;
-          }
-          Curl_client_write(conn, CLIENTWRITE_BODY, (char *)": ", 2);
-          data->req.bytecount += 2;
-          if(val_b64_sz > 0) {
-            Curl_client_write(conn, CLIENTWRITE_BODY, val_b64, val_b64_sz);
-            free(val_b64);
-            data->req.bytecount += val_b64_sz;
-          }
-        }
-        else {
-          Curl_client_write(conn, CLIENTWRITE_BODY, (char *)" ", 1);
-          Curl_client_write(conn, CLIENTWRITE_BODY, bvals[i].bv_val,
-                            bvals[i].bv_len);
-          data->req.bytecount += bvals[i].bv_len + 1;
-        }
-        Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
-        data->req.bytecount++;
-      }
-      ber_memfree(bvals);
-      Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
-      data->req.bytecount++;
-    }
-    Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0);
-    data->req.bytecount++;
-    ber_free(ber, 0);
-  }
-  ldap_msgfree(result);
-  return ret;
-}
-
-#ifdef USE_SSL
-static int
-ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg)
-{
-  sbiod->sbiod_pvt = arg;
-  return 0;
-}
-
-static int
-ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod)
-{
-  sbiod->sbiod_pvt = NULL;
-  return 0;
-}
-
-/* We don't need to do anything because libcurl does it already */
-static int
-ldapsb_tls_close(Sockbuf_IO_Desc *sbiod)
-{
-  (void)sbiod;
-  return 0;
-}
-
-static int
-ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg)
-{
-  (void)arg;
-  if(opt == LBER_SB_OPT_DATA_READY) {
-    struct connectdata *conn = sbiod->sbiod_pvt;
-    return Curl_ssl_data_pending(conn, FIRSTSOCKET);
-  }
-  return 0;
-}
-
-static ber_slen_t
-ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
-{
-  struct connectdata *conn = sbiod->sbiod_pvt;
-  ldapconninfo *li = conn->proto.generic;
-  ber_slen_t ret;
-  CURLcode err = CURLE_RECV_ERROR;
-
-  ret = li->recv(conn, FIRSTSOCKET, buf, len, &err);
-  if(ret < 0 && err == CURLE_AGAIN) {
-    SET_SOCKERRNO(EWOULDBLOCK);
-  }
-  return ret;
-}
-
-static ber_slen_t
-ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
-{
-  struct connectdata *conn = sbiod->sbiod_pvt;
-  ldapconninfo *li = conn->proto.generic;
-  ber_slen_t ret;
-  CURLcode err = CURLE_SEND_ERROR;
-
-  ret = li->send(conn, FIRSTSOCKET, buf, len, &err);
-  if(ret < 0 && err == CURLE_AGAIN) {
-    SET_SOCKERRNO(EWOULDBLOCK);
-  }
-  return ret;
-}
-
-static Sockbuf_IO ldapsb_tls =
-{
-  ldapsb_tls_setup,
-  ldapsb_tls_remove,
-  ldapsb_tls_ctrl,
-  ldapsb_tls_read,
-  ldapsb_tls_write,
-  ldapsb_tls_close
-};
-#endif /* USE_SSL */
-
-#endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */
diff --git a/lib/parsedate.c b/lib/parsedate.c
deleted file mode 100644 (file)
index a50b607..0000000
+++ /dev/null
@@ -1,580 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-/*
-  A brief summary of the date string formats this parser groks:
-
-  RFC 2616 3.3.1
-
-  Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
-  Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
-  Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format
-
-  we support dates without week day name:
-
-  06 Nov 1994 08:49:37 GMT
-  06-Nov-94 08:49:37 GMT
-  Nov  6 08:49:37 1994
-
-  without the time zone:
-
-  06 Nov 1994 08:49:37
-  06-Nov-94 08:49:37
-
-  weird order:
-
-  1994 Nov 6 08:49:37  (GNU date fails)
-  GMT 08:49:37 06-Nov-94 Sunday
-  94 6 Nov 08:49:37    (GNU date fails)
-
-  time left out:
-
-  1994 Nov 6
-  06-Nov-94
-  Sun Nov 6 94
-
-  unusual separators:
-
-  1994.Nov.6
-  Sun/Nov/6/94/GMT
-
-  commonly used time zone names:
-
-  Sun, 06 Nov 1994 08:49:37 CET
-  06 Nov 1994 08:49:37 EST
-
-  time zones specified using RFC822 style:
-
-  Sun, 12 Sep 2004 15:05:58 -0700
-  Sat, 11 Sep 2004 21:32:11 +0200
-
-  compact numerical date strings:
-
-  20040912 15:05:58 -0700
-  20040911 +0200
-
-*/
-
-#include "curl_setup.h"
-
-#ifdef HAVE_LIMITS_H
-#include <limits.h>
-#endif
-
-#include <curl/curl.h>
-#include "curl_rawstr.h"
-#include "curl_warnless.h"
-#include "curl_parsedate.h"
-
-const char * const Curl_wkday[] =
-{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
-static const char * const weekday[] =
-{ "Monday", "Tuesday", "Wednesday", "Thursday",
-  "Friday", "Saturday", "Sunday" };
-const char * const Curl_month[]=
-{ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
-
-struct tzinfo {
-  char name[5];
-  int offset; /* +/- in minutes */
-};
-
-/*
- * parsedate()
- *
- * Returns:
- *
- * PARSEDATE_OK     - a fine conversion
- * PARSEDATE_FAIL   - failed to convert
- * PARSEDATE_LATER  - time overflow at the far end of time_t
- * PARSEDATE_SOONER - time underflow at the low end of time_t
- */
-
-static int parsedate(const char *date, time_t *output);
-
-#define PARSEDATE_OK     0
-#define PARSEDATE_FAIL   -1
-#define PARSEDATE_LATER  1
-#define PARSEDATE_SOONER 2
-
-/* Here's a bunch of frequently used time zone names. These were supported
-   by the old getdate parser. */
-#define tDAYZONE -60       /* offset for daylight savings time */
-static const struct tzinfo tz[]= {
-  {"GMT", 0},              /* Greenwich Mean */
-  {"UTC", 0},              /* Universal (Coordinated) */
-  {"WET", 0},              /* Western European */
-  {"BST", 0 tDAYZONE},     /* British Summer */
-  {"WAT", 60},             /* West Africa */
-  {"AST", 240},            /* Atlantic Standard */
-  {"ADT", 240 tDAYZONE},   /* Atlantic Daylight */
-  {"EST", 300},            /* Eastern Standard */
-  {"EDT", 300 tDAYZONE},   /* Eastern Daylight */
-  {"CST", 360},            /* Central Standard */
-  {"CDT", 360 tDAYZONE},   /* Central Daylight */
-  {"MST", 420},            /* Mountain Standard */
-  {"MDT", 420 tDAYZONE},   /* Mountain Daylight */
-  {"PST", 480},            /* Pacific Standard */
-  {"PDT", 480 tDAYZONE},   /* Pacific Daylight */
-  {"YST", 540},            /* Yukon Standard */
-  {"YDT", 540 tDAYZONE},   /* Yukon Daylight */
-  {"HST", 600},            /* Hawaii Standard */
-  {"HDT", 600 tDAYZONE},   /* Hawaii Daylight */
-  {"CAT", 600},            /* Central Alaska */
-  {"AHST", 600},           /* Alaska-Hawaii Standard */
-  {"NT",  660},            /* Nome */
-  {"IDLW", 720},           /* International Date Line West */
-  {"CET", -60},            /* Central European */
-  {"MET", -60},            /* Middle European */
-  {"MEWT", -60},           /* Middle European Winter */
-  {"MEST", -60 tDAYZONE},  /* Middle European Summer */
-  {"CEST", -60 tDAYZONE},  /* Central European Summer */
-  {"MESZ", -60 tDAYZONE},  /* Middle European Summer */
-  {"FWT", -60},            /* French Winter */
-  {"FST", -60 tDAYZONE},   /* French Summer */
-  {"EET", -120},           /* Eastern Europe, USSR Zone 1 */
-  {"WAST", -420},          /* West Australian Standard */
-  {"WADT", -420 tDAYZONE}, /* West Australian Daylight */
-  {"CCT", -480},           /* China Coast, USSR Zone 7 */
-  {"JST", -540},           /* Japan Standard, USSR Zone 8 */
-  {"EAST", -600},          /* Eastern Australian Standard */
-  {"EADT", -600 tDAYZONE}, /* Eastern Australian Daylight */
-  {"GST", -600},           /* Guam Standard, USSR Zone 9 */
-  {"NZT", -720},           /* New Zealand */
-  {"NZST", -720},          /* New Zealand Standard */
-  {"NZDT", -720 tDAYZONE}, /* New Zealand Daylight */
-  {"IDLE", -720},          /* International Date Line East */
-  /* Next up: Military timezone names. RFC822 allowed these, but (as noted in
-     RFC 1123) had their signs wrong. Here we use the correct signs to match
-     actual military usage.
-   */
-  {"A",  +1 * 60},         /* Alpha */
-  {"B",  +2 * 60},         /* Bravo */
-  {"C",  +3 * 60},         /* Charlie */
-  {"D",  +4 * 60},         /* Delta */
-  {"E",  +5 * 60},         /* Echo */
-  {"F",  +6 * 60},         /* Foxtrot */
-  {"G",  +7 * 60},         /* Golf */
-  {"H",  +8 * 60},         /* Hotel */
-  {"I",  +9 * 60},         /* India */
-  /* "J", Juliet is not used as a timezone, to indicate the observer's local
-     time */
-  {"K", +10 * 60},         /* Kilo */
-  {"L", +11 * 60},         /* Lima */
-  {"M", +12 * 60},         /* Mike */
-  {"N",  -1 * 60},         /* November */
-  {"O",  -2 * 60},         /* Oscar */
-  {"P",  -3 * 60},         /* Papa */
-  {"Q",  -4 * 60},         /* Quebec */
-  {"R",  -5 * 60},         /* Romeo */
-  {"S",  -6 * 60},         /* Sierra */
-  {"T",  -7 * 60},         /* Tango */
-  {"U",  -8 * 60},         /* Uniform */
-  {"V",  -9 * 60},         /* Victor */
-  {"W", -10 * 60},         /* Whiskey */
-  {"X", -11 * 60},         /* X-ray */
-  {"Y", -12 * 60},         /* Yankee */
-  {"Z", 0},                /* Zulu, zero meridian, a.k.a. UTC */
-};
-
-/* returns:
-   -1 no day
-   0 monday - 6 sunday
-*/
-
-static int checkday(const char *check, size_t len)
-{
-  int i;
-  const char * const *what;
-  bool found= FALSE;
-  if(len > 3)
-    what = &weekday[0];
-  else
-    what = &Curl_wkday[0];
-  for(i=0; i<7; i++) {
-    if(Curl_raw_equal(check, what[0])) {
-      found=TRUE;
-      break;
-    }
-    what++;
-  }
-  return found?i:-1;
-}
-
-static int checkmonth(const char *check)
-{
-  int i;
-  const char * const *what;
-  bool found= FALSE;
-
-  what = &Curl_month[0];
-  for(i=0; i<12; i++) {
-    if(Curl_raw_equal(check, what[0])) {
-      found=TRUE;
-      break;
-    }
-    what++;
-  }
-  return found?i:-1; /* return the offset or -1, no real offset is -1 */
-}
-
-/* return the time zone offset between GMT and the input one, in number
-   of seconds or -1 if the timezone wasn't found/legal */
-
-static int checktz(const char *check)
-{
-  unsigned int i;
-  const struct tzinfo *what;
-  bool found= FALSE;
-
-  what = tz;
-  for(i=0; i< sizeof(tz)/sizeof(tz[0]); i++) {
-    if(Curl_raw_equal(check, what->name)) {
-      found=TRUE;
-      break;
-    }
-    what++;
-  }
-  return found?what->offset*60:-1;
-}
-
-static void skip(const char **date)
-{
-  /* skip everything that aren't letters or digits */
-  while(**date && !ISALNUM(**date))
-    (*date)++;
-}
-
-enum assume {
-  DATE_MDAY,
-  DATE_YEAR,
-  DATE_TIME
-};
-
-/* this is a clone of 'struct tm' but with all fields we don't need or use
-   cut out */
-struct my_tm {
-  int tm_sec;
-  int tm_min;
-  int tm_hour;
-  int tm_mday;
-  int tm_mon;
-  int tm_year;
-};
-
-/* struct tm to time since epoch in GMT time zone.
- * This is similar to the standard mktime function but for GMT only, and
- * doesn't suffer from the various bugs and portability problems that
- * some systems' implementations have.
- */
-static time_t my_timegm(struct my_tm *tm)
-{
-  static const int month_days_cumulative [12] =
-    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
-  int month, year, leap_days;
-
-  if(tm->tm_year < 70)
-    /* we don't support years before 1970 as they will cause this function
-       to return a negative value */
-    return -1;
-
-  year = tm->tm_year + 1900;
-  month = tm->tm_mon;
-  if(month < 0) {
-    year += (11 - month) / 12;
-    month = 11 - (11 - month) % 12;
-  }
-  else if(month >= 12) {
-    year -= month / 12;
-    month = month % 12;
-  }
-
-  leap_days = year - (tm->tm_mon <= 1);
-  leap_days = ((leap_days / 4) - (leap_days / 100) + (leap_days / 400)
-               - (1969 / 4) + (1969 / 100) - (1969 / 400));
-
-  return ((((time_t) (year - 1970) * 365
-            + leap_days + month_days_cumulative [month] + tm->tm_mday - 1) * 24
-           + tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec;
-}
-
-/*
- * parsedate()
- *
- * Returns:
- *
- * PARSEDATE_OK     - a fine conversion
- * PARSEDATE_FAIL   - failed to convert
- * PARSEDATE_LATER  - time overflow at the far end of time_t
- * PARSEDATE_SOONER - time underflow at the low end of time_t
- */
-
-static int parsedate(const char *date, time_t *output)
-{
-  time_t t = 0;
-  int wdaynum=-1;  /* day of the week number, 0-6 (mon-sun) */
-  int monnum=-1;   /* month of the year number, 0-11 */
-  int mdaynum=-1; /* day of month, 1 - 31 */
-  int hournum=-1;
-  int minnum=-1;
-  int secnum=-1;
-  int yearnum=-1;
-  int tzoff=-1;
-  struct my_tm tm;
-  enum assume dignext = DATE_MDAY;
-  const char *indate = date; /* save the original pointer */
-  int part = 0; /* max 6 parts */
-
-  while(*date && (part < 6)) {
-    bool found=FALSE;
-
-    skip(&date);
-
-    if(ISALPHA(*date)) {
-      /* a name coming up */
-      char buf[32]="";
-      size_t len;
-      sscanf(date, "%31[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]",
-             buf);
-      len = strlen(buf);
-
-      if(wdaynum == -1) {
-        wdaynum = checkday(buf, len);
-        if(wdaynum != -1)
-          found = TRUE;
-      }
-      if(!found && (monnum == -1)) {
-        monnum = checkmonth(buf);
-        if(monnum != -1)
-          found = TRUE;
-      }
-
-      if(!found && (tzoff == -1)) {
-        /* this just must be a time zone string */
-        tzoff = checktz(buf);
-        if(tzoff != -1)
-          found = TRUE;
-      }
-
-      if(!found)
-        return PARSEDATE_FAIL; /* bad string */
-
-      date += len;
-    }
-    else if(ISDIGIT(*date)) {
-      /* a digit */
-      int val;
-      char *end;
-      if((secnum == -1) &&
-         (3 == sscanf(date, "%02d:%02d:%02d", &hournum, &minnum, &secnum))) {
-        /* time stamp! */
-        date += 8;
-      }
-      else if((secnum == -1) &&
-              (2 == sscanf(date, "%02d:%02d", &hournum, &minnum))) {
-        /* time stamp without seconds */
-        date += 5;
-        secnum = 0;
-      }
-      else {
-        long lval;
-        int error;
-        int old_errno;
-
-        old_errno = ERRNO;
-        SET_ERRNO(0);
-        lval = strtol(date, &end, 10);
-        error = ERRNO;
-        if(error != old_errno)
-          SET_ERRNO(old_errno);
-
-        if(error)
-          return PARSEDATE_FAIL;
-
-        if((lval > (long)INT_MAX) || (lval < (long)INT_MIN))
-          return PARSEDATE_FAIL;
-
-        val = curlx_sltosi(lval);
-
-        if((tzoff == -1) &&
-           ((end - date) == 4) &&
-           (val <= 1400) &&
-           (indate< date) &&
-           ((date[-1] == '+' || date[-1] == '-'))) {
-          /* four digits and a value less than or equal to 1400 (to take into
-             account all sorts of funny time zone diffs) and it is preceded
-             with a plus or minus. This is a time zone indication.  1400 is
-             picked since +1300 is frequently used and +1400 is mentioned as
-             an edge number in the document "ISO C 200X Proposal: Timezone
-             Functions" at http://david.tribble.com/text/c0xtimezone.html If
-             anyone has a more authoritative source for the exact maximum time
-             zone offsets, please speak up! */
-          found = TRUE;
-          tzoff = (val/100 * 60 + val%100)*60;
-
-          /* the + and - prefix indicates the local time compared to GMT,
-             this we need ther reversed math to get what we want */
-          tzoff = date[-1]=='+'?-tzoff:tzoff;
-        }
-
-        if(((end - date) == 8) &&
-           (yearnum == -1) &&
-           (monnum == -1) &&
-           (mdaynum == -1)) {
-          /* 8 digits, no year, month or day yet. This is YYYYMMDD */
-          found = TRUE;
-          yearnum = val/10000;
-          monnum = (val%10000)/100-1; /* month is 0 - 11 */
-          mdaynum = val%100;
-        }
-
-        if(!found && (dignext == DATE_MDAY) && (mdaynum == -1)) {
-          if((val > 0) && (val<32)) {
-            mdaynum = val;
-            found = TRUE;
-          }
-          dignext = DATE_YEAR;
-        }
-
-        if(!found && (dignext == DATE_YEAR) && (yearnum == -1)) {
-          yearnum = val;
-          found = TRUE;
-          if(yearnum < 1900) {
-            if(yearnum > 70)
-              yearnum += 1900;
-            else
-              yearnum += 2000;
-          }
-          if(mdaynum == -1)
-            dignext = DATE_MDAY;
-        }
-
-        if(!found)
-          return PARSEDATE_FAIL;
-
-        date = end;
-      }
-    }
-
-    part++;
-  }
-
-  if(-1 == secnum)
-    secnum = minnum = hournum = 0; /* no time, make it zero */
-
-  if((-1 == mdaynum) ||
-     (-1 == monnum) ||
-     (-1 == yearnum))
-    /* lacks vital info, fail */
-    return PARSEDATE_FAIL;
-
-#if SIZEOF_TIME_T < 5
-  /* 32 bit time_t can only hold dates to the beginning of 2038 */
-  if(yearnum > 2037) {
-    *output = 0x7fffffff;
-    return PARSEDATE_LATER;
-  }
-#endif
-
-  if(yearnum < 1970) {
-    *output = 0;
-    return PARSEDATE_SOONER;
-  }
-
-  if((mdaynum > 31) || (monnum > 11) ||
-     (hournum > 23) || (minnum > 59) || (secnum > 60))
-    return PARSEDATE_FAIL; /* clearly an illegal date */
-
-  tm.tm_sec = secnum;
-  tm.tm_min = minnum;
-  tm.tm_hour = hournum;
-  tm.tm_mday = mdaynum;
-  tm.tm_mon = monnum;
-  tm.tm_year = yearnum - 1900;
-
-  /* my_timegm() returns a time_t. time_t is often 32 bits, even on many
-     architectures that feature 64 bit 'long'.
-
-     Some systems have 64 bit time_t and deal with years beyond 2038. However,
-     even on some of the systems with 64 bit time_t mktime() returns -1 for
-     dates beyond 03:14:07 UTC, January 19, 2038. (Such as AIX 5100-06)
-  */
-  t = my_timegm(&tm);
-
-  /* time zone adjust (cast t to int to compare to negative one) */
-  if(-1 != (int)t) {
-
-    /* Add the time zone diff between local time zone and GMT. */
-    long delta = (long)(tzoff!=-1?tzoff:0);
-
-    if((delta>0) && (t + delta < t))
-      return -1; /* time_t overflow */
-
-    t += delta;
-  }
-
-  *output = t;
-
-  return PARSEDATE_OK;
-}
-
-time_t curl_getdate(const char *p, const time_t *now)
-{
-  time_t parsed;
-  int rc = parsedate(p, &parsed);
-  (void)now; /* legacy argument from the past that we ignore */
-
-  switch(rc) {
-  case PARSEDATE_OK:
-  case PARSEDATE_LATER:
-  case PARSEDATE_SOONER:
-    return parsed;
-  }
-  /* everything else is fail */
-  return -1;
-}
-
-/*
- * Curl_gmtime() is a gmtime() replacement for portability. Do not use the
- * gmtime_r() or gmtime() functions anywhere else but here.
- *
- * To make sure no such function calls slip in, we define them to cause build
- * errors, which is why we use the name within parentheses in this function.
- *
- */
-
-CURLcode Curl_gmtime(time_t intime, struct tm *store)
-{
-  const struct tm *tm;
-#ifdef HAVE_GMTIME_R
-  /* thread-safe version */
-  tm = (struct tm *)gmtime_r(&intime, store);
-#else
-  tm = gmtime(&intime);
-  if(tm)
-    *store = *tm; /* copy the pointed struct to the local copy */
-#endif
-
-  if(!tm)
-    return CURLE_BAD_FUNCTION_ARGUMENT;
-  return CURLE_OK;
-}
diff --git a/lib/pingpong.c b/lib/pingpong.c
deleted file mode 100644 (file)
index d28e78a..0000000
+++ /dev/null
@@ -1,538 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- *   'pingpong' is for generic back-and-forth support functions used by FTP,
- *   IMAP, POP3, SMTP and whatever more that likes them.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_select.h"
-#include "curl_progress.h"
-#include "curl_speedcheck.h"
-#include "curl_pingpong.h"
-#include "curl_multiif.h"
-#include "curl_non_ascii.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-#ifdef USE_PINGPONG
-
-/* Returns timeout in ms. 0 or negative number means the timeout has already
-   triggered */
-long Curl_pp_state_timeout(struct pingpong *pp)
-{
-  struct connectdata *conn = pp->conn;
-  struct SessionHandle *data=conn->data;
-  long timeout_ms; /* in milliseconds */
-  long timeout2_ms; /* in milliseconds */
-  long response_time= (data->set.server_response_timeout)?
-    data->set.server_response_timeout: pp->response_time;
-
-  /* if CURLOPT_SERVER_RESPONSE_TIMEOUT is set, use that to determine
-     remaining time, or use pp->response because SERVER_RESPONSE_TIMEOUT is
-     supposed to govern the response for any given server response, not for
-     the time from connect to the given server response. */
-
-  /* Without a requested timeout, we only wait 'response_time' seconds for the
-     full response to arrive before we bail out */
-  timeout_ms = response_time -
-    Curl_tvdiff(Curl_tvnow(), pp->response); /* spent time */
-
-  if(data->set.timeout) {
-    /* if timeout is requested, find out how much remaining time we have */
-    timeout2_ms = data->set.timeout - /* timeout time */
-      Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
-
-    /* pick the lowest number */
-    timeout_ms = CURLMIN(timeout_ms, timeout2_ms);
-  }
-
-  return timeout_ms;
-}
-
-
-/*
- * Curl_pp_multi_statemach()
- *
- * called repeatedly until done when the multi interface is used.
- */
-CURLcode Curl_pp_multi_statemach(struct pingpong *pp)
-{
-  struct connectdata *conn = pp->conn;
-  curl_socket_t sock = conn->sock[FIRSTSOCKET];
-  int rc;
-  struct SessionHandle *data=conn->data;
-  CURLcode result = CURLE_OK;
-  long timeout_ms = Curl_pp_state_timeout(pp);
-
-  if(timeout_ms <= 0) {
-    failf(data, "server response timeout");
-    return CURLE_OPERATION_TIMEDOUT;
-  }
-
-  rc = Curl_socket_ready(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
-                         pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
-                         0);
-
-  if(rc == -1) {
-    failf(data, "select/poll error");
-    return CURLE_OUT_OF_MEMORY;
-  }
-  else if(rc != 0)
-    result = pp->statemach_act(conn);
-
-  /* if rc == 0, then select() timed out */
-
-  return result;
-}
-
-/*
- * Curl_pp_easy_statemach()
- *
- * called repeatedly until done when the easy interface is used.
- */
-CURLcode Curl_pp_easy_statemach(struct pingpong *pp)
-{
-  struct connectdata *conn = pp->conn;
-  curl_socket_t sock = conn->sock[FIRSTSOCKET];
-  int rc;
-  long interval_ms;
-  long timeout_ms = Curl_pp_state_timeout(pp);
-  struct SessionHandle *data=conn->data;
-  CURLcode result;
-
-  if(timeout_ms <=0 ) {
-    failf(data, "server response timeout");
-    return CURLE_OPERATION_TIMEDOUT; /* already too little time */
-  }
-
-  interval_ms = 1000;  /* use 1 second timeout intervals */
-  if(timeout_ms < interval_ms)
-    interval_ms = timeout_ms;
-
-  rc = Curl_socket_ready(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
-                         pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
-                         interval_ms);
-
-  if(Curl_pgrsUpdate(conn))
-    result = CURLE_ABORTED_BY_CALLBACK;
-  else
-    result = Curl_speedcheck(data, Curl_tvnow());
-
-  if(result)
-    ;
-  else if(rc == -1) {
-    failf(data, "select/poll error");
-    result = CURLE_OUT_OF_MEMORY;
-  }
-  else if(rc)
-    result = pp->statemach_act(conn);
-
-  return result;
-}
-
-/* initialize stuff to prepare for reading a fresh new response */
-void Curl_pp_init(struct pingpong *pp)
-{
-  struct connectdata *conn = pp->conn;
-  pp->nread_resp = 0;
-  pp->linestart_resp = conn->data->state.buffer;
-  pp->pending_resp = TRUE;
-  pp->response = Curl_tvnow(); /* start response time-out now! */
-}
-
-
-
-/***********************************************************************
- *
- * Curl_pp_vsendf()
- *
- * Send the formated string as a command to a pingpong server. Note that
- * the string should not have any CRLF appended, as this function will
- * append the necessary things itself.
- *
- * made to never block
- */
-CURLcode Curl_pp_vsendf(struct pingpong *pp,
-                        const char *fmt,
-                        va_list args)
-{
-  ssize_t bytes_written;
-  size_t write_len;
-  char *fmt_crlf;
-  char *s;
-  CURLcode error;
-  struct connectdata *conn = pp->conn;
-  struct SessionHandle *data = conn->data;
-
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
-  enum protection_level data_sec = conn->data_prot;
-#endif
-
-  DEBUGASSERT(pp->sendleft == 0);
-  DEBUGASSERT(pp->sendsize == 0);
-  DEBUGASSERT(pp->sendthis == NULL);
-
-  fmt_crlf = aprintf("%s\r\n", fmt); /* append a trailing CRLF */
-  if(!fmt_crlf)
-    return CURLE_OUT_OF_MEMORY;
-
-  s = vaprintf(fmt_crlf, args); /* trailing CRLF appended */
-  free(fmt_crlf);
-  if(!s)
-    return CURLE_OUT_OF_MEMORY;
-
-  bytes_written = 0;
-  write_len = strlen(s);
-
-  Curl_pp_init(pp);
-
-  error = Curl_convert_to_network(data, s, write_len);
-  /* Curl_convert_to_network calls failf if unsuccessful */
-  if(error) {
-    free(s);
-    return error;
-  }
-
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
-  conn->data_prot = PROT_CMD;
-#endif
-  error = Curl_write(conn, conn->sock[FIRSTSOCKET], s, write_len,
-                     &bytes_written);
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
-  DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
-  conn->data_prot = data_sec;
-#endif
-
-  if(error) {
-    free(s);
-    return error;
-  }
-
-  if(conn->data->set.verbose)
-    Curl_debug(conn->data, CURLINFO_HEADER_OUT,
-               s, (size_t)bytes_written, conn);
-
-  if(bytes_written != (ssize_t)write_len) {
-    /* the whole chunk was not sent, keep it around and adjust sizes */
-    pp->sendthis = s;
-    pp->sendsize = write_len;
-    pp->sendleft = write_len - bytes_written;
-  }
-  else {
-    free(s);
-    pp->sendthis = NULL;
-    pp->sendleft = pp->sendsize = 0;
-    pp->response = Curl_tvnow();
-  }
-
-  return CURLE_OK;
-}
-
-
-/***********************************************************************
- *
- * Curl_pp_sendf()
- *
- * Send the formated string as a command to a pingpong server. Note that
- * the string should not have any CRLF appended, as this function will
- * append the necessary things itself.
- *
- * made to never block
- */
-CURLcode Curl_pp_sendf(struct pingpong *pp,
-                       const char *fmt, ...)
-{
-  CURLcode res;
-  va_list ap;
-  va_start(ap, fmt);
-
-  res = Curl_pp_vsendf(pp, fmt, ap);
-
-  va_end(ap);
-
-  return res;
-}
-
-/*
- * Curl_pp_readresp()
- *
- * Reads a piece of a server response.
- */
-CURLcode Curl_pp_readresp(curl_socket_t sockfd,
-                          struct pingpong *pp,
-                          int *code, /* return the server code if done */
-                          size_t *size) /* size of the response */
-{
-  ssize_t perline; /* count bytes per line */
-  bool keepon=TRUE;
-  ssize_t gotbytes;
-  char *ptr;
-  struct connectdata *conn = pp->conn;
-  struct SessionHandle *data = conn->data;
-  char * const buf = data->state.buffer;
-  CURLcode result = CURLE_OK;
-
-  *code = 0; /* 0 for errors or not done */
-  *size = 0;
-
-  ptr=buf + pp->nread_resp;
-
-  /* number of bytes in the current line, so far */
-  perline = (ssize_t)(ptr-pp->linestart_resp);
-
-  keepon=TRUE;
-
-  while((pp->nread_resp<BUFSIZE) && (keepon && !result)) {
-
-    if(pp->cache) {
-      /* we had data in the "cache", copy that instead of doing an actual
-       * read
-       *
-       * pp->cache_size is cast to ssize_t here.  This should be safe, because
-       * it would have been populated with something of size int to begin
-       * with, even though its datatype may be larger than an int.
-       */
-      DEBUGASSERT((ptr+pp->cache_size) <= (buf+BUFSIZE+1));
-      memcpy(ptr, pp->cache, pp->cache_size);
-      gotbytes = (ssize_t)pp->cache_size;
-      free(pp->cache);    /* free the cache */
-      pp->cache = NULL;   /* clear the pointer */
-      pp->cache_size = 0; /* zero the size just in case */
-    }
-    else {
-      int res;
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
-      enum protection_level prot = conn->data_prot;
-      conn->data_prot = PROT_CLEAR;
-#endif
-      DEBUGASSERT((ptr+BUFSIZE-pp->nread_resp) <= (buf+BUFSIZE+1));
-      res = Curl_read(conn, sockfd, ptr, BUFSIZE-pp->nread_resp,
-                      &gotbytes);
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
-      DEBUGASSERT(prot  > PROT_NONE && prot < PROT_LAST);
-      conn->data_prot = prot;
-#endif
-      if(res == CURLE_AGAIN)
-        return CURLE_OK; /* return */
-
-      if((res == CURLE_OK) && (gotbytes > 0))
-        /* convert from the network encoding */
-        res = Curl_convert_from_network(data, ptr, gotbytes);
-      /* Curl_convert_from_network calls failf if unsuccessful */
-
-      if(CURLE_OK != res) {
-        result = (CURLcode)res; /* Set outer result variable to this error. */
-        keepon = FALSE;
-      }
-    }
-
-    if(!keepon)
-      ;
-    else if(gotbytes <= 0) {
-      keepon = FALSE;
-      result = CURLE_RECV_ERROR;
-      failf(data, "response reading failed");
-    }
-    else {
-      /* we got a whole chunk of data, which can be anything from one
-       * byte to a set of lines and possible just a piece of the last
-       * line */
-      ssize_t i;
-      ssize_t clipamount = 0;
-      bool restart = FALSE;
-
-      data->req.headerbytecount += (long)gotbytes;
-
-      pp->nread_resp += gotbytes;
-      for(i = 0; i < gotbytes; ptr++, i++) {
-        perline++;
-        if(*ptr=='\n') {
-          /* a newline is CRLF in pp-talk, so the CR is ignored as
-             the line isn't really terminated until the LF comes */
-
-          /* output debug output if that is requested */
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
-          if(!conn->sec_complete)
-#endif
-            if(data->set.verbose)
-              Curl_debug(data, CURLINFO_HEADER_IN,
-                         pp->linestart_resp, (size_t)perline, conn);
-
-          /*
-           * We pass all response-lines to the callback function registered
-           * for "headers". The response lines can be seen as a kind of
-           * headers.
-           */
-          result = Curl_client_write(conn, CLIENTWRITE_HEADER,
-                                     pp->linestart_resp, perline);
-          if(result)
-            return result;
-
-          if(pp->endofresp(pp, code)) {
-            /* This is the end of the last line, copy the last line to the
-               start of the buffer and zero terminate, for old times sake (and
-               krb4)! */
-            char *meow;
-            int n;
-            for(meow=pp->linestart_resp, n=0; meow<ptr; meow++, n++)
-              buf[n] = *meow;
-            *meow=0; /* zero terminate */
-            keepon=FALSE;
-            pp->linestart_resp = ptr+1; /* advance pointer */
-            i++; /* skip this before getting out */
-
-            *size = pp->nread_resp; /* size of the response */
-            pp->nread_resp = 0; /* restart */
-            break;
-          }
-          perline=0; /* line starts over here */
-          pp->linestart_resp = ptr+1;
-        }
-      }
-
-      if(!keepon && (i != gotbytes)) {
-        /* We found the end of the response lines, but we didn't parse the
-           full chunk of data we have read from the server. We therefore need
-           to store the rest of the data to be checked on the next invoke as
-           it may actually contain another end of response already! */
-        clipamount = gotbytes - i;
-        restart = TRUE;
-        DEBUGF(infof(data, "Curl_pp_readresp_ %d bytes of trailing "
-                     "server response left\n",
-                     (int)clipamount));
-      }
-      else if(keepon) {
-
-        if((perline == gotbytes) && (gotbytes > BUFSIZE/2)) {
-          /* We got an excessive line without newlines and we need to deal
-             with it. We keep the first bytes of the line then we throw
-             away the rest. */
-          infof(data, "Excessive server response line length received, "
-                "%zd bytes. Stripping\n", gotbytes);
-          restart = TRUE;
-
-          /* we keep 40 bytes since all our pingpong protocols are only
-             interested in the first piece */
-          clipamount = 40;
-        }
-        else if(pp->nread_resp > BUFSIZE/2) {
-          /* We got a large chunk of data and there's potentially still
-             trailing data to take care of, so we put any such part in the
-             "cache", clear the buffer to make space and restart. */
-          clipamount = perline;
-          restart = TRUE;
-        }
-      }
-      else if(i == gotbytes)
-        restart = TRUE;
-
-      if(clipamount) {
-        pp->cache_size = clipamount;
-        pp->cache = malloc(pp->cache_size);
-        if(pp->cache)
-          memcpy(pp->cache, pp->linestart_resp, pp->cache_size);
-        else
-          return CURLE_OUT_OF_MEMORY;
-      }
-      if(restart) {
-        /* now reset a few variables to start over nicely from the start of
-           the big buffer */
-        pp->nread_resp = 0; /* start over from scratch in the buffer */
-        ptr = pp->linestart_resp = buf;
-        perline = 0;
-      }
-
-    } /* there was data */
-
-  } /* while there's buffer left and loop is requested */
-
-  pp->pending_resp = FALSE;
-
-  return result;
-}
-
-int Curl_pp_getsock(struct pingpong *pp,
-                    curl_socket_t *socks,
-                    int numsocks)
-{
-  struct connectdata *conn = pp->conn;
-
-  if(!numsocks)
-    return GETSOCK_BLANK;
-
-  socks[0] = conn->sock[FIRSTSOCKET];
-
-  if(pp->sendleft) {
-    /* write mode */
-    return GETSOCK_WRITESOCK(0);
-  }
-
-  /* read mode */
-  return GETSOCK_READSOCK(0);
-}
-
-CURLcode Curl_pp_flushsend(struct pingpong *pp)
-{
-  /* we have a piece of a command still left to send */
-  struct connectdata *conn = pp->conn;
-  ssize_t written;
-  CURLcode result = CURLE_OK;
-  curl_socket_t sock = conn->sock[FIRSTSOCKET];
-
-  result = Curl_write(conn, sock, pp->sendthis + pp->sendsize -
-                      pp->sendleft, pp->sendleft, &written);
-  if(result)
-    return result;
-
-  if(written != (ssize_t)pp->sendleft) {
-    /* only a fraction was sent */
-    pp->sendleft -= written;
-  }
-  else {
-    free(pp->sendthis);
-    pp->sendthis=NULL;
-    pp->sendleft = pp->sendsize = 0;
-    pp->response = Curl_tvnow();
-  }
-  return CURLE_OK;
-}
-
-CURLcode Curl_pp_disconnect(struct pingpong *pp)
-{
-  if(pp->cache) {
-    free(pp->cache);
-    pp->cache = NULL;
-  }
-  return CURLE_OK;
-}
-
-
-
-#endif
diff --git a/lib/polarssl.c b/lib/polarssl.c
deleted file mode 100644 (file)
index 81c7026..0000000
+++ /dev/null
@@ -1,596 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 2010, 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com>
- * Copyright (C) 2012 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-/*
- * Source file for all PolarSSL-specific code for the TLS/SSL layer. No code
- * but curl_sslgen.c should ever call or use these functions.
- *
- */
-
-#include "curl_setup.h"
-
-#ifdef USE_POLARSSL
-
-#include <polarssl/net.h>
-#include <polarssl/ssl.h>
-#include <polarssl/havege.h>
-#include <polarssl/certs.h>
-#include <polarssl/x509.h>
-#include <polarssl/version.h>
-
-#include <polarssl/entropy.h>
-#include <polarssl/ctr_drbg.h>
-
-#if POLARSSL_VERSION_NUMBER<0x01000000
-/*
-  Earlier versions of polarssl had no WANT_READ or WANT_WRITE, only TRY_AGAIN
-*/
-#define POLARSSL_ERR_NET_WANT_READ  POLARSSL_ERR_NET_TRY_AGAIN
-#define POLARSSL_ERR_NET_WANT_WRITE POLARSSL_ERR_NET_TRY_AGAIN
-#endif
-
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_inet_pton.h"
-#include "curl_polarssl.h"
-#include "curl_sslgen.h"
-#include "curl_parsedate.h"
-#include "curl_connect.h" /* for the connect timeout */
-#include "curl_select.h"
-#include "curl_rawstr.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/* version dependent differences */
-#if POLARSSL_VERSION_NUMBER < 0x01010000
-/* the old way */
-#define HAVEGE_RANDOM havege_rand
-#else
-/* from 1.1.0 */
-#define HAVEGE_RANDOM havege_random
-#endif
-
-/* Define this to enable lots of debugging for PolarSSL */
-#undef POLARSSL_DEBUG
-
-#ifdef POLARSSL_DEBUG
-static void polarssl_debug(void *context, int level, char *line)
-{
-  struct SessionHandle *data = NULL;
-
-  if(!context)
-    return;
-
-  data = (struct SessionHandle *)context;
-
-  infof(data, "%s\n", line);
-}
-#else
-#endif
-
-static Curl_recv polarssl_recv;
-static Curl_send polarssl_send;
-
-
-static CURLcode
-polarssl_connect_step1(struct connectdata *conn,
-                     int sockindex)
-{
-  struct SessionHandle *data = conn->data;
-  struct ssl_connect_data* connssl = &conn->ssl[sockindex];
-
-  bool sni = TRUE; /* default is SNI enabled */
-  int ret = -1;
-#ifdef ENABLE_IPV6
-  struct in6_addr addr;
-#else
-  struct in_addr addr;
-#endif
-  void *old_session = NULL;
-  size_t old_session_size = 0;
-
-  /* PolarSSL only supports SSLv3 and TLSv1 */
-  if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) {
-    failf(data, "PolarSSL does not support SSLv2");
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-  else if(data->set.ssl.version == CURL_SSLVERSION_SSLv3)
-    sni = FALSE; /* SSLv3 has no SNI */
-
-  havege_init(&connssl->hs);
-
-  /* Load the trusted CA */
-  memset(&connssl->cacert, 0, sizeof(x509_cert));
-
-  if(data->set.str[STRING_SSL_CAFILE]) {
-    ret = x509parse_crtfile(&connssl->cacert,
-                            data->set.str[STRING_SSL_CAFILE]);
-
-    if(ret<0) {
-      failf(data, "Error reading ca cert file %s: -0x%04X",
-            data->set.str[STRING_SSL_CAFILE], ret);
-
-      if(data->set.ssl.verifypeer)
-        return CURLE_SSL_CACERT_BADFILE;
-    }
-  }
-
-  /* Load the client certificate */
-  memset(&connssl->clicert, 0, sizeof(x509_cert));
-
-  if(data->set.str[STRING_CERT]) {
-    ret = x509parse_crtfile(&connssl->clicert,
-                            data->set.str[STRING_CERT]);
-
-    if(ret) {
-      failf(data, "Error reading client cert file %s: -0x%04X",
-            data->set.str[STRING_CERT], -ret);
-      return CURLE_SSL_CERTPROBLEM;
-    }
-  }
-
-  /* Load the client private key */
-  if(data->set.str[STRING_KEY]) {
-    ret = x509parse_keyfile(&connssl->rsa,
-                            data->set.str[STRING_KEY],
-                            data->set.str[STRING_KEY_PASSWD]);
-
-    if(ret) {
-      failf(data, "Error reading private key %s: -0x%04X",
-            data->set.str[STRING_KEY], -ret);
-      return CURLE_SSL_CERTPROBLEM;
-    }
-  }
-
-  /* Load the CRL */
-  memset(&connssl->crl, 0, sizeof(x509_crl));
-
-  if(data->set.str[STRING_SSL_CRLFILE]) {
-    ret = x509parse_crlfile(&connssl->crl,
-                            data->set.str[STRING_SSL_CRLFILE]);
-
-    if(ret) {
-      failf(data, "Error reading CRL file %s: -0x%04X",
-            data->set.str[STRING_SSL_CRLFILE], -ret);
-      return CURLE_SSL_CRL_BADFILE;
-    }
-  }
-
-  infof(data, "PolarSSL: Connecting to %s:%d\n",
-        conn->host.name, conn->remote_port);
-
-  if(ssl_init(&connssl->ssl)) {
-    failf(data, "PolarSSL: ssl_init failed");
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-
-  ssl_set_endpoint(&connssl->ssl, SSL_IS_CLIENT);
-  ssl_set_authmode(&connssl->ssl, SSL_VERIFY_OPTIONAL);
-
-  ssl_set_rng(&connssl->ssl, HAVEGE_RANDOM,
-              &connssl->hs);
-  ssl_set_bio(&connssl->ssl,
-              net_recv, &conn->sock[sockindex],
-              net_send, &conn->sock[sockindex]);
-
-
-#if POLARSSL_VERSION_NUMBER<0x01000000
-  ssl_set_ciphers(&connssl->ssl, ssl_default_ciphers);
-#else
-  ssl_set_ciphersuites(&connssl->ssl, ssl_default_ciphersuites);
-#endif
-  if(!Curl_ssl_getsessionid(conn, &old_session, &old_session_size)) {
-    memcpy(&connssl->ssn, old_session, old_session_size);
-    infof(data, "PolarSSL re-using session\n");
-  }
-
-/* PolarSSL SVN revision r1316 to r1317, matching <1.2.0 is to cover Ubuntu's
-   1.1.4 version and the like */
-#if POLARSSL_VERSION_NUMBER<0x01020000
-  ssl_set_session(&connssl->ssl, 1, 600,
-                  &connssl->ssn);
-#else
-  ssl_set_session(&connssl->ssl,
-                  &connssl->ssn);
-#endif
-
-  ssl_set_ca_chain(&connssl->ssl,
-                   &connssl->cacert,
-                   &connssl->crl,
-                   conn->host.name);
-
-  ssl_set_own_cert(&connssl->ssl,
-                   &connssl->clicert, &connssl->rsa);
-
-  if(!Curl_inet_pton(AF_INET, conn->host.name, &addr) &&
-#ifdef ENABLE_IPV6
-     !Curl_inet_pton(AF_INET6, conn->host.name, &addr) &&
-#endif
-     sni && ssl_set_hostname(&connssl->ssl, conn->host.name)) {
-     infof(data, "WARNING: failed to configure "
-                 "server name indication (SNI) TLS extension\n");
-  }
-
-#ifdef POLARSSL_DEBUG
-  ssl_set_dbg(&connssl->ssl, polarssl_debug, data);
-#endif
-
-  connssl->connecting_state = ssl_connect_2;
-
-  return CURLE_OK;
-}
-
-static CURLcode
-polarssl_connect_step2(struct connectdata *conn,
-                     int sockindex)
-{
-  int ret;
-  struct SessionHandle *data = conn->data;
-  struct ssl_connect_data* connssl = &conn->ssl[sockindex];
-  char buffer[1024];
-
-  conn->recv[sockindex] = polarssl_recv;
-  conn->send[sockindex] = polarssl_send;
-
-  for(;;) {
-    if(!(ret = ssl_handshake(&connssl->ssl)))
-      break;
-    else if(ret != POLARSSL_ERR_NET_WANT_READ &&
-            ret != POLARSSL_ERR_NET_WANT_WRITE) {
-      failf(data, "ssl_handshake returned -0x%04X", -ret);
-      return CURLE_SSL_CONNECT_ERROR;
-    }
-    else {
-      if(ret == POLARSSL_ERR_NET_WANT_READ) {
-        connssl->connecting_state = ssl_connect_2_reading;
-        return CURLE_OK;
-      }
-      if(ret == POLARSSL_ERR_NET_WANT_WRITE) {
-        connssl->connecting_state = ssl_connect_2_writing;
-        return CURLE_OK;
-      }
-      failf(data, "SSL_connect failed with error %d.", ret);
-      return CURLE_SSL_CONNECT_ERROR;
-
-    }
-  }
-
-  infof(data, "PolarSSL: Handshake complete, cipher is %s\n",
-#if POLARSSL_VERSION_NUMBER<0x01000000
-        ssl_get_cipher(&conn->ssl[sockindex].ssl)
-#elif POLARSSL_VERSION_NUMBER >= 0x01010000
-        ssl_get_ciphersuite(&conn->ssl[sockindex].ssl)
-#else
-        ssl_get_ciphersuite_name(&conn->ssl[sockindex].ssl)
-#endif
-    );
-
-  ret = ssl_get_verify_result(&conn->ssl[sockindex].ssl);
-
-  if(ret && data->set.ssl.verifypeer) {
-    if(ret & BADCERT_EXPIRED)
-      failf(data, "Cert verify failed: BADCERT_EXPIRED");
-
-    if(ret & BADCERT_REVOKED) {
-      failf(data, "Cert verify failed: BADCERT_REVOKED");
-      return CURLE_SSL_CACERT;
-    }
-
-    if(ret & BADCERT_CN_MISMATCH)
-      failf(data, "Cert verify failed: BADCERT_CN_MISMATCH");
-
-    if(ret & BADCERT_NOT_TRUSTED)
-      failf(data, "Cert verify failed: BADCERT_NOT_TRUSTED");
-
-    return CURLE_PEER_FAILED_VERIFICATION;
-  }
-
-/* PolarSSL SVN revision r1316 to r1317, matching <1.2.0 is to cover Ubuntu's
-   1.1.4 version and the like */
-#if POLARSSL_VERSION_NUMBER<0x01020000
-  if(conn->ssl[sockindex].ssl.peer_cert) {
-#else
-  if(ssl_get_peer_cert(&(connssl->ssl))) {
-#endif
-    /* If the session was resumed, there will be no peer certs */
-    memset(buffer, 0, sizeof(buffer));
-
-/* PolarSSL SVN revision r1316 to r1317, matching <1.2.0 is to cover Ubuntu's
-   1.1.4 version and the like */
-#if POLARSSL_VERSION_NUMBER<0x01020000
-    if(x509parse_cert_info(buffer, sizeof(buffer), (char *)"* ",
-                           conn->ssl[sockindex].ssl.peer_cert) != -1)
-#else
-    if(x509parse_cert_info(buffer, sizeof(buffer), (char *)"* ",
-                           ssl_get_peer_cert(&(connssl->ssl))) != -1)
-#endif
-      infof(data, "Dumping cert info:\n%s\n", buffer);
-  }
-
-  connssl->connecting_state = ssl_connect_3;
-  infof(data, "SSL connected\n");
-
-  return CURLE_OK;
-}
-
-static CURLcode
-polarssl_connect_step3(struct connectdata *conn,
-                     int sockindex)
-{
-  CURLcode retcode = CURLE_OK;
-  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-  struct SessionHandle *data = conn->data;
-  void *old_ssl_sessionid = NULL;
-  ssl_session *our_ssl_sessionid = &conn->ssl[sockindex].ssn ;
-  int incache;
-
-  DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
-
-  /* Save the current session data for possible re-use */
-  incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL));
-  if(incache) {
-    if(old_ssl_sessionid != our_ssl_sessionid) {
-      infof(data, "old SSL session ID is stale, removing\n");
-      Curl_ssl_delsessionid(conn, old_ssl_sessionid);
-      incache = FALSE;
-    }
-  }
-  if(!incache) {
-    void *new_session = malloc(sizeof(ssl_session));
-
-    if(new_session) {
-      memcpy(new_session, our_ssl_sessionid,
-             sizeof(ssl_session));
-
-      retcode = Curl_ssl_addsessionid(conn, new_session,
-                                   sizeof(ssl_session));
-    }
-    else {
-      retcode = CURLE_OUT_OF_MEMORY;
-    }
-
-    if(retcode) {
-      failf(data, "failed to store ssl session");
-      return retcode;
-    }
-  }
-
-  connssl->connecting_state = ssl_connect_done;
-
-  return CURLE_OK;
-}
-
-static ssize_t polarssl_send(struct connectdata *conn,
-                             int sockindex,
-                             const void *mem,
-                             size_t len,
-                             CURLcode *curlcode)
-{
-  int ret = -1;
-
-  ret = ssl_write(&conn->ssl[sockindex].ssl,
-                  (unsigned char *)mem, len);
-
-  if(ret < 0) {
-    *curlcode = (ret == POLARSSL_ERR_NET_WANT_WRITE) ?
-      CURLE_AGAIN : CURLE_SEND_ERROR;
-    ret = -1;
-  }
-
-  return ret;
-}
-
-void Curl_polarssl_close_all(struct SessionHandle *data)
-{
-  (void)data;
-}
-
-void Curl_polarssl_close(struct connectdata *conn, int sockindex)
-{
-  rsa_free(&conn->ssl[sockindex].rsa);
-  x509_free(&conn->ssl[sockindex].clicert);
-  x509_free(&conn->ssl[sockindex].cacert);
-  x509_crl_free(&conn->ssl[sockindex].crl);
-  ssl_free(&conn->ssl[sockindex].ssl);
-}
-
-static ssize_t polarssl_recv(struct connectdata *conn,
-                             int num,
-                             char *buf,
-                             size_t buffersize,
-                             CURLcode *curlcode)
-{
-  int ret = -1;
-  ssize_t len = -1;
-
-  memset(buf, 0, buffersize);
-  ret = ssl_read(&conn->ssl[num].ssl, (unsigned char *)buf, buffersize);
-
-  if(ret <= 0) {
-    if(ret == POLARSSL_ERR_SSL_PEER_CLOSE_NOTIFY)
-      return 0;
-
-    *curlcode = (ret == POLARSSL_ERR_NET_WANT_READ) ?
-      CURLE_AGAIN : CURLE_RECV_ERROR;
-    return -1;
-  }
-
-  len = ret;
-
-  return len;
-}
-
-void Curl_polarssl_session_free(void *ptr)
-{
-  free(ptr);
-}
-
-size_t Curl_polarssl_version(char *buffer, size_t size)
-{
-  unsigned int version = version_get_number();
-  return snprintf(buffer, size, "PolarSSL/%d.%d.%d", version>>24,
-                  (version>>16)&0xff, (version>>8)&0xff);
-}
-
-static CURLcode
-polarssl_connect_common(struct connectdata *conn,
-                        int sockindex,
-                        bool nonblocking,
-                        bool *done)
-{
-  CURLcode retcode;
-  struct SessionHandle *data = conn->data;
-  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-  curl_socket_t sockfd = conn->sock[sockindex];
-  long timeout_ms;
-  int what;
-
-  /* check if the connection has already been established */
-  if(ssl_connection_complete == connssl->state) {
-    *done = TRUE;
-    return CURLE_OK;
-  }
-
-  if(ssl_connect_1==connssl->connecting_state) {
-    /* Find out how much more time we're allowed */
-    timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
-    if(timeout_ms < 0) {
-      /* no need to continue if time already is up */
-      failf(data, "SSL connection timeout");
-      return CURLE_OPERATION_TIMEDOUT;
-    }
-    retcode = polarssl_connect_step1(conn, sockindex);
-    if(retcode)
-      return retcode;
-  }
-
-  while(ssl_connect_2 == connssl->connecting_state ||
-        ssl_connect_2_reading == connssl->connecting_state ||
-        ssl_connect_2_writing == connssl->connecting_state) {
-
-    /* check allowed time left */
-    timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
-    if(timeout_ms < 0) {
-      /* no need to continue if time already is up */
-      failf(data, "SSL connection timeout");
-      return CURLE_OPERATION_TIMEDOUT;
-    }
-
-    /* if ssl is expecting something, check if it's available. */
-    if(connssl->connecting_state == ssl_connect_2_reading
-       || connssl->connecting_state == ssl_connect_2_writing) {
-
-      curl_socket_t writefd = ssl_connect_2_writing==
-        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
-      curl_socket_t readfd = ssl_connect_2_reading==
-        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
-
-      what = Curl_socket_ready(readfd, writefd, nonblocking?0:timeout_ms);
-      if(what < 0) {
-        /* fatal error */
-        failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
-        return CURLE_SSL_CONNECT_ERROR;
-      }
-      else if(0 == what) {
-        if(nonblocking) {
-          *done = FALSE;
-          return CURLE_OK;
-        }
-        else {
-          /* timeout */
-          failf(data, "SSL connection timeout");
-          return CURLE_OPERATION_TIMEDOUT;
-        }
-      }
-      /* socket is readable or writable */
-    }
-
-    /* Run transaction, and return to the caller if it failed or if
-     * this connection is part of a multi handle and this loop would
-     * execute again. This permits the owner of a multi handle to
-     * abort a connection attempt before step2 has completed while
-     * ensuring that a client using select() or epoll() will always
-     * have a valid fdset to wait on.
-     */
-    retcode = polarssl_connect_step2(conn, sockindex);
-    if(retcode || (nonblocking &&
-                   (ssl_connect_2 == connssl->connecting_state ||
-                    ssl_connect_2_reading == connssl->connecting_state ||
-                    ssl_connect_2_writing == connssl->connecting_state)))
-      return retcode;
-
-  } /* repeat step2 until all transactions are done. */
-
-  if(ssl_connect_3==connssl->connecting_state) {
-    retcode = polarssl_connect_step3(conn, sockindex);
-    if(retcode)
-      return retcode;
-  }
-
-  if(ssl_connect_done==connssl->connecting_state) {
-    connssl->state = ssl_connection_complete;
-    conn->recv[sockindex] = polarssl_recv;
-    conn->send[sockindex] = polarssl_send;
-    *done = TRUE;
-  }
-  else
-    *done = FALSE;
-
-  /* Reset our connect state machine */
-  connssl->connecting_state = ssl_connect_1;
-
-  return CURLE_OK;
-}
-
-CURLcode
-Curl_polarssl_connect_nonblocking(struct connectdata *conn,
-                                int sockindex,
-                                bool *done)
-{
-  return polarssl_connect_common(conn, sockindex, TRUE, done);
-}
-
-
-CURLcode
-Curl_polarssl_connect(struct connectdata *conn,
-                    int sockindex)
-{
-  CURLcode retcode;
-  bool done = FALSE;
-
-  retcode = polarssl_connect_common(conn, sockindex, FALSE, &done);
-  if(retcode)
-    return retcode;
-
-  DEBUGASSERT(done);
-
-  return CURLE_OK;
-}
-
-#endif
diff --git a/lib/pop3.c b/lib/pop3.c
deleted file mode 100644 (file)
index 0d157f0..0000000
+++ /dev/null
@@ -1,1764 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * RFC1734 POP3 Authentication
- * RFC1939 POP3 protocol
- * RFC2195 CRAM-MD5 authentication
- * RFC2384 POP URL Scheme
- * RFC2449 POP3 Extension Mechanism
- * RFC2595 Using TLS with IMAP, POP3 and ACAP
- * RFC2831 DIGEST-MD5 authentication
- * RFC4616 PLAIN authentication
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifndef CURL_DISABLE_POP3
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef HAVE_UTSNAME_H
-#include <sys/utsname.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef __VMS
-#include <in.h>
-#include <inet.h>
-#endif
-
-#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
-#undef in_addr_t
-#define in_addr_t unsigned long
-#endif
-
-#include <curl/curl.h>
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_if2ip.h"
-#include "curl_hostip.h"
-#include "curl_progress.h"
-#include "curl_transfer.h"
-#include "curl_escape.h"
-#include "curl_http.h" /* for HTTP proxy tunnel stuff */
-#include "curl_socks.h"
-#include "curl_pop3.h"
-
-#include "curl_strtoofft.h"
-#include "curl_strequal.h"
-#include "curl_sslgen.h"
-#include "curl_connect.h"
-#include "curl_strerror.h"
-#include "curl_select.h"
-#include "curl_multiif.h"
-#include "curl_url.h"
-#include "curl_rawstr.h"
-#include "curl_sasl.h"
-#include "curl_md5.h"
-#include "curl_warnless.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/* Local API functions */
-static CURLcode pop3_parse_url_path(struct connectdata *conn);
-static CURLcode pop3_parse_custom_request(struct connectdata *conn);
-static CURLcode pop3_regular_transfer(struct connectdata *conn, bool *done);
-static CURLcode pop3_do(struct connectdata *conn, bool *done);
-static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
-                          bool premature);
-static CURLcode pop3_connect(struct connectdata *conn, bool *done);
-static CURLcode pop3_disconnect(struct connectdata *conn, bool dead);
-static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done);
-static int pop3_getsock(struct connectdata *conn,
-                        curl_socket_t *socks,
-                        int numsocks);
-static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done);
-static CURLcode pop3_setup_connection(struct connectdata *conn);
-
-/*
- * POP3 protocol handler.
- */
-
-const struct Curl_handler Curl_handler_pop3 = {
-  "POP3",                           /* scheme */
-  pop3_setup_connection,            /* setup_connection */
-  pop3_do,                          /* do_it */
-  pop3_done,                        /* done */
-  ZERO_NULL,                        /* do_more */
-  pop3_connect,                     /* connect_it */
-  pop3_multi_statemach,             /* connecting */
-  pop3_doing,                       /* doing */
-  pop3_getsock,                     /* proto_getsock */
-  pop3_getsock,                     /* doing_getsock */
-  ZERO_NULL,                        /* domore_getsock */
-  ZERO_NULL,                        /* perform_getsock */
-  pop3_disconnect,                  /* disconnect */
-  ZERO_NULL,                        /* readwrite */
-  PORT_POP3,                        /* defport */
-  CURLPROTO_POP3,                   /* protocol */
-  PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
-};
-
-#ifdef USE_SSL
-/*
- * POP3S protocol handler.
- */
-
-const struct Curl_handler Curl_handler_pop3s = {
-  "POP3S",                          /* scheme */
-  pop3_setup_connection,            /* setup_connection */
-  pop3_do,                          /* do_it */
-  pop3_done,                        /* done */
-  ZERO_NULL,                        /* do_more */
-  pop3_connect,                     /* connect_it */
-  pop3_multi_statemach,             /* connecting */
-  pop3_doing,                       /* doing */
-  pop3_getsock,                     /* proto_getsock */
-  pop3_getsock,                     /* doing_getsock */
-  ZERO_NULL,                        /* domore_getsock */
-  ZERO_NULL,                        /* perform_getsock */
-  pop3_disconnect,                  /* disconnect */
-  ZERO_NULL,                        /* readwrite */
-  PORT_POP3S,                       /* defport */
-  CURLPROTO_POP3 | CURLPROTO_POP3S, /* protocol */
-  PROTOPT_CLOSEACTION | PROTOPT_SSL
-  | PROTOPT_NOURLQUERY              /* flags */
-};
-#endif
-
-#ifndef CURL_DISABLE_HTTP
-/*
- * HTTP-proxyed POP3 protocol handler.
- */
-
-static const struct Curl_handler Curl_handler_pop3_proxy = {
-  "POP3",                               /* scheme */
-  ZERO_NULL,                            /* setup_connection */
-  Curl_http,                            /* do_it */
-  Curl_http_done,                       /* done */
-  ZERO_NULL,                            /* do_more */
-  ZERO_NULL,                            /* connect_it */
-  ZERO_NULL,                            /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
-  ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* readwrite */
-  PORT_POP3,                            /* defport */
-  CURLPROTO_HTTP,                       /* protocol */
-  PROTOPT_NONE                          /* flags */
-};
-
-#ifdef USE_SSL
-/*
- * HTTP-proxyed POP3S protocol handler.
- */
-
-static const struct Curl_handler Curl_handler_pop3s_proxy = {
-  "POP3S",                              /* scheme */
-  ZERO_NULL,                            /* setup_connection */
-  Curl_http,                            /* do_it */
-  Curl_http_done,                       /* done */
-  ZERO_NULL,                            /* do_more */
-  ZERO_NULL,                            /* connect_it */
-  ZERO_NULL,                            /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
-  ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* readwrite */
-  PORT_POP3S,                           /* defport */
-  CURLPROTO_HTTP,                       /* protocol */
-  PROTOPT_NONE                          /* flags */
-};
-#endif
-#endif
-
-/* Function that checks for an ending pop3 status code at the start of the
-   given string, but also detects the APOP timestamp from the server greeting
-   as well as the supported authentication types and allowed SASL mechanisms
-   from the CAPA response. */
-static int pop3_endofresp(struct pingpong *pp, int *resp)
-{
-  char *line = pp->linestart_resp;
-  size_t len = strlen(pp->linestart_resp);
-  struct connectdata *conn = pp->conn;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-  size_t wordlen;
-  size_t i;
-
-  /* Do we have an error response? */
-  if(len >= 4 && !memcmp("-ERR", line, 4)) {
-    *resp = '-';
-
-    return FALSE;
-  }
-
-  /* Are we processing servergreet responses */
-  if(pop3c->state == POP3_SERVERGREET) {
-    /* Look for the APOP timestamp */
-    if(len >= 3 && line[len - 3] == '>') {
-      for(i = 0; i < len - 3; ++i) {
-        if(line[i] == '<') {
-          /* Calculate the length of the timestamp */
-          size_t timestamplen = len - 2 - i;
-
-          /* Allocate some memory for the timestamp */
-          pop3c->apoptimestamp = (char *)calloc(1, timestamplen + 1);
-
-          if(!pop3c->apoptimestamp)
-            break;
-
-          /* Copy the timestamp */
-          memcpy(pop3c->apoptimestamp, line + i, timestamplen);
-          pop3c->apoptimestamp[timestamplen] = '\0';
-          break;
-        }
-      }
-    }
-  }
-  /* Are we processing CAPA command responses? */
-  else if(pop3c->state == POP3_CAPA) {
-
-    /* Do we have the terminating character? */
-    if(len >= 1 && !memcmp(line, ".", 1)) {
-      *resp = '+';
-
-      return TRUE;
-    }
-
-    /* Does the server support clear text? */
-    if(len >= 4 && !memcmp(line, "USER", 4)) {
-      pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
-      return FALSE;
-    }
-
-    /* Does the server support APOP? */
-    if(len >= 4 && !memcmp(line, "APOP", 4)) {
-      pop3c->authtypes |= POP3_TYPE_APOP;
-      return FALSE;
-    }
-
-    /* Does the server support SASL? */
-    if(len < 4 || memcmp(line, "SASL", 4))
-      return FALSE;
-
-    pop3c->authtypes |= POP3_TYPE_SASL;
-
-    /* Advance past the SASL keyword */
-    line += 4;
-    len -= 4;
-
-    /* Loop through the data line */
-    for(;;) {
-      while(len &&
-            (*line == ' ' || *line == '\t' ||
-             *line == '\r' || *line == '\n')) {
-
-        if(*line == '\n')
-          return FALSE;
-
-        line++;
-        len--;
-      }
-
-      if(!len)
-        break;
-
-      /* Extract the word */
-      for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
-            line[wordlen] != '\t' && line[wordlen] != '\r' &&
-            line[wordlen] != '\n';)
-        wordlen++;
-
-      /* Test the word for a matching authentication mechanism */
-      if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
-        pop3c->authmechs |= SASL_MECH_LOGIN;
-      else if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
-        pop3c->authmechs |= SASL_MECH_PLAIN;
-      else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
-        pop3c->authmechs |= SASL_MECH_CRAM_MD5;
-      else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
-        pop3c->authmechs |= SASL_MECH_DIGEST_MD5;
-      else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
-        pop3c->authmechs |= SASL_MECH_GSSAPI;
-      else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
-        pop3c->authmechs |= SASL_MECH_EXTERNAL;
-      else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
-        pop3c->authmechs |= SASL_MECH_NTLM;
-
-      line += wordlen;
-      len -= wordlen;
-    }
-  }
-
-  if((len < 1 || memcmp("+", line, 1)) &&
-     (len < 3 || memcmp("+OK", line, 3)))
-  return FALSE; /* Nothing for us */
-
-  /* Otherwise it's a positive response */
-  *resp = '+';
-
-  return TRUE;
-}
-
-/* This is the ONLY way to change POP3 state! */
-static void state(struct connectdata *conn, pop3state newstate)
-{
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
-  /* for debug purposes */
-  static const char * const names[] = {
-    "STOP",
-    "SERVERGREET",
-    "STARTTLS",
-    "CAPA",
-    "AUTH_PLAIN",
-    "AUTH_LOGIN",
-    "AUTH_LOGIN_PASSWD",
-    "AUTH_CRAMMD5",
-    "AUTH_DIGESTMD5",
-    "AUTH_DIGESTMD5_RESP",
-    "AUTH_NTLM",
-    "AUTH_NTLM_TYPE2MSG",
-    "AUTH",
-    "APOP",
-    "USER",
-    "PASS",
-    "COMMAND",
-    "QUIT",
-    /* LAST */
-  };
-
-  if(pop3c->state != newstate)
-    infof(conn->data, "POP3 %p state change from %s to %s\n",
-          pop3c, names[pop3c->state], names[newstate]);
-#endif
-
-  pop3c->state = newstate;
-}
-
-static CURLcode pop3_state_capa(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-
-  pop3c->authmechs = 0;         /* No known authentication mechanisms yet */
-  pop3c->authused = 0;          /* Clear the authentication mechanism used */
-
-  /* Check we have a username and password to authenticate with and end the
-     connect phase if we don't */
-  if(!conn->bits.user_passwd) {
-    state(conn, POP3_STOP);
-
-    return result;
-  }
-
-  /* Send the CAPA command */
-  result = Curl_pp_sendf(&pop3c->pp, "CAPA");
-
-  if(result)
-    return result;
-
-  state(conn, POP3_CAPA);
-
-  return CURLE_OK;
-}
-
-static CURLcode pop3_state_user(struct connectdata *conn)
-{
-  CURLcode result;
-  struct FTP *pop3 = conn->data->state.proto.pop3;
-
-  /* Send the USER command */
-  result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s",
-                         pop3->user ? pop3->user : "");
-  if(result)
-    return result;
-
-  state(conn, POP3_USER);
-
-  return CURLE_OK;
-}
-
-#ifndef CURL_DISABLE_CRYPTO_AUTH
-static CURLcode pop3_state_apop(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-  size_t i;
-  MD5_context *ctxt;
-  unsigned char digest[MD5_DIGEST_LEN];
-  char secret[2 * MD5_DIGEST_LEN + 1];
-
-  ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
-  if(!ctxt)
-    return CURLE_OUT_OF_MEMORY;
-
-  Curl_MD5_update(ctxt, (const unsigned char *) pop3c->apoptimestamp,
-                  curlx_uztoui(strlen(pop3c->apoptimestamp)));
-
-  Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd,
-                  curlx_uztoui(strlen(conn->passwd)));
-
-  /* Finalise the digest */
-  Curl_MD5_final(ctxt, digest);
-
-  /* Convert the calculated 16 octet digest into a 32 byte hex string */
-  for(i = 0; i < MD5_DIGEST_LEN; i++)
-    snprintf(&secret[2 * i], 3, "%02x", digest[i]);
-
-  result = Curl_pp_sendf(&pop3c->pp, "APOP %s %s", conn->user, secret);
-
-  if(!result)
-    state(conn, POP3_APOP);
-
-  return result;
-}
-#endif
-
-static CURLcode pop3_authenticate(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-  const char *mech = NULL;
-  pop3state authstate = POP3_STOP;
-
-  /* Check supported authentication mechanisms by decreasing order of
-     security */
-#ifndef CURL_DISABLE_CRYPTO_AUTH
-  if(pop3c->authmechs & SASL_MECH_DIGEST_MD5) {
-    mech = "DIGEST-MD5";
-    authstate = POP3_AUTH_DIGESTMD5;
-    pop3c->authused = SASL_MECH_DIGEST_MD5;
-  }
-  else if(pop3c->authmechs & SASL_MECH_CRAM_MD5) {
-    mech = "CRAM-MD5";
-    authstate = POP3_AUTH_CRAMMD5;
-    pop3c->authused = SASL_MECH_CRAM_MD5;
-  }
-  else
-#endif
-#ifdef USE_NTLM
-  if(pop3c->authmechs & SASL_MECH_NTLM) {
-    mech = "NTLM";
-    authstate = POP3_AUTH_NTLM;
-    pop3c->authused = SASL_MECH_NTLM;
-  }
-  else
-#endif
-  if(pop3c->authmechs & SASL_MECH_LOGIN) {
-    mech = "LOGIN";
-    authstate = POP3_AUTH_LOGIN;
-    pop3c->authused = SASL_MECH_LOGIN;
-  }
-  else if(pop3c->authmechs & SASL_MECH_PLAIN) {
-    mech = "PLAIN";
-    authstate = POP3_AUTH_PLAIN;
-    pop3c->authused = SASL_MECH_PLAIN;
-  }
-  else {
-    infof(conn->data, "No known SASL authentication mechanisms supported!\n");
-    result = CURLE_LOGIN_DENIED; /* Other mechanisms not supported */
-  }
-
-  if(!result) {
-    result = Curl_pp_sendf(&pop3c->pp, "AUTH %s", mech);
-
-    if(!result)
-      state(conn, authstate);
-  }
-
-  return result;
-}
-
-/* For the POP3 "protocol connect" and "doing" phases only */
-static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
-                        int numsocks)
-{
-  return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks);
-}
-
-#ifdef USE_SSL
-static void pop3_to_pop3s(struct connectdata *conn)
-{
-  conn->handler = &Curl_handler_pop3s;
-}
-#else
-#define pop3_to_pop3s(x) Curl_nop_stmt
-#endif
-
-/* For the initial server greeting */
-static CURLcode pop3_state_servergreet_resp(struct connectdata *conn,
-                                            int pop3code,
-                                            pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    failf(data, "Got unexpected pop3-server response");
-    return CURLE_FTP_WEIRD_SERVER_REPLY;
-  }
-
-  if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
-    /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
-       to TLS connection now */
-    result = Curl_pp_sendf(&pop3c->pp, "STLS");
-    state(conn, POP3_STARTTLS);
-  }
-  else
-    result = pop3_state_capa(conn);
-
-  return result;
-}
-
-/* For STARTTLS responses */
-static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
-                                         int pop3code,
-                                         pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    if(data->set.use_ssl != CURLUSESSL_TRY) {
-      failf(data, "STARTTLS denied. %c", pop3code);
-      result = CURLE_USE_SSL_FAILED;
-    }
-    else
-      result = pop3_state_capa(conn);
-  }
-  else {
-    /* Curl_ssl_connect is BLOCKING */
-    result = Curl_ssl_connect(conn, FIRSTSOCKET);
-    if(CURLE_OK == result) {
-      pop3_to_pop3s(conn);
-      result = pop3_state_capa(conn);
-    }
-  }
-
-  return result;
-}
-
-/* For CAPA responses */
-static CURLcode pop3_state_capa_resp(struct connectdata *conn,
-                                     int pop3code,
-                                     pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+')
-    result = pop3_state_user(conn);
-  else {
-    /* Check supported authentication types by decreasing order of security */
-    if(conn->proto.pop3c.authtypes & POP3_TYPE_SASL)
-      result = pop3_authenticate(conn);
-#ifndef CURL_DISABLE_CRYPTO_AUTH
-    else if(conn->proto.pop3c.authtypes & POP3_TYPE_APOP)
-      result = pop3_state_apop(conn);
-#endif
-    else if(conn->proto.pop3c.authtypes & POP3_TYPE_CLEARTEXT)
-      result = pop3_state_user(conn);
-    else {
-      infof(conn->data, "No known authentication types supported!\n");
-      result = CURLE_LOGIN_DENIED; /* Other types not supported */
-    }
-  }
-
-  return result;
-}
-
-/* For AUTH PLAIN responses */
-static CURLcode pop3_state_auth_plain_resp(struct connectdata *conn,
-                                           int pop3code,
-                                           pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  size_t len = 0;
-  char *plainauth = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    failf(data, "Access denied. %c", pop3code);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the authorisation message */
-    result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
-                                            &plainauth, &len);
-
-    /* Send the message */
-    if(!result) {
-      if(plainauth) {
-        result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", plainauth);
-
-        if(!result)
-          state(conn, POP3_AUTH);
-      }
-
-      Curl_safefree(plainauth);
-    }
-  }
-
-  return result;
-}
-
-/* For AUTH LOGIN responses */
-static CURLcode pop3_state_auth_login_resp(struct connectdata *conn,
-                                           int pop3code,
-                                           pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  size_t len = 0;
-  char *authuser = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    failf(data, "Access denied: %d", pop3code);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the user message */
-    result = Curl_sasl_create_login_message(data, conn->user,
-                                            &authuser, &len);
-
-    /* Send the user */
-    if(!result) {
-      if(authuser) {
-        result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authuser);
-
-        if(!result)
-          state(conn, POP3_AUTH_LOGIN_PASSWD);
-      }
-
-      Curl_safefree(authuser);
-    }
-  }
-
-  return result;
-}
-
-/* For AUTH LOGIN user entry responses */
-static CURLcode pop3_state_auth_login_password_resp(struct connectdata *conn,
-                                                    int pop3code,
-                                                    pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  size_t len = 0;
-  char *authpasswd = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    failf(data, "Access denied: %d", pop3code);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the password message */
-    result = Curl_sasl_create_login_message(data, conn->passwd,
-                                            &authpasswd, &len);
-
-    /* Send the password */
-    if(!result) {
-      if(authpasswd) {
-        result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authpasswd);
-
-        if(!result)
-          state(conn, POP3_AUTH);
-      }
-
-      Curl_safefree(authpasswd);
-    }
-  }
-
-  return result;
-}
-
-#ifndef CURL_DISABLE_CRYPTO_AUTH
-/* For AUTH CRAM-MD5 responses */
-static CURLcode pop3_state_auth_cram_resp(struct connectdata *conn,
-                                          int pop3code,
-                                          pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  char *chlg64 = data->state.buffer;
-  size_t len = 0;
-  char *rplyb64 = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    failf(data, "Access denied: %d", pop3code);
-    return CURLE_LOGIN_DENIED;
-  }
-
-  /* Get the challenge */
-  for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
-    ;
-
-  /* Terminate the challenge */
-  if(*chlg64 != '=') {
-    for(len = strlen(chlg64); len--;)
-      if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
-         chlg64[len] != '\t')
-        break;
-
-    if(++len) {
-      chlg64[len] = '\0';
-    }
-  }
-
-  /* Create the response message */
-  result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
-                                             conn->passwd, &rplyb64, &len);
-
-  /* Send the response */
-  if(!result) {
-    if(rplyb64) {
-      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64);
-
-      if(!result)
-        state(conn, POP3_AUTH);
-    }
-
-    Curl_safefree(rplyb64);
-  }
-
-  return result;
-}
-
-/* For AUTH DIGEST-MD5 challenge responses */
-static CURLcode pop3_state_auth_digest_resp(struct connectdata *conn,
-                                            int pop3code,
-                                            pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  char *chlg64 = data->state.buffer;
-  size_t len = 0;
-  char *rplyb64 = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    failf(data, "Access denied: %d", pop3code);
-    return CURLE_LOGIN_DENIED;
-  }
-
-  /* Get the challenge */
-  for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
-    ;
-
-  /* Create the response message */
-  result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
-                                               conn->passwd, "pop",
-                                               &rplyb64, &len);
-
-  /* Send the response */
-  if(!result) {
-    if(rplyb64) {
-      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64);
-
-      if(!result)
-        state(conn, POP3_AUTH_DIGESTMD5_RESP);
-    }
-
-    Curl_safefree(rplyb64);
-  }
-
-  return result;
-}
-
-/* For AUTH DIGEST-MD5 challenge-response responses */
-static CURLcode pop3_state_auth_digest_resp_resp(struct connectdata *conn,
-                                                 int pop3code,
-                                                 pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    failf(data, "Authentication failed: %d", pop3code);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Send an empty response */
-    result = Curl_pp_sendf(&conn->proto.pop3c.pp, "");
-
-    if(!result)
-      state(conn, POP3_AUTH);
-  }
-
-  return result;
-}
-#endif
-
-#ifdef USE_NTLM
-/* For AUTH NTLM responses */
-static CURLcode pop3_state_auth_ntlm_resp(struct connectdata *conn,
-                                          int pop3code,
-                                          pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  size_t len = 0;
-  char *type1msg = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    failf(data, "Access denied: %d", pop3code);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the type-1 message */
-    result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
-                                                 &conn->ntlm,
-                                                 &type1msg, &len);
-
-    /* Send the message */
-    if(!result) {
-      if(type1msg) {
-        result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type1msg);
-
-        if(!result)
-          state(conn, POP3_AUTH_NTLM_TYPE2MSG);
-      }
-
-      Curl_safefree(type1msg);
-    }
-  }
-
-  return result;
-}
-
-/* For NTLM type-2 responses (sent in reponse to our type-1 message) */
-static CURLcode pop3_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
-                                                   int pop3code,
-                                                   pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  size_t len = 0;
-  char *type3msg = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    failf(data, "Access denied: %d", pop3code);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the type-3 message */
-    result = Curl_sasl_create_ntlm_type3_message(data,
-                                                 data->state.buffer + 2,
-                                                 conn->user, conn->passwd,
-                                                 &conn->ntlm,
-                                                 &type3msg, &len);
-
-    /* Send the message */
-    if(!result) {
-      if(type3msg) {
-        result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type3msg);
-
-        if(!result)
-          state(conn, POP3_AUTH);
-      }
-
-      Curl_safefree(type3msg);
-    }
-  }
-
-  return result;
-}
-#endif
-
-/* For final responses to the AUTH sequence */
-static CURLcode pop3_state_auth_final_resp(struct connectdata *conn,
-                                           int pop3code,
-                                           pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    failf(data, "Authentication failed: %d", pop3code);
-    result = CURLE_LOGIN_DENIED;
-  }
-
-  /* End of connect phase */
-  state(conn, POP3_STOP);
-
-  return result;
-}
-
-static CURLcode pop3_state_apop_resp(struct connectdata *conn,
-                                     int pop3code,
-                                     pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    failf(data, "Authentication failed: %d", pop3code);
-    result = CURLE_LOGIN_DENIED;
-  }
-
-  /* End of connect phase */
-  state(conn, POP3_STOP);
-
-  return result;
-}
-
-/* For USER responses */
-static CURLcode pop3_state_user_resp(struct connectdata *conn,
-                                     int pop3code,
-                                     pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  struct FTP *pop3 = data->state.proto.pop3;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    failf(data, "Access denied. %c", pop3code);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else
-    /* Send the PASS command */
-    result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s",
-                           pop3->passwd ? pop3->passwd : "");
-  if(result)
-    return result;
-
-  state(conn, POP3_PASS);
-
-  return result;
-}
-
-/* For PASS responses */
-static CURLcode pop3_state_pass_resp(struct connectdata *conn,
-                                     int pop3code,
-                                     pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    failf(data, "Access denied. %c", pop3code);
-    result = CURLE_LOGIN_DENIED;
-  }
-
-  /* End of connect phase */
-  state(conn, POP3_STOP);
-
-  return result;
-}
-
-/* Start the DO phase for the command */
-static CURLcode pop3_command(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-  const char *command = NULL;
-
-  /* Calculate the default command */
-  if(pop3c->mailbox[0] == '\0' || conn->data->set.ftp_list_only) {
-    command = "LIST";
-
-    if(pop3c->mailbox[0] != '\0') {
-      /* Message specific LIST so skip the BODY transfer */
-      struct FTP *pop3 = conn->data->state.proto.pop3;
-      pop3->transfer = FTPTRANSFER_INFO;
-    }
-  }
-  else
-    command = "RETR";
-
-  /* Send the command */
-  if(pop3c->mailbox[0] != '\0')
-    result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s %s",
-                           (pop3c->custom && pop3c->custom[0] != '\0' ?
-                            pop3c->custom : command), pop3c->mailbox);
-  else
-    result = Curl_pp_sendf(&conn->proto.pop3c.pp,
-                           (pop3c->custom && pop3c->custom[0] != '\0' ?
-                            pop3c->custom : command));
-
-  if(result)
-    return result;
-
-  state(conn, POP3_COMMAND);
-
-  return result;
-}
-
-/* For command responses */
-static CURLcode pop3_state_command_resp(struct connectdata *conn,
-                                        int pop3code,
-                                        pop3state instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  struct FTP *pop3 = data->state.proto.pop3;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-  struct pingpong *pp = &pop3c->pp;
-
-  (void)instate; /* no use for this yet */
-
-  if(pop3code != '+') {
-    state(conn, POP3_STOP);
-    return CURLE_RECV_ERROR;
-  }
-
-  /* This 'OK' line ends with a CR LF pair which is the two first bytes of the
-     EOB string so count this is two matching bytes. This is necessary to make
-     the code detect the EOB if the only data than comes now is %2e CR LF like
-     when there is no body to return. */
-  pop3c->eob = 2;
-
-  /* But since this initial CR LF pair is not part of the actual body, we set
-     the strip counter here so that these bytes won't be delivered. */
-  pop3c->strip = 2;
-
-  /* POP3 download */
-  Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, pop3->bytecountp,
-                      -1, NULL); /* no upload here */
-
-  if(pp->cache) {
-    /* The header "cache" contains a bunch of data that is actually body
-       content so send it as such. Note that there may even be additional
-       "headers" after the body */
-
-    if(!data->set.opt_no_body) {
-      result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
-      if(result)
-        return result;
-    }
-
-    /* Free the cache */
-    Curl_safefree(pp->cache);
-
-    /* Reset the cache size */
-    pp->cache_size = 0;
-  }
-
-  /* End of do phase */
-  state(conn, POP3_STOP);
-
-  return result;
-}
-
-static CURLcode pop3_statemach_act(struct connectdata *conn)
-{
-  CURLcode result;
-  curl_socket_t sock = conn->sock[FIRSTSOCKET];
-  int pop3code;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-  struct pingpong *pp = &pop3c->pp;
-  size_t nread = 0;
-
-  /* Flush any data that needs to be sent */
-  if(pp->sendleft)
-    return Curl_pp_flushsend(pp);
-
-  /* Read the response from the server */
-  result = Curl_pp_readresp(sock, pp, &pop3code, &nread);
-  if(result)
-    return result;
-
-  if(pop3code) {
-    /* We have now received a full POP3 server response */
-    switch(pop3c->state) {
-    case POP3_SERVERGREET:
-      result = pop3_state_servergreet_resp(conn, pop3code, pop3c->state);
-      break;
-
-    case POP3_STARTTLS:
-      result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
-      break;
-
-    case POP3_CAPA:
-      result = pop3_state_capa_resp(conn, pop3code, pop3c->state);
-      break;
-
-    case POP3_AUTH_PLAIN:
-      result = pop3_state_auth_plain_resp(conn, pop3code, pop3c->state);
-      break;
-
-    case POP3_AUTH_LOGIN:
-      result = pop3_state_auth_login_resp(conn, pop3code, pop3c->state);
-      break;
-
-    case POP3_AUTH_LOGIN_PASSWD:
-      result = pop3_state_auth_login_password_resp(conn, pop3code,
-                                                   pop3c->state);
-      break;
-
-#ifndef CURL_DISABLE_CRYPTO_AUTH
-    case POP3_AUTH_CRAMMD5:
-      result = pop3_state_auth_cram_resp(conn, pop3code, pop3c->state);
-      break;
-
-    case POP3_AUTH_DIGESTMD5:
-      result = pop3_state_auth_digest_resp(conn, pop3code, pop3c->state);
-      break;
-
-    case POP3_AUTH_DIGESTMD5_RESP:
-      result = pop3_state_auth_digest_resp_resp(conn, pop3code, pop3c->state);
-      break;
-#endif
-
-#ifdef USE_NTLM
-    case POP3_AUTH_NTLM:
-      result = pop3_state_auth_ntlm_resp(conn, pop3code, pop3c->state);
-      break;
-
-    case POP3_AUTH_NTLM_TYPE2MSG:
-      result = pop3_state_auth_ntlm_type2msg_resp(conn, pop3code,
-                                                  pop3c->state);
-      break;
-#endif
-
-    case POP3_AUTH:
-      result = pop3_state_auth_final_resp(conn, pop3code, pop3c->state);
-      break;
-
-    case POP3_APOP:
-      result = pop3_state_apop_resp(conn, pop3code, pop3c->state);
-      break;
-
-    case POP3_USER:
-      result = pop3_state_user_resp(conn, pop3code, pop3c->state);
-      break;
-
-    case POP3_PASS:
-      result = pop3_state_pass_resp(conn, pop3code, pop3c->state);
-      break;
-
-    case POP3_COMMAND:
-      result = pop3_state_command_resp(conn, pop3code, pop3c->state);
-      break;
-
-    case POP3_QUIT:
-      /* fallthrough, just stop! */
-    default:
-      /* internal error */
-      state(conn, POP3_STOP);
-      break;
-    }
-  }
-
-  return result;
-}
-
-/* Called repeatedly until done from curl_multi.c */
-static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done)
-{
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-  CURLcode result = Curl_pp_multi_statemach(&pop3c->pp);
-
-  *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE;
-
-  return result;
-}
-
-static CURLcode pop3_easy_statemach(struct connectdata *conn)
-{
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-  struct pingpong *pp = &pop3c->pp;
-  CURLcode result = CURLE_OK;
-
-  while(pop3c->state != POP3_STOP) {
-    result = Curl_pp_easy_statemach(pp);
-    if(result)
-      break;
-  }
-
-  return result;
-}
-
-/* Allocate and initialize the POP3 struct for the current SessionHandle if
-   required */
-static CURLcode pop3_init(struct connectdata *conn)
-{
-  struct SessionHandle *data = conn->data;
-  struct FTP *pop3 = data->state.proto.pop3;
-
-  if(!pop3) {
-    pop3 = data->state.proto.pop3 = calloc(sizeof(struct FTP), 1);
-    if(!pop3)
-      return CURLE_OUT_OF_MEMORY;
-  }
-
-  /* Get some initial data into the pop3 struct */
-  pop3->bytecountp = &data->req.bytecount;
-
-  /* No need to duplicate user+password, the connectdata struct won't change
-     during a session, but we re-init them here since on subsequent inits
-     since the conn struct may have changed or been replaced.
-  */
-  pop3->user = conn->user;
-  pop3->passwd = conn->passwd;
-
-  return CURLE_OK;
-}
-
-/***********************************************************************
- *
- * pop3_connect()
- *
- * This function should do everything that is to be considered a part of the
- * connection phase.
- *
- * The variable 'done' points to will be TRUE if the protocol-layer connect
- * phase is done when this function returns, or FALSE is not. When called as
- * a part of the easy interface, it will always be TRUE.
- */
-static CURLcode pop3_connect(struct connectdata *conn, bool *done)
-{
-  CURLcode result;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-  struct SessionHandle *data = conn->data;
-  struct pingpong *pp = &pop3c->pp;
-
-  *done = FALSE; /* default to not done yet */
-
-  /* If there already is a protocol-specific struct allocated for this
-     sessionhandle, deal with it */
-  Curl_reset_reqproto(conn);
-
-  result = pop3_init(conn);
-  if(CURLE_OK != result)
-    return result;
-
-  /* We always support persistent connections on pop3 */
-  conn->bits.close = FALSE;
-
-  pp->response_time = RESP_TIMEOUT; /* set default response time-out */
-  pp->statemach_act = pop3_statemach_act;
-  pp->endofresp = pop3_endofresp;
-  pp->conn = conn;
-
-  if(conn->handler->flags & PROTOPT_SSL) {
-    /* POP3S is simply pop3 with SSL for the control channel */
-    /* so perform the SSL initialization for this socket */
-    result = Curl_ssl_connect(conn, FIRSTSOCKET);
-    if(result)
-      return result;
-  }
-
-  /* Initialise the response reader stuff */
-  Curl_pp_init(pp);
-
-  /* Start off waiting for the server greeting response */
-  state(conn, POP3_SERVERGREET);
-
-  if(data->state.used_interface == Curl_if_multi)
-    result = pop3_multi_statemach(conn, done);
-  else {
-    result = pop3_easy_statemach(conn);
-    if(!result)
-      *done = TRUE;
-  }
-
-  return result;
-}
-
-/***********************************************************************
- *
- * pop3_done()
- *
- * The DONE function. This does what needs to be done after a single DO has
- * performed.
- *
- * Input argument is already checked for validity.
- */
-static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
-                          bool premature)
-{
-  struct SessionHandle *data = conn->data;
-  struct FTP *pop3 = data->state.proto.pop3;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-  CURLcode result = CURLE_OK;
-
-  (void)premature;
-
-  if(!pop3)
-    /* When the easy handle is removed from the multi while libcurl is still
-     * trying to resolve the host name, it seems that the pop3 struct is not
-     * yet initialized, but the removal action calls Curl_done() which calls
-     * this function. So we simply return success if no pop3 pointer is set.
-     */
-    return CURLE_OK;
-
-  if(status) {
-    conn->bits.close = TRUE; /* marked for closure */
-    result = status;         /* use the already set error code */
-  }
-
-  /* Cleanup our do based variables */
-  Curl_safefree(pop3c->mailbox);
-  Curl_safefree(pop3c->custom);
-
-  /* Clear the transfer mode for the next connection */
-  pop3->transfer = FTPTRANSFER_BODY;
-
-  return result;
-}
-
-/***********************************************************************
- *
- * pop3_perform()
- *
- * This is the actual DO function for POP3. Get a file/directory according to
- * the options previously setup.
- */
-static CURLcode pop3_perform(struct connectdata *conn, bool *connected,
-                             bool *dophase_done)
-{
-  /* This is POP3 and no proxy */
-  CURLcode result = CURLE_OK;
-
-  DEBUGF(infof(conn->data, "DO phase starts\n"));
-
-  if(conn->data->set.opt_no_body) {
-    /* Requested no body means no transfer */
-    struct FTP *pop3 = conn->data->state.proto.pop3;
-    pop3->transfer = FTPTRANSFER_INFO;
-  }
-
-  *dophase_done = FALSE; /* not done yet */
-
-  /* Start the first command in the DO phase */
-  result = pop3_command(conn);
-  if(result)
-    return result;
-
-  /* Run the state-machine */
-  if(conn->data->state.used_interface == Curl_if_multi)
-    result = pop3_multi_statemach(conn, dophase_done);
-  else {
-    result = pop3_easy_statemach(conn);
-    *dophase_done = TRUE; /* with the easy interface we are done here */
-  }
-  *connected = conn->bits.tcpconnect[FIRSTSOCKET];
-
-  if(*dophase_done)
-    DEBUGF(infof(conn->data, "DO phase is complete\n"));
-
-  return result;
-}
-
-/***********************************************************************
- *
- * pop3_do()
- *
- * This function is registered as 'curl_do' function. It decodes the path
- * parts etc as a wrapper to the actual DO function (pop3_perform).
- *
- * The input argument is already checked for validity.
- */
-static CURLcode pop3_do(struct connectdata *conn, bool *done)
-{
-  CURLcode retcode = CURLE_OK;
-
-  *done = FALSE; /* default to false */
-
-  /*
-    Since connections can be re-used between SessionHandles, this might be a
-    connection already existing but on a fresh SessionHandle struct so we must
-    make sure we have a good 'struct POP3' to play with. For new connections,
-    the struct POP3 is allocated and setup in the pop3_connect() function.
-  */
-  Curl_reset_reqproto(conn);
-  retcode = pop3_init(conn);
-  if(retcode)
-    return retcode;
-
-  /* Parse the URL path */
-  retcode = pop3_parse_url_path(conn);
-  if(retcode)
-    return retcode;
-
-  /* Parse the custom request */
-  retcode = pop3_parse_custom_request(conn);
-  if(retcode)
-    return retcode;
-
-  retcode = pop3_regular_transfer(conn, done);
-
-  return retcode;
-}
-
-/***********************************************************************
- *
- * pop3_quit()
- *
- * This should be called before calling sclose().  We should then wait for the
- * response from the server before returning. The calling code should then try
- * to close the connection.
- */
-static CURLcode pop3_quit(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-
-  result = Curl_pp_sendf(&conn->proto.pop3c.pp, "QUIT", NULL);
-  if(result)
-    return result;
-
-  state(conn, POP3_QUIT);
-
-  result = pop3_easy_statemach(conn);
-
-  return result;
-}
-
-/***********************************************************************
- *
- * pop3_disconnect()
- *
- * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
- * resources. BLOCKING.
- */
-static CURLcode pop3_disconnect(struct connectdata *conn,
-                                bool dead_connection)
-{
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-
-  /* We cannot send quit unconditionally. If this connection is stale or
-     bad in any way, sending quit and waiting around here will make the
-     disconnect wait in vain and cause more problems than we need to */
-
-  /* The POP3 session may or may not have been allocated/setup at this
-     point! */
-  if(!dead_connection && pop3c->pp.conn)
-    (void)pop3_quit(conn); /* ignore errors on the LOGOUT */
-
-  /* Disconnect from the server */
-  Curl_pp_disconnect(&pop3c->pp);
-
-  /* Cleanup the SASL module */
-  Curl_sasl_cleanup(conn, pop3c->authused);
-
-  /* Cleanup our connection based variables */
-  Curl_safefree(pop3c->apoptimestamp);
-
-  return CURLE_OK;
-}
-
-/***********************************************************************
- *
- * pop3_parse_url_path()
- *
- * Parse the URL path into separate path components.
- */
-static CURLcode pop3_parse_url_path(struct connectdata *conn)
-{
-  /* The POP3 struct is already initialised in pop3_connect() */
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-  struct SessionHandle *data = conn->data;
-  const char *path = data->state.path;
-
-  /* URL decode the path and use this mailbox */
-  return Curl_urldecode(data, path, 0, &pop3c->mailbox, NULL, TRUE);
-}
-
-static CURLcode pop3_parse_custom_request(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-  struct SessionHandle *data = conn->data;
-  const char *custom = conn->data->set.str[STRING_CUSTOMREQUEST];
-
-  /* URL decode the custom request */
-  if(custom)
-    result = Curl_urldecode(data, custom, 0, &pop3c->custom, NULL, TRUE);
-
-  return result;
-}
-
-/* Call this when the DO phase has completed */
-static CURLcode pop3_dophase_done(struct connectdata *conn, bool connected)
-{
-  struct FTP *pop3 = conn->data->state.proto.pop3;
-
-  (void)connected;
-
-  if(pop3->transfer != FTPTRANSFER_BODY)
-    /* no data to transfer */
-    Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
-
-  return CURLE_OK;
-}
-
-/* Called from curl_multi.c while DOing */
-static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done)
-{
-  CURLcode result = pop3_multi_statemach(conn, dophase_done);
-
-  if(result)
-    DEBUGF(infof(conn->data, "DO phase failed\n"));
-  else {
-    if(*dophase_done) {
-      result = pop3_dophase_done(conn, FALSE /* not connected */);
-
-      DEBUGF(infof(conn->data, "DO phase is complete\n"));
-    }
-  }
-
-  return result;
-}
-
-/***********************************************************************
- *
- * pop3_regular_transfer()
- *
- * The input argument is already checked for validity.
- *
- * Performs all commands done before a regular transfer between a local and a
- * remote host.
- */
-static CURLcode pop3_regular_transfer(struct connectdata *conn,
-                                      bool *dophase_done)
-{
-  CURLcode result = CURLE_OK;
-  bool connected = FALSE;
-  struct SessionHandle *data = conn->data;
-
-  /* Make sure size is unknown at this point */
-  data->req.size = -1;
-
-  Curl_pgrsSetUploadCounter(data, 0);
-  Curl_pgrsSetDownloadCounter(data, 0);
-  Curl_pgrsSetUploadSize(data, 0);
-  Curl_pgrsSetDownloadSize(data, 0);
-
-  result = pop3_perform(conn, &connected, dophase_done);
-
-  if(CURLE_OK == result) {
-    if(!*dophase_done)
-      /* The DO phase has not completed yet */
-      return CURLE_OK;
-
-    result = pop3_dophase_done(conn, connected);
-    if(result)
-      return result;
-  }
-
-  return result;
-}
-
-static CURLcode pop3_setup_connection(struct connectdata * conn)
-{
-  struct SessionHandle *data = conn->data;
-
-  if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
-    /* Unless we have asked to tunnel pop3 operations through the proxy, we
-       switch and use HTTP operations only */
-#ifndef CURL_DISABLE_HTTP
-    if(conn->handler == &Curl_handler_pop3)
-      conn->handler = &Curl_handler_pop3_proxy;
-    else {
-#ifdef USE_SSL
-      conn->handler = &Curl_handler_pop3s_proxy;
-#else
-      failf(data, "POP3S not supported!");
-      return CURLE_UNSUPPORTED_PROTOCOL;
-#endif
-    }
-
-    /* We explicitly mark this connection as persistent here as we're doing
-       POP3 over HTTP and thus we accidentally avoid setting this value
-       otherwise */
-    conn->bits.close = FALSE;
-#else
-    failf(data, "POP3 over http proxy requires HTTP support built-in!");
-    return CURLE_UNSUPPORTED_PROTOCOL;
-#endif
-  }
-
-  data->state.path++;   /* don't include the initial slash */
-
-  return CURLE_OK;
-}
-
-/* This function scans the body after the end-of-body and writes everything
-   until the end is found */
-CURLcode Curl_pop3_write(struct connectdata *conn, char *str, size_t nread)
-{
-  /* This code could be made into a special function in the handler struct */
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  struct SingleRequest *k = &data->req;
-
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-  bool strip_dot = FALSE;
-  size_t last = 0;
-  size_t i;
-
-  /* Search through the buffer looking for the end-of-body marker which is
-     5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
-     the eob so the server will have prefixed it with an extra dot which we
-     need to strip out. Additionally the marker could of course be spread out
-     over 5 different data chunks */
-  for(i = 0; i < nread; i++) {
-    size_t prev = pop3c->eob;
-
-    switch(str[i]) {
-    case 0x0d:
-      if(pop3c->eob == 0) {
-        pop3c->eob++;
-
-        if(i) {
-          /* Write out the body part that didn't match */
-          result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
-                                     i - last);
-
-          if(result)
-            return result;
-
-          last = i;
-        }
-      }
-      else if(pop3c->eob == 3)
-        pop3c->eob++;
-      else
-        /* If the character match wasn't at position 0 or 3 then restart the
-           pattern matching */
-        pop3c->eob = 1;
-      break;
-
-    case 0x0a:
-      if(pop3c->eob == 1 || pop3c->eob == 4)
-        pop3c->eob++;
-      else
-        /* If the character match wasn't at position 1 or 4 then start the
-           search again */
-        pop3c->eob = 0;
-      break;
-
-    case 0x2e:
-      if(pop3c->eob == 2)
-        pop3c->eob++;
-      else if(pop3c->eob == 3) {
-        /* We have an extra dot after the CRLF which we need to strip off */
-        strip_dot = TRUE;
-        pop3c->eob = 0;
-      }
-      else
-        /* If the character match wasn't at position 2 then start the search
-           again */
-        pop3c->eob = 0;
-      break;
-
-    default:
-      pop3c->eob = 0;
-      break;
-    }
-
-    /* Did we have a partial match which has subsequently failed? */
-    if(prev && prev >= pop3c->eob) {
-      /* Strip can only be non-zero for the very first mismatch after CRLF
-         and then both prev and strip are equal and nothing will be output
-         below */
-      while(prev && pop3c->strip) {
-        prev--;
-        pop3c->strip--;
-      }
-
-      if(prev) {
-        /* If the partial match was the CRLF and dot then only write the CRLF
-           as the server would have inserted the dot */
-        result = Curl_client_write(conn, CLIENTWRITE_BODY, (char*)POP3_EOB,
-                                   strip_dot ? prev - 1 : prev);
-
-        if(result)
-          return result;
-
-        last = i;
-        strip_dot = FALSE;
-      }
-    }
-  }
-
-  if(pop3c->eob == POP3_EOB_LEN) {
-    /* We have a full match so the transfer is done, however we must transfer
-    the CRLF at the start of the EOB as this is considered to be part of the
-    message as per RFC-1939, sect. 3 */
-    result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
-
-    k->keepon &= ~KEEP_RECV;
-    pop3c->eob = 0;
-
-    return result;
-  }
-
-  if(pop3c->eob)
-    /* While EOB is matching nothing should be output */
-    return CURLE_OK;
-
-  if(nread - last) {
-    result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
-                               nread - last);
-  }
-
-  return result;
-}
-
-#endif /* CURL_DISABLE_POP3 */
diff --git a/lib/progress.c b/lib/progress.c
deleted file mode 100644 (file)
index 88f802d..0000000
+++ /dev/null
@@ -1,474 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_progress.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-/* Provide a string that is 2 + 1 + 2 + 1 + 2 = 8 letters long (plus the zero
-   byte) */
-static void time2str(char *r, curl_off_t seconds)
-{
-  curl_off_t d, h, m, s;
-  if(seconds <= 0) {
-    strcpy(r, "--:--:--");
-    return;
-  }
-  h = seconds / CURL_OFF_T_C(3600);
-  if(h <= CURL_OFF_T_C(99)) {
-    m = (seconds - (h*CURL_OFF_T_C(3600))) / CURL_OFF_T_C(60);
-    s = (seconds - (h*CURL_OFF_T_C(3600))) - (m*CURL_OFF_T_C(60));
-    snprintf(r, 9, "%2" FORMAT_OFF_T ":%02" FORMAT_OFF_T ":%02" FORMAT_OFF_T,
-             h, m, s);
-  }
-  else {
-    /* this equals to more than 99 hours, switch to a more suitable output
-       format to fit within the limits. */
-    d = seconds / CURL_OFF_T_C(86400);
-    h = (seconds - (d*CURL_OFF_T_C(86400))) / CURL_OFF_T_C(3600);
-    if(d <= CURL_OFF_T_C(999))
-      snprintf(r, 9, "%3" FORMAT_OFF_T "d %02" FORMAT_OFF_T "h", d, h);
-    else
-      snprintf(r, 9, "%7" FORMAT_OFF_T "d", d);
-  }
-}
-
-/* The point of this function would be to return a string of the input data,
-   but never longer than 5 columns (+ one zero byte).
-   Add suffix k, M, G when suitable... */
-static char *max5data(curl_off_t bytes, char *max5)
-{
-#define ONE_KILOBYTE  CURL_OFF_T_C(1024)
-#define ONE_MEGABYTE (CURL_OFF_T_C(1024) * ONE_KILOBYTE)
-#define ONE_GIGABYTE (CURL_OFF_T_C(1024) * ONE_MEGABYTE)
-#define ONE_TERABYTE (CURL_OFF_T_C(1024) * ONE_GIGABYTE)
-#define ONE_PETABYTE (CURL_OFF_T_C(1024) * ONE_TERABYTE)
-
-  if(bytes < CURL_OFF_T_C(100000))
-    snprintf(max5, 6, "%5" FORMAT_OFF_T, bytes);
-
-  else if(bytes < CURL_OFF_T_C(10000) * ONE_KILOBYTE)
-    snprintf(max5, 6, "%4" FORMAT_OFF_T "k", bytes/ONE_KILOBYTE);
-
-  else if(bytes < CURL_OFF_T_C(100) * ONE_MEGABYTE)
-    /* 'XX.XM' is good as long as we're less than 100 megs */
-    snprintf(max5, 6, "%2" FORMAT_OFF_T ".%0" FORMAT_OFF_T "M",
-              bytes/ONE_MEGABYTE,
-             (bytes%ONE_MEGABYTE) / (ONE_MEGABYTE/CURL_OFF_T_C(10)) );
-
-#if (CURL_SIZEOF_CURL_OFF_T > 4)
-
-  else if(bytes < CURL_OFF_T_C(10000) * ONE_MEGABYTE)
-    /* 'XXXXM' is good until we're at 10000MB or above */
-    snprintf(max5, 6, "%4" FORMAT_OFF_T "M", bytes/ONE_MEGABYTE);
-
-  else if(bytes < CURL_OFF_T_C(100) * ONE_GIGABYTE)
-    /* 10000 MB - 100 GB, we show it as XX.XG */
-    snprintf(max5, 6, "%2" FORMAT_OFF_T ".%0" FORMAT_OFF_T "G",
-              bytes/ONE_GIGABYTE,
-             (bytes%ONE_GIGABYTE) / (ONE_GIGABYTE/CURL_OFF_T_C(10)) );
-
-  else if(bytes < CURL_OFF_T_C(10000) * ONE_GIGABYTE)
-    /* up to 10000GB, display without decimal: XXXXG */
-    snprintf(max5, 6, "%4" FORMAT_OFF_T "G", bytes/ONE_GIGABYTE);
-
-  else if(bytes < CURL_OFF_T_C(10000) * ONE_TERABYTE)
-    /* up to 10000TB, display without decimal: XXXXT */
-    snprintf(max5, 6, "%4" FORMAT_OFF_T "T", bytes/ONE_TERABYTE);
-
-  else
-    /* up to 10000PB, display without decimal: XXXXP */
-    snprintf(max5, 6, "%4" FORMAT_OFF_T "P", bytes/ONE_PETABYTE);
-
-    /* 16384 petabytes (16 exabytes) is the maximum a 64 bit unsigned number
-       can hold, but our data type is signed so 8192PB will be the maximum. */
-
-#else
-
-  else
-    snprintf(max5, 6, "%4" FORMAT_OFF_T "M", bytes/ONE_MEGABYTE);
-
-#endif
-
-  return max5;
-}
-
-/*
-
-   New proposed interface, 9th of February 2000:
-
-   pgrsStartNow() - sets start time
-   pgrsSetDownloadSize(x) - known expected download size
-   pgrsSetUploadSize(x) - known expected upload size
-   pgrsSetDownloadCounter() - amount of data currently downloaded
-   pgrsSetUploadCounter() - amount of data currently uploaded
-   pgrsUpdate() - show progress
-   pgrsDone() - transfer complete
-
-*/
-
-int Curl_pgrsDone(struct connectdata *conn)
-{
-  int rc;
-  struct SessionHandle *data = conn->data;
-  data->progress.lastshow=0;
-  rc = Curl_pgrsUpdate(conn); /* the final (forced) update */
-  if(rc)
-    return rc;
-
-  if(!(data->progress.flags & PGRS_HIDE) &&
-     !data->progress.callback)
-    /* only output if we don't use a progress callback and we're not
-     * hidden */
-    fprintf(data->set.err, "\n");
-
-  data->progress.speeder_c = 0; /* reset the progress meter display */
-  return 0;
-}
-
-/* reset all times except redirect, and reset the known transfer sizes */
-void Curl_pgrsResetTimesSizes(struct SessionHandle *data)
-{
-  data->progress.t_nslookup = 0.0;
-  data->progress.t_connect = 0.0;
-  data->progress.t_pretransfer = 0.0;
-  data->progress.t_starttransfer = 0.0;
-
-  Curl_pgrsSetDownloadSize(data, 0);
-  Curl_pgrsSetUploadSize(data, 0);
-}
-
-void Curl_pgrsTime(struct SessionHandle *data, timerid timer)
-{
-  struct timeval now = Curl_tvnow();
-
-  switch(timer) {
-  default:
-  case TIMER_NONE:
-    /* mistake filter */
-    break;
-  case TIMER_STARTSINGLE:
-    /* This is set at the start of a single fetch */
-    data->progress.t_startsingle = now;
-    break;
-
-  case TIMER_STARTACCEPT:
-    data->progress.t_acceptdata = Curl_tvnow();
-    break;
-
-  case TIMER_NAMELOOKUP:
-    data->progress.t_nslookup =
-      Curl_tvdiff_secs(now, data->progress.t_startsingle);
-    break;
-  case TIMER_CONNECT:
-    data->progress.t_connect =
-      Curl_tvdiff_secs(now, data->progress.t_startsingle);
-    break;
-  case TIMER_APPCONNECT:
-    data->progress.t_appconnect =
-      Curl_tvdiff_secs(now, data->progress.t_startsingle);
-    break;
-  case TIMER_PRETRANSFER:
-    data->progress.t_pretransfer =
-      Curl_tvdiff_secs(now, data->progress.t_startsingle);
-    break;
-  case TIMER_STARTTRANSFER:
-    data->progress.t_starttransfer =
-      Curl_tvdiff_secs(now, data->progress.t_startsingle);
-    break;
-  case TIMER_POSTRANSFER:
-    /* this is the normal end-of-transfer thing */
-    break;
-  case TIMER_REDIRECT:
-    data->progress.t_redirect = Curl_tvdiff_secs(now, data->progress.start);
-    break;
-  }
-}
-
-void Curl_pgrsStartNow(struct SessionHandle *data)
-{
-  data->progress.speeder_c = 0; /* reset the progress meter display */
-  data->progress.start = Curl_tvnow();
-  /* clear all bits except HIDE and HEADERS_OUT */
-  data->progress.flags &= PGRS_HIDE|PGRS_HEADERS_OUT;
-}
-
-void Curl_pgrsSetDownloadCounter(struct SessionHandle *data, curl_off_t size)
-{
-  data->progress.downloaded = size;
-}
-
-void Curl_pgrsSetUploadCounter(struct SessionHandle *data, curl_off_t size)
-{
-  data->progress.uploaded = size;
-}
-
-void Curl_pgrsSetDownloadSize(struct SessionHandle *data, curl_off_t size)
-{
-  data->progress.size_dl = size;
-  if(size >= 0)
-    data->progress.flags |= PGRS_DL_SIZE_KNOWN;
-  else
-    data->progress.flags &= ~PGRS_DL_SIZE_KNOWN;
-}
-
-void Curl_pgrsSetUploadSize(struct SessionHandle *data, curl_off_t size)
-{
-  data->progress.size_ul = size;
-  if(size >= 0)
-    data->progress.flags |= PGRS_UL_SIZE_KNOWN;
-  else
-    data->progress.flags &= ~PGRS_UL_SIZE_KNOWN;
-}
-
-/*
- * Curl_pgrsUpdate() returns 0 for success or the value returned by the
- * progress callback!
- */
-int Curl_pgrsUpdate(struct connectdata *conn)
-{
-  struct timeval now;
-  int result;
-  char max5[6][10];
-  curl_off_t dlpercen=0;
-  curl_off_t ulpercen=0;
-  curl_off_t total_percen=0;
-  curl_off_t total_transfer;
-  curl_off_t total_expected_transfer;
-  curl_off_t timespent;
-  struct SessionHandle *data = conn->data;
-  int nowindex = data->progress.speeder_c% CURR_TIME;
-  int checkindex;
-  int countindex; /* amount of seconds stored in the speeder array */
-  char time_left[10];
-  char time_total[10];
-  char time_spent[10];
-  curl_off_t ulestimate=0;
-  curl_off_t dlestimate=0;
-  curl_off_t total_estimate;
-  bool shownow=FALSE;
-
-  now = Curl_tvnow(); /* what time is it */
-
-  /* The time spent so far (from the start) */
-  data->progress.timespent =
-    (double)(now.tv_sec - data->progress.start.tv_sec) +
-    (double)(now.tv_usec - data->progress.start.tv_usec)/1000000.0;
-  timespent = (curl_off_t)data->progress.timespent;
-
-  /* The average download speed this far */
-  data->progress.dlspeed = (curl_off_t)
-    ((double)data->progress.downloaded/
-     (data->progress.timespent>0?data->progress.timespent:1));
-
-  /* The average upload speed this far */
-  data->progress.ulspeed = (curl_off_t)
-    ((double)data->progress.uploaded/
-     (data->progress.timespent>0?data->progress.timespent:1));
-
-  /* Calculations done at most once a second, unless end is reached */
-  if(data->progress.lastshow != (long)now.tv_sec) {
-    shownow = TRUE;
-
-    data->progress.lastshow = now.tv_sec;
-
-    /* Let's do the "current speed" thing, which should use the fastest
-       of the dl/ul speeds. Store the faster speed at entry 'nowindex'. */
-    data->progress.speeder[ nowindex ] =
-      data->progress.downloaded>data->progress.uploaded?
-      data->progress.downloaded:data->progress.uploaded;
-
-    /* remember the exact time for this moment */
-    data->progress.speeder_time [ nowindex ] = now;
-
-    /* advance our speeder_c counter, which is increased every time we get
-       here and we expect it to never wrap as 2^32 is a lot of seconds! */
-    data->progress.speeder_c++;
-
-    /* figure out how many index entries of data we have stored in our speeder
-       array. With N_ENTRIES filled in, we have about N_ENTRIES-1 seconds of
-       transfer. Imagine, after one second we have filled in two entries,
-       after two seconds we've filled in three entries etc. */
-    countindex = ((data->progress.speeder_c>=CURR_TIME)?
-                  CURR_TIME:data->progress.speeder_c) - 1;
-
-    /* first of all, we don't do this if there's no counted seconds yet */
-    if(countindex) {
-      long span_ms;
-
-      /* Get the index position to compare with the 'nowindex' position.
-         Get the oldest entry possible. While we have less than CURR_TIME
-         entries, the first entry will remain the oldest. */
-      checkindex = (data->progress.speeder_c>=CURR_TIME)?
-        data->progress.speeder_c%CURR_TIME:0;
-
-      /* Figure out the exact time for the time span */
-      span_ms = Curl_tvdiff(now,
-                            data->progress.speeder_time[checkindex]);
-      if(0 == span_ms)
-        span_ms=1; /* at least one millisecond MUST have passed */
-
-      /* Calculate the average speed the last 'span_ms' milliseconds */
-      {
-        curl_off_t amount = data->progress.speeder[nowindex]-
-          data->progress.speeder[checkindex];
-
-        if(amount > CURL_OFF_T_C(4294967) /* 0xffffffff/1000 */)
-          /* the 'amount' value is bigger than would fit in 32 bits if
-             multiplied with 1000, so we use the double math for this */
-          data->progress.current_speed = (curl_off_t)
-            ((double)amount/((double)span_ms/1000.0));
-        else
-          /* the 'amount' value is small enough to fit within 32 bits even
-             when multiplied with 1000 */
-          data->progress.current_speed = amount*CURL_OFF_T_C(1000)/span_ms;
-      }
-    }
-    else
-      /* the first second we use the main average */
-      data->progress.current_speed =
-        (data->progress.ulspeed>data->progress.dlspeed)?
-        data->progress.ulspeed:data->progress.dlspeed;
-
-  } /* Calculations end */
-
-  if(!(data->progress.flags & PGRS_HIDE)) {
-
-    /* progress meter has not been shut off */
-
-    if(data->set.fprogress) {
-      /* There's a callback set, so we call that instead of writing
-         anything ourselves. This really is the way to go. */
-      result= data->set.fprogress(data->set.progress_client,
-                                  (double)data->progress.size_dl,
-                                  (double)data->progress.downloaded,
-                                  (double)data->progress.size_ul,
-                                  (double)data->progress.uploaded);
-      if(result)
-        failf(data, "Callback aborted");
-      return result;
-    }
-
-    if(!shownow)
-      /* only show the internal progress meter once per second */
-      return 0;
-
-    /* If there's no external callback set, use internal code to show
-       progress */
-
-    if(!(data->progress.flags & PGRS_HEADERS_OUT)) {
-      if(data->state.resume_from) {
-        fprintf(data->set.err,
-                "** Resuming transfer from byte position %" FORMAT_OFF_T "\n",
-                data->state.resume_from);
-      }
-      fprintf(data->set.err,
-              "  %% Total    %% Received %% Xferd  Average Speed   "
-              "Time    Time     Time  Current\n"
-              "                                 Dload  Upload   "
-              "Total   Spent    Left  Speed\n");
-      data->progress.flags |= PGRS_HEADERS_OUT; /* headers are shown */
-    }
-
-    /* Figure out the estimated time of arrival for the upload */
-    if((data->progress.flags & PGRS_UL_SIZE_KNOWN) &&
-       (data->progress.ulspeed > CURL_OFF_T_C(0))) {
-      ulestimate = data->progress.size_ul / data->progress.ulspeed;
-
-      if(data->progress.size_ul > CURL_OFF_T_C(10000))
-        ulpercen = data->progress.uploaded /
-          (data->progress.size_ul/CURL_OFF_T_C(100));
-      else if(data->progress.size_ul > CURL_OFF_T_C(0))
-        ulpercen = (data->progress.uploaded*100) /
-          data->progress.size_ul;
-    }
-
-    /* ... and the download */
-    if((data->progress.flags & PGRS_DL_SIZE_KNOWN) &&
-       (data->progress.dlspeed > CURL_OFF_T_C(0))) {
-      dlestimate = data->progress.size_dl / data->progress.dlspeed;
-
-      if(data->progress.size_dl > CURL_OFF_T_C(10000))
-        dlpercen = data->progress.downloaded /
-          (data->progress.size_dl/CURL_OFF_T_C(100));
-      else if(data->progress.size_dl > CURL_OFF_T_C(0))
-        dlpercen = (data->progress.downloaded*100) /
-          data->progress.size_dl;
-    }
-
-    /* Now figure out which of them is slower and use that one for the
-       total estimate! */
-    total_estimate = ulestimate>dlestimate?ulestimate:dlestimate;
-
-    /* create the three time strings */
-    time2str(time_left, total_estimate > 0?(total_estimate - timespent):0);
-    time2str(time_total, total_estimate);
-    time2str(time_spent, timespent);
-
-    /* Get the total amount of data expected to get transferred */
-    total_expected_transfer =
-      (data->progress.flags & PGRS_UL_SIZE_KNOWN?
-       data->progress.size_ul:data->progress.uploaded)+
-      (data->progress.flags & PGRS_DL_SIZE_KNOWN?
-       data->progress.size_dl:data->progress.downloaded);
-
-    /* We have transferred this much so far */
-    total_transfer = data->progress.downloaded + data->progress.uploaded;
-
-    /* Get the percentage of data transferred so far */
-    if(total_expected_transfer > CURL_OFF_T_C(10000))
-      total_percen = total_transfer /
-        (total_expected_transfer/CURL_OFF_T_C(100));
-    else if(total_expected_transfer > CURL_OFF_T_C(0))
-      total_percen = (total_transfer*100) / total_expected_transfer;
-
-    fprintf(data->set.err,
-            "\r"
-            "%3" FORMAT_OFF_T " %s  "
-            "%3" FORMAT_OFF_T " %s  "
-            "%3" FORMAT_OFF_T " %s  %s  %s %s %s %s %s",
-            total_percen,  /* 3 letters */                /* total % */
-            max5data(total_expected_transfer, max5[2]),   /* total size */
-            dlpercen,      /* 3 letters */                /* rcvd % */
-            max5data(data->progress.downloaded, max5[0]), /* rcvd size */
-            ulpercen,      /* 3 letters */                /* xfer % */
-            max5data(data->progress.uploaded, max5[1]),   /* xfer size */
-            max5data(data->progress.dlspeed, max5[3]),    /* avrg dl speed */
-            max5data(data->progress.ulspeed, max5[4]),    /* avrg ul speed */
-            time_total,    /* 8 letters */                /* total time */
-            time_spent,    /* 8 letters */                /* time spent */
-            time_left,     /* 8 letters */                /* time left */
-            max5data(data->progress.current_speed, max5[5]) /* current speed */
-            );
-
-    /* we flush the output stream to make it appear as soon as possible */
-    fflush(data->set.err);
-
-  } /* !(data->progress.flags & PGRS_HIDE) */
-
-  return 0;
-}
diff --git a/lib/qssl.c b/lib/qssl.c
deleted file mode 100644 (file)
index d140dc9..0000000
+++ /dev/null
@@ -1,501 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef USE_QSOSSL
-
-#include <qsossl.h>
-
-#ifdef HAVE_LIMITS_H
-#  include <limits.h>
-#endif
-
-#include <curl/curl.h>
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_qssl.h"
-#include "curl_sslgen.h"
-#include "curl_connect.h" /* for the connect timeout */
-#include "curl_select.h"
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-
-int Curl_qsossl_init(void)
-
-{
-  /* Nothing to do here. We must have connection data to initialize ssl, so
-   * defer.
-   */
-
-  return 1;
-}
-
-
-void Curl_qsossl_cleanup(void)
-
-{
-  /* Nothing to do. */
-}
-
-
-static CURLcode Curl_qsossl_init_session(struct SessionHandle * data)
-
-{
-  int rc;
-  char * certname;
-  SSLInit initstr;
-  SSLInitApp initappstr;
-
-  /* Initialize the job for SSL according to the current parameters.
-   * QsoSSL offers two ways to do it: SSL_Init_Application() that uses an
-   *  application identifier to select certificates in the main certificate
-   *  store, and SSL_Init() that uses named keyring files and a password.
-   * It is not possible to have different keyrings for the CAs and the
-   *  local certificate. We thus use the certificate name to identify the
-   *  keyring if given, else the CA file name.
-   * If the key file name is given, it is taken as the password for the
-   *  keyring in certificate file.
-   * We first try to SSL_Init_Application(), then SSL_Init() if it failed.
-   */
-
-  certname = data->set.str[STRING_CERT];
-
-  if(!certname) {
-    certname = data->set.str[STRING_SSL_CAFILE];
-
-    if(!certname)
-      return CURLE_OK;          /* Use previous setup. */
-    }
-
-  memset((char *) &initappstr, 0, sizeof initappstr);
-  initappstr.applicationID = certname;
-  initappstr.applicationIDLen = strlen(certname);
-  initappstr.protocol = SSL_VERSION_CURRENT;    /* TLSV1 compat. SSLV[23]. */
-  initappstr.sessionType = SSL_REGISTERED_AS_CLIENT;
-  rc = SSL_Init_Application(&initappstr);
-
-  if(rc == SSL_ERROR_NOT_REGISTERED) {
-    initstr.keyringFileName = certname;
-    initstr.keyringPassword = data->set.str[STRING_KEY];
-    initstr.cipherSuiteList = NULL;    /* Use default. */
-    initstr.cipherSuiteListLen = 0;
-    rc = SSL_Init(&initstr);
-    }
-
-  switch (rc) {
-
-  case 0:                             /* No error. */
-    break;
-
-  case SSL_ERROR_IO:
-    failf(data, "SSL_Init() I/O error: %s", strerror(errno));
-    return CURLE_SSL_CONNECT_ERROR;
-
-  case SSL_ERROR_BAD_CIPHER_SUITE:
-    return CURLE_SSL_CIPHER;
-
-  case SSL_ERROR_KEYPASSWORD_EXPIRED:
-  case SSL_ERROR_NOT_REGISTERED:
-    return CURLE_SSL_CONNECT_ERROR;
-
-  case SSL_ERROR_NO_KEYRING:
-    return CURLE_SSL_CACERT;
-
-  case SSL_ERROR_CERT_EXPIRED:
-    return CURLE_SSL_CERTPROBLEM;
-
-  default:
-    failf(data, "SSL_Init(): %s", SSL_Strerror(rc, NULL));
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-
-  return CURLE_OK;
-}
-
-
-static CURLcode Curl_qsossl_create(struct connectdata * conn, int sockindex)
-
-{
-  SSLHandle * h;
-  struct ssl_connect_data * connssl = &conn->ssl[sockindex];
-
-  h = SSL_Create(conn->sock[sockindex], SSL_ENCRYPT);
-
-  if(!h) {
-    failf(conn->data, "SSL_Create() I/O error: %s", strerror(errno));
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-
-  connssl->handle = h;
-  return CURLE_OK;
-}
-
-
-static int Curl_qsossl_trap_cert(SSLHandle * h)
-
-{
-  return 1;       /* Accept certificate. */
-}
-
-
-static CURLcode Curl_qsossl_handshake(struct connectdata * conn, int sockindex)
-
-{
-  int rc;
-  struct SessionHandle * data = conn->data;
-  struct ssl_connect_data * connssl = &conn->ssl[sockindex];
-  SSLHandle * h = connssl->handle;
-  long timeout_ms;
-
-  h->exitPgm = NULL;
-
-  if(!data->set.ssl.verifyhost)
-    h->exitPgm = Curl_qsossl_trap_cert;
-
-  /* figure out how long time we should wait at maximum */
-  timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
-  if(timeout_ms < 0) {
-    /* time-out, bail out, go home */
-    failf(data, "Connection time-out");
-    return CURLE_OPERATION_TIMEDOUT;
-  }
-
-  /* SSL_Handshake() timeout resolution is second, so round up. */
-  h->timeout = (timeout_ms + 1000 - 1) / 1000;
-
-  /* Set-up protocol. */
-
-  switch (data->set.ssl.version) {
-
-  default:
-  case CURL_SSLVERSION_DEFAULT:
-    h->protocol = SSL_VERSION_CURRENT;          /* TLSV1 compat. SSLV[23]. */
-    break;
-
-  case CURL_SSLVERSION_TLSv1:
-    h->protocol = TLS_VERSION_1;
-    break;
-
-  case CURL_SSLVERSION_SSLv2:
-    h->protocol = SSL_VERSION_2;
-    break;
-
-  case CURL_SSLVERSION_SSLv3:
-    h->protocol = SSL_VERSION_3;
-    break;
-  }
-
-  rc = SSL_Handshake(h, SSL_HANDSHAKE_AS_CLIENT);
-
-  switch (rc) {
-
-  case 0:                             /* No error. */
-    break;
-
-  case SSL_ERROR_BAD_CERTIFICATE:
-  case SSL_ERROR_BAD_CERT_SIG:
-  case SSL_ERROR_NOT_TRUSTED_ROOT:
-    return CURLE_PEER_FAILED_VERIFICATION;
-
-  case SSL_ERROR_BAD_CIPHER_SUITE:
-  case SSL_ERROR_NO_CIPHERS:
-    return CURLE_SSL_CIPHER;
-
-  case SSL_ERROR_CERTIFICATE_REJECTED:
-  case SSL_ERROR_CERT_EXPIRED:
-  case SSL_ERROR_NO_CERTIFICATE:
-    return CURLE_SSL_CERTPROBLEM;
-
-  case SSL_ERROR_IO:
-    failf(data, "SSL_Handshake() I/O error: %s", strerror(errno));
-    return CURLE_SSL_CONNECT_ERROR;
-
-  default:
-    failf(data, "SSL_Handshake(): %s", SSL_Strerror(rc, NULL));
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-
-  return CURLE_OK;
-}
-
-
-static Curl_recv qsossl_recv;
-static Curl_send qsossl_send;
-
-CURLcode Curl_qsossl_connect(struct connectdata * conn, int sockindex)
-
-{
-  struct SessionHandle * data = conn->data;
-  struct ssl_connect_data * connssl = &conn->ssl[sockindex];
-  int rc;
-
-  rc = Curl_qsossl_init_session(data);
-
-  if(rc == CURLE_OK) {
-    rc = Curl_qsossl_create(conn, sockindex);
-
-    if(rc == CURLE_OK)
-      rc = Curl_qsossl_handshake(conn, sockindex);
-    else {
-      SSL_Destroy(connssl->handle);
-      connssl->handle = NULL;
-      connssl->use = FALSE;
-      connssl->state = ssl_connection_none;
-    }
-  }
-  if(rc == CURLE_OK) {
-    connssl->state = ssl_connection_complete;
-    conn->recv[sockindex] = qsossl_recv;
-    conn->send[sockindex] = qsossl_send;
-  }
-
-  return rc;
-}
-
-
-static int Curl_qsossl_close_one(struct ssl_connect_data * conn,
-                                 struct SessionHandle * data)
-
-{
-  int rc;
-
-  if(!conn->handle)
-    return 0;
-
-  rc = SSL_Destroy(conn->handle);
-
-  if(rc) {
-    if(rc == SSL_ERROR_IO) {
-      failf(data, "SSL_Destroy() I/O error: %s", strerror(errno));
-      return -1;
-    }
-
-    /* An SSL error. */
-    failf(data, "SSL_Destroy() returned error %s", SSL_Strerror(rc, NULL));
-    return -1;
-  }
-
-  conn->handle = NULL;
-  return 0;
-}
-
-
-void Curl_qsossl_close(struct connectdata *conn, int sockindex)
-
-{
-  struct SessionHandle *data = conn->data;
-  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-
-  if(connssl->use)
-    (void) Curl_qsossl_close_one(connssl, data);
-}
-
-
-int Curl_qsossl_close_all(struct SessionHandle * data)
-
-{
-  /* Unimplemented. */
-  (void) data;
-  return 0;
-}
-
-
-int Curl_qsossl_shutdown(struct connectdata * conn, int sockindex)
-
-{
-  struct ssl_connect_data * connssl = &conn->ssl[sockindex];
-  struct SessionHandle *data = conn->data;
-  ssize_t nread;
-  int what;
-  int rc;
-  char buf[120];
-
-  if(!connssl->handle)
-    return 0;
-
-  if(data->set.ftp_ccc != CURLFTPSSL_CCC_ACTIVE)
-    return 0;
-
-  if(Curl_qsossl_close_one(connssl, data))
-    return -1;
-
-  rc = 0;
-
-  what = Curl_socket_ready(conn->sock[sockindex],
-                           CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT);
-
-  for(;;) {
-    if(what < 0) {
-      /* anything that gets here is fatally bad */
-      failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
-      rc = -1;
-      break;
-    }
-
-    if(!what) {                                /* timeout */
-      failf(data, "SSL shutdown timeout");
-      break;
-    }
-
-    /* Something to read, let's do it and hope that it is the close
-       notify alert from the server. No way to SSL_Read now, so use read(). */
-
-    nread = read(conn->sock[sockindex], buf, sizeof(buf));
-
-    if(nread < 0) {
-      failf(data, "read: %s", strerror(errno));
-      rc = -1;
-    }
-
-    if(nread <= 0)
-      break;
-
-    what = Curl_socket_ready(conn->sock[sockindex], CURL_SOCKET_BAD, 0);
-  }
-
-  return rc;
-}
-
-
-static ssize_t qsossl_send(struct connectdata * conn, int sockindex,
-                           const void * mem, size_t len, CURLcode * curlcode)
-
-{
-  /* SSL_Write() is said to return 'int' while write() and send() returns
-     'size_t' */
-  int rc;
-
-  rc = SSL_Write(conn->ssl[sockindex].handle, (void *) mem, (int) len);
-
-  if(rc < 0) {
-    switch(rc) {
-
-    case SSL_ERROR_BAD_STATE:
-      /* The operation did not complete; the same SSL I/O function
-         should be called again later. This is basically an EWOULDBLOCK
-         equivalent. */
-      *curlcode = CURLE_AGAIN;
-      return -1;
-
-    case SSL_ERROR_IO:
-      switch (errno) {
-      case EWOULDBLOCK:
-      case EINTR:
-        *curlcode = CURLE_AGAIN;
-        return -1;
-        }
-
-      failf(conn->data, "SSL_Write() I/O error: %s", strerror(errno));
-      *curlcode = CURLE_SEND_ERROR;
-      return -1;
-    }
-
-    /* An SSL error. */
-    failf(conn->data, "SSL_Write() returned error %s",
-          SSL_Strerror(rc, NULL));
-    *curlcode = CURLE_SEND_ERROR;
-    return -1;
-  }
-
-  return (ssize_t) rc; /* number of bytes */
-}
-
-
-static ssize_t qsossl_recv(struct connectdata * conn, int num, char * buf,
-                           size_t buffersize, CURLcode * curlcode)
-
-{
-  char error_buffer[120]; /* OpenSSL documents that this must be at
-                             least 120 bytes long. */
-  unsigned long sslerror;
-  int buffsize;
-  int nread;
-
-  buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
-  nread = SSL_Read(conn->ssl[num].handle, buf, buffsize);
-
-  if(nread < 0) {
-    /* failed SSL_read */
-
-    switch (nread) {
-
-    case SSL_ERROR_BAD_STATE:
-      /* there's data pending, re-invoke SSL_Read(). */
-      *curlcode = CURLE_AGAIN;
-      return -1;
-
-    case SSL_ERROR_IO:
-      switch (errno) {
-      case EWOULDBLOCK:
-        *curlcode = CURLE_AGAIN;
-        return -1;
-        }
-
-      failf(conn->data, "SSL_Read() I/O error: %s", strerror(errno));
-      *curlcode = CURLE_RECV_ERROR;
-      return -1;
-
-    default:
-      failf(conn->data, "SSL read error: %s", SSL_Strerror(nread, NULL));
-      *curlcode = CURLE_RECV_ERROR;
-      return -1;
-    }
-  }
-  return (ssize_t) nread;
-}
-
-
-size_t Curl_qsossl_version(char * buffer, size_t size)
-
-{
-  strncpy(buffer, "IBM OS/400 SSL", size);
-  return strlen(buffer);
-}
-
-
-int Curl_qsossl_check_cxn(struct connectdata * cxn)
-
-{
-  int err;
-  int errlen;
-
-  /* The only thing that can be tested here is at the socket level. */
-
-  if(!cxn->ssl[FIRSTSOCKET].handle)
-    return 0; /* connection has been closed */
-
-  err = 0;
-  errlen = sizeof err;
-
-  if(getsockopt(cxn->sock[FIRSTSOCKET], SOL_SOCKET, SO_ERROR,
-                 (unsigned char *) &err, &errlen) ||
-      errlen != sizeof err || err)
-    return 0; /* connection has been closed */
-
-  return -1;  /* connection status unknown */
-}
-
-#endif /* USE_QSOSSL */
diff --git a/lib/rawstr.c b/lib/rawstr.c
deleted file mode 100644 (file)
index 17fd1f3..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#include "curl_rawstr.h"
-
-/* Portable, consistent toupper (remember EBCDIC). Do not use toupper() because
-   its behavior is altered by the current locale. */
-char Curl_raw_toupper(char in)
-{
-  switch (in) {
-  case 'a':
-    return 'A';
-  case 'b':
-    return 'B';
-  case 'c':
-    return 'C';
-  case 'd':
-    return 'D';
-  case 'e':
-    return 'E';
-  case 'f':
-    return 'F';
-  case 'g':
-    return 'G';
-  case 'h':
-    return 'H';
-  case 'i':
-    return 'I';
-  case 'j':
-    return 'J';
-  case 'k':
-    return 'K';
-  case 'l':
-    return 'L';
-  case 'm':
-    return 'M';
-  case 'n':
-    return 'N';
-  case 'o':
-    return 'O';
-  case 'p':
-    return 'P';
-  case 'q':
-    return 'Q';
-  case 'r':
-    return 'R';
-  case 's':
-    return 'S';
-  case 't':
-    return 'T';
-  case 'u':
-    return 'U';
-  case 'v':
-    return 'V';
-  case 'w':
-    return 'W';
-  case 'x':
-    return 'X';
-  case 'y':
-    return 'Y';
-  case 'z':
-    return 'Z';
-  }
-  return in;
-}
-
-/*
- * Curl_raw_equal() is for doing "raw" case insensitive strings. This is meant
- * to be locale independent and only compare strings we know are safe for
- * this.  See http://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/ for
- * some further explanation to why this function is necessary.
- *
- * The function is capable of comparing a-z case insensitively even for
- * non-ascii.
- */
-
-int Curl_raw_equal(const char *first, const char *second)
-{
-  while(*first && *second) {
-    if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second))
-      /* get out of the loop as soon as they don't match */
-      break;
-    first++;
-    second++;
-  }
-  /* we do the comparison here (possibly again), just to make sure that if the
-     loop above is skipped because one of the strings reached zero, we must not
-     return this as a successful match */
-  return (Curl_raw_toupper(*first) == Curl_raw_toupper(*second));
-}
-
-int Curl_raw_nequal(const char *first, const char *second, size_t max)
-{
-  while(*first && *second && max) {
-    if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second)) {
-      break;
-    }
-    max--;
-    first++;
-    second++;
-  }
-  if(0 == max)
-    return 1; /* they are equal this far */
-
-  return Curl_raw_toupper(*first) == Curl_raw_toupper(*second);
-}
-
-/* Copy an upper case version of the string from src to dest.  The
- * strings may overlap.  No more than n characters of the string are copied
- * (including any NUL) and the destination string will NOT be
- * NUL-terminated if that limit is reached.
- */
-void Curl_strntoupper(char *dest, const char *src, size_t n)
-{
-  if(n < 1)
-    return;
-
-  do {
-    *dest++ = Curl_raw_toupper(*src);
-  } while(*src++ && --n);
-}
diff --git a/lib/rtsp.c b/lib/rtsp.c
deleted file mode 100644 (file)
index 71e434c..0000000
+++ /dev/null
@@ -1,807 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifndef CURL_DISABLE_RTSP
-
-#include "curl_urldata.h"
-#include <curl/curl.h>
-#include "curl_transfer.h"
-#include "curl_sendf.h"
-#include "curl_multiif.h"
-#include "curl_http.h"
-#include "curl_url.h"
-#include "curl_progress.h"
-#include "curl_rtsp.h"
-#include "curl_rawstr.h"
-#include "curl_memory.h"
-#include "curl_select.h"
-#include "curl_connect.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/*
- * TODO (general)
- *  -incoming server requests
- *      -server CSeq counter
- *  -digest authentication
- *  -connect thru proxy
- *  -pipelining?
- */
-
-
-#define RTP_PKT_CHANNEL(p)   ((int)((unsigned char)((p)[1])))
-
-#define RTP_PKT_LENGTH(p)  ((((int)((unsigned char)((p)[2]))) << 8) | \
-                             ((int)((unsigned char)((p)[3]))))
-
-/* protocol-specific functions set up to be called by the main engine */
-static CURLcode rtsp_do(struct connectdata *conn, bool *done);
-static CURLcode rtsp_done(struct connectdata *conn, CURLcode, bool premature);
-static CURLcode rtsp_connect(struct connectdata *conn, bool *done);
-static CURLcode rtsp_disconnect(struct connectdata *conn, bool dead);
-
-static int rtsp_getsock_do(struct connectdata *conn,
-                           curl_socket_t *socks,
-                           int numsocks);
-
-/*
- * Parse and write out any available RTP data.
- *
- * nread: amount of data left after k->str. will be modified if RTP
- *        data is parsed and k->str is moved up
- * readmore: whether or not the RTP parser needs more data right away
- */
-static CURLcode rtsp_rtp_readwrite(struct SessionHandle *data,
-                                   struct connectdata *conn,
-                                   ssize_t *nread,
-                                   bool *readmore);
-
-
-/* this returns the socket to wait for in the DO and DOING state for the multi
-   interface and then we're always _sending_ a request and thus we wait for
-   the single socket to become writable only */
-static int rtsp_getsock_do(struct connectdata *conn,
-                           curl_socket_t *socks,
-                           int numsocks)
-{
-  /* write mode */
-  (void)numsocks; /* unused, we trust it to be at least 1 */
-  socks[0] = conn->sock[FIRSTSOCKET];
-  return GETSOCK_WRITESOCK(0);
-}
-
-static
-CURLcode rtp_client_write(struct connectdata *conn, char *ptr, size_t len);
-
-
-/*
- * RTSP handler interface.
- */
-const struct Curl_handler Curl_handler_rtsp = {
-  "RTSP",                               /* scheme */
-  ZERO_NULL,                            /* setup_connection */
-  rtsp_do,                              /* do_it */
-  rtsp_done,                            /* done */
-  ZERO_NULL,                            /* do_more */
-  rtsp_connect,                         /* connect_it */
-  ZERO_NULL,                            /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  rtsp_getsock_do,                      /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
-  rtsp_disconnect,                      /* disconnect */
-  rtsp_rtp_readwrite,                   /* readwrite */
-  PORT_RTSP,                            /* defport */
-  CURLPROTO_RTSP,                       /* protocol */
-  PROTOPT_NONE                          /* flags */
-};
-
-/*
- * The server may send us RTP data at any point, and RTSPREQ_RECEIVE does not
- * want to block the application forever while receiving a stream. Therefore,
- * we cannot assume that an RTSP socket is dead just because it is readable.
- *
- * Instead, if it is readable, run Curl_getconnectinfo() to peek at the socket
- * and distinguish between closed and data.
- */
-bool Curl_rtsp_connisdead(struct connectdata *check)
-{
-  int sval;
-  bool ret_val = TRUE;
-
-  sval = Curl_socket_ready(check->sock[FIRSTSOCKET], CURL_SOCKET_BAD, 0);
-  if(sval == 0) {
-    /* timeout */
-    ret_val = FALSE;
-  }
-  else if(sval & CURL_CSELECT_ERR) {
-    /* socket is in an error state */
-    ret_val = TRUE;
-  }
-  else if((sval & CURL_CSELECT_IN) && check->data) {
-    /* readable with no error. could be closed or could be alive but we can
-       only check if we have a proper SessionHandle for the connection */
-    curl_socket_t connectinfo = Curl_getconnectinfo(check->data, &check);
-    if(connectinfo != CURL_SOCKET_BAD)
-      ret_val = FALSE;
-  }
-
-  return ret_val;
-}
-
-static CURLcode rtsp_connect(struct connectdata *conn, bool *done)
-{
-  CURLcode httpStatus;
-  struct SessionHandle *data = conn->data;
-
-  httpStatus = Curl_http_connect(conn, done);
-
-  /* Initialize the CSeq if not already done */
-  if(data->state.rtsp_next_client_CSeq == 0)
-    data->state.rtsp_next_client_CSeq = 1;
-  if(data->state.rtsp_next_server_CSeq == 0)
-    data->state.rtsp_next_server_CSeq = 1;
-
-  conn->proto.rtspc.rtp_channel = -1;
-
-  return httpStatus;
-}
-
-static CURLcode rtsp_disconnect(struct connectdata *conn, bool dead)
-{
-  (void) dead;
-  Curl_safefree(conn->proto.rtspc.rtp_buf);
-  return CURLE_OK;
-}
-
-
-static CURLcode rtsp_done(struct connectdata *conn,
-                          CURLcode status, bool premature)
-{
-  struct SessionHandle *data = conn->data;
-  struct RTSP *rtsp = data->state.proto.rtsp;
-  CURLcode httpStatus;
-  long CSeq_sent;
-  long CSeq_recv;
-
-  /* Bypass HTTP empty-reply checks on receive */
-  if(data->set.rtspreq == RTSPREQ_RECEIVE)
-    premature = TRUE;
-
-  httpStatus = Curl_http_done(conn, status, premature);
-
-  if(rtsp) {
-    /* Check the sequence numbers */
-    CSeq_sent = rtsp->CSeq_sent;
-    CSeq_recv = rtsp->CSeq_recv;
-    if((data->set.rtspreq != RTSPREQ_RECEIVE) && (CSeq_sent != CSeq_recv)) {
-      failf(data,
-            "The CSeq of this request %ld did not match the response %ld",
-            CSeq_sent, CSeq_recv);
-      return CURLE_RTSP_CSEQ_ERROR;
-    }
-    else if(data->set.rtspreq == RTSPREQ_RECEIVE &&
-            (conn->proto.rtspc.rtp_channel == -1)) {
-      infof(data, "Got an RTP Receive with a CSeq of %ld\n", CSeq_recv);
-      /* TODO CPC: Server -> Client logic here */
-    }
-  }
-
-  return httpStatus;
-}
-
-static CURLcode rtsp_do(struct connectdata *conn, bool *done)
-{
-  struct SessionHandle *data = conn->data;
-  CURLcode result=CURLE_OK;
-  Curl_RtspReq rtspreq = data->set.rtspreq;
-  struct RTSP *rtsp;
-  struct HTTP *http;
-  Curl_send_buffer *req_buffer;
-  curl_off_t postsize = 0; /* for ANNOUNCE and SET_PARAMETER */
-  curl_off_t putsize = 0; /* for ANNOUNCE and SET_PARAMETER */
-
-  const char *p_request = NULL;
-  const char *p_session_id = NULL;
-  const char *p_accept = NULL;
-  const char *p_accept_encoding = NULL;
-  const char *p_range = NULL;
-  const char *p_referrer = NULL;
-  const char *p_stream_uri = NULL;
-  const char *p_transport = NULL;
-  const char *p_uagent = NULL;
-
-  *done = TRUE;
-
-  Curl_reset_reqproto(conn);
-
-  if(!data->state.proto.rtsp) {
-    /* Only allocate this struct if we don't already have it! */
-
-    rtsp = calloc(1, sizeof(struct RTSP));
-    if(!rtsp)
-      return CURLE_OUT_OF_MEMORY;
-    data->state.proto.rtsp = rtsp;
-  }
-  else {
-    rtsp = data->state.proto.rtsp;
-  }
-
-  http = &(rtsp->http_wrapper);
-  /* Assert that no one has changed the RTSP struct in an evil way */
-  DEBUGASSERT((void *)http == (void *)rtsp);
-
-  rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq;
-  rtsp->CSeq_recv = 0;
-
-  /* Setup the 'p_request' pointer to the proper p_request string
-   * Since all RTSP requests are included here, there is no need to
-   * support custom requests like HTTP.
-   **/
-  DEBUGASSERT((rtspreq > RTSPREQ_NONE && rtspreq < RTSPREQ_LAST));
-  data->set.opt_no_body = TRUE; /* most requests don't contain a body */
-  switch(rtspreq) {
-  case RTSPREQ_NONE:
-    failf(data, "Got invalid RTSP request: RTSPREQ_NONE");
-    return CURLE_BAD_FUNCTION_ARGUMENT;
-  case RTSPREQ_OPTIONS:
-    p_request = "OPTIONS";
-    break;
-  case RTSPREQ_DESCRIBE:
-    p_request = "DESCRIBE";
-    data->set.opt_no_body = FALSE;
-    break;
-  case RTSPREQ_ANNOUNCE:
-    p_request = "ANNOUNCE";
-    break;
-  case RTSPREQ_SETUP:
-    p_request = "SETUP";
-    break;
-  case RTSPREQ_PLAY:
-    p_request = "PLAY";
-    break;
-  case RTSPREQ_PAUSE:
-    p_request = "PAUSE";
-    break;
-  case RTSPREQ_TEARDOWN:
-    p_request = "TEARDOWN";
-    break;
-  case RTSPREQ_GET_PARAMETER:
-    /* GET_PARAMETER's no_body status is determined later */
-    p_request = "GET_PARAMETER";
-    data->set.opt_no_body = FALSE;
-    break;
-  case RTSPREQ_SET_PARAMETER:
-    p_request = "SET_PARAMETER";
-    break;
-  case RTSPREQ_RECORD:
-    p_request = "RECORD";
-    break;
-  case RTSPREQ_RECEIVE:
-    p_request = "";
-    /* Treat interleaved RTP as body*/
-    data->set.opt_no_body = FALSE;
-    break;
-  case RTSPREQ_LAST:
-    failf(data, "Got invalid RTSP request: RTSPREQ_LAST");
-    return CURLE_BAD_FUNCTION_ARGUMENT;
-  }
-
-  if(rtspreq == RTSPREQ_RECEIVE) {
-    Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
-                        &http->readbytecount, -1, NULL);
-
-    return result;
-  }
-
-  p_session_id = data->set.str[STRING_RTSP_SESSION_ID];
-  if(!p_session_id &&
-     (rtspreq & ~(RTSPREQ_OPTIONS | RTSPREQ_DESCRIBE | RTSPREQ_SETUP))) {
-    failf(data, "Refusing to issue an RTSP request [%s] without a session ID.",
-          p_request ? p_request : "");
-    return CURLE_BAD_FUNCTION_ARGUMENT;
-  }
-
-  /* TODO: auth? */
-  /* TODO: proxy? */
-
-  /* Stream URI. Default to server '*' if not specified */
-  if(data->set.str[STRING_RTSP_STREAM_URI]) {
-    p_stream_uri = data->set.str[STRING_RTSP_STREAM_URI];
-  }
-  else {
-    p_stream_uri = "*";
-  }
-
-  /* Transport Header for SETUP requests */
-  p_transport = Curl_checkheaders(data, "Transport:");
-  if(rtspreq == RTSPREQ_SETUP && !p_transport) {
-    /* New Transport: setting? */
-    if(data->set.str[STRING_RTSP_TRANSPORT]) {
-      Curl_safefree(conn->allocptr.rtsp_transport);
-
-      conn->allocptr.rtsp_transport =
-        aprintf("Transport: %s\r\n",
-                data->set.str[STRING_RTSP_TRANSPORT]);
-      if(!conn->allocptr.rtsp_transport)
-        return CURLE_OUT_OF_MEMORY;
-    }
-    else {
-      failf(data,
-            "Refusing to issue an RTSP SETUP without a Transport: header.");
-      return CURLE_BAD_FUNCTION_ARGUMENT;
-    }
-
-    p_transport = conn->allocptr.rtsp_transport;
-  }
-
-  /* Accept Headers for DESCRIBE requests */
-  if(rtspreq == RTSPREQ_DESCRIBE) {
-    /* Accept Header */
-    p_accept = Curl_checkheaders(data, "Accept:")?
-      NULL:"Accept: application/sdp\r\n";
-
-    /* Accept-Encoding header */
-    if(!Curl_checkheaders(data, "Accept-Encoding:") &&
-       data->set.str[STRING_ENCODING]) {
-      Curl_safefree(conn->allocptr.accept_encoding);
-      conn->allocptr.accept_encoding =
-        aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
-
-      if(!conn->allocptr.accept_encoding)
-        return CURLE_OUT_OF_MEMORY;
-
-      p_accept_encoding = conn->allocptr.accept_encoding;
-    }
-  }
-
-  /* The User-Agent string might have been allocated in curl_url.c already,
-     because it might have been used in the proxy connect, but if we have
-     got a header with the user-agent string specified, we erase the
-     previously made string here. */
-  if(Curl_checkheaders(data, "User-Agent:") && conn->allocptr.uagent) {
-    Curl_safefree(conn->allocptr.uagent);
-    conn->allocptr.uagent = NULL;
-  }
-  else if(!Curl_checkheaders(data, "User-Agent:") &&
-          data->set.str[STRING_USERAGENT]) {
-    p_uagent = conn->allocptr.uagent;
-  }
-
-  /* Referrer */
-  Curl_safefree(conn->allocptr.ref);
-  if(data->change.referer && !Curl_checkheaders(data, "Referer:"))
-    conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer);
-  else
-    conn->allocptr.ref = NULL;
-
-  p_referrer = conn->allocptr.ref;
-
-  /*
-   * Range Header
-   * Only applies to PLAY, PAUSE, RECORD
-   *
-   * Go ahead and use the Range stuff supplied for HTTP
-   */
-  if(data->state.use_range &&
-     (rtspreq  & (RTSPREQ_PLAY | RTSPREQ_PAUSE | RTSPREQ_RECORD))) {
-
-    /* Check to see if there is a range set in the custom headers */
-    if(!Curl_checkheaders(data, "Range:") && data->state.range) {
-      Curl_safefree(conn->allocptr.rangeline);
-      conn->allocptr.rangeline = aprintf("Range: %s\r\n", data->state.range);
-      p_range = conn->allocptr.rangeline;
-    }
-  }
-
-  /*
-   * Sanity check the custom headers
-   */
-  if(Curl_checkheaders(data, "CSeq:")) {
-    failf(data, "CSeq cannot be set as a custom header.");
-    return CURLE_RTSP_CSEQ_ERROR;
-  }
-  if(Curl_checkheaders(data, "Session:")) {
-    failf(data, "Session ID cannot be set as a custom header.");
-    return CURLE_BAD_FUNCTION_ARGUMENT;
-  }
-
-  /* Initialize a dynamic send buffer */
-  req_buffer = Curl_add_buffer_init();
-
-  if(!req_buffer)
-    return CURLE_OUT_OF_MEMORY;
-
-  result =
-    Curl_add_bufferf(req_buffer,
-                     "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */
-                     "CSeq: %ld\r\n", /* CSeq */
-                     (p_request ? p_request : ""), p_stream_uri,
-                     rtsp->CSeq_sent);
-  if(result)
-    return result;
-
-  /*
-   * Rather than do a normal alloc line, keep the session_id unformatted
-   * to make comparison easier
-   */
-  if(p_session_id) {
-    result = Curl_add_bufferf(req_buffer, "Session: %s\r\n", p_session_id);
-    if(result)
-      return result;
-  }
-
-  /*
-   * Shared HTTP-like options
-   */
-  result = Curl_add_bufferf(req_buffer,
-                            "%s" /* transport */
-                            "%s" /* accept */
-                            "%s" /* accept-encoding */
-                            "%s" /* range */
-                            "%s" /* referrer */
-                            "%s" /* user-agent */
-                            ,
-                            p_transport ? p_transport : "",
-                            p_accept ? p_accept : "",
-                            p_accept_encoding ? p_accept_encoding : "",
-                            p_range ? p_range : "",
-                            p_referrer ? p_referrer : "",
-                            p_uagent ? p_uagent : "");
-  if(result)
-    return result;
-
-  if((rtspreq == RTSPREQ_SETUP) || (rtspreq == RTSPREQ_DESCRIBE)) {
-    result = Curl_add_timecondition(data, req_buffer);
-    if(result)
-      return result;
-  }
-
-  result = Curl_add_custom_headers(conn, req_buffer);
-  if(result)
-    return result;
-
-  if(rtspreq == RTSPREQ_ANNOUNCE ||
-     rtspreq == RTSPREQ_SET_PARAMETER ||
-     rtspreq == RTSPREQ_GET_PARAMETER) {
-
-    if(data->set.upload) {
-      putsize = data->set.infilesize;
-      data->set.httpreq = HTTPREQ_PUT;
-
-    }
-    else {
-      postsize = (data->set.postfieldsize != -1)?
-        data->set.postfieldsize:
-        (data->set.postfields? (curl_off_t)strlen(data->set.postfields):0);
-      data->set.httpreq = HTTPREQ_POST;
-    }
-
-    if(putsize > 0 || postsize > 0) {
-      /* As stated in the http comments, it is probably not wise to
-       * actually set a custom Content-Length in the headers */
-      if(!Curl_checkheaders(data, "Content-Length:")) {
-        result = Curl_add_bufferf(req_buffer,
-            "Content-Length: %" FORMAT_OFF_T"\r\n",
-            (data->set.upload ? putsize : postsize));
-        if(result)
-          return result;
-      }
-
-      if(rtspreq == RTSPREQ_SET_PARAMETER ||
-         rtspreq == RTSPREQ_GET_PARAMETER) {
-        if(!Curl_checkheaders(data, "Content-Type:")) {
-          result = Curl_add_bufferf(req_buffer,
-              "Content-Type: text/parameters\r\n");
-          if(result)
-            return result;
-        }
-      }
-
-      if(rtspreq == RTSPREQ_ANNOUNCE) {
-        if(!Curl_checkheaders(data, "Content-Type:")) {
-          result = Curl_add_bufferf(req_buffer,
-              "Content-Type: application/sdp\r\n");
-          if(result)
-            return result;
-        }
-      }
-
-      data->state.expect100header = FALSE; /* RTSP posts are simple/small */
-    }
-    else if(rtspreq == RTSPREQ_GET_PARAMETER) {
-      /* Check for an empty GET_PARAMETER (heartbeat) request */
-      data->set.httpreq = HTTPREQ_HEAD;
-      data->set.opt_no_body = TRUE;
-    }
-  }
-
-  /* RTSP never allows chunked transfer */
-  data->req.forbidchunk = TRUE;
-  /* Finish the request buffer */
-  result = Curl_add_buffer(req_buffer, "\r\n", 2);
-  if(result)
-    return result;
-
-  if(postsize > 0) {
-    result = Curl_add_buffer(req_buffer, data->set.postfields,
-                             (size_t)postsize);
-    if(result)
-      return result;
-  }
-
-  /* issue the request */
-  result = Curl_add_buffer_send(req_buffer, conn,
-                                &data->info.request_size, 0, FIRSTSOCKET);
-  if(result) {
-    failf(data, "Failed sending RTSP request");
-    return result;
-  }
-
-  Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount,
-                      putsize?FIRSTSOCKET:-1,
-                      putsize?&http->writebytecount:NULL);
-
-  /* Increment the CSeq on success */
-  data->state.rtsp_next_client_CSeq++;
-
-  if(http->writebytecount) {
-    /* if a request-body has been sent off, we make sure this progress is
-       noted properly */
-    Curl_pgrsSetUploadCounter(data, http->writebytecount);
-    if(Curl_pgrsUpdate(conn))
-      result = CURLE_ABORTED_BY_CALLBACK;
-  }
-
-  return result;
-}
-
-
-static CURLcode rtsp_rtp_readwrite(struct SessionHandle *data,
-                                   struct connectdata *conn,
-                                   ssize_t *nread,
-                                   bool *readmore) {
-  struct SingleRequest *k = &data->req;
-  struct rtsp_conn *rtspc = &(conn->proto.rtspc);
-
-  char *rtp; /* moving pointer to rtp data */
-  ssize_t rtp_dataleft; /* how much data left to parse in this round */
-  char *scratch;
-  CURLcode result;
-
-  if(rtspc->rtp_buf) {
-    /* There was some leftover data the last time. Merge buffers */
-    char *newptr = realloc(rtspc->rtp_buf, rtspc->rtp_bufsize + *nread);
-    if(!newptr) {
-      Curl_safefree(rtspc->rtp_buf);
-      rtspc->rtp_buf = NULL;
-      rtspc->rtp_bufsize = 0;
-      return CURLE_OUT_OF_MEMORY;
-    }
-    rtspc->rtp_buf = newptr;
-    memcpy(rtspc->rtp_buf + rtspc->rtp_bufsize, k->str, *nread);
-    rtspc->rtp_bufsize += *nread;
-    rtp = rtspc->rtp_buf;
-    rtp_dataleft = rtspc->rtp_bufsize;
-  }
-  else {
-    /* Just parse the request buffer directly */
-    rtp = k->str;
-    rtp_dataleft = *nread;
-  }
-
-  while((rtp_dataleft > 0) &&
-        (rtp[0] == '$')) {
-    if(rtp_dataleft > 4) {
-      int rtp_length;
-
-      /* Parse the header */
-      /* The channel identifier immediately follows and is 1 byte */
-      rtspc->rtp_channel = RTP_PKT_CHANNEL(rtp);
-
-      /* The length is two bytes */
-      rtp_length = RTP_PKT_LENGTH(rtp);
-
-      if(rtp_dataleft < rtp_length + 4) {
-        /* Need more - incomplete payload*/
-        *readmore = TRUE;
-        break;
-      }
-      else {
-        /* We have the full RTP interleaved packet
-         * Write out the header including the leading '$' */
-        DEBUGF(infof(data, "RTP write channel %d rtp_length %d\n",
-              rtspc->rtp_channel, rtp_length));
-        result = rtp_client_write(conn, &rtp[0], rtp_length + 4);
-        if(result) {
-          failf(data, "Got an error writing an RTP packet");
-          *readmore = FALSE;
-          Curl_safefree(rtspc->rtp_buf);
-          rtspc->rtp_buf = NULL;
-          rtspc->rtp_bufsize = 0;
-          return result;
-        }
-
-        /* Move forward in the buffer */
-        rtp_dataleft -= rtp_length + 4;
-        rtp += rtp_length + 4;
-
-        if(data->set.rtspreq == RTSPREQ_RECEIVE) {
-          /* If we are in a passive receive, give control back
-           * to the app as often as we can.
-           */
-          k->keepon &= ~KEEP_RECV;
-        }
-      }
-    }
-    else {
-      /* Need more - incomplete header */
-      *readmore = TRUE;
-      break;
-    }
-  }
-
-  if(rtp_dataleft != 0 && rtp[0] == '$') {
-    DEBUGF(infof(data, "RTP Rewinding %zu %s\n", rtp_dataleft,
-          *readmore ? "(READMORE)" : ""));
-
-    /* Store the incomplete RTP packet for a "rewind" */
-    scratch = malloc(rtp_dataleft);
-    if(!scratch) {
-      Curl_safefree(rtspc->rtp_buf);
-      rtspc->rtp_buf = NULL;
-      rtspc->rtp_bufsize = 0;
-      return CURLE_OUT_OF_MEMORY;
-    }
-    memcpy(scratch, rtp, rtp_dataleft);
-    Curl_safefree(rtspc->rtp_buf);
-    rtspc->rtp_buf = scratch;
-    rtspc->rtp_bufsize = rtp_dataleft;
-
-    /* As far as the transfer is concerned, this data is consumed */
-    *nread = 0;
-    return CURLE_OK;
-  }
-  else {
-    /* Fix up k->str to point just after the last RTP packet */
-    k->str += *nread - rtp_dataleft;
-
-    /* either all of the data has been read or...
-     * rtp now points at the next byte to parse
-     */
-    if(rtp_dataleft > 0)
-      DEBUGASSERT(k->str[0] == rtp[0]);
-
-    DEBUGASSERT(rtp_dataleft <= *nread); /* sanity check */
-
-    *nread = rtp_dataleft;
-  }
-
-  /* If we get here, we have finished with the leftover/merge buffer */
-  Curl_safefree(rtspc->rtp_buf);
-  rtspc->rtp_buf = NULL;
-  rtspc->rtp_bufsize = 0;
-
-  return CURLE_OK;
-}
-
-static
-CURLcode rtp_client_write(struct connectdata *conn, char *ptr, size_t len)
-{
-  struct SessionHandle *data = conn->data;
-  size_t wrote;
-  curl_write_callback writeit;
-
-  if(len == 0) {
-    failf (data, "Cannot write a 0 size RTP packet.");
-    return CURLE_WRITE_ERROR;
-  }
-
-  writeit = data->set.fwrite_rtp?data->set.fwrite_rtp:data->set.fwrite_func;
-  wrote = writeit(ptr, 1, len, data->set.rtp_out);
-
-  if(CURL_WRITEFUNC_PAUSE == wrote) {
-    failf (data, "Cannot pause RTP");
-    return CURLE_WRITE_ERROR;
-  }
-
-  if(wrote != len) {
-    failf (data, "Failed writing RTP data");
-    return CURLE_WRITE_ERROR;
-  }
-
-  return CURLE_OK;
-}
-
-CURLcode Curl_rtsp_parseheader(struct connectdata *conn,
-                               char *header)
-{
-  struct SessionHandle *data = conn->data;
-  long CSeq = 0;
-
-  if(checkprefix("CSeq:", header)) {
-    /* Store the received CSeq. Match is verified in rtsp_done */
-    int nc = sscanf(&header[4], ": %ld", &CSeq);
-    if(nc == 1) {
-      data->state.proto.rtsp->CSeq_recv = CSeq; /* mark the request */
-      data->state.rtsp_CSeq_recv = CSeq; /* update the handle */
-    }
-    else {
-      failf(data, "Unable to read the CSeq header: [%s]", header);
-      return CURLE_RTSP_CSEQ_ERROR;
-    }
-  }
-  else if(checkprefix("Session:", header)) {
-    char *start;
-
-    /* Find the first non-space letter */
-    start = header + 9;
-    while(*start && ISSPACE(*start))
-      start++;
-
-    if(!*start) {
-      failf(data, "Got a blank Session ID");
-    }
-    else if(data->set.str[STRING_RTSP_SESSION_ID]) {
-      /* If the Session ID is set, then compare */
-      if(strncmp(start, data->set.str[STRING_RTSP_SESSION_ID],
-                 strlen(data->set.str[STRING_RTSP_SESSION_ID]))  != 0) {
-        failf(data, "Got RTSP Session ID Line [%s], but wanted ID [%s]",
-              start, data->set.str[STRING_RTSP_SESSION_ID]);
-        return CURLE_RTSP_SESSION_ERROR;
-      }
-    }
-    else {
-      /* If the Session ID is not set, and we find it in a response, then
-         set it */
-
-      /* The session ID can be an alphanumeric or a 'safe' character
-       *
-       * RFC 2326 15.1 Base Syntax:
-       * safe =  "\$" | "-" | "_" | "." | "+"
-       * */
-      char *end = start;
-      while(*end &&
-            (ISALNUM(*end) || *end == '-' || *end == '_' || *end == '.' ||
-             *end == '+' ||
-             (*end == '\\' && *(end + 1) && *(end + 1) == '$' && (++end, 1))))
-        end++;
-
-      /* Copy the id substring into a new buffer */
-      data->set.str[STRING_RTSP_SESSION_ID] = malloc(end - start + 1);
-      if(data->set.str[STRING_RTSP_SESSION_ID] == NULL)
-        return CURLE_OUT_OF_MEMORY;
-      memcpy(data->set.str[STRING_RTSP_SESSION_ID], start, end - start);
-      (data->set.str[STRING_RTSP_SESSION_ID])[end - start] = '\0';
-    }
-  }
-  return CURLE_OK;
-}
-
-#endif /* CURL_DISABLE_RTSP */
diff --git a/lib/security.c b/lib/security.c
deleted file mode 100644 (file)
index b7544ff..0000000
+++ /dev/null
@@ -1,604 +0,0 @@
-/* This source code was modified by Martin Hedenfalk <mhe@stacken.kth.se> for
- * use in Curl. His latest changes were done 2000-09-18.
- *
- * It has since been patched and modified a lot by Daniel Stenberg
- * <daniel@haxx.se> to make it better applied to curl conditions, and to make
- * it not use globals, pollute name space and more. This source code awaits a
- * rewrite to work around the paragraph 2 in the BSD licenses as explained
- * below.
- *
- * Copyright (c) 1998, 1999 Kungliga Tekniska Högskolan
- * (Royal Institute of Technology, Stockholm, Sweden).
- *
- * Copyright (C) 2001 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * 3. Neither the name of the Institute nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.  */
-
-#include "curl_setup.h"
-
-#ifndef CURL_DISABLE_FTP
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
-
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-
-#ifdef HAVE_LIMITS_H
-#include <limits.h>
-#endif
-
-#include "curl_urldata.h"
-#include "curl_base64.h"
-#include "curl_memory.h"
-#include "curl_krb4.h"
-#include "curl_ftp.h"
-#include "curl_sendf.h"
-#include "curl_rawstr.h"
-#include "curl_warnless.h"
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-static const struct {
-  enum protection_level level;
-  const char *name;
-} level_names[] = {
-  { PROT_CLEAR, "clear" },
-  { PROT_SAFE, "safe" },
-  { PROT_CONFIDENTIAL, "confidential" },
-  { PROT_PRIVATE, "private" }
-};
-
-static enum protection_level
-name_to_level(const char *name)
-{
-  int i;
-  for(i = 0; i < (int)sizeof(level_names)/(int)sizeof(level_names[0]); i++)
-    if(checkprefix(name, level_names[i].name))
-      return level_names[i].level;
-  return PROT_NONE;
-}
-
-/* Convert a protocol |level| to its char representation.
-   We take an int to catch programming mistakes. */
-static char level_to_char(int level) {
-  switch(level) {
-  case PROT_CLEAR:
-    return 'C';
-  case PROT_SAFE:
-    return 'S';
-  case PROT_CONFIDENTIAL:
-    return 'E';
-  case PROT_PRIVATE:
-    return 'P';
-  case PROT_CMD:
-    /* Fall through */
-  default:
-    /* Those 2 cases should not be reached! */
-    break;
-  }
-  DEBUGASSERT(0);
-  /* Default to the most secure alternative. */
-  return 'P';
-}
-
-static const struct Curl_sec_client_mech * const mechs[] = {
-#if defined(HAVE_GSSAPI)
-  &Curl_krb5_client_mech,
-#endif
-#if defined(HAVE_KRB4)
-  &Curl_krb4_client_mech,
-#endif
-  NULL
-};
-
-/* Send an FTP command defined by |message| and the optional arguments. The
-   function returns the ftp_code. If an error occurs, -1 is returned. */
-static int ftp_send_command(struct connectdata *conn, const char *message, ...)
-{
-  int ftp_code;
-  ssize_t nread;
-  va_list args;
-  char print_buffer[50];
-
-  va_start(args, message);
-  vsnprintf(print_buffer, sizeof(print_buffer), message, args);
-  va_end(args);
-
-  if(Curl_ftpsendf(conn, print_buffer) != CURLE_OK) {
-    ftp_code = -1;
-  }
-  else {
-    if(Curl_GetFTPResponse(&nread, conn, &ftp_code) != CURLE_OK)
-      ftp_code = -1;
-  }
-
-  (void)nread; /* Unused */
-  return ftp_code;
-}
-
-/* Read |len| from the socket |fd| and store it in |to|. Return a CURLcode
-   saying whether an error occurred or CURLE_OK if |len| was read. */
-static CURLcode
-socket_read(curl_socket_t fd, void *to, size_t len)
-{
-  char *to_p = to;
-  CURLcode code;
-  ssize_t nread;
-
-  while(len > 0) {
-    code = Curl_read_plain(fd, to_p, len, &nread);
-    if(code == CURLE_OK) {
-      len -= nread;
-      to_p += nread;
-    }
-    else {
-      /* FIXME: We are doing a busy wait */
-      if(code == CURLE_AGAIN)
-        continue;
-      return code;
-    }
-  }
-  return CURLE_OK;
-}
-
-
-/* Write |len| bytes from the buffer |to| to the socket |fd|. Return a
-   CURLcode saying whether an error occurred or CURLE_OK if |len| was
-   written. */
-static CURLcode
-socket_write(struct connectdata *conn, curl_socket_t fd, const void *to,
-             size_t len)
-{
-  const char *to_p = to;
-  CURLcode code;
-  ssize_t written;
-
-  while(len > 0) {
-    code = Curl_write_plain(conn, fd, to_p, len, &written);
-    if(code == CURLE_OK) {
-      len -= written;
-      to_p += written;
-    }
-    else {
-      /* FIXME: We are doing a busy wait */
-      if(code == CURLE_AGAIN)
-        continue;
-      return code;
-    }
-  }
-  return CURLE_OK;
-}
-
-static CURLcode read_data(struct connectdata *conn,
-                          curl_socket_t fd,
-                          struct krb4buffer *buf)
-{
-  int len;
-  void* tmp;
-  CURLcode ret;
-
-  ret = socket_read(fd, &len, sizeof(len));
-  if(ret != CURLE_OK)
-    return ret;
-
-  len = ntohl(len);
-  tmp = realloc(buf->data, len);
-  if(tmp == NULL)
-    return CURLE_OUT_OF_MEMORY;
-
-  buf->data = tmp;
-  ret = socket_read(fd, buf->data, len);
-  if(ret != CURLE_OK)
-    return ret;
-  buf->size = conn->mech->decode(conn->app_data, buf->data, len,
-                                 conn->data_prot, conn);
-  buf->index = 0;
-  return CURLE_OK;
-}
-
-static size_t
-buffer_read(struct krb4buffer *buf, void *data, size_t len)
-{
-  if(buf->size - buf->index < len)
-    len = buf->size - buf->index;
-  memcpy(data, (char*)buf->data + buf->index, len);
-  buf->index += len;
-  return len;
-}
-
-/* Matches Curl_recv signature */
-static ssize_t sec_recv(struct connectdata *conn, int sockindex,
-                        char *buffer, size_t len, CURLcode *err)
-{
-  size_t bytes_read;
-  size_t total_read = 0;
-  curl_socket_t fd = conn->sock[sockindex];
-
-  *err = CURLE_OK;
-
-  /* Handle clear text response. */
-  if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR)
-      return read(fd, buffer, len);
-
-  if(conn->in_buffer.eof_flag) {
-    conn->in_buffer.eof_flag = 0;
-    return 0;
-  }
-
-  bytes_read = buffer_read(&conn->in_buffer, buffer, len);
-  len -= bytes_read;
-  total_read += bytes_read;
-  buffer += bytes_read;
-
-  while(len > 0) {
-    if(read_data(conn, fd, &conn->in_buffer) != CURLE_OK)
-      return -1;
-    if(conn->in_buffer.size == 0) {
-      if(bytes_read > 0)
-        conn->in_buffer.eof_flag = 1;
-      return bytes_read;
-    }
-    bytes_read = buffer_read(&conn->in_buffer, buffer, len);
-    len -= bytes_read;
-    total_read += bytes_read;
-    buffer += bytes_read;
-  }
-  /* FIXME: Check for overflow */
-  return total_read;
-}
-
-/* Send |length| bytes from |from| to the |fd| socket taking care of encoding
-   and negociating with the server. |from| can be NULL. */
-/* FIXME: We don't check for errors nor report any! */
-static void do_sec_send(struct connectdata *conn, curl_socket_t fd,
-                        const char *from, int length)
-{
-  int bytes, htonl_bytes; /* 32-bit integers for htonl */
-  char *buffer = NULL;
-  char *cmd_buffer;
-  size_t cmd_size = 0;
-  CURLcode error;
-  enum protection_level prot_level = conn->data_prot;
-  bool iscmd = (prot_level == PROT_CMD)?TRUE:FALSE;
-
-  DEBUGASSERT(prot_level > PROT_NONE && prot_level < PROT_LAST);
-
-  if(iscmd) {
-    if(!strncmp(from, "PASS ", 5) || !strncmp(from, "ACCT ", 5))
-      prot_level = PROT_PRIVATE;
-    else
-      prot_level = conn->command_prot;
-  }
-  bytes = conn->mech->encode(conn->app_data, from, length, prot_level,
-                             (void**)&buffer, conn);
-  if(!buffer || bytes <= 0)
-    return; /* error */
-
-  if(iscmd) {
-    error = Curl_base64_encode(conn->data, buffer, curlx_sitouz(bytes),
-                               &cmd_buffer, &cmd_size);
-    if(error) {
-      free(buffer);
-      return; /* error */
-    }
-    if(cmd_size > 0) {
-      static const char *enc = "ENC ";
-      static const char *mic = "MIC ";
-      if(prot_level == PROT_PRIVATE)
-        socket_write(conn, fd, enc, 4);
-      else
-        socket_write(conn, fd, mic, 4);
-
-      socket_write(conn, fd, cmd_buffer, cmd_size);
-      socket_write(conn, fd, "\r\n", 2);
-      infof(conn->data, "Send: %s%s\n", prot_level == PROT_PRIVATE?enc:mic,
-            cmd_buffer);
-      free(cmd_buffer);
-    }
-  }
-  else {
-    htonl_bytes = htonl(bytes);
-    socket_write(conn, fd, &htonl_bytes, sizeof(htonl_bytes));
-    socket_write(conn, fd, buffer, curlx_sitouz(bytes));
-  }
-  free(buffer);
-}
-
-static ssize_t sec_write(struct connectdata *conn, curl_socket_t fd,
-                         const char *buffer, size_t length)
-{
-  /* FIXME: Check for overflow */
-  ssize_t tx = 0, len = conn->buffer_size;
-
-  len -= conn->mech->overhead(conn->app_data, conn->data_prot,
-                              curlx_sztosi(len));
-  if(len <= 0)
-    len = length;
-  while(length) {
-    if(len >= 0 || length < (size_t)len) {
-      /* FIXME: Check for overflow. */
-      len = length;
-    }
-    do_sec_send(conn, fd, buffer, curlx_sztosi(len));
-    length -= len;
-    buffer += len;
-    tx += len;
-  }
-  return tx;
-}
-
-/* Matches Curl_send signature */
-static ssize_t sec_send(struct connectdata *conn, int sockindex,
-                        const void *buffer, size_t len, CURLcode *err)
-{
-  curl_socket_t fd = conn->sock[sockindex];
-  *err = CURLE_OK;
-  return sec_write(conn, fd, buffer, len);
-}
-
-int Curl_sec_read_msg(struct connectdata *conn, char *buffer,
-                      enum protection_level level)
-{
-  /* decoded_len should be size_t or ssize_t but conn->mech->decode returns an
-     int */
-  int decoded_len;
-  char *buf;
-  int ret_code;
-  size_t decoded_sz = 0;
-  CURLcode error;
-
-  DEBUGASSERT(level > PROT_NONE && level < PROT_LAST);
-
-  error = Curl_base64_decode(buffer + 4, (unsigned char **)&buf, &decoded_sz);
-  if(error || decoded_sz == 0)
-    return -1;
-
-  if(decoded_sz > (size_t)INT_MAX) {
-    free(buf);
-    return -1;
-  }
-  decoded_len = curlx_uztosi(decoded_sz);
-
-  decoded_len = conn->mech->decode(conn->app_data, buf, decoded_len,
-                                   level, conn);
-  if(decoded_len <= 0) {
-    free(buf);
-    return -1;
-  }
-
-  if(conn->data->set.verbose) {
-    buf[decoded_len] = '\n';
-    Curl_debug(conn->data, CURLINFO_HEADER_IN, buf, decoded_len + 1, conn);
-  }
-
-  buf[decoded_len] = '\0';
-  DEBUGASSERT(decoded_len > 3);
-  if(buf[3] == '-')
-    ret_code = 0;
-  else {
-    /* Check for error? */
-    sscanf(buf, "%d", &ret_code);
-  }
-
-  if(buf[decoded_len - 1] == '\n')
-    buf[decoded_len - 1] = '\0';
-  /* FIXME: Is |buffer| length always greater than |decoded_len|? */
-  strcpy(buffer, buf);
-  free(buf);
-  return ret_code;
-}
-
-/* FIXME: The error code returned here is never checked. */
-static int sec_set_protection_level(struct connectdata *conn)
-{
-  int code;
-  char* pbsz;
-  static unsigned int buffer_size = 1 << 20; /* 1048576 */
-  enum protection_level level = conn->request_data_prot;
-
-  DEBUGASSERT(level > PROT_NONE && level < PROT_LAST);
-
-  if(!conn->sec_complete) {
-    infof(conn->data, "Trying to change the protection level after the"
-                      "completion of the data exchange.\n");
-    return -1;
-  }
-
-  /* Bail out if we try to set up the same level */
-  if(conn->data_prot == level)
-    return 0;
-
-  if(level) {
-    code = ftp_send_command(conn, "PBSZ %u", buffer_size);
-    if(code < 0)
-      return -1;
-
-    if(code/100 != 2) {
-      failf(conn->data, "Failed to set the protection's buffer size.");
-      return -1;
-    }
-    conn->buffer_size = buffer_size;
-
-    pbsz = strstr(conn->data->state.buffer, "PBSZ=");
-    if(pbsz) {
-      /* FIXME: Checks for errors in sscanf? */
-      sscanf(pbsz, "PBSZ=%u", &buffer_size);
-      if(buffer_size < conn->buffer_size)
-        conn->buffer_size = buffer_size;
-    }
-  }
-
-  /* Now try to negiociate the protection level. */
-  code = ftp_send_command(conn, "PROT %c", level_to_char(level));
-
-  if(code < 0)
-    return -1;
-
-  if(code/100 != 2) {
-    failf(conn->data, "Failed to set the protection level.");
-    return -1;
-  }
-
-  conn->data_prot = level;
-  if(level == PROT_PRIVATE)
-    conn->command_prot = level;
-
-  return 0;
-}
-
-int
-Curl_sec_request_prot(struct connectdata *conn, const char *level)
-{
-  enum protection_level l = name_to_level(level);
-  if(l == PROT_NONE)
-    return -1;
-  DEBUGASSERT(l > PROT_NONE && l < PROT_LAST);
-  conn->request_data_prot = l;
-  return 0;
-}
-
-static CURLcode choose_mech(struct connectdata *conn)
-{
-  int ret;
-  struct SessionHandle *data = conn->data;
-  const struct Curl_sec_client_mech * const *mech;
-  void *tmp_allocation;
-  const char *mech_name;
-
-  for(mech = mechs; (*mech); ++mech) {
-    mech_name = (*mech)->name;
-    /* We have no mechanism with a NULL name but keep this check */
-    DEBUGASSERT(mech_name != NULL);
-    if(mech_name == NULL) {
-      infof(data, "Skipping mechanism with empty name (%p)\n", mech);
-      continue;
-    }
-    tmp_allocation = realloc(conn->app_data, (*mech)->size);
-    if(tmp_allocation == NULL) {
-      failf(data, "Failed realloc of size %u", (*mech)->size);
-      mech = NULL;
-      return CURLE_OUT_OF_MEMORY;
-    }
-    conn->app_data = tmp_allocation;
-
-    if((*mech)->init) {
-      ret = (*mech)->init(conn->app_data);
-      if(ret != 0) {
-        infof(data, "Failed initialization for %s. Skipping it.\n", mech_name);
-        continue;
-      }
-    }
-
-    infof(data, "Trying mechanism %s...\n", mech_name);
-    ret = ftp_send_command(conn, "AUTH %s", mech_name);
-    if(ret < 0)
-      /* FIXME: This error is too generic but it is OK for now. */
-      return CURLE_COULDNT_CONNECT;
-
-    if(ret/100 != 3) {
-      switch(ret) {
-      case 504:
-        infof(data, "Mechanism %s is not supported by the server (server "
-                    "returned ftp code: 504).\n", mech_name);
-        break;
-      case 534:
-        infof(data, "Mechanism %s was rejected by the server (server returned "
-                    "ftp code: 534).\n", mech_name);
-        break;
-      default:
-        if(ret/100 == 5) {
-          infof(data, "server does not support the security extensions\n");
-          return CURLE_USE_SSL_FAILED;
-        }
-        break;
-      }
-      continue;
-    }
-
-    /* Authenticate */
-    ret = (*mech)->auth(conn->app_data, conn);
-
-    if(ret == AUTH_CONTINUE)
-      continue;
-    else if(ret != AUTH_OK) {
-      /* Mechanism has dumped the error to stderr, don't error here. */
-      return -1;
-    }
-    DEBUGASSERT(ret == AUTH_OK);
-
-    conn->mech = *mech;
-    conn->sec_complete = 1;
-    conn->recv[FIRSTSOCKET] = sec_recv;
-    conn->send[FIRSTSOCKET] = sec_send;
-    conn->recv[SECONDARYSOCKET] = sec_recv;
-    conn->send[SECONDARYSOCKET] = sec_send;
-    conn->command_prot = PROT_SAFE;
-    /* Set the requested protection level */
-    /* BLOCKING */
-    (void)sec_set_protection_level(conn);
-    break;
-  }
-
-  return mech != NULL ? CURLE_OK : CURLE_FAILED_INIT;
-}
-
-CURLcode
-Curl_sec_login(struct connectdata *conn)
-{
-  return choose_mech(conn);
-}
-
-
-void
-Curl_sec_end(struct connectdata *conn)
-{
-  if(conn->mech != NULL && conn->mech->end)
-    conn->mech->end(conn->app_data);
-  if(conn->app_data) {
-    free(conn->app_data);
-    conn->app_data = NULL;
-  }
-  if(conn->in_buffer.data) {
-    free(conn->in_buffer.data);
-    conn->in_buffer.data = NULL;
-    conn->in_buffer.size = 0;
-    conn->in_buffer.index = 0;
-    /* FIXME: Is this really needed? */
-    conn->in_buffer.eof_flag = 0;
-  }
-  conn->sec_complete = 0;
-  conn->data_prot = PROT_CLEAR;
-  conn->mech = NULL;
-}
-
-#endif /* HAVE_KRB4 || HAVE_GSSAPI */
-
-#endif /* CURL_DISABLE_FTP */
diff --git a/lib/select.c b/lib/select.c
deleted file mode 100644 (file)
index d4519d3..0000000
+++ /dev/null
@@ -1,529 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef HAVE_SYS_SELECT_H
-#include <sys/select.h>
-#endif
-
-#if !defined(HAVE_SELECT) && !defined(HAVE_POLL_FINE)
-#error "We can't compile without select() or poll() support."
-#endif
-
-#if defined(__BEOS__) && !defined(__HAIKU__)
-/* BeOS has FD_SET defined in socket.h */
-#include <socket.h>
-#endif
-
-#ifdef MSDOS
-#include <dos.h>  /* delay() */
-#endif
-
-#include <curl/curl.h>
-
-#include "curl_urldata.h"
-#include "curl_connect.h"
-#include "curl_select.h"
-#include "curl_warnless.h"
-
-/* Convenience local macros */
-
-#define elapsed_ms  (int)curlx_tvdiff(curlx_tvnow(), initial_tv)
-
-#ifdef CURL_ACKNOWLEDGE_EINTR
-#define error_not_EINTR (1)
-#else
-#define error_not_EINTR (error != EINTR)
-#endif
-
-/*
- * Internal function used for waiting a specific amount of ms
- * in Curl_socket_ready() and Curl_poll() when no file descriptor
- * is provided to wait on, just being used to delay execution.
- * WinSock select() and poll() timeout mechanisms need a valid
- * socket descriptor in a not null file descriptor set to work.
- * Waiting indefinitely with this function is not allowed, a
- * zero or negative timeout value will return immediately.
- * Timeout resolution, accuracy, as well as maximum supported
- * value is system dependent, neither factor is a citical issue
- * for the intended use of this function in the library.
- * On non-DOS and non-Winsock platforms, when compiled with
- * CURL_ACKNOWLEDGE_EINTR defined, EINTR condition is honored
- * and function might exit early without awaiting full timeout,
- * otherwise EINTR will be ignored and full timeout will elapse.
- *
- * Return values:
- *   -1 = system call error, invalid timeout value, or interrupted
- *    0 = specified timeout has elapsed
- */
-int Curl_wait_ms(int timeout_ms)
-{
-#if !defined(MSDOS) && !defined(USE_WINSOCK)
-#ifndef HAVE_POLL_FINE
-  struct timeval pending_tv;
-#endif
-  struct timeval initial_tv;
-  int pending_ms;
-  int error;
-#endif
-  int r = 0;
-
-  if(!timeout_ms)
-    return 0;
-  if(timeout_ms < 0) {
-    SET_SOCKERRNO(EINVAL);
-    return -1;
-  }
-#if defined(MSDOS)
-  delay(timeout_ms);
-#elif defined(USE_WINSOCK)
-  Sleep(timeout_ms);
-#else
-  pending_ms = timeout_ms;
-  initial_tv = curlx_tvnow();
-  do {
-#if defined(HAVE_POLL_FINE)
-    r = poll(NULL, 0, pending_ms);
-#else
-    pending_tv.tv_sec = pending_ms / 1000;
-    pending_tv.tv_usec = (pending_ms % 1000) * 1000;
-    r = select(0, NULL, NULL, NULL, &pending_tv);
-#endif /* HAVE_POLL_FINE */
-    if(r != -1)
-      break;
-    error = SOCKERRNO;
-    if(error && error_not_EINTR)
-      break;
-    pending_ms = timeout_ms - elapsed_ms;
-    if(pending_ms <= 0)
-      break;
-  } while(r == -1);
-#endif /* USE_WINSOCK */
-  if(r)
-    r = -1;
-  return r;
-}
-
-/*
- * Wait for read or write events on a set of file descriptors. It uses poll()
- * when a fine poll() is available, in order to avoid limits with FD_SETSIZE,
- * otherwise select() is used.  An error is returned if select() is being used
- * and a file descriptor is too large for FD_SETSIZE.
- *
- * A negative timeout value makes this function wait indefinitely,
- * unles no valid file descriptor is given, when this happens the
- * negative timeout is ignored and the function times out immediately.
- * When compiled with CURL_ACKNOWLEDGE_EINTR defined, EINTR condition
- * is honored and function might exit early without awaiting timeout,
- * otherwise EINTR will be ignored.
- *
- * Return values:
- *   -1 = system call error or fd >= FD_SETSIZE
- *    0 = timeout
- *    [bitmask] = action as described below
- *
- * CURL_CSELECT_IN - first socket is readable
- * CURL_CSELECT_IN2 - second socket is readable
- * CURL_CSELECT_OUT - write socket is writable
- * CURL_CSELECT_ERR - an error condition occurred
- */
-int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */
-                      curl_socket_t readfd1,
-                      curl_socket_t writefd, /* socket to write to */
-                      long timeout_ms)       /* milliseconds to wait */
-{
-#ifdef HAVE_POLL_FINE
-  struct pollfd pfd[3];
-  int num;
-#else
-  struct timeval pending_tv;
-  struct timeval *ptimeout;
-  fd_set fds_read;
-  fd_set fds_write;
-  fd_set fds_err;
-  curl_socket_t maxfd;
-#endif
-  struct timeval initial_tv = {0,0};
-  int pending_ms = 0;
-  int error;
-  int r;
-  int ret;
-
-  if((readfd0 == CURL_SOCKET_BAD) && (readfd1 == CURL_SOCKET_BAD) &&
-     (writefd == CURL_SOCKET_BAD)) {
-    /* no sockets, just wait */
-    r = Curl_wait_ms((int)timeout_ms);
-    return r;
-  }
-
-  /* Avoid initial timestamp, avoid curlx_tvnow() call, when elapsed
-     time in this function does not need to be measured. This happens
-     when function is called with a zero timeout or a negative timeout
-     value indicating a blocking call should be performed. */
-
-  if(timeout_ms > 0) {
-    pending_ms = (int)timeout_ms;
-    initial_tv = curlx_tvnow();
-  }
-
-#ifdef HAVE_POLL_FINE
-
-  num = 0;
-  if(readfd0 != CURL_SOCKET_BAD) {
-    pfd[num].fd = readfd0;
-    pfd[num].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI;
-    pfd[num].revents = 0;
-    num++;
-  }
-  if(readfd1 != CURL_SOCKET_BAD) {
-    pfd[num].fd = readfd1;
-    pfd[num].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI;
-    pfd[num].revents = 0;
-    num++;
-  }
-  if(writefd != CURL_SOCKET_BAD) {
-    pfd[num].fd = writefd;
-    pfd[num].events = POLLWRNORM|POLLOUT;
-    pfd[num].revents = 0;
-    num++;
-  }
-
-  do {
-    if(timeout_ms < 0)
-      pending_ms = -1;
-    else if(!timeout_ms)
-      pending_ms = 0;
-    r = poll(pfd, num, pending_ms);
-    if(r != -1)
-      break;
-    error = SOCKERRNO;
-    if(error && error_not_EINTR)
-      break;
-    if(timeout_ms > 0) {
-      pending_ms = (int)(timeout_ms - elapsed_ms);
-      if(pending_ms <= 0) {
-        r = 0;  /* Simulate a "call timed out" case */
-        break;
-      }
-    }
-  } while(r == -1);
-
-  if(r < 0)
-    return -1;
-  if(r == 0)
-    return 0;
-
-  ret = 0;
-  num = 0;
-  if(readfd0 != CURL_SOCKET_BAD) {
-    if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP))
-      ret |= CURL_CSELECT_IN;
-    if(pfd[num].revents & (POLLRDBAND|POLLPRI|POLLNVAL))
-      ret |= CURL_CSELECT_ERR;
-    num++;
-  }
-  if(readfd1 != CURL_SOCKET_BAD) {
-    if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP))
-      ret |= CURL_CSELECT_IN2;
-    if(pfd[num].revents & (POLLRDBAND|POLLPRI|POLLNVAL))
-      ret |= CURL_CSELECT_ERR;
-    num++;
-  }
-  if(writefd != CURL_SOCKET_BAD) {
-    if(pfd[num].revents & (POLLWRNORM|POLLOUT))
-      ret |= CURL_CSELECT_OUT;
-    if(pfd[num].revents & (POLLERR|POLLHUP|POLLNVAL))
-      ret |= CURL_CSELECT_ERR;
-  }
-
-  return ret;
-
-#else  /* HAVE_POLL_FINE */
-
-  FD_ZERO(&fds_err);
-  maxfd = (curl_socket_t)-1;
-
-  FD_ZERO(&fds_read);
-  if(readfd0 != CURL_SOCKET_BAD) {
-    VERIFY_SOCK(readfd0);
-    FD_SET(readfd0, &fds_read);
-    FD_SET(readfd0, &fds_err);
-    maxfd = readfd0;
-  }
-  if(readfd1 != CURL_SOCKET_BAD) {
-    VERIFY_SOCK(readfd1);
-    FD_SET(readfd1, &fds_read);
-    FD_SET(readfd1, &fds_err);
-    if(readfd1 > maxfd)
-      maxfd = readfd1;
-  }
-
-  FD_ZERO(&fds_write);
-  if(writefd != CURL_SOCKET_BAD) {
-    VERIFY_SOCK(writefd);
-    FD_SET(writefd, &fds_write);
-    FD_SET(writefd, &fds_err);
-    if(writefd > maxfd)
-      maxfd = writefd;
-  }
-
-  ptimeout = (timeout_ms < 0) ? NULL : &pending_tv;
-
-  do {
-    if(timeout_ms > 0) {
-      pending_tv.tv_sec = pending_ms / 1000;
-      pending_tv.tv_usec = (pending_ms % 1000) * 1000;
-    }
-    else if(!timeout_ms) {
-      pending_tv.tv_sec = 0;
-      pending_tv.tv_usec = 0;
-    }
-    r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, ptimeout);
-    if(r != -1)
-      break;
-    error = SOCKERRNO;
-    if(error && error_not_EINTR)
-      break;
-    if(timeout_ms > 0) {
-      pending_ms = timeout_ms - elapsed_ms;
-      if(pending_ms <= 0) {
-        r = 0;  /* Simulate a "call timed out" case */
-        break;
-      }
-    }
-  } while(r == -1);
-
-  if(r < 0)
-    return -1;
-  if(r == 0)
-    return 0;
-
-  ret = 0;
-  if(readfd0 != CURL_SOCKET_BAD) {
-    if(FD_ISSET(readfd0, &fds_read))
-      ret |= CURL_CSELECT_IN;
-    if(FD_ISSET(readfd0, &fds_err))
-      ret |= CURL_CSELECT_ERR;
-  }
-  if(readfd1 != CURL_SOCKET_BAD) {
-    if(FD_ISSET(readfd1, &fds_read))
-      ret |= CURL_CSELECT_IN2;
-    if(FD_ISSET(readfd1, &fds_err))
-      ret |= CURL_CSELECT_ERR;
-  }
-  if(writefd != CURL_SOCKET_BAD) {
-    if(FD_ISSET(writefd, &fds_write))
-      ret |= CURL_CSELECT_OUT;
-    if(FD_ISSET(writefd, &fds_err))
-      ret |= CURL_CSELECT_ERR;
-  }
-
-  return ret;
-
-#endif  /* HAVE_POLL_FINE */
-
-}
-
-/*
- * This is a wrapper around poll().  If poll() does not exist, then
- * select() is used instead.  An error is returned if select() is
- * being used and a file descriptor is too large for FD_SETSIZE.
- * A negative timeout value makes this function wait indefinitely,
- * unles no valid file descriptor is given, when this happens the
- * negative timeout is ignored and the function times out immediately.
- * When compiled with CURL_ACKNOWLEDGE_EINTR defined, EINTR condition
- * is honored and function might exit early without awaiting timeout,
- * otherwise EINTR will be ignored.
- *
- * Return values:
- *   -1 = system call error or fd >= FD_SETSIZE
- *    0 = timeout
- *    N = number of structures with non zero revent fields
- */
-int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms)
-{
-#ifndef HAVE_POLL_FINE
-  struct timeval pending_tv;
-  struct timeval *ptimeout;
-  fd_set fds_read;
-  fd_set fds_write;
-  fd_set fds_err;
-  curl_socket_t maxfd;
-#endif
-  struct timeval initial_tv = {0,0};
-  bool fds_none = TRUE;
-  unsigned int i;
-  int pending_ms = 0;
-  int error;
-  int r;
-
-  if(ufds) {
-    for(i = 0; i < nfds; i++) {
-      if(ufds[i].fd != CURL_SOCKET_BAD) {
-        fds_none = FALSE;
-        break;
-      }
-    }
-  }
-  if(fds_none) {
-    r = Curl_wait_ms(timeout_ms);
-    return r;
-  }
-
-  /* Avoid initial timestamp, avoid curlx_tvnow() call, when elapsed
-     time in this function does not need to be measured. This happens
-     when function is called with a zero timeout or a negative timeout
-     value indicating a blocking call should be performed. */
-
-  if(timeout_ms > 0) {
-    pending_ms = timeout_ms;
-    initial_tv = curlx_tvnow();
-  }
-
-#ifdef HAVE_POLL_FINE
-
-  do {
-    if(timeout_ms < 0)
-      pending_ms = -1;
-    else if(!timeout_ms)
-      pending_ms = 0;
-    r = poll(ufds, nfds, pending_ms);
-    if(r != -1)
-      break;
-    error = SOCKERRNO;
-    if(error && error_not_EINTR)
-      break;
-    if(timeout_ms > 0) {
-      pending_ms = timeout_ms - elapsed_ms;
-      if(pending_ms <= 0)
-        break;
-    }
-  } while(r == -1);
-
-  if(r < 0)
-    return -1;
-  if(r == 0)
-    return 0;
-
-  for(i = 0; i < nfds; i++) {
-    if(ufds[i].fd == CURL_SOCKET_BAD)
-      continue;
-    if(ufds[i].revents & POLLHUP)
-      ufds[i].revents |= POLLIN;
-    if(ufds[i].revents & POLLERR)
-      ufds[i].revents |= (POLLIN|POLLOUT);
-  }
-
-#else  /* HAVE_POLL_FINE */
-
-  FD_ZERO(&fds_read);
-  FD_ZERO(&fds_write);
-  FD_ZERO(&fds_err);
-  maxfd = (curl_socket_t)-1;
-
-  for(i = 0; i < nfds; i++) {
-    ufds[i].revents = 0;
-    if(ufds[i].fd == CURL_SOCKET_BAD)
-      continue;
-    VERIFY_SOCK(ufds[i].fd);
-    if(ufds[i].events & (POLLIN|POLLOUT|POLLPRI|
-                          POLLRDNORM|POLLWRNORM|POLLRDBAND)) {
-      if(ufds[i].fd > maxfd)
-        maxfd = ufds[i].fd;
-      if(ufds[i].events & (POLLRDNORM|POLLIN))
-        FD_SET(ufds[i].fd, &fds_read);
-      if(ufds[i].events & (POLLWRNORM|POLLOUT))
-        FD_SET(ufds[i].fd, &fds_write);
-      if(ufds[i].events & (POLLRDBAND|POLLPRI))
-        FD_SET(ufds[i].fd, &fds_err);
-    }
-  }
-
-  ptimeout = (timeout_ms < 0) ? NULL : &pending_tv;
-
-  do {
-    if(timeout_ms > 0) {
-      pending_tv.tv_sec = pending_ms / 1000;
-      pending_tv.tv_usec = (pending_ms % 1000) * 1000;
-    }
-    else if(!timeout_ms) {
-      pending_tv.tv_sec = 0;
-      pending_tv.tv_usec = 0;
-    }
-    r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, ptimeout);
-    if(r != -1)
-      break;
-    error = SOCKERRNO;
-    if(error && error_not_EINTR)
-      break;
-    if(timeout_ms > 0) {
-      pending_ms = timeout_ms - elapsed_ms;
-      if(pending_ms <= 0)
-        break;
-    }
-  } while(r == -1);
-
-  if(r < 0)
-    return -1;
-  if(r == 0)
-    return 0;
-
-  r = 0;
-  for(i = 0; i < nfds; i++) {
-    ufds[i].revents = 0;
-    if(ufds[i].fd == CURL_SOCKET_BAD)
-      continue;
-    if(FD_ISSET(ufds[i].fd, &fds_read))
-      ufds[i].revents |= POLLIN;
-    if(FD_ISSET(ufds[i].fd, &fds_write))
-      ufds[i].revents |= POLLOUT;
-    if(FD_ISSET(ufds[i].fd, &fds_err))
-      ufds[i].revents |= POLLPRI;
-    if(ufds[i].revents != 0)
-      r++;
-  }
-
-#endif  /* HAVE_POLL_FINE */
-
-  return r;
-}
-
-#ifdef TPF
-/*
- * This is a replacement for select() on the TPF platform.
- * It is used whenever libcurl calls select().
- * The call below to tpf_process_signals() is required because
- * TPF's select calls are not signal interruptible.
- *
- * Return values are the same as select's.
- */
-int tpf_select_libcurl(int maxfds, fd_set* reads, fd_set* writes,
-                       fd_set* excepts, struct timeval* tv)
-{
-   int rc;
-
-   rc = tpf_select_bsd(maxfds, reads, writes, excepts, tv);
-   tpf_process_signals();
-   return(rc);
-}
-#endif /* TPF */
diff --git a/lib/sendf.c b/lib/sendf.c
deleted file mode 100644 (file)
index a1ec509..0000000
+++ /dev/null
@@ -1,687 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#include <curl/curl.h>
-
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_connect.h"
-#include "curl_sslgen.h"
-#include "curl_ssh.h"
-#include "curl_multiif.h"
-#include "curl_non_ascii.h"
-
-#define _MPRINTF_REPLACE /* use the internal *printf() functions */
-#include <curl/mprintf.h>
-
-/* the krb4 functions only exists for FTP and if krb4 or gssapi is defined */
-#if !defined(CURL_DISABLE_FTP) && (defined(HAVE_KRB4) || defined(HAVE_GSSAPI))
-#include "curl_krb4.h"
-#else
-#define Curl_sec_send(a,b,c,d) -1
-#define Curl_sec_read(a,b,c,d) -1
-#endif
-
-#include "curl_memory.h"
-#include "curl_strerror.h"
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-#ifdef CURL_DO_LINEEND_CONV
-/*
- * convert_lineends() changes CRLF (\r\n) end-of-line markers to a single LF
- * (\n), with special processing for CRLF sequences that are split between two
- * blocks of data.  Remaining, bare CRs are changed to LFs.  The possibly new
- * size of the data is returned.
- */
-static size_t convert_lineends(struct SessionHandle *data,
-                               char *startPtr, size_t size)
-{
-  char *inPtr, *outPtr;
-
-  /* sanity check */
-  if((startPtr == NULL) || (size < 1)) {
-    return(size);
-  }
-
-  if(data->state.prev_block_had_trailing_cr) {
-    /* The previous block of incoming data
-       had a trailing CR, which was turned into a LF. */
-    if(*startPtr == '\n') {
-      /* This block of incoming data starts with the
-         previous block's LF so get rid of it */
-      memmove(startPtr, startPtr+1, size-1);
-      size--;
-      /* and it wasn't a bare CR but a CRLF conversion instead */
-      data->state.crlf_conversions++;
-    }
-    data->state.prev_block_had_trailing_cr = FALSE; /* reset the flag */
-  }
-
-  /* find 1st CR, if any */
-  inPtr = outPtr = memchr(startPtr, '\r', size);
-  if(inPtr) {
-    /* at least one CR, now look for CRLF */
-    while(inPtr < (startPtr+size-1)) {
-      /* note that it's size-1, so we'll never look past the last byte */
-      if(memcmp(inPtr, "\r\n", 2) == 0) {
-        /* CRLF found, bump past the CR and copy the NL */
-        inPtr++;
-        *outPtr = *inPtr;
-        /* keep track of how many CRLFs we converted */
-        data->state.crlf_conversions++;
-      }
-      else {
-        if(*inPtr == '\r') {
-          /* lone CR, move LF instead */
-          *outPtr = '\n';
-        }
-        else {
-          /* not a CRLF nor a CR, just copy whatever it is */
-          *outPtr = *inPtr;
-        }
-      }
-      outPtr++;
-      inPtr++;
-    } /* end of while loop */
-
-    if(inPtr < startPtr+size) {
-      /* handle last byte */
-      if(*inPtr == '\r') {
-        /* deal with a CR at the end of the buffer */
-        *outPtr = '\n'; /* copy a NL instead */
-        /* note that a CRLF might be split across two blocks */
-        data->state.prev_block_had_trailing_cr = TRUE;
-      }
-      else {
-        /* copy last byte */
-        *outPtr = *inPtr;
-      }
-      outPtr++;
-    }
-    if(outPtr < startPtr+size)
-      /* tidy up by null terminating the now shorter data */
-      *outPtr = '\0';
-
-    return(outPtr - startPtr);
-  }
-  return(size);
-}
-#endif /* CURL_DO_LINEEND_CONV */
-
-/* Curl_infof() is for info message along the way */
-
-void Curl_infof(struct SessionHandle *data, const char *fmt, ...)
-{
-  if(data && data->set.verbose) {
-    va_list ap;
-    size_t len;
-    char print_buffer[2048 + 1];
-    va_start(ap, fmt);
-    vsnprintf(print_buffer, sizeof(print_buffer), fmt, ap);
-    va_end(ap);
-    len = strlen(print_buffer);
-    Curl_debug(data, CURLINFO_TEXT, print_buffer, len, NULL);
-  }
-}
-
-/* Curl_failf() is for messages stating why we failed.
- * The message SHALL NOT include any LF or CR.
- */
-
-void Curl_failf(struct SessionHandle *data, const char *fmt, ...)
-{
-  va_list ap;
-  size_t len;
-  va_start(ap, fmt);
-
-  vsnprintf(data->state.buffer, BUFSIZE, fmt, ap);
-
-  if(data->set.errorbuffer && !data->state.errorbuf) {
-    snprintf(data->set.errorbuffer, CURL_ERROR_SIZE, "%s", data->state.buffer);
-    data->state.errorbuf = TRUE; /* wrote error string */
-  }
-  if(data->set.verbose) {
-    len = strlen(data->state.buffer);
-    if(len < BUFSIZE - 1) {
-      data->state.buffer[len] = '\n';
-      data->state.buffer[++len] = '\0';
-    }
-    Curl_debug(data, CURLINFO_TEXT, data->state.buffer, len, NULL);
-  }
-
-  va_end(ap);
-}
-
-/* Curl_sendf() sends formated data to the server */
-CURLcode Curl_sendf(curl_socket_t sockfd, struct connectdata *conn,
-                    const char *fmt, ...)
-{
-  struct SessionHandle *data = conn->data;
-  ssize_t bytes_written;
-  size_t write_len;
-  CURLcode res = CURLE_OK;
-  char *s;
-  char *sptr;
-  va_list ap;
-  va_start(ap, fmt);
-  s = vaprintf(fmt, ap); /* returns an allocated string */
-  va_end(ap);
-  if(!s)
-    return CURLE_OUT_OF_MEMORY; /* failure */
-
-  bytes_written=0;
-  write_len = strlen(s);
-  sptr = s;
-
-  for(;;) {
-    /* Write the buffer to the socket */
-    res = Curl_write(conn, sockfd, sptr, write_len, &bytes_written);
-
-    if(CURLE_OK != res)
-      break;
-
-    if(data->set.verbose)
-      Curl_debug(data, CURLINFO_DATA_OUT, sptr, (size_t)bytes_written, conn);
-
-    if((size_t)bytes_written != write_len) {
-      /* if not all was written at once, we must advance the pointer, decrease
-         the size left and try again! */
-      write_len -= bytes_written;
-      sptr += bytes_written;
-    }
-    else
-      break;
-  }
-
-  free(s); /* free the output string */
-
-  return res;
-}
-
-/*
- * Curl_write() is an internal write function that sends data to the
- * server. Works with plain sockets, SCP, SSL or kerberos.
- *
- * If the write would block (CURLE_AGAIN), we return CURLE_OK and
- * (*written == 0). Otherwise we return regular CURLcode value.
- */
-CURLcode Curl_write(struct connectdata *conn,
-                    curl_socket_t sockfd,
-                    const void *mem,
-                    size_t len,
-                    ssize_t *written)
-{
-  ssize_t bytes_written;
-  CURLcode curlcode = CURLE_OK;
-  int num = (sockfd == conn->sock[SECONDARYSOCKET]);
-
-  bytes_written = conn->send[num](conn, num, mem, len, &curlcode);
-
-  *written = bytes_written;
-  if(bytes_written >= 0)
-    /* we completely ignore the curlcode value when subzero is not returned */
-    return CURLE_OK;
-
-  /* handle CURLE_AGAIN or a send failure */
-  switch(curlcode) {
-  case CURLE_AGAIN:
-    *written = 0;
-    return CURLE_OK;
-
-  case CURLE_OK:
-    /* general send failure */
-    return CURLE_SEND_ERROR;
-
-  default:
-    /* we got a specific curlcode, forward it */
-    return curlcode;
-  }
-}
-
-ssize_t Curl_send_plain(struct connectdata *conn, int num,
-                        const void *mem, size_t len, CURLcode *code)
-{
-  curl_socket_t sockfd = conn->sock[num];
-  ssize_t bytes_written = swrite(sockfd, mem, len);
-
-  *code = CURLE_OK;
-  if(-1 == bytes_written) {
-    int err = SOCKERRNO;
-
-    if(
-#ifdef WSAEWOULDBLOCK
-      /* This is how Windows does it */
-      (WSAEWOULDBLOCK == err)
-#else
-      /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
-         due to its inability to send off data without blocking. We therefor
-         treat both error codes the same here */
-      (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err)
-#endif
-      ) {
-      /* this is just a case of EWOULDBLOCK */
-      bytes_written=0;
-      *code = CURLE_AGAIN;
-    }
-    else {
-      failf(conn->data, "Send failure: %s",
-            Curl_strerror(conn, err));
-      conn->data->state.os_errno = err;
-      *code = CURLE_SEND_ERROR;
-    }
-  }
-  return bytes_written;
-}
-
-/*
- * Curl_write_plain() is an internal write function that sends data to the
- * server using plain sockets only. Otherwise meant to have the exact same
- * proto as Curl_write()
- */
-CURLcode Curl_write_plain(struct connectdata *conn,
-                          curl_socket_t sockfd,
-                          const void *mem,
-                          size_t len,
-                          ssize_t *written)
-{
-  ssize_t bytes_written;
-  CURLcode retcode;
-  int num = (sockfd == conn->sock[SECONDARYSOCKET]);
-
-  bytes_written = Curl_send_plain(conn, num, mem, len, &retcode);
-
-  *written = bytes_written;
-
-  return retcode;
-}
-
-ssize_t Curl_recv_plain(struct connectdata *conn, int num, char *buf,
-                        size_t len, CURLcode *code)
-{
-  curl_socket_t sockfd = conn->sock[num];
-  ssize_t nread = sread(sockfd, buf, len);
-
-  *code = CURLE_OK;
-  if(-1 == nread) {
-    int err = SOCKERRNO;
-
-    if(
-#ifdef WSAEWOULDBLOCK
-      /* This is how Windows does it */
-      (WSAEWOULDBLOCK == err)
-#else
-      /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
-         due to its inability to send off data without blocking. We therefor
-         treat both error codes the same here */
-      (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err)
-#endif
-      ) {
-      /* this is just a case of EWOULDBLOCK */
-      *code = CURLE_AGAIN;
-    }
-    else {
-      failf(conn->data, "Recv failure: %s",
-            Curl_strerror(conn, err));
-      conn->data->state.os_errno = err;
-      *code = CURLE_RECV_ERROR;
-    }
-  }
-  return nread;
-}
-
-static CURLcode pausewrite(struct SessionHandle *data,
-                           int type, /* what type of data */
-                           const char *ptr,
-                           size_t len)
-{
-  /* signalled to pause sending on this connection, but since we have data
-     we want to send we need to dup it to save a copy for when the sending
-     is again enabled */
-  struct SingleRequest *k = &data->req;
-  char *dupl = malloc(len);
-  if(!dupl)
-    return CURLE_OUT_OF_MEMORY;
-
-  memcpy(dupl, ptr, len);
-
-  /* store this information in the state struct for later use */
-  data->state.tempwrite = dupl;
-  data->state.tempwritesize = len;
-  data->state.tempwritetype = type;
-
-  /* mark the connection as RECV paused */
-  k->keepon |= KEEP_RECV_PAUSE;
-
-  DEBUGF(infof(data, "Pausing with %zu bytes in buffer for type %02x\n",
-               len, type));
-
-  return CURLE_OK;
-}
-
-
-/* Curl_client_write() sends data to the write callback(s)
-
-   The bit pattern defines to what "streams" to write to. Body and/or header.
-   The defines are in curl_sendf.h of course.
-
-   If CURL_DO_LINEEND_CONV is enabled, data is converted IN PLACE to the
-   local character encoding.  This is a problem and should be changed in
-   the future to leave the original data alone.
- */
-CURLcode Curl_client_write(struct connectdata *conn,
-                           int type,
-                           char *ptr,
-                           size_t len)
-{
-  struct SessionHandle *data = conn->data;
-  size_t wrote;
-
-  if(0 == len)
-    len = strlen(ptr);
-
-  /* If reading is actually paused, we're forced to append this chunk of data
-     to the already held data, but only if it is the same type as otherwise it
-     can't work and it'll return error instead. */
-  if(data->req.keepon & KEEP_RECV_PAUSE) {
-    size_t newlen;
-    char *newptr;
-    if(type != data->state.tempwritetype)
-      /* major internal confusion */
-      return CURLE_RECV_ERROR;
-
-    DEBUGASSERT(data->state.tempwrite);
-
-    /* figure out the new size of the data to save */
-    newlen = len + data->state.tempwritesize;
-    /* allocate the new memory area */
-    newptr = realloc(data->state.tempwrite, newlen);
-    if(!newptr)
-      return CURLE_OUT_OF_MEMORY;
-    /* copy the new data to the end of the new area */
-    memcpy(newptr + data->state.tempwritesize, ptr, len);
-    /* update the pointer and the size */
-    data->state.tempwrite = newptr;
-    data->state.tempwritesize = newlen;
-
-    return CURLE_OK;
-  }
-
-  if(type & CLIENTWRITE_BODY) {
-    if((conn->handler->protocol&CURLPROTO_FTP) &&
-       conn->proto.ftpc.transfertype == 'A') {
-      /* convert from the network encoding */
-      CURLcode rc = Curl_convert_from_network(data, ptr, len);
-      /* Curl_convert_from_network calls failf if unsuccessful */
-      if(rc)
-        return rc;
-
-#ifdef CURL_DO_LINEEND_CONV
-      /* convert end-of-line markers */
-      len = convert_lineends(data, ptr, len);
-#endif /* CURL_DO_LINEEND_CONV */
-    }
-    /* If the previous block of data ended with CR and this block of data is
-       just a NL, then the length might be zero */
-    if(len) {
-      wrote = data->set.fwrite_func(ptr, 1, len, data->set.out);
-    }
-    else {
-      wrote = len;
-    }
-
-    if(CURL_WRITEFUNC_PAUSE == wrote)
-      return pausewrite(data, type, ptr, len);
-
-    if(wrote != len) {
-      failf(data, "Failed writing body (%zu != %zu)", wrote, len);
-      return CURLE_WRITE_ERROR;
-    }
-  }
-
-  if((type & CLIENTWRITE_HEADER) &&
-     (data->set.fwrite_header || data->set.writeheader) ) {
-    /*
-     * Write headers to the same callback or to the especially setup
-     * header callback function (added after version 7.7.1).
-     */
-    curl_write_callback writeit=
-      data->set.fwrite_header?data->set.fwrite_header:data->set.fwrite_func;
-
-    /* Note: The header is in the host encoding
-       regardless of the ftp transfer mode (ASCII/Image) */
-
-    wrote = writeit(ptr, 1, len, data->set.writeheader);
-    if(CURL_WRITEFUNC_PAUSE == wrote)
-      /* here we pass in the HEADER bit only since if this was body as well
-         then it was passed already and clearly that didn't trigger the pause,
-         so this is saved for later with the HEADER bit only */
-      return pausewrite(data, CLIENTWRITE_HEADER, ptr, len);
-
-    if(wrote != len) {
-      failf (data, "Failed writing header");
-      return CURLE_WRITE_ERROR;
-    }
-  }
-
-  return CURLE_OK;
-}
-
-CURLcode Curl_read_plain(curl_socket_t sockfd,
-                         char *buf,
-                         size_t bytesfromsocket,
-                         ssize_t *n)
-{
-  ssize_t nread = sread(sockfd, buf, bytesfromsocket);
-
-  if(-1 == nread) {
-    int err = SOCKERRNO;
-#ifdef USE_WINSOCK
-    if(WSAEWOULDBLOCK == err)
-#else
-    if((EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err))
-#endif
-      return CURLE_AGAIN;
-    else
-      return CURLE_RECV_ERROR;
-  }
-
-  /* we only return number of bytes read when we return OK */
-  *n = nread;
-  return CURLE_OK;
-}
-
-/*
- * Internal read-from-socket function. This is meant to deal with plain
- * sockets, SSL sockets and kerberos sockets.
- *
- * Returns a regular CURLcode value.
- */
-CURLcode Curl_read(struct connectdata *conn, /* connection data */
-                   curl_socket_t sockfd,     /* read from this socket */
-                   char *buf,                /* store read data here */
-                   size_t sizerequested,     /* max amount to read */
-                   ssize_t *n)               /* amount bytes read */
-{
-  CURLcode curlcode = CURLE_RECV_ERROR;
-  ssize_t nread = 0;
-  size_t bytesfromsocket = 0;
-  char *buffertofill = NULL;
-  bool pipelining = (conn->data->multi &&
-                     Curl_multi_canPipeline(conn->data->multi)) ? TRUE : FALSE;
-
-  /* Set 'num' to 0 or 1, depending on which socket that has been sent here.
-     If it is the second socket, we set num to 1. Otherwise to 0. This lets
-     us use the correct ssl handle. */
-  int num = (sockfd == conn->sock[SECONDARYSOCKET]);
-
-  *n=0; /* reset amount to zero */
-
-  /* If session can pipeline, check connection buffer  */
-  if(pipelining) {
-    size_t bytestocopy = CURLMIN(conn->buf_len - conn->read_pos,
-                                 sizerequested);
-
-    /* Copy from our master buffer first if we have some unread data there*/
-    if(bytestocopy > 0) {
-      memcpy(buf, conn->master_buffer + conn->read_pos, bytestocopy);
-      conn->read_pos += bytestocopy;
-      conn->bits.stream_was_rewound = FALSE;
-
-      *n = (ssize_t)bytestocopy;
-      return CURLE_OK;
-    }
-    /* If we come here, it means that there is no data to read from the buffer,
-     * so we read from the socket */
-    bytesfromsocket = CURLMIN(sizerequested, BUFSIZE * sizeof (char));
-    buffertofill = conn->master_buffer;
-  }
-  else {
-    bytesfromsocket = CURLMIN((long)sizerequested,
-                              conn->data->set.buffer_size ?
-                              conn->data->set.buffer_size : BUFSIZE);
-    buffertofill = buf;
-  }
-
-  nread = conn->recv[num](conn, num, buffertofill, bytesfromsocket, &curlcode);
-  if(nread < 0)
-    return curlcode;
-
-  if(pipelining) {
-    memcpy(buf, conn->master_buffer, nread);
-    conn->buf_len = nread;
-    conn->read_pos = nread;
-  }
-
-  *n += nread;
-
-  return CURLE_OK;
-}
-
-/* return 0 on success */
-static int showit(struct SessionHandle *data, curl_infotype type,
-                  char *ptr, size_t size)
-{
-  static const char s_infotype[CURLINFO_END][3] = {
-    "* ", "< ", "> ", "{ ", "} ", "{ ", "} " };
-
-#ifdef CURL_DOES_CONVERSIONS
-  char buf[BUFSIZE+1];
-  size_t conv_size = 0;
-
-  switch(type) {
-  case CURLINFO_HEADER_OUT:
-    /* assume output headers are ASCII */
-    /* copy the data into my buffer so the original is unchanged */
-    if(size > BUFSIZE) {
-      size = BUFSIZE; /* truncate if necessary */
-      buf[BUFSIZE] = '\0';
-    }
-    conv_size = size;
-    memcpy(buf, ptr, size);
-    /* Special processing is needed for this block if it
-     * contains both headers and data (separated by CRLFCRLF).
-     * We want to convert just the headers, leaving the data as-is.
-     */
-    if(size > 4) {
-      size_t i;
-      for(i = 0; i < size-4; i++) {
-        if(memcmp(&buf[i], "\x0d\x0a\x0d\x0a", 4) == 0) {
-          /* convert everything through this CRLFCRLF but no further */
-          conv_size = i + 4;
-          break;
-        }
-      }
-    }
-
-    Curl_convert_from_network(data, buf, conv_size);
-    /* Curl_convert_from_network calls failf if unsuccessful */
-    /* we might as well continue even if it fails...   */
-    ptr = buf; /* switch pointer to use my buffer instead */
-    break;
-  default:
-    /* leave everything else as-is */
-    break;
-  }
-#endif /* CURL_DOES_CONVERSIONS */
-
-  if(data->set.fdebug)
-    return (*data->set.fdebug)(data, type, ptr, size,
-                               data->set.debugdata);
-
-  switch(type) {
-  case CURLINFO_TEXT:
-  case CURLINFO_HEADER_OUT:
-  case CURLINFO_HEADER_IN:
-    fwrite(s_infotype[type], 2, 1, data->set.err);
-    fwrite(ptr, size, 1, data->set.err);
-#ifdef CURL_DOES_CONVERSIONS
-    if(size != conv_size) {
-      /* we had untranslated data so we need an explicit newline */
-      fwrite("\n", 1, 1, data->set.err);
-    }
-#endif
-    break;
-  default: /* nada */
-    break;
-  }
-  return 0;
-}
-
-int Curl_debug(struct SessionHandle *data, curl_infotype type,
-               char *ptr, size_t size,
-               struct connectdata *conn)
-{
-  int rc;
-  if(data->set.printhost && conn && conn->host.dispname) {
-    char buffer[160];
-    const char *t=NULL;
-    const char *w="Data";
-    switch (type) {
-    case CURLINFO_HEADER_IN:
-      w = "Header";
-    case CURLINFO_DATA_IN:
-      t = "from";
-      break;
-    case CURLINFO_HEADER_OUT:
-      w = "Header";
-    case CURLINFO_DATA_OUT:
-      t = "to";
-      break;
-    default:
-      break;
-    }
-
-    if(t) {
-      snprintf(buffer, sizeof(buffer), "[%s %s %s]", w, t,
-               conn->host.dispname);
-      rc = showit(data, CURLINFO_TEXT, buffer, strlen(buffer));
-      if(rc)
-        return rc;
-    }
-  }
-  rc = showit(data, type, ptr, size);
-  return rc;
-}
diff --git a/lib/share.c b/lib/share.c
deleted file mode 100644 (file)
index 182e6e9..0000000
+++ /dev/null
@@ -1,254 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#include <curl/curl.h>
-#include "curl_urldata.h"
-#include "curl_share.h"
-#include "curl_sslgen.h"
-#include "curl_memory.h"
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-CURLSH *
-curl_share_init(void)
-{
-  struct Curl_share *share = calloc(1, sizeof(struct Curl_share));
-  if(share)
-    share->specifier |= (1<<CURL_LOCK_DATA_SHARE);
-
-  return share;
-}
-
-#undef curl_share_setopt
-CURLSHcode
-curl_share_setopt(CURLSH *sh, CURLSHoption option, ...)
-{
-  struct Curl_share *share = (struct Curl_share *)sh;
-  va_list param;
-  int type;
-  curl_lock_function lockfunc;
-  curl_unlock_function unlockfunc;
-  void *ptr;
-  CURLSHcode res = CURLSHE_OK;
-
-  if(share->dirty)
-    /* don't allow setting options while one or more handles are already
-       using this share */
-    return CURLSHE_IN_USE;
-
-  va_start(param, option);
-
-  switch(option) {
-  case CURLSHOPT_SHARE:
-    /* this is a type this share will share */
-    type = va_arg(param, int);
-    share->specifier |= (1<<type);
-    switch( type ) {
-    case CURL_LOCK_DATA_DNS:
-      if(!share->hostcache) {
-        share->hostcache = Curl_mk_dnscache();
-        if(!share->hostcache)
-          res = CURLSHE_NOMEM;
-      }
-      break;
-
-    case CURL_LOCK_DATA_COOKIE:
-#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
-      if(!share->cookies) {
-        share->cookies = Curl_cookie_init(NULL, NULL, NULL, TRUE );
-        if(!share->cookies)
-          res = CURLSHE_NOMEM;
-      }
-#else   /* CURL_DISABLE_HTTP */
-      res = CURLSHE_NOT_BUILT_IN;
-#endif
-      break;
-
-    case CURL_LOCK_DATA_SSL_SESSION:
-#ifdef USE_SSL
-      if(!share->sslsession) {
-        share->max_ssl_sessions = 8;
-        share->sslsession = calloc(share->max_ssl_sessions,
-                                   sizeof(struct curl_ssl_session));
-        share->sessionage = 0;
-        if(!share->sslsession)
-          res = CURLSHE_NOMEM;
-      }
-#else
-      res = CURLSHE_NOT_BUILT_IN;
-#endif
-      break;
-
-    case CURL_LOCK_DATA_CONNECT:     /* not supported (yet) */
-      break;
-
-    default:
-      res = CURLSHE_BAD_OPTION;
-    }
-    break;
-
-  case CURLSHOPT_UNSHARE:
-    /* this is a type this share will no longer share */
-    type = va_arg(param, int);
-    share->specifier &= ~(1<<type);
-    switch( type ) {
-    case CURL_LOCK_DATA_DNS:
-      if(share->hostcache) {
-        Curl_hash_destroy(share->hostcache);
-        share->hostcache = NULL;
-      }
-      break;
-
-    case CURL_LOCK_DATA_COOKIE:
-#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
-      if(share->cookies) {
-        Curl_cookie_cleanup(share->cookies);
-        share->cookies = NULL;
-      }
-#else   /* CURL_DISABLE_HTTP */
-      res = CURLSHE_NOT_BUILT_IN;
-#endif
-      break;
-
-    case CURL_LOCK_DATA_SSL_SESSION:
-#ifdef USE_SSL
-      Curl_safefree(share->sslsession);
-#else
-      res = CURLSHE_NOT_BUILT_IN;
-#endif
-      break;
-
-    case CURL_LOCK_DATA_CONNECT:
-      break;
-
-    default:
-      res = CURLSHE_BAD_OPTION;
-      break;
-    }
-    break;
-
-  case CURLSHOPT_LOCKFUNC:
-    lockfunc = va_arg(param, curl_lock_function);
-    share->lockfunc = lockfunc;
-    break;
-
-  case CURLSHOPT_UNLOCKFUNC:
-    unlockfunc = va_arg(param, curl_unlock_function);
-    share->unlockfunc = unlockfunc;
-    break;
-
-  case CURLSHOPT_USERDATA:
-    ptr = va_arg(param, void *);
-    share->clientdata = ptr;
-    break;
-
-  default:
-    res = CURLSHE_BAD_OPTION;
-    break;
-  }
-
-  va_end(param);
-
-  return res;
-}
-
-CURLSHcode
-curl_share_cleanup(CURLSH *sh)
-{
-  struct Curl_share *share = (struct Curl_share *)sh;
-
-  if(share == NULL)
-    return CURLSHE_INVALID;
-
-  if(share->lockfunc)
-    share->lockfunc(NULL, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE,
-                    share->clientdata);
-
-  if(share->dirty) {
-    if(share->unlockfunc)
-      share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata);
-    return CURLSHE_IN_USE;
-  }
-
-  if(share->hostcache) {
-    Curl_hash_destroy(share->hostcache);
-    share->hostcache = NULL;
-  }
-
-#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
-  if(share->cookies)
-    Curl_cookie_cleanup(share->cookies);
-#endif
-
-#ifdef USE_SSL
-  if(share->sslsession) {
-    size_t i;
-    for(i = 0; i < share->max_ssl_sessions; i++)
-      Curl_ssl_kill_session(&(share->sslsession[i]));
-    free(share->sslsession);
-  }
-#endif
-
-  if(share->unlockfunc)
-    share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata);
-  free(share);
-
-  return CURLSHE_OK;
-}
-
-
-CURLSHcode
-Curl_share_lock(struct SessionHandle *data, curl_lock_data type,
-                curl_lock_access accesstype)
-{
-  struct Curl_share *share = data->share;
-
-  if(share == NULL)
-    return CURLSHE_INVALID;
-
-  if(share->specifier & (1<<type)) {
-    if(share->lockfunc) /* only call this if set! */
-      share->lockfunc(data, type, accesstype, share->clientdata);
-  }
-  /* else if we don't share this, pretend successful lock */
-
-  return CURLSHE_OK;
-}
-
-CURLSHcode
-Curl_share_unlock(struct SessionHandle *data, curl_lock_data type)
-{
-  struct Curl_share *share = data->share;
-
-  if(share == NULL)
-    return CURLSHE_INVALID;
-
-  if(share->specifier & (1<<type)) {
-    if(share->unlockfunc) /* only call this if set! */
-      share->unlockfunc (data, type, share->clientdata);
-  }
-
-  return CURLSHE_OK;
-}
diff --git a/lib/slist.c b/lib/slist.c
deleted file mode 100644 (file)
index 2a30ea6..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#include "curl_memory.h"
-#include "curl_slist.h"
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/* returns last node in linked list */
-static struct curl_slist *slist_get_last(struct curl_slist *list)
-{
-  struct curl_slist     *item;
-
-  /* if caller passed us a NULL, return now */
-  if(!list)
-    return NULL;
-
-  /* loop through to find the last item */
-  item = list;
-  while(item->next) {
-    item = item->next;
-  }
-  return item;
-}
-
-/*
- * curl_slist_append() appends a string to the linked list. It always returns
- * the address of the first record, so that you can use this function as an
- * initialization function as well as an append function. If you find this
- * bothersome, then simply create a separate _init function and call it
- * appropriately from within the program.
- */
-struct curl_slist *curl_slist_append(struct curl_slist *list,
-                                     const char *data)
-{
-  struct curl_slist     *last;
-  struct curl_slist     *new_item;
-
-  new_item = malloc(sizeof(struct curl_slist));
-  if(new_item) {
-    char *dupdata = strdup(data);
-    if(dupdata) {
-      new_item->next = NULL;
-      new_item->data = dupdata;
-    }
-    else {
-      free(new_item);
-      return NULL;
-    }
-  }
-  else
-    return NULL;
-
-  if(list) {
-    last = slist_get_last(list);
-    last->next = new_item;
-    return list;
-  }
-
-  /* if this is the first item, then new_item *is* the list */
-  return new_item;
-}
-
-/*
- * Curl_slist_duplicate() duplicates a linked list. It always returns the
- * address of the first record of the cloned list or NULL in case of an
- * error (or if the input list was NULL).
- */
-struct curl_slist *Curl_slist_duplicate(struct curl_slist *inlist)
-{
-  struct curl_slist *outlist = NULL;
-  struct curl_slist *tmp;
-
-  while(inlist) {
-    tmp = curl_slist_append(outlist, inlist->data);
-
-    if(!tmp) {
-      curl_slist_free_all(outlist);
-      return NULL;
-    }
-
-    outlist = tmp;
-    inlist = inlist->next;
-  }
-  return outlist;
-}
-
-/* be nice and clean up resources */
-void curl_slist_free_all(struct curl_slist *list)
-{
-  struct curl_slist     *next;
-  struct curl_slist     *item;
-
-  if(!list)
-    return;
-
-  item = list;
-  do {
-    next = item->next;
-    Curl_safefree(item->data);
-    free(item);
-    item = next;
-  } while(next);
-}
-
diff --git a/lib/smtp.c b/lib/smtp.c
deleted file mode 100644 (file)
index 5c4c25c..0000000
+++ /dev/null
@@ -1,1750 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * RFC2821 SMTP protocol
- * RFC3207 SMTP over TLS
- * RFC4954 SMTP Authentication
- * RFC2195 CRAM-MD5 authentication
- * RFC2831 DIGEST-MD5 authentication
- * RFC4616 PLAIN authentication
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifndef CURL_DISABLE_SMTP
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef HAVE_UTSNAME_H
-#include <sys/utsname.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef __VMS
-#include <in.h>
-#include <inet.h>
-#endif
-
-#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
-#undef in_addr_t
-#define in_addr_t unsigned long
-#endif
-
-#include <curl/curl.h>
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_if2ip.h"
-#include "curl_hostip.h"
-#include "curl_progress.h"
-#include "curl_transfer.h"
-#include "curl_escape.h"
-#include "curl_http.h" /* for HTTP proxy tunnel stuff */
-#include "curl_socks.h"
-#include "curl_smtp.h"
-
-#include "curl_strtoofft.h"
-#include "curl_strequal.h"
-#include "curl_sslgen.h"
-#include "curl_connect.h"
-#include "curl_strerror.h"
-#include "curl_select.h"
-#include "curl_multiif.h"
-#include "curl_url.h"
-#include "curl_rawstr.h"
-#include "curl_gethostname.h"
-#include "curl_sasl.h"
-#include "curl_warnless.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/* Local API functions */
-static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
-static CURLcode smtp_do(struct connectdata *conn, bool *done);
-static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
-                          bool premature);
-static CURLcode smtp_connect(struct connectdata *conn, bool *done);
-static CURLcode smtp_disconnect(struct connectdata *conn, bool dead);
-static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
-static int smtp_getsock(struct connectdata *conn,
-                        curl_socket_t *socks,
-                        int numsocks);
-static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done);
-static CURLcode smtp_setup_connection(struct connectdata *conn);
-static CURLcode smtp_state_upgrade_tls(struct connectdata *conn);
-
-/*
- * SMTP protocol handler.
- */
-
-const struct Curl_handler Curl_handler_smtp = {
-  "SMTP",                           /* scheme */
-  smtp_setup_connection,            /* setup_connection */
-  smtp_do,                          /* do_it */
-  smtp_done,                        /* done */
-  ZERO_NULL,                        /* do_more */
-  smtp_connect,                     /* connect_it */
-  smtp_multi_statemach,             /* connecting */
-  smtp_doing,                       /* doing */
-  smtp_getsock,                     /* proto_getsock */
-  smtp_getsock,                     /* doing_getsock */
-  ZERO_NULL,                        /* domore_getsock */
-  ZERO_NULL,                        /* perform_getsock */
-  smtp_disconnect,                  /* disconnect */
-  ZERO_NULL,                        /* readwrite */
-  PORT_SMTP,                        /* defport */
-  CURLPROTO_SMTP,                   /* protocol */
-  PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
-};
-
-#ifdef USE_SSL
-/*
- * SMTPS protocol handler.
- */
-
-const struct Curl_handler Curl_handler_smtps = {
-  "SMTPS",                          /* scheme */
-  smtp_setup_connection,            /* setup_connection */
-  smtp_do,                          /* do_it */
-  smtp_done,                        /* done */
-  ZERO_NULL,                        /* do_more */
-  smtp_connect,                     /* connect_it */
-  smtp_multi_statemach,             /* connecting */
-  smtp_doing,                       /* doing */
-  smtp_getsock,                     /* proto_getsock */
-  smtp_getsock,                     /* doing_getsock */
-  ZERO_NULL,                        /* domore_getsock */
-  ZERO_NULL,                        /* perform_getsock */
-  smtp_disconnect,                  /* disconnect */
-  ZERO_NULL,                        /* readwrite */
-  PORT_SMTPS,                       /* defport */
-  CURLPROTO_SMTP | CURLPROTO_SMTPS, /* protocol */
-  PROTOPT_CLOSEACTION | PROTOPT_SSL
-  | PROTOPT_NOURLQUERY              /* flags */
-};
-#endif
-
-#ifndef CURL_DISABLE_HTTP
-/*
- * HTTP-proxyed SMTP protocol handler.
- */
-
-static const struct Curl_handler Curl_handler_smtp_proxy = {
-  "SMTP",                               /* scheme */
-  ZERO_NULL,                            /* setup_connection */
-  Curl_http,                            /* do_it */
-  Curl_http_done,                       /* done */
-  ZERO_NULL,                            /* do_more */
-  ZERO_NULL,                            /* connect_it */
-  ZERO_NULL,                            /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
-  ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* readwrite */
-  PORT_SMTP,                            /* defport */
-  CURLPROTO_HTTP,                       /* protocol */
-  PROTOPT_NONE                          /* flags */
-};
-
-#ifdef USE_SSL
-/*
- * HTTP-proxyed SMTPS protocol handler.
- */
-
-static const struct Curl_handler Curl_handler_smtps_proxy = {
-  "SMTPS",                              /* scheme */
-  ZERO_NULL,                            /* setup_connection */
-  Curl_http,                            /* do_it */
-  Curl_http_done,                       /* done */
-  ZERO_NULL,                            /* do_more */
-  ZERO_NULL,                            /* connect_it */
-  ZERO_NULL,                            /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
-  ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* readwrite */
-  PORT_SMTPS,                           /* defport */
-  CURLPROTO_HTTP,                       /* protocol */
-  PROTOPT_NONE                          /* flags */
-};
-#endif
-#endif
-
-/* Function that checks for an ending smtp status code at the start of the
-   given string, but also detects the supported authentication mechanisms
-   from  the EHLO AUTH response. */
-static int smtp_endofresp(struct pingpong *pp, int *resp)
-{
-  char *line = pp->linestart_resp;
-  size_t len = pp->nread_resp;
-  struct connectdata *conn = pp->conn;
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
-  int result;
-  size_t wordlen;
-
-  if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
-    return FALSE;       /* Nothing for us */
-
-  if((result = (line[3] == ' ')) != 0)
-    *resp = curlx_sltosi(strtol(line, NULL, 10));
-
-  line += 4;
-  len -= 4;
-
-  if(smtpc->state == SMTP_EHLO && len >= 4 && !memcmp(line, "SIZE", 4)) {
-    DEBUGF(infof(conn->data, "Server supports SIZE extension.\n"));
-    smtpc->size_supported = true;
-  }
-
-  if(smtpc->state == SMTP_EHLO && len >= 5 && !memcmp(line, "AUTH ", 5)) {
-    line += 5;
-    len -= 5;
-
-    for(;;) {
-      while(len &&
-            (*line == ' ' || *line == '\t' ||
-             *line == '\r' || *line == '\n')) {
-        line++;
-        len--;
-      }
-
-      if(!len)
-        break;
-
-      for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
-            line[wordlen] != '\t' && line[wordlen] != '\r' &&
-            line[wordlen] != '\n';)
-        wordlen++;
-
-      if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
-        smtpc->authmechs |= SASL_MECH_LOGIN;
-      else if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
-        smtpc->authmechs |= SASL_MECH_PLAIN;
-      else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
-        smtpc->authmechs |= SASL_MECH_CRAM_MD5;
-      else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
-        smtpc->authmechs |= SASL_MECH_DIGEST_MD5;
-      else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
-        smtpc->authmechs |= SASL_MECH_GSSAPI;
-      else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
-        smtpc->authmechs |= SASL_MECH_EXTERNAL;
-      else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
-        smtpc->authmechs |= SASL_MECH_NTLM;
-
-      line += wordlen;
-      len -= wordlen;
-    }
-  }
-
-  return result;
-}
-
-/* This is the ONLY way to change SMTP state! */
-static void state(struct connectdata *conn, smtpstate newstate)
-{
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
-  /* for debug purposes */
-  static const char * const names[] = {
-    "STOP",
-    "SERVERGREET",
-    "EHLO",
-    "HELO",
-    "STARTTLS",
-    "UPGRADETLS",
-    "AUTH_PLAIN",
-    "AUTH_LOGIN",
-    "AUTH_PASSWD",
-    "AUTH_CRAMMD5",
-    "AUTH_DIGESTMD5",
-    "AUTH_DIGESTMD5_RESP",
-    "AUTH_NTLM",
-    "AUTH_NTLM_TYPE2MSG",
-    "AUTH",
-    "MAIL",
-    "RCPT",
-    "DATA",
-    "POSTDATA",
-    "QUIT",
-    /* LAST */
-  };
-
-  if(smtpc->state != newstate)
-    infof(conn->data, "SMTP %p state change from %s to %s\n",
-          smtpc, names[smtpc->state], names[newstate]);
-#endif
-
-  smtpc->state = newstate;
-}
-
-static CURLcode smtp_state_ehlo(struct connectdata *conn)
-{
-  CURLcode result;
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
-
-  smtpc->authmechs = 0;         /* No known authentication mechanisms yet */
-  smtpc->authused = 0;          /* Clear the authentication mechanism used
-                                   for esmtp connections */
-
-  /* send EHLO */
-  result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
-
-  if(result)
-    return result;
-
-  state(conn, SMTP_EHLO);
-
-  return CURLE_OK;
-}
-
-static CURLcode smtp_state_helo(struct connectdata *conn)
-{
-  CURLcode result;
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
-
-  smtpc->authused = 0;          /* No authentication mechanism used in smtp
-                                   connections */
-
-  /* send HELO */
-  result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
-
-  if(result)
-    return result;
-
-  state(conn, SMTP_HELO);
-
-  return CURLE_OK;
-}
-
-static CURLcode smtp_authenticate(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
-  char *initresp = NULL;
-  const char *mech = NULL;
-  size_t len = 0;
-  smtpstate state1 = SMTP_STOP;
-  smtpstate state2 = SMTP_STOP;
-
-  /* Check we have a username and password to authenticate with and end the
-     connect phase if we don't */
-  if(!conn->bits.user_passwd) {
-    state(conn, SMTP_STOP);
-
-    return result;
-  }
-
-  /* Check supported authentication mechanisms by decreasing order of
-     security */
-#ifndef CURL_DISABLE_CRYPTO_AUTH
-  if(smtpc->authmechs & SASL_MECH_DIGEST_MD5) {
-    mech = "DIGEST-MD5";
-    state1 = SMTP_AUTH_DIGESTMD5;
-    smtpc->authused = SASL_MECH_DIGEST_MD5;
-  }
-  else if(smtpc->authmechs & SASL_MECH_CRAM_MD5) {
-    mech = "CRAM-MD5";
-    state1 = SMTP_AUTH_CRAMMD5;
-    smtpc->authused = SASL_MECH_CRAM_MD5;
-  }
-  else
-#endif
-#ifdef USE_NTLM
-  if(smtpc->authmechs & SASL_MECH_NTLM) {
-    mech = "NTLM";
-    state1 = SMTP_AUTH_NTLM;
-    state2 = SMTP_AUTH_NTLM_TYPE2MSG;
-    smtpc->authused = SASL_MECH_NTLM;
-    result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
-                                                 &conn->ntlm,
-                                                 &initresp, &len);
-  }
-  else
-#endif
-  if(smtpc->authmechs & SASL_MECH_LOGIN) {
-    mech = "LOGIN";
-    state1 = SMTP_AUTH_LOGIN;
-    state2 = SMTP_AUTH_PASSWD;
-    smtpc->authused = SASL_MECH_LOGIN;
-    result = Curl_sasl_create_login_message(conn->data, conn->user,
-                                            &initresp, &len);
-  }
-  else if(smtpc->authmechs & SASL_MECH_PLAIN) {
-    mech = "PLAIN";
-    state1 = SMTP_AUTH_PLAIN;
-    state2 = SMTP_AUTH;
-    smtpc->authused = SASL_MECH_PLAIN;
-    result = Curl_sasl_create_plain_message(conn->data, conn->user,
-                                            conn->passwd, &initresp, &len);
-  }
-  else {
-    infof(conn->data, "No known authentication mechanisms supported!\n");
-    result = CURLE_LOGIN_DENIED; /* Other mechanisms not supported */
-  }
-
-  if(!result) {
-    if(initresp &&
-       strlen(mech) + len <= 512 - 8) { /* AUTH <mech> ...<crlf> */
-       result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
-
-      if(!result)
-        state(conn, state2);
-    }
-    else {
-      result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
-
-      if(!result)
-        state(conn, state1);
-    }
-    Curl_safefree(initresp);
-  }
-
-  return result;
-}
-
-/* For the SMTP "protocol connect" and "doing" phases only */
-static int smtp_getsock(struct connectdata *conn,
-                        curl_socket_t *socks,
-                        int numsocks)
-{
-  return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
-}
-
-#ifdef USE_SSL
-static void smtp_to_smtps(struct connectdata *conn)
-{
-  conn->handler = &Curl_handler_smtps;
-}
-#else
-#define smtp_to_smtps(x) Curl_nop_stmt
-#endif
-
-/* For the initial server greeting */
-static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
-                                            int smtpcode,
-                                            smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode/100 != 2) {
-    failf(data, "Got unexpected smtp-server response: %d", smtpcode);
-    return CURLE_FTP_WEIRD_SERVER_REPLY;
-  }
-
-  result = smtp_state_ehlo(conn);
-
-  return result;
-}
-
-/* For STARTTLS responses */
-static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
-                                         int smtpcode,
-                                         smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 220) {
-    if(data->set.use_ssl != CURLUSESSL_TRY) {
-      failf(data, "STARTTLS denied. %c", smtpcode);
-      result = CURLE_USE_SSL_FAILED;
-    }
-    else
-      result = smtp_authenticate(conn);
-  }
-  else {
-    if(data->state.used_interface == Curl_if_multi) {
-      state(conn, SMTP_UPGRADETLS);
-      result = smtp_state_upgrade_tls(conn);
-    }
-    else {
-      result = Curl_ssl_connect(conn, FIRSTSOCKET);
-      if(CURLE_OK == result) {
-        smtp_to_smtps(conn);
-        result = smtp_state_ehlo(conn);
-      }
-    }
-  }
-
-  return result;
-}
-
-static CURLcode smtp_state_upgrade_tls(struct connectdata *conn)
-{
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
-  CURLcode result;
-
-  result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
-
-  if(smtpc->ssldone) {
-    smtp_to_smtps(conn);
-    result = smtp_state_ehlo(conn);
-  }
-
-  return result;
-}
-
-/* For EHLO responses */
-static CURLcode smtp_state_ehlo_resp(struct connectdata *conn,
-                                     int smtpcode,
-                                     smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode/100 != 2) {
-    if((data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) &&
-     !conn->bits.user_passwd)
-      result = smtp_state_helo(conn);
-    else {
-      failf(data, "Remote access denied: %d", smtpcode);
-      result = CURLE_REMOTE_ACCESS_DENIED;
-    }
-  }
-  else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
-    /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
-       to TLS connection now */
-    result = Curl_pp_sendf(&conn->proto.smtpc.pp, "STARTTLS");
-    state(conn, SMTP_STARTTLS);
-  }
-  else
-    result = smtp_authenticate(conn);
-
-  return result;
-}
-
-/* For HELO responses */
-static CURLcode smtp_state_helo_resp(struct connectdata *conn,
-                                     int smtpcode,
-                                     smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode/100 != 2) {
-    failf(data, "Remote access denied: %d", smtpcode);
-    result = CURLE_REMOTE_ACCESS_DENIED;
-  }
-  else
-    /* End of connect phase */
-    state(conn, SMTP_STOP);
-
-  return result;
-}
-
-/* For AUTH PLAIN (without initial response) responses */
-static CURLcode smtp_state_auth_plain_resp(struct connectdata *conn,
-                                           int smtpcode,
-                                           smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  size_t len = 0;
-  char *plainauth = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 334) {
-    failf(data, "Access denied: %d", smtpcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the authorisation message */
-    result = Curl_sasl_create_plain_message(conn->data, conn->user,
-                                            conn->passwd, &plainauth, &len);
-
-    /* Send the message */
-    if(!result) {
-      if(plainauth) {
-        result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth);
-
-        if(!result)
-          state(conn, SMTP_AUTH);
-      }
-
-      Curl_safefree(plainauth);
-    }
-  }
-
-  return result;
-}
-
-/* For AUTH LOGIN (without initial response) responses */
-static CURLcode smtp_state_auth_login_resp(struct connectdata *conn,
-                                           int smtpcode,
-                                           smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  size_t len = 0;
-  char *authuser = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 334) {
-    failf(data, "Access denied: %d", smtpcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the user message */
-    result = Curl_sasl_create_login_message(conn->data, conn->user,
-                                            &authuser, &len);
-
-    /* Send the user */
-    if(!result) {
-      if(authuser) {
-        result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser);
-
-        if(!result)
-          state(conn, SMTP_AUTH_PASSWD);
-      }
-
-      Curl_safefree(authuser);
-    }
-  }
-
-  return result;
-}
-
-/* For responses to user entry of AUTH LOGIN */
-static CURLcode smtp_state_auth_passwd_resp(struct connectdata *conn,
-                                            int smtpcode,
-                                            smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  size_t len = 0;
-  char *authpasswd = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 334) {
-    failf(data, "Access denied: %d", smtpcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the password message */
-    result = Curl_sasl_create_login_message(conn->data, conn->passwd,
-                                            &authpasswd, &len);
-
-    /* Send the password */
-    if(!result) {
-      if(authpasswd) {
-        result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd);
-
-        if(!result)
-          state(conn, SMTP_AUTH);
-      }
-
-      Curl_safefree(authpasswd);
-    }
-  }
-
-  return result;
-}
-
-#ifndef CURL_DISABLE_CRYPTO_AUTH
-/* For AUTH CRAM-MD5 responses */
-static CURLcode smtp_state_auth_cram_resp(struct connectdata *conn,
-                                          int smtpcode,
-                                          smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  char *chlg64 = data->state.buffer;
-  size_t len = 0;
-  char *rplyb64 = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 334) {
-    failf(data, "Access denied: %d", smtpcode);
-    return CURLE_LOGIN_DENIED;
-  }
-
-  /* Get the challenge */
-  for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
-    ;
-
-  /* Terminate the challenge */
-  if(*chlg64 != '=') {
-    for(len = strlen(chlg64); len--;)
-      if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
-         chlg64[len] != '\t')
-        break;
-
-    if(++len) {
-      chlg64[len] = '\0';
-    }
-  }
-
-  /* Create the response message */
-  result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
-                                             conn->passwd, &rplyb64, &len);
-
-  /* Send the response */
-  if(!result) {
-    if(rplyb64) {
-      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
-
-      if(!result)
-        state(conn, SMTP_AUTH);
-    }
-
-    Curl_safefree(rplyb64);
-  }
-
-  return result;
-}
-
-/* For AUTH DIGEST-MD5 challenge responses */
-static CURLcode smtp_state_auth_digest_resp(struct connectdata *conn,
-                                            int smtpcode,
-                                            smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  char *chlg64 = data->state.buffer;
-  size_t len = 0;
-  char *rplyb64 = NULL;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 334) {
-    failf(data, "Access denied: %d", smtpcode);
-    return CURLE_LOGIN_DENIED;
-  }
-
-  /* Get the challenge */
-  for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
-    ;
-
-  /* Create the response message */
-  result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
-                                               conn->passwd, "smtp",
-                                               &rplyb64, &len);
-
-  /* Send the response */
-  if(!result) {
-    if(rplyb64) {
-      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
-
-      if(!result)
-        state(conn, SMTP_AUTH_DIGESTMD5_RESP);
-    }
-
-    Curl_safefree(rplyb64);
-  }
-
-  return result;
-}
-
-/* For AUTH DIGEST-MD5 challenge-response responses */
-static CURLcode smtp_state_auth_digest_resp_resp(struct connectdata *conn,
-                                                 int smtpcode,
-                                                 smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 334) {
-    failf(data, "Authentication failed: %d", smtpcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Send an empty response */
-    result = Curl_pp_sendf(&conn->proto.smtpc.pp, "");
-
-    if(!result)
-      state(conn, SMTP_AUTH);
-  }
-
-  return result;
-}
-
-#endif
-
-#ifdef USE_NTLM
-/* For AUTH NTLM (without initial response) responses */
-static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn,
-                                          int smtpcode,
-                                          smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  char *type1msg = NULL;
-  size_t len = 0;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 334) {
-    failf(data, "Access denied: %d", smtpcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the type-1 message */
-    result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
-                                                 &conn->ntlm,
-                                                 &type1msg, &len);
-
-    /* Send the message */
-    if(!result) {
-      if(type1msg) {
-        result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1msg);
-
-        if(!result)
-          state(conn, SMTP_AUTH_NTLM_TYPE2MSG);
-      }
-
-      Curl_safefree(type1msg);
-    }
-  }
-
-  return result;
-}
-
-/* For NTLM type-2 responses (sent in reponse to our type-1 message) */
-static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
-                                                   int smtpcode,
-                                                   smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  char *type3msg = NULL;
-  size_t len = 0;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 334) {
-    failf(data, "Access denied: %d", smtpcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else {
-    /* Create the type-3 message */
-    result = Curl_sasl_create_ntlm_type3_message(data,
-                                                 data->state.buffer + 4,
-                                                 conn->user, conn->passwd,
-                                                 &conn->ntlm,
-                                                 &type3msg, &len);
-
-    /* Send the message */
-    if(!result) {
-      if(type3msg) {
-        result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg);
-
-        if(!result)
-          state(conn, SMTP_AUTH);
-      }
-
-      Curl_safefree(type3msg);
-    }
-  }
-
-  return result;
-}
-#endif
-
-/* For the final responses to the AUTH sequence */
-static CURLcode smtp_state_auth_resp(struct connectdata *conn,
-                                     int smtpcode,
-                                     smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 235) {
-    failf(data, "Authentication failed: %d", smtpcode);
-    result = CURLE_LOGIN_DENIED;
-  }
-  else
-    /* End of connect phase */
-    state(conn, SMTP_STOP);
-
-  return result;
-}
-
-/* Start the DO phase */
-static CURLcode smtp_mail(struct connectdata *conn)
-{
-  char *from = NULL;
-  char *auth = NULL;
-  char *size = NULL;
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  /* Calculate the FROM parameter */
-  if(!data->set.str[STRING_MAIL_FROM])
-    /* Null reverse-path, RFC-2821, sect. 3.7 */
-    from = strdup("<>");
-  else if(data->set.str[STRING_MAIL_FROM][0] == '<')
-    from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
-  else
-    from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
-
-  if(!from)
-    return CURLE_OUT_OF_MEMORY;
-
-  /* Calculate the optional AUTH parameter */
-  if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.authused) {
-    if(data->set.str[STRING_MAIL_AUTH][0] != '\0')
-      auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]);
-    else
-      /* Empty AUTH, RFC-2554, sect. 5 */
-      auth = strdup("<>");
-
-    if(!auth) {
-      Curl_safefree(from);
-
-      return CURLE_OUT_OF_MEMORY;
-    }
-  }
-
-  /* calculate the optional SIZE parameter */
-  if(conn->proto.smtpc.size_supported && conn->data->set.infilesize > 0) {
-    size = aprintf("%" FORMAT_OFF_T, data->set.infilesize);
-
-    if(!size) {
-      Curl_safefree(from);
-      Curl_safefree(auth);
-
-      return CURLE_OUT_OF_MEMORY;
-    }
-  }
-
-  /* Send the MAIL command */
-  if(!auth && !size)
-    result = Curl_pp_sendf(&conn->proto.smtpc.pp,
-                           "MAIL FROM:%s", from);
-  else if(auth && !size)
-    result = Curl_pp_sendf(&conn->proto.smtpc.pp,
-                           "MAIL FROM:%s AUTH=%s", from, auth);
-  else if(auth && size)
-    result = Curl_pp_sendf(&conn->proto.smtpc.pp,
-                           "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size);
-  else
-    result = Curl_pp_sendf(&conn->proto.smtpc.pp,
-                           "MAIL FROM:%s SIZE=%s", from, size);
-
-  Curl_safefree(from);
-  Curl_safefree(auth);
-  Curl_safefree(size);
-
-  if(result)
-    return result;
-
-  state(conn, SMTP_MAIL);
-
-  return result;
-}
-
-static CURLcode smtp_rcpt_to(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
-
-  /* Send the RCPT TO command */
-  if(smtpc->rcpt) {
-    if(smtpc->rcpt->data[0] == '<')
-      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
-                             smtpc->rcpt->data);
-    else
-      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
-                             smtpc->rcpt->data);
-    if(!result)
-      state(conn, SMTP_RCPT);
-  }
-
-  return result;
-}
-
-/* For MAIL responses */
-static CURLcode smtp_state_mail_resp(struct connectdata *conn,
-                                     int smtpcode,
-                                     smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode/100 != 2) {
-    failf(data, "MAIL failed: %d", smtpcode);
-    result = CURLE_SEND_ERROR;
-    state(conn, SMTP_STOP);
-  }
-  else {
-    struct smtp_conn *smtpc = &conn->proto.smtpc;
-    smtpc->rcpt = data->set.mail_rcpt;
-
-    result = smtp_rcpt_to(conn);
-  }
-
-  return result;
-}
-
-/* For RCPT responses */
-static CURLcode smtp_state_rcpt_resp(struct connectdata *conn,
-                                     int smtpcode,
-                                     smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode/100 != 2) {
-    failf(data, "RCPT failed: %d", smtpcode);
-    result = CURLE_SEND_ERROR;
-    state(conn, SMTP_STOP);
-  }
-  else {
-    struct smtp_conn *smtpc = &conn->proto.smtpc;
-
-    if(smtpc->rcpt) {
-      smtpc->rcpt = smtpc->rcpt->next;
-      result = smtp_rcpt_to(conn);
-
-      /* If we failed or still are sending RCPT data then return */
-      if(result || smtpc->rcpt)
-        return result;
-    }
-
-    /* Send the DATA command */
-    result = Curl_pp_sendf(&conn->proto.smtpc.pp, "DATA");
-
-    if(result)
-      return result;
-
-    state(conn, SMTP_DATA);
-  }
-
-  return result;
-}
-
-/* For DATA response */
-static CURLcode smtp_state_data_resp(struct connectdata *conn,
-                                     int smtpcode,
-                                     smtpstate instate)
-{
-  struct SessionHandle *data = conn->data;
-  struct FTP *smtp = data->state.proto.smtp;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 354) {
-    state(conn, SMTP_STOP);
-    return CURLE_SEND_ERROR;
-  }
-
-  /* SMTP upload */
-  Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
-                      FIRSTSOCKET, smtp->bytecountp);
-
-  /* End of do phase */
-  state(conn, SMTP_STOP);
-
-  return CURLE_OK;
-}
-
-/* For POSTDATA responses, which are received after the entire DATA
-   part has been sent to the server */
-static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
-                                         int smtpcode,
-                                         smtpstate instate)
-{
-  CURLcode result = CURLE_OK;
-
-  (void)instate; /* no use for this yet */
-
-  if(smtpcode != 250)
-    result = CURLE_RECV_ERROR;
-
-  /* End of done phase */
-  state(conn, SMTP_STOP);
-
-  return result;
-}
-
-static CURLcode smtp_statemach_act(struct connectdata *conn)
-{
-  CURLcode result;
-  curl_socket_t sock = conn->sock[FIRSTSOCKET];
-  struct SessionHandle *data = conn->data;
-  int smtpcode;
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
-  struct pingpong *pp = &smtpc->pp;
-  size_t nread = 0;
-
-  /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
-  if(smtpc->state == SMTP_UPGRADETLS)
-    return smtp_state_upgrade_tls(conn);
-
-  /* Flush any data that needs to be sent */
-  if(pp->sendleft)
-    return Curl_pp_flushsend(pp);
-
-  /* Read the response from the server */
-  result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
-  if(result)
-    return result;
-
-  /* Store the latest response for later retrieval */
-  if(smtpc->state != SMTP_QUIT)
-    data->info.httpcode = smtpcode;
-
-  if(smtpcode) {
-    /* We have now received a full SMTP server response */
-    switch(smtpc->state) {
-    case SMTP_SERVERGREET:
-      result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
-      break;
-
-    case SMTP_EHLO:
-      result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
-      break;
-
-    case SMTP_HELO:
-      result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
-      break;
-
-    case SMTP_STARTTLS:
-      result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
-      break;
-
-    case SMTP_AUTH_PLAIN:
-      result = smtp_state_auth_plain_resp(conn, smtpcode, smtpc->state);
-      break;
-
-    case SMTP_AUTH_LOGIN:
-      result = smtp_state_auth_login_resp(conn, smtpcode, smtpc->state);
-      break;
-
-    case SMTP_AUTH_PASSWD:
-      result = smtp_state_auth_passwd_resp(conn, smtpcode, smtpc->state);
-      break;
-
-#ifndef CURL_DISABLE_CRYPTO_AUTH
-    case SMTP_AUTH_CRAMMD5:
-      result = smtp_state_auth_cram_resp(conn, smtpcode, smtpc->state);
-      break;
-
-    case SMTP_AUTH_DIGESTMD5:
-      result = smtp_state_auth_digest_resp(conn, smtpcode, smtpc->state);
-      break;
-
-    case SMTP_AUTH_DIGESTMD5_RESP:
-      result = smtp_state_auth_digest_resp_resp(conn, smtpcode, smtpc->state);
-      break;
-#endif
-
-#ifdef USE_NTLM
-    case SMTP_AUTH_NTLM:
-      result = smtp_state_auth_ntlm_resp(conn, smtpcode, smtpc->state);
-      break;
-
-    case SMTP_AUTH_NTLM_TYPE2MSG:
-      result = smtp_state_auth_ntlm_type2msg_resp(conn, smtpcode,
-                                                  smtpc->state);
-      break;
-#endif
-
-    case SMTP_AUTH:
-      result = smtp_state_auth_resp(conn, smtpcode, smtpc->state);
-      break;
-
-    case SMTP_MAIL:
-      result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
-      break;
-
-    case SMTP_RCPT:
-      result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
-      break;
-
-    case SMTP_DATA:
-      result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
-      break;
-
-    case SMTP_POSTDATA:
-      result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
-      break;
-
-    case SMTP_QUIT:
-      /* fallthrough, just stop! */
-    default:
-      /* internal error */
-      state(conn, SMTP_STOP);
-      break;
-    }
-  }
-
-  return result;
-}
-
-/* Called repeatedly until done from curl_multi.c */
-static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
-{
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
-  CURLcode result;
-
-  if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone)
-    result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
-  else
-    result = Curl_pp_multi_statemach(&smtpc->pp);
-
-  *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
-
-  return result;
-}
-
-static CURLcode smtp_easy_statemach(struct connectdata *conn)
-{
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
-  struct pingpong *pp = &smtpc->pp;
-  CURLcode result = CURLE_OK;
-
-  while(smtpc->state != SMTP_STOP) {
-    result = Curl_pp_easy_statemach(pp);
-    if(result)
-      break;
-  }
-
-  return result;
-}
-
-/* Allocate and initialize the SMTP struct for the current SessionHandle if
-   required */
-static CURLcode smtp_init(struct connectdata *conn)
-{
-  struct SessionHandle *data = conn->data;
-  struct FTP *smtp = data->state.proto.smtp;
-
-  if(!smtp) {
-    smtp = data->state.proto.smtp = calloc(sizeof(struct FTP), 1);
-    if(!smtp)
-      return CURLE_OUT_OF_MEMORY;
-  }
-
-  /* Get some initial data into the smtp struct */
-  smtp->bytecountp = &data->req.bytecount;
-
-  /* No need to duplicate user+password, the connectdata struct won't change
-     during a session, but we re-init them here since on subsequent inits
-     since the conn struct may have changed or been replaced.
-  */
-  smtp->user = conn->user;
-  smtp->passwd = conn->passwd;
-
-  return CURLE_OK;
-}
-
-/***********************************************************************
- *
- * smtp_connect()
- *
- * This function should do everything that is to be considered a part of
- * the connection phase.
- *
- * The variable pointed to by 'done' will be TRUE if the protocol-layer
- * connect phase is done when this function returns, or FALSE if not. When
- * called as a part of the easy interface, it will always be TRUE.
- */
-static CURLcode smtp_connect(struct connectdata *conn, bool *done)
-{
-  CURLcode result;
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
-  struct SessionHandle *data = conn->data;
-  struct pingpong *pp = &smtpc->pp;
-  const char *path = conn->data->state.path;
-  char localhost[HOSTNAME_MAX + 1];
-
-  *done = FALSE; /* default to not done yet */
-
-  /* If there already is a protocol-specific struct allocated for this
-     sessionhandle, deal with it */
-  Curl_reset_reqproto(conn);
-
-  result = smtp_init(conn);
-  if(CURLE_OK != result)
-    return result;
-
-  /* We always support persistent connections on smtp */
-  conn->bits.close = FALSE;
-
-  pp->response_time = RESP_TIMEOUT; /* set default response time-out */
-  pp->statemach_act = smtp_statemach_act;
-  pp->endofresp = smtp_endofresp;
-  pp->conn = conn;
-
-  if((conn->handler->protocol & CURLPROTO_SMTPS) &&
-      data->state.used_interface != Curl_if_multi) {
-    /* SMTPS is simply smtp with SSL for the control channel */
-    /* so perform the SSL initialization for this socket */
-    result = Curl_ssl_connect(conn, FIRSTSOCKET);
-    if(result)
-      return result;
-  }
-
-  /* Initialise the response reader stuff */
-  Curl_pp_init(pp);
-
-  /* Set the default response time-out */
-  pp->response_time = RESP_TIMEOUT;
-  pp->statemach_act = smtp_statemach_act;
-  pp->endofresp = smtp_endofresp;
-  pp->conn = conn;
-
-  /* Calculate the path if necessary */
-  if(!*path) {
-    if(!Curl_gethostname(localhost, sizeof(localhost)))
-      path = localhost;
-    else
-      path = "localhost";
-  }
-
-  /* URL decode the path and use it as the domain in our EHLO */
-  result = Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
-  if(result)
-    return result;
-
-  /* Start off waiting for the server greeting response */
-  state(conn, SMTP_SERVERGREET);
-
-  if(data->state.used_interface == Curl_if_multi)
-    result = smtp_multi_statemach(conn, done);
-  else {
-    result = smtp_easy_statemach(conn);
-    if(!result)
-      *done = TRUE;
-  }
-
-  return result;
-}
-
-/***********************************************************************
- *
- * smtp_done()
- *
- * The DONE function. This does what needs to be done after a single DO has
- * performed.
- *
- * Input argument is already checked for validity.
- */
-static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
-                          bool premature)
-{
-  struct SessionHandle *data = conn->data;
-  struct FTP *smtp = data->state.proto.smtp;
-  CURLcode result = CURLE_OK;
-  ssize_t bytes_written;
-
-  (void)premature;
-
-  if(!smtp)
-    /* When the easy handle is removed from the multi while libcurl is still
-     * trying to resolve the host name, it seems that the smtp struct is not
-     * yet initialized, but the removal action calls Curl_done() which calls
-     * this function. So we simply return success if no smtp pointer is set.
-     */
-    return CURLE_OK;
-
-  if(status) {
-    conn->bits.close = TRUE; /* marked for closure */
-    result = status;         /* use the already set error code */
-  }
-  else if(!data->set.connect_only) {
-    struct smtp_conn *smtpc = &conn->proto.smtpc;
-    struct pingpong *pp = &smtpc->pp;
-
-    /* Send the end of block data */
-    result = Curl_write(conn,
-                        conn->writesockfd,  /* socket to send to */
-                        SMTP_EOB,           /* buffer pointer */
-                        SMTP_EOB_LEN,       /* buffer size */
-                        &bytes_written);    /* actually sent away */
-
-    if(result)
-      return result;
-
-    if(bytes_written != SMTP_EOB_LEN) {
-      /* The whole chunk was not sent so keep it around and adjust the
-         pingpong structure accordingly */
-      pp->sendthis = strdup(SMTP_EOB);
-      pp->sendsize = SMTP_EOB_LEN;
-      pp->sendleft = SMTP_EOB_LEN - bytes_written;
-    }
-    else
-      /* Successfully sent so adjust the response timeout relative to now */
-      pp->response = Curl_tvnow();
-
-    state(conn, SMTP_POSTDATA);
-
-    /* Run the state-machine
-
-       TODO: when the multi interface is used, this _really_ should be using
-       the smtp_multi_statemach function but we have no general support for
-       non-blocking DONE operations, not in the multi state machine and with
-       Curl_done() invokes on several places in the code!
-    */
-    result = smtp_easy_statemach(conn);
-  }
-
-  /* Clear the transfer mode for the next connection */
-  smtp->transfer = FTPTRANSFER_BODY;
-
-  return result;
-}
-
-/***********************************************************************
- *
- * smtp_perform()
- *
- * This is the actual DO function for SMTP. Get a file/directory according to
- * the options previously setup.
- */
-static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
-                             bool *dophase_done)
-{
-  /* This is SMTP and no proxy */
-  CURLcode result = CURLE_OK;
-
-  DEBUGF(infof(conn->data, "DO phase starts\n"));
-
-  if(conn->data->set.opt_no_body) {
-    /* Requested no body means no transfer */
-    struct FTP *smtp = conn->data->state.proto.smtp;
-    smtp->transfer = FTPTRANSFER_INFO;
-  }
-
-  *dophase_done = FALSE; /* not done yet */
-
-  /* Start the first command in the DO phase */
-  result = smtp_mail(conn);
-  if(result)
-    return result;
-
-  /* Run the state-machine */
-  if(conn->data->state.used_interface == Curl_if_multi)
-    result = smtp_multi_statemach(conn, dophase_done);
-  else {
-    result = smtp_easy_statemach(conn);
-    *dophase_done = TRUE; /* with the easy interface we are done here */
-  }
-  *connected = conn->bits.tcpconnect[FIRSTSOCKET];
-
-  if(*dophase_done)
-    DEBUGF(infof(conn->data, "DO phase is complete\n"));
-
-  return result;
-}
-
-/***********************************************************************
- *
- * smtp_do()
- *
- * This function is registered as 'curl_do' function. It decodes the path
- * parts etc as a wrapper to the actual DO function (smtp_perform).
- *
- * The input argument is already checked for validity.
- */
-static CURLcode smtp_do(struct connectdata *conn, bool *done)
-{
-  CURLcode retcode = CURLE_OK;
-
-  *done = FALSE; /* default to false */
-
-  /*
-    Since connections can be re-used between SessionHandles, this might be a
-    connection already existing but on a fresh SessionHandle struct so we must
-    make sure we have a good 'struct SMTP' to play with. For new connections,
-    the struct SMTP is allocated and setup in the smtp_connect() function.
-  */
-  Curl_reset_reqproto(conn);
-  retcode = smtp_init(conn);
-  if(retcode)
-    return retcode;
-
-  retcode = smtp_regular_transfer(conn, done);
-
-  return retcode;
-}
-
-/***********************************************************************
- *
- * smtp_quit()
- *
- * This should be called before calling sclose().  We should then wait for the
- * response from the server before returning. The calling code should then try
- * to close the connection.
- */
-static CURLcode smtp_quit(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-
-  result = Curl_pp_sendf(&conn->proto.smtpc.pp, "QUIT");
-  if(result)
-    return result;
-
-  state(conn, SMTP_QUIT);
-
-  result = smtp_easy_statemach(conn);
-
-  return result;
-}
-
-/***********************************************************************
- *
- * smtp_disconnect()
- *
- * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
- * resources. BLOCKING.
- */
-static CURLcode smtp_disconnect(struct connectdata *conn,
-                                bool dead_connection)
-{
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
-
-  /* We cannot send quit unconditionally. If this connection is stale or
-     bad in any way, sending quit and waiting around here will make the
-     disconnect wait in vain and cause more problems than we need to */
-
-  /* The SMTP session may or may not have been allocated/setup at this
-     point! */
-  if(!dead_connection && smtpc->pp.conn)
-    (void)smtp_quit(conn); /* ignore errors on the LOGOUT */
-
-  /* Disconnect from the server */
-  Curl_pp_disconnect(&smtpc->pp);
-
-  /* Cleanup the SASL module */
-  Curl_sasl_cleanup(conn, smtpc->authused);
-
-  /* Cleanup our connection based variables */
-  Curl_safefree(smtpc->domain);
-
-  return CURLE_OK;
-}
-
-/* Call this when the DO phase has completed */
-static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
-{
-  struct FTP *smtp = conn->data->state.proto.smtp;
-
-  (void)connected;
-
-  if(smtp->transfer != FTPTRANSFER_BODY)
-    /* no data to transfer */
-    Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
-
-  return CURLE_OK;
-}
-
-/* Called from curl_multi.c while DOing */
-static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
-{
-  CURLcode result = smtp_multi_statemach(conn, dophase_done);
-
-  if(result)
-    DEBUGF(infof(conn->data, "DO phase failed\n"));
-  else {
-    if(*dophase_done) {
-      result = smtp_dophase_done(conn, FALSE /* not connected */);
-
-      DEBUGF(infof(conn->data, "DO phase is complete\n"));
-    }
-  }
-
-  return result;
-}
-
-/***********************************************************************
- *
- * smtp_regular_transfer()
- *
- * The input argument is already checked for validity.
- *
- * Performs all commands done before a regular transfer between a local and a
- * remote host.
- */
-static CURLcode smtp_regular_transfer(struct connectdata *conn,
-                                      bool *dophase_done)
-{
-  CURLcode result = CURLE_OK;
-  bool connected = FALSE;
-  struct SessionHandle *data = conn->data;
-
-  /* Make sure size is unknown at this point */
-  data->req.size = -1;
-
-  Curl_pgrsSetUploadCounter(data, 0);
-  Curl_pgrsSetDownloadCounter(data, 0);
-  Curl_pgrsSetUploadSize(data, 0);
-  Curl_pgrsSetDownloadSize(data, 0);
-
-  result = smtp_perform(conn, &connected, dophase_done);
-
-  if(CURLE_OK == result) {
-    if(!*dophase_done)
-      /* The DO phase has not completed yet */
-      return CURLE_OK;
-
-    result = smtp_dophase_done(conn, connected);
-    if(result)
-      return result;
-  }
-
-  return result;
-}
-
-static CURLcode smtp_setup_connection(struct connectdata *conn)
-{
-  struct SessionHandle *data = conn->data;
-
-  if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
-    /* Unless we have asked to tunnel smtp operations through the proxy, we
-       switch and use HTTP operations only */
-#ifndef CURL_DISABLE_HTTP
-    if(conn->handler == &Curl_handler_smtp)
-      conn->handler = &Curl_handler_smtp_proxy;
-    else {
-#ifdef USE_SSL
-      conn->handler = &Curl_handler_smtps_proxy;
-#else
-      failf(data, "SMTPS not supported!");
-      return CURLE_UNSUPPORTED_PROTOCOL;
-#endif
-    }
-
-    /* We explicitly mark this connection as persistent here as we're doing
-       SMTP over HTTP and thus we accidentally avoid setting this value
-       otherwise */
-    conn->bits.close = FALSE;
-#else
-    failf(data, "SMTP over http proxy requires HTTP support built-in!");
-    return CURLE_UNSUPPORTED_PROTOCOL;
-#endif
-  }
-
-  data->state.path++;   /* don't include the initial slash */
-
-  return CURLE_OK;
-}
-
-CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread)
-{
-  /* When sending a SMTP payload we must detect CRLF. sequences making sure
-     they are sent as CRLF.. instead, as a . on the beginning of a line will
-     be deleted by the server when not part of an EOB terminator and a
-     genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
-     data by the server.
-  */
-  ssize_t i;
-  ssize_t si;
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
-  struct SessionHandle *data = conn->data;
-
-  /* Do we need to allocate the scatch buffer? */
-  if(!data->state.scratch) {
-    data->state.scratch = malloc(2 * BUFSIZE);
-
-    if(!data->state.scratch) {
-      failf (data, "Failed to alloc scratch buffer!");
-      return CURLE_OUT_OF_MEMORY;
-    }
-  }
-
-  /* This loop can be improved by some kind of Boyer-Moore style of
-     approach but that is saved for later... */
-  for(i = 0, si = 0; i < nread; i++) {
-    if(SMTP_EOB[smtpc->eob] == data->req.upload_fromhere[i])
-      smtpc->eob++;
-    else if(smtpc->eob) {
-      /* A previous substring matched so output that first */
-      memcpy(&data->state.scratch[si], SMTP_EOB, smtpc->eob);
-      si += smtpc->eob;
-
-      /* Then compare the first byte */
-      if(SMTP_EOB[0] == data->req.upload_fromhere[i])
-        smtpc->eob = 1;
-      else
-        smtpc->eob = 0;
-    }
-
-    /* Do we have a match for CRLF. as per RFC-2821, sect. 4.5.2 */
-    if(SMTP_EOB_FIND_LEN == smtpc->eob) {
-      /* Copy the replacement data to the target buffer */
-      memcpy(&data->state.scratch[si], SMTP_EOB_REPL, SMTP_EOB_REPL_LEN);
-      si += SMTP_EOB_REPL_LEN;
-      smtpc->eob = 0;
-    }
-    else if(!smtpc->eob)
-      data->state.scratch[si++] = data->req.upload_fromhere[i];
-  }
-
-  if(smtpc->eob) {
-    /* A substring matched before processing ended so output that now */
-    memcpy(&data->state.scratch[si], SMTP_EOB, smtpc->eob);
-    si += smtpc->eob;
-    smtpc->eob = 0;
-  }
-
-  if(si != nread) {
-    /* Only use the new buffer if we replaced something */
-    nread = si;
-
-    /* Upload from the new (replaced) buffer instead */
-    data->req.upload_fromhere = data->state.scratch;
-
-    /* Set the new amount too */
-    data->req.upload_present = nread;
-  }
-
-  return CURLE_OK;
-}
-
-#endif /* CURL_DISABLE_SMTP */
diff --git a/lib/socks.c b/lib/socks.c
deleted file mode 100644 (file)
index 1b70dd6..0000000
+++ /dev/null
@@ -1,744 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#if !defined(CURL_DISABLE_PROXY)
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_strequal.h"
-#include "curl_select.h"
-#include "curl_connect.h"
-#include "curl_timeval.h"
-#include "curl_socks.h"
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/*
- * Helper read-from-socket functions. Does the same as Curl_read() but it
- * blocks until all bytes amount of buffersize will be read. No more, no less.
- *
- * This is STUPID BLOCKING behaviour which we frown upon, but right now this
- * is what we have...
- */
-int Curl_blockread_all(struct connectdata *conn, /* connection data */
-                       curl_socket_t sockfd,     /* read from this socket */
-                       char *buf,                /* store read data here */
-                       ssize_t buffersize,       /* max amount to read */
-                       ssize_t *n)               /* amount bytes read */
-{
-  ssize_t nread;
-  ssize_t allread = 0;
-  int result;
-  long timeleft;
-  *n = 0;
-  for(;;) {
-    timeleft = Curl_timeleft(conn->data, NULL, TRUE);
-    if(timeleft < 0) {
-      /* we already got the timeout */
-      result = CURLE_OPERATION_TIMEDOUT;
-      break;
-    }
-    if(Curl_socket_ready(sockfd, CURL_SOCKET_BAD, timeleft) <= 0) {
-      result = ~CURLE_OK;
-      break;
-    }
-    result = Curl_read_plain(sockfd, buf, buffersize, &nread);
-    if(CURLE_AGAIN == result)
-      continue;
-    else if(result)
-      break;
-
-    if(buffersize == nread) {
-      allread += nread;
-      *n = allread;
-      result = CURLE_OK;
-      break;
-    }
-    if(!nread) {
-      result = ~CURLE_OK;
-      break;
-    }
-
-    buffersize -= nread;
-    buf += nread;
-    allread += nread;
-  }
-  return result;
-}
-
-/*
-* This function logs in to a SOCKS4 proxy and sends the specifics to the final
-* destination server.
-*
-* Reference :
-*   http://socks.permeo.com/protocol/socks4.protocol
-*
-* Note :
-*   Set protocol4a=true for  "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
-*   Nonsupport "Identification Protocol (RFC1413)"
-*/
-CURLcode Curl_SOCKS4(const char *proxy_name,
-                     const char *hostname,
-                     int remote_port,
-                     int sockindex,
-                     struct connectdata *conn,
-                     bool protocol4a)
-{
-#define SOCKS4REQLEN 262
-  unsigned char socksreq[SOCKS4REQLEN]; /* room for SOCKS4 request incl. user
-                                           id */
-  int result;
-  CURLcode code;
-  curl_socket_t sock = conn->sock[sockindex];
-  struct SessionHandle *data = conn->data;
-
-  if(Curl_timeleft(data, NULL, TRUE) < 0) {
-    /* time-out, bail out, go home */
-    failf(data, "Connection time-out");
-    return CURLE_OPERATION_TIMEDOUT;
-  }
-
-  curlx_nonblock(sock, FALSE);
-
-  /*
-   * Compose socks4 request
-   *
-   * Request format
-   *
-   *     +----+----+----+----+----+----+----+----+----+----+....+----+
-   *     | VN | CD | DSTPORT |      DSTIP        | USERID       |NULL|
-   *     +----+----+----+----+----+----+----+----+----+----+....+----+
-   * # of bytes:  1    1      2              4           variable       1
-   */
-
-  socksreq[0] = 4; /* version (SOCKS4) */
-  socksreq[1] = 1; /* connect */
-  socksreq[2] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */
-  socksreq[3] = (unsigned char)(remote_port & 0xff);        /* PORT LSB */
-
-  /* DNS resolve only for SOCKS4, not SOCKS4a */
-  if(!protocol4a) {
-    struct Curl_dns_entry *dns;
-    Curl_addrinfo *hp=NULL;
-    int rc;
-
-    rc = Curl_resolv(conn, hostname, remote_port, &dns);
-
-    if(rc == CURLRESOLV_ERROR)
-      return CURLE_COULDNT_RESOLVE_PROXY;
-
-    if(rc == CURLRESOLV_PENDING)
-      /* ignores the return code, but 'dns' remains NULL on failure */
-      (void)Curl_resolver_wait_resolv(conn, &dns);
-
-    /*
-     * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
-     * returns a Curl_addrinfo pointer that may not always look the same.
-     */
-    if(dns)
-      hp=dns->addr;
-    if(hp) {
-      char buf[64];
-      unsigned short ip[4];
-      Curl_printable_address(hp, buf, sizeof(buf));
-
-      if(4 == sscanf( buf, "%hu.%hu.%hu.%hu",
-                      &ip[0], &ip[1], &ip[2], &ip[3])) {
-        /* Set DSTIP */
-        socksreq[4] = (unsigned char)ip[0];
-        socksreq[5] = (unsigned char)ip[1];
-        socksreq[6] = (unsigned char)ip[2];
-        socksreq[7] = (unsigned char)ip[3];
-      }
-      else
-        hp = NULL; /* fail! */
-
-      Curl_resolv_unlock(data, dns); /* not used anymore from now on */
-
-    }
-    if(!hp) {
-      failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
-            hostname);
-      return CURLE_COULDNT_RESOLVE_HOST;
-    }
-  }
-
-  /*
-   * This is currently not supporting "Identification Protocol (RFC1413)".
-   */
-  socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
-  if(proxy_name)
-    strlcat((char*)socksreq + 8, proxy_name, sizeof(socksreq) - 8);
-
-  /*
-   * Make connection
-   */
-  {
-    ssize_t actualread;
-    ssize_t written;
-    ssize_t hostnamelen = 0;
-    int packetsize = 9 +
-      (int)strlen((char*)socksreq + 8); /* size including NUL */
-
-    /* If SOCKS4a, set special invalid IP address 0.0.0.x */
-    if(protocol4a) {
-      socksreq[4] = 0;
-      socksreq[5] = 0;
-      socksreq[6] = 0;
-      socksreq[7] = 1;
-      /* If still enough room in buffer, also append hostname */
-      hostnamelen = (ssize_t)strlen(hostname) + 1; /* length including NUL */
-      if(packetsize + hostnamelen <= SOCKS4REQLEN)
-        strcpy((char*)socksreq + packetsize, hostname);
-      else
-        hostnamelen = 0; /* Flag: hostname did not fit in buffer */
-    }
-
-    /* Send request */
-    code = Curl_write_plain(conn, sock, (char *)socksreq,
-                            packetsize + hostnamelen,
-                            &written);
-    if((code != CURLE_OK) || (written != packetsize + hostnamelen)) {
-      failf(data, "Failed to send SOCKS4 connect request.");
-      return CURLE_COULDNT_CONNECT;
-    }
-    if(protocol4a && hostnamelen == 0) {
-      /* SOCKS4a with very long hostname - send that name separately */
-      hostnamelen = (ssize_t)strlen(hostname) + 1;
-      code = Curl_write_plain(conn, sock, (char *)hostname, hostnamelen,
-                              &written);
-      if((code != CURLE_OK) || (written != hostnamelen)) {
-        failf(data, "Failed to send SOCKS4 connect request.");
-        return CURLE_COULDNT_CONNECT;
-      }
-    }
-
-    packetsize = 8; /* receive data size */
-
-    /* Receive response */
-    result = Curl_blockread_all(conn, sock, (char *)socksreq, packetsize,
-                                &actualread);
-    if((result != CURLE_OK) || (actualread != packetsize)) {
-      failf(data, "Failed to receive SOCKS4 connect request ack.");
-      return CURLE_COULDNT_CONNECT;
-    }
-
-    /*
-     * Response format
-     *
-     *     +----+----+----+----+----+----+----+----+
-     *     | VN | CD | DSTPORT |      DSTIP        |
-     *     +----+----+----+----+----+----+----+----+
-     * # of bytes:  1    1      2              4
-     *
-     * VN is the version of the reply code and should be 0. CD is the result
-     * code with one of the following values:
-     *
-     * 90: request granted
-     * 91: request rejected or failed
-     * 92: request rejected because SOCKS server cannot connect to
-     *     identd on the client
-     * 93: request rejected because the client program and identd
-     *     report different user-ids
-     */
-
-    /* wrong version ? */
-    if(socksreq[0] != 0) {
-      failf(data,
-            "SOCKS4 reply has wrong version, version should be 4.");
-      return CURLE_COULDNT_CONNECT;
-    }
-
-    /* Result */
-    switch(socksreq[1]) {
-    case 90:
-      infof(data, "SOCKS4%s request granted.\n", protocol4a?"a":"");
-      break;
-    case 91:
-      failf(data,
-            "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
-            ", request rejected or failed.",
-            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
-            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
-            ((socksreq[8] << 8) | socksreq[9]),
-            socksreq[1]);
-      return CURLE_COULDNT_CONNECT;
-    case 92:
-      failf(data,
-            "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
-            ", request rejected because SOCKS server cannot connect to "
-            "identd on the client.",
-            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
-            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
-            ((socksreq[8] << 8) | socksreq[9]),
-            socksreq[1]);
-      return CURLE_COULDNT_CONNECT;
-    case 93:
-      failf(data,
-            "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
-            ", request rejected because the client program and identd "
-            "report different user-ids.",
-            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
-            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
-            ((socksreq[8] << 8) | socksreq[9]),
-            socksreq[1]);
-      return CURLE_COULDNT_CONNECT;
-    default:
-      failf(data,
-            "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
-            ", Unknown.",
-            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
-            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
-            ((socksreq[8] << 8) | socksreq[9]),
-            socksreq[1]);
-      return CURLE_COULDNT_CONNECT;
-    }
-  }
-
-  curlx_nonblock(sock, TRUE);
-
-  return CURLE_OK; /* Proxy was successful! */
-}
-
-/*
- * This function logs in to a SOCKS5 proxy and sends the specifics to the final
- * destination server.
- */
-CURLcode Curl_SOCKS5(const char *proxy_name,
-                     const char *proxy_password,
-                     const char *hostname,
-                     int remote_port,
-                     int sockindex,
-                     struct connectdata *conn)
-{
-  /*
-    According to the RFC1928, section "6.  Replies". This is what a SOCK5
-    replies:
-
-        +----+-----+-------+------+----------+----------+
-        |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
-        +----+-----+-------+------+----------+----------+
-        | 1  |  1  | X'00' |  1   | Variable |    2     |
-        +----+-----+-------+------+----------+----------+
-
-    Where:
-
-    o  VER    protocol version: X'05'
-    o  REP    Reply field:
-    o  X'00' succeeded
-  */
-
-  unsigned char socksreq[600]; /* room for large user/pw (255 max each) */
-  ssize_t actualread;
-  ssize_t written;
-  int result;
-  CURLcode code;
-  curl_socket_t sock = conn->sock[sockindex];
-  struct SessionHandle *data = conn->data;
-  long timeout;
-  bool socks5_resolve_local = (conn->proxytype == CURLPROXY_SOCKS5)?TRUE:FALSE;
-  const size_t hostname_len = strlen(hostname);
-  ssize_t len = 0;
-
-  /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
-  if(!socks5_resolve_local && hostname_len > 255) {
-    infof(conn->data,"SOCKS5: server resolving disabled for hostnames of "
-          "length > 255 [actual len=%zu]\n", hostname_len);
-    socks5_resolve_local = TRUE;
-  }
-
-  /* get timeout */
-  timeout = Curl_timeleft(data, NULL, TRUE);
-
-  if(timeout < 0) {
-    /* time-out, bail out, go home */
-    failf(data, "Connection time-out");
-    return CURLE_OPERATION_TIMEDOUT;
-  }
-
-  curlx_nonblock(sock, TRUE);
-
-  /* wait until socket gets connected */
-  result = Curl_socket_ready(CURL_SOCKET_BAD, sock, timeout);
-
-  if(-1 == result) {
-    failf(conn->data, "SOCKS5: no connection here");
-    return CURLE_COULDNT_CONNECT;
-  }
-  else if(0 == result) {
-    failf(conn->data, "SOCKS5: connection timeout");
-    return CURLE_OPERATION_TIMEDOUT;
-  }
-
-  if(result & CURL_CSELECT_ERR) {
-    failf(conn->data, "SOCKS5: error occurred during connection");
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  socksreq[0] = 5; /* version */
-#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
-  socksreq[1] = (char)(proxy_name ? 3 : 2); /* number of methods (below) */
-  socksreq[2] = 0; /* no authentication */
-  socksreq[3] = 1; /* gssapi */
-  socksreq[4] = 2; /* username/password */
-#else
-  socksreq[1] = (char)(proxy_name ? 2 : 1); /* number of methods (below) */
-  socksreq[2] = 0; /* no authentication */
-  socksreq[3] = 2; /* username/password */
-#endif
-
-  curlx_nonblock(sock, FALSE);
-
-  code = Curl_write_plain(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]),
-                          &written);
-  if((code != CURLE_OK) || (written != (2 + (int)socksreq[1]))) {
-    failf(data, "Unable to send initial SOCKS5 request.");
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  curlx_nonblock(sock, TRUE);
-
-  result = Curl_socket_ready(sock, CURL_SOCKET_BAD, timeout);
-
-  if(-1 == result) {
-    failf(conn->data, "SOCKS5 nothing to read");
-    return CURLE_COULDNT_CONNECT;
-  }
-  else if(0 == result) {
-    failf(conn->data, "SOCKS5 read timeout");
-    return CURLE_OPERATION_TIMEDOUT;
-  }
-
-  if(result & CURL_CSELECT_ERR) {
-    failf(conn->data, "SOCKS5 read error occurred");
-    return CURLE_RECV_ERROR;
-  }
-
-  curlx_nonblock(sock, FALSE);
-
-  result=Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread);
-  if((result != CURLE_OK) || (actualread != 2)) {
-    failf(data, "Unable to receive initial SOCKS5 response.");
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  if(socksreq[0] != 5) {
-    failf(data, "Received invalid version in initial SOCKS5 response.");
-    return CURLE_COULDNT_CONNECT;
-  }
-  if(socksreq[1] == 0) {
-    /* Nothing to do, no authentication needed */
-    ;
-  }
-#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
-  else if(socksreq[1] == 1) {
-    code = Curl_SOCKS5_gssapi_negotiate(sockindex, conn);
-    if(code != CURLE_OK) {
-      failf(data, "Unable to negotiate SOCKS5 gssapi context.");
-      return CURLE_COULDNT_CONNECT;
-    }
-  }
-#endif
-  else if(socksreq[1] == 2) {
-    /* Needs user name and password */
-    size_t proxy_name_len, proxy_password_len;
-    if(proxy_name && proxy_password) {
-      proxy_name_len = strlen(proxy_name);
-      proxy_password_len = strlen(proxy_password);
-    }
-    else {
-      proxy_name_len = 0;
-      proxy_password_len = 0;
-    }
-
-    /*   username/password request looks like
-     * +----+------+----------+------+----------+
-     * |VER | ULEN |  UNAME   | PLEN |  PASSWD  |
-     * +----+------+----------+------+----------+
-     * | 1  |  1   | 1 to 255 |  1   | 1 to 255 |
-     * +----+------+----------+------+----------+
-     */
-    len = 0;
-    socksreq[len++] = 1;    /* username/pw subnegotiation version */
-    socksreq[len++] = (unsigned char) proxy_name_len;
-    if(proxy_name && proxy_name_len)
-      memcpy(socksreq + len, proxy_name, proxy_name_len);
-    len += proxy_name_len;
-    socksreq[len++] = (unsigned char) proxy_password_len;
-    if(proxy_password && proxy_password_len)
-      memcpy(socksreq + len, proxy_password, proxy_password_len);
-    len += proxy_password_len;
-
-    code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written);
-    if((code != CURLE_OK) || (len != written)) {
-      failf(data, "Failed to send SOCKS5 sub-negotiation request.");
-      return CURLE_COULDNT_CONNECT;
-    }
-
-    result=Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread);
-    if((result != CURLE_OK) || (actualread != 2)) {
-      failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
-      return CURLE_COULDNT_CONNECT;
-    }
-
-    /* ignore the first (VER) byte */
-    if(socksreq[1] != 0) { /* status */
-      failf(data, "User was rejected by the SOCKS5 server (%d %d).",
-            socksreq[0], socksreq[1]);
-      return CURLE_COULDNT_CONNECT;
-    }
-
-    /* Everything is good so far, user was authenticated! */
-  }
-  else {
-    /* error */
-#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
-    if(socksreq[1] == 255) {
-#else
-    if(socksreq[1] == 1) {
-      failf(data,
-            "SOCKS5 GSSAPI per-message authentication is not supported.");
-      return CURLE_COULDNT_CONNECT;
-    }
-    else if(socksreq[1] == 255) {
-#endif
-      if(!proxy_name || !*proxy_name) {
-        failf(data,
-              "No authentication method was acceptable. (It is quite likely"
-              " that the SOCKS5 server wanted a username/password, since none"
-              " was supplied to the server on this connection.)");
-      }
-      else {
-        failf(data, "No authentication method was acceptable.");
-      }
-      return CURLE_COULDNT_CONNECT;
-    }
-    else {
-      failf(data,
-            "Undocumented SOCKS5 mode attempted to be used by server.");
-      return CURLE_COULDNT_CONNECT;
-    }
-  }
-
-  /* Authentication is complete, now specify destination to the proxy */
-  len = 0;
-  socksreq[len++] = 5; /* version (SOCKS5) */
-  socksreq[len++] = 1; /* connect */
-  socksreq[len++] = 0; /* must be zero */
-
-  if(!socks5_resolve_local) {
-    socksreq[len++] = 3; /* ATYP: domain name = 3 */
-    socksreq[len++] = (char) hostname_len; /* address length */
-    memcpy(&socksreq[len], hostname, hostname_len); /* address str w/o NULL */
-    len += hostname_len;
-  }
-  else {
-    struct Curl_dns_entry *dns;
-    Curl_addrinfo *hp = NULL;
-    int rc = Curl_resolv(conn, hostname, remote_port, &dns);
-
-    if(rc == CURLRESOLV_ERROR)
-      return CURLE_COULDNT_RESOLVE_HOST;
-
-    if(rc == CURLRESOLV_PENDING) {
-      /* this requires that we're in "wait for resolve" state */
-      code = Curl_resolver_wait_resolv(conn, &dns);
-      if(code != CURLE_OK)
-        return code;
-    }
-
-    /*
-     * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
-     * returns a Curl_addrinfo pointer that may not always look the same.
-     */
-    if(dns)
-      hp=dns->addr;
-    if(hp) {
-      struct sockaddr_in *saddr_in;
-#ifdef ENABLE_IPV6
-      struct sockaddr_in6 *saddr_in6;
-#endif
-      int i;
-
-      if(hp->ai_family == AF_INET) {
-        socksreq[len++] = 1; /* ATYP: IPv4 = 1 */
-
-        saddr_in = (struct sockaddr_in*)hp->ai_addr;
-        for(i = 0; i < 4; i++) {
-          socksreq[len++] = ((unsigned char*)&saddr_in->sin_addr.s_addr)[i];
-          infof(data, "%d\n", socksreq[len-1]);
-        }
-      }
-#ifdef ENABLE_IPV6
-      else if(hp->ai_family == AF_INET6) {
-        socksreq[len++] = 4; /* ATYP: IPv6 = 4 */
-
-        saddr_in6 = (struct sockaddr_in6*)hp->ai_addr;
-        for(i = 0; i < 16; i++) {
-          socksreq[len++] = ((unsigned char*)&saddr_in6->sin6_addr.s6_addr)[i];
-        }
-      }
-#endif
-      else
-        hp = NULL; /* fail! */
-
-      Curl_resolv_unlock(data, dns); /* not used anymore from now on */
-    }
-    if(!hp) {
-      failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
-            hostname);
-      return CURLE_COULDNT_RESOLVE_HOST;
-    }
-  }
-
-  socksreq[len++] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */
-  socksreq[len++] = (unsigned char)(remote_port & 0xff);        /* PORT LSB */
-
-#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
-  if(conn->socks5_gssapi_enctype) {
-    failf(data, "SOCKS5 gssapi protection not yet implemented.");
-  }
-  else
-#endif
-    code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written);
-
-  if((code != CURLE_OK) || (len != written)) {
-    failf(data, "Failed to send SOCKS5 connect request.");
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  len = 10; /* minimum packet size is 10 */
-
-#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
-  if(conn->socks5_gssapi_enctype) {
-    failf(data, "SOCKS5 gssapi protection not yet implemented.");
-  }
-  else
-#endif
-    result = Curl_blockread_all(conn, sock, (char *)socksreq,
-                                len, &actualread);
-
-  if((result != CURLE_OK) || (len != actualread)) {
-    failf(data, "Failed to receive SOCKS5 connect request ack.");
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  if(socksreq[0] != 5) { /* version */
-    failf(data,
-          "SOCKS5 reply has wrong version, version should be 5.");
-    return CURLE_COULDNT_CONNECT;
-  }
-  if(socksreq[1] != 0) { /* Anything besides 0 is an error */
-    if(socksreq[3] == 1) {
-      failf(data,
-            "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)",
-            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
-            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
-            ((socksreq[8] << 8) | socksreq[9]),
-            socksreq[1]);
-    }
-    else if(socksreq[3] == 3) {
-      failf(data,
-            "Can't complete SOCKS5 connection to %s:%d. (%d)",
-            hostname,
-            ((socksreq[8] << 8) | socksreq[9]),
-            socksreq[1]);
-    }
-    else if(socksreq[3] == 4) {
-      failf(data,
-            "Can't complete SOCKS5 connection to %02x%02x:%02x%02x:"
-            "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%d. (%d)",
-            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
-            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
-            (unsigned char)socksreq[8], (unsigned char)socksreq[9],
-            (unsigned char)socksreq[10], (unsigned char)socksreq[11],
-            (unsigned char)socksreq[12], (unsigned char)socksreq[13],
-            (unsigned char)socksreq[14], (unsigned char)socksreq[15],
-            (unsigned char)socksreq[16], (unsigned char)socksreq[17],
-            (unsigned char)socksreq[18], (unsigned char)socksreq[19],
-            ((socksreq[8] << 8) | socksreq[9]),
-            socksreq[1]);
-    }
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  /* Fix: in general, returned BND.ADDR is variable length parameter by RFC
-     1928, so the reply packet should be read until the end to avoid errors at
-     subsequent protocol level.
-
-    +----+-----+-------+------+----------+----------+
-    |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
-    +----+-----+-------+------+----------+----------+
-    | 1  |  1  | X'00' |  1   | Variable |    2     |
-    +----+-----+-------+------+----------+----------+
-
-     ATYP:
-     o  IP v4 address: X'01', BND.ADDR = 4 byte
-     o  domain name:  X'03', BND.ADDR = [ 1 byte length, string ]
-     o  IP v6 address: X'04', BND.ADDR = 16 byte
-     */
-
-  /* Calculate real packet size */
-  if(socksreq[3] == 3) {
-    /* domain name */
-    int addrlen = (int) socksreq[4];
-    len = 5 + addrlen + 2;
-  }
-  else if(socksreq[3] == 4) {
-    /* IPv6 */
-    len = 4 + 16 + 2;
-  }
-
-  /* At this point we already read first 10 bytes */
-#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
-  if(!conn->socks5_gssapi_enctype) {
-    /* decrypt_gssapi_blockread already read the whole packet */
-#endif
-    if(len > 10) {
-      len -= 10;
-      result = Curl_blockread_all(conn, sock, (char *)&socksreq[10],
-                                  len, &actualread);
-      if((result != CURLE_OK) || (len != actualread)) {
-        failf(data, "Failed to receive SOCKS5 connect request ack.");
-        return CURLE_COULDNT_CONNECT;
-      }
-    }
-#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
-  }
-#endif
-
-  curlx_nonblock(sock, TRUE);
-  return CURLE_OK; /* Proxy was successful! */
-}
-
-#endif /* CURL_DISABLE_PROXY */
-
diff --git a/lib/socks_gssapi.c b/lib/socks_gssapi.c
deleted file mode 100644 (file)
index 2bd3d45..0000000
+++ /dev/null
@@ -1,533 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 2009, 2011, Markus Moeller, <markus_moeller@compuserve.com>
- * Copyright (C) 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifndef CURL_DISABLE_PROXY
-
-#ifdef HAVE_GSSAPI
-#ifdef HAVE_OLD_GSSMIT
-#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
-#define NCOMPAT 1
-#endif
-#ifndef gss_nt_service_name
-#define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
-#endif
-
-#include "curl_gssapi.h"
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_connect.h"
-#include "curl_timeval.h"
-#include "curl_socks.h"
-#include "curl_warnless.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-static gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
-
-/*
- * Helper gssapi error functions.
- */
-static int check_gss_err(struct SessionHandle *data,
-                         OM_uint32 major_status,
-                         OM_uint32 minor_status,
-                         const char* function)
-{
-  if(GSS_ERROR(major_status)) {
-    OM_uint32 maj_stat,min_stat;
-    OM_uint32 msg_ctx = 0;
-    gss_buffer_desc status_string;
-    char buf[1024];
-    size_t len;
-
-    len = 0;
-    msg_ctx = 0;
-    while(!msg_ctx) {
-      /* convert major status code (GSS-API error) to text */
-      maj_stat = gss_display_status(&min_stat, major_status,
-                                    GSS_C_GSS_CODE,
-                                    GSS_C_NULL_OID,
-                                    &msg_ctx, &status_string);
-      if(maj_stat == GSS_S_COMPLETE) {
-        if(sizeof(buf) > len + status_string.length + 1) {
-          strcpy(buf+len, (char*) status_string.value);
-          len += status_string.length;
-        }
-        gss_release_buffer(&min_stat, &status_string);
-        break;
-      }
-      gss_release_buffer(&min_stat, &status_string);
-    }
-    if(sizeof(buf) > len + 3) {
-      strcpy(buf+len, ".\n");
-      len += 2;
-    }
-    msg_ctx = 0;
-    while(!msg_ctx) {
-      /* convert minor status code (underlying routine error) to text */
-      maj_stat = gss_display_status(&min_stat, minor_status,
-                                    GSS_C_MECH_CODE,
-                                    GSS_C_NULL_OID,
-                                    &msg_ctx, &status_string);
-      if(maj_stat == GSS_S_COMPLETE) {
-        if(sizeof(buf) > len + status_string.length)
-          strcpy(buf+len, (char*) status_string.value);
-        gss_release_buffer(&min_stat, &status_string);
-        break;
-      }
-      gss_release_buffer(&min_stat, &status_string);
-    }
-    failf(data, "GSSAPI error: %s failed:\n%s", function, buf);
-    return(1);
-  }
-
-  return(0);
-}
-
-CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
-                                      struct connectdata *conn)
-{
-  struct SessionHandle *data = conn->data;
-  curl_socket_t sock = conn->sock[sockindex];
-  CURLcode code;
-  ssize_t actualread;
-  ssize_t written;
-  int result;
-  OM_uint32 gss_major_status, gss_minor_status, gss_status;
-  OM_uint32 gss_ret_flags;
-  int gss_conf_state, gss_enc;
-  gss_buffer_desc  service = GSS_C_EMPTY_BUFFER;
-  gss_buffer_desc  gss_send_token = GSS_C_EMPTY_BUFFER;
-  gss_buffer_desc  gss_recv_token = GSS_C_EMPTY_BUFFER;
-  gss_buffer_desc  gss_w_token = GSS_C_EMPTY_BUFFER;
-  gss_buffer_desc* gss_token = GSS_C_NO_BUFFER;
-  gss_name_t       server = GSS_C_NO_NAME;
-  gss_name_t       gss_client_name = GSS_C_NO_NAME;
-  unsigned short   us_length;
-  char             *user=NULL;
-  unsigned char socksreq[4]; /* room for gssapi exchange header only */
-  char *serviceptr = data->set.str[STRING_SOCKS5_GSSAPI_SERVICE];
-
-  /*   GSSAPI request looks like
-   * +----+------+-----+----------------+
-   * |VER | MTYP | LEN |     TOKEN      |
-   * +----+------+----------------------+
-   * | 1  |  1   |  2  | up to 2^16 - 1 |
-   * +----+------+-----+----------------+
-   */
-
-  /* prepare service name */
-  if(strchr(serviceptr,'/')) {
-    service.value = malloc(strlen(serviceptr));
-    if(!service.value)
-      return CURLE_OUT_OF_MEMORY;
-    service.length = strlen(serviceptr);
-    memcpy(service.value, serviceptr, service.length);
-
-    gss_major_status = gss_import_name(&gss_minor_status, &service,
-                                       (gss_OID) GSS_C_NULL_OID, &server);
-  }
-  else {
-    service.value = malloc(strlen(serviceptr) +strlen(conn->proxy.name)+2);
-    if(!service.value)
-      return CURLE_OUT_OF_MEMORY;
-    service.length = strlen(serviceptr) +strlen(conn->proxy.name)+1;
-    snprintf(service.value, service.length+1, "%s@%s",
-             serviceptr, conn->proxy.name);
-
-    gss_major_status = gss_import_name(&gss_minor_status, &service,
-                                       gss_nt_service_name, &server);
-  }
-
-  gss_release_buffer(&gss_status, &service); /* clear allocated memory */
-
-  if(check_gss_err(data,gss_major_status,
-                   gss_minor_status,"gss_import_name()")) {
-    failf(data, "Failed to create service name.");
-    gss_release_name(&gss_status, &server);
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  /* As long as we need to keep sending some context info, and there's no  */
-  /* errors, keep sending it...                                            */
-  for(;;) {
-    gss_major_status = Curl_gss_init_sec_context(data,
-                                                 &gss_minor_status,
-                                                 &gss_context,
-                                                 server,
-                                                 NULL,
-                                                 gss_token,
-                                                 &gss_send_token,
-                                                 &gss_ret_flags);
-
-    if(gss_token != GSS_C_NO_BUFFER)
-      gss_release_buffer(&gss_status, &gss_recv_token);
-    if(check_gss_err(data,gss_major_status,
-                     gss_minor_status,"gss_init_sec_context")) {
-      gss_release_name(&gss_status, &server);
-      gss_release_buffer(&gss_status, &gss_recv_token);
-      gss_release_buffer(&gss_status, &gss_send_token);
-      gss_delete_sec_context(&gss_status, &gss_context, NULL);
-      failf(data, "Failed to initial GSSAPI token.");
-      return CURLE_COULDNT_CONNECT;
-    }
-
-    if(gss_send_token.length != 0) {
-      socksreq[0] = 1;    /* gssapi subnegotiation version */
-      socksreq[1] = 1;    /* authentication message type */
-      us_length = htons((short)gss_send_token.length);
-      memcpy(socksreq+2,&us_length,sizeof(short));
-
-      code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
-      if((code != CURLE_OK) || (4 != written)) {
-        failf(data, "Failed to send GSSAPI authentication request.");
-        gss_release_name(&gss_status, &server);
-        gss_release_buffer(&gss_status, &gss_recv_token);
-        gss_release_buffer(&gss_status, &gss_send_token);
-        gss_delete_sec_context(&gss_status, &gss_context, NULL);
-        return CURLE_COULDNT_CONNECT;
-      }
-
-      code = Curl_write_plain(conn, sock, (char *)gss_send_token.value,
-                              gss_send_token.length, &written);
-
-      if((code != CURLE_OK) || ((ssize_t)gss_send_token.length != written)) {
-        failf(data, "Failed to send GSSAPI authentication token.");
-        gss_release_name(&gss_status, &server);
-        gss_release_buffer(&gss_status, &gss_recv_token);
-        gss_release_buffer(&gss_status, &gss_send_token);
-        gss_delete_sec_context(&gss_status, &gss_context, NULL);
-        return CURLE_COULDNT_CONNECT;
-      }
-
-    }
-
-    gss_release_buffer(&gss_status, &gss_send_token);
-    gss_release_buffer(&gss_status, &gss_recv_token);
-    if(gss_major_status != GSS_S_CONTINUE_NEEDED) break;
-
-    /* analyse response */
-
-    /*   GSSAPI response looks like
-     * +----+------+-----+----------------+
-     * |VER | MTYP | LEN |     TOKEN      |
-     * +----+------+----------------------+
-     * | 1  |  1   |  2  | up to 2^16 - 1 |
-     * +----+------+-----+----------------+
-     */
-
-    result=Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread);
-    if(result != CURLE_OK || actualread != 4) {
-      failf(data, "Failed to receive GSSAPI authentication response.");
-      gss_release_name(&gss_status, &server);
-      gss_delete_sec_context(&gss_status, &gss_context, NULL);
-      return CURLE_COULDNT_CONNECT;
-    }
-
-    /* ignore the first (VER) byte */
-    if(socksreq[1] == 255) { /* status / message type */
-      failf(data, "User was rejected by the SOCKS5 server (%d %d).",
-            socksreq[0], socksreq[1]);
-      gss_release_name(&gss_status, &server);
-      gss_delete_sec_context(&gss_status, &gss_context, NULL);
-      return CURLE_COULDNT_CONNECT;
-    }
-
-    if(socksreq[1] != 1) { /* status / messgae type */
-      failf(data, "Invalid GSSAPI authentication response type (%d %d).",
-            socksreq[0], socksreq[1]);
-      gss_release_name(&gss_status, &server);
-      gss_delete_sec_context(&gss_status, &gss_context, NULL);
-      return CURLE_COULDNT_CONNECT;
-    }
-
-    memcpy(&us_length, socksreq+2, sizeof(short));
-    us_length = ntohs(us_length);
-
-    gss_recv_token.length=us_length;
-    gss_recv_token.value=malloc(us_length);
-    if(!gss_recv_token.value) {
-      failf(data,
-            "Could not allocate memory for GSSAPI authentication "
-            "response token.");
-      gss_release_name(&gss_status, &server);
-      gss_delete_sec_context(&gss_status, &gss_context, NULL);
-      return CURLE_OUT_OF_MEMORY;
-    }
-
-    result=Curl_blockread_all(conn, sock, (char *)gss_recv_token.value,
-                              gss_recv_token.length, &actualread);
-
-    if(result != CURLE_OK || actualread != us_length) {
-      failf(data, "Failed to receive GSSAPI authentication token.");
-      gss_release_name(&gss_status, &server);
-      gss_release_buffer(&gss_status, &gss_recv_token);
-      gss_delete_sec_context(&gss_status, &gss_context, NULL);
-      return CURLE_COULDNT_CONNECT;
-    }
-
-    gss_token = &gss_recv_token;
-  }
-
-  gss_release_name(&gss_status, &server);
-
-  /* Everything is good so far, user was authenticated! */
-  gss_major_status = gss_inquire_context (&gss_minor_status, gss_context,
-                                          &gss_client_name, NULL, NULL, NULL,
-                                          NULL, NULL, NULL);
-  if(check_gss_err(data,gss_major_status,
-                   gss_minor_status,"gss_inquire_context")) {
-    gss_delete_sec_context(&gss_status, &gss_context, NULL);
-    gss_release_name(&gss_status, &gss_client_name);
-    failf(data, "Failed to determine user name.");
-    return CURLE_COULDNT_CONNECT;
-  }
-  gss_major_status = gss_display_name(&gss_minor_status, gss_client_name,
-                                      &gss_send_token, NULL);
-  if(check_gss_err(data,gss_major_status,
-                   gss_minor_status,"gss_display_name")) {
-    gss_delete_sec_context(&gss_status, &gss_context, NULL);
-    gss_release_name(&gss_status, &gss_client_name);
-    gss_release_buffer(&gss_status, &gss_send_token);
-    failf(data, "Failed to determine user name.");
-    return CURLE_COULDNT_CONNECT;
-  }
-  user=malloc(gss_send_token.length+1);
-  if(!user) {
-    gss_delete_sec_context(&gss_status, &gss_context, NULL);
-    gss_release_name(&gss_status, &gss_client_name);
-    gss_release_buffer(&gss_status, &gss_send_token);
-    return CURLE_OUT_OF_MEMORY;
-  }
-
-  memcpy(user, gss_send_token.value, gss_send_token.length);
-  user[gss_send_token.length] = '\0';
-  gss_release_name(&gss_status, &gss_client_name);
-  gss_release_buffer(&gss_status, &gss_send_token);
-  infof(data, "SOCKS5 server authencticated user %s with gssapi.\n",user);
-  free(user);
-  user=NULL;
-
-  /* Do encryption */
-  socksreq[0] = 1;    /* gssapi subnegotiation version */
-  socksreq[1] = 2;    /* encryption message type */
-
-  gss_enc = 0; /* no data protection */
-  /* do confidentiality protection if supported */
-  if(gss_ret_flags & GSS_C_CONF_FLAG)
-    gss_enc = 2;
-  /* else do integrity protection */
-  else if(gss_ret_flags & GSS_C_INTEG_FLAG)
-    gss_enc = 1;
-
-  infof(data, "SOCKS5 server supports gssapi %s data protection.\n",
-        (gss_enc==0)?"no":((gss_enc==1)?"integrity":"confidentiality"));
-  /* force for the moment to no data protection */
-  gss_enc = 0;
-  /*
-   * Sending the encryption type in clear seems wrong. It should be
-   * protected with gss_seal()/gss_wrap(). See RFC1961 extract below
-   * The NEC reference implementations on which this is based is
-   * therefore at fault
-   *
-   *  +------+------+------+.......................+
-   *  + ver  | mtyp | len  |   token               |
-   *  +------+------+------+.......................+
-   *  + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets |
-   *  +------+------+------+.......................+
-   *
-   *   Where:
-   *
-   *  - "ver" is the protocol version number, here 1 to represent the
-   *    first version of the SOCKS/GSS-API protocol
-   *
-   *  - "mtyp" is the message type, here 2 to represent a protection
-   *    -level negotiation message
-   *
-   *  - "len" is the length of the "token" field in octets
-   *
-   *  - "token" is the GSS-API encapsulated protection level
-   *
-   * The token is produced by encapsulating an octet containing the
-   * required protection level using gss_seal()/gss_wrap() with conf_req
-   * set to FALSE.  The token is verified using gss_unseal()/
-   * gss_unwrap().
-   *
-   */
-  if(data->set.socks5_gssapi_nec) {
-    us_length = htons((short)1);
-    memcpy(socksreq+2,&us_length,sizeof(short));
-  }
-  else {
-    gss_send_token.length = 1;
-    gss_send_token.value = malloc(1);
-    if(!gss_send_token.value) {
-      gss_delete_sec_context(&gss_status, &gss_context, NULL);
-      return CURLE_OUT_OF_MEMORY;
-    }
-    memcpy(gss_send_token.value, &gss_enc, 1);
-
-    gss_major_status = gss_wrap(&gss_minor_status, gss_context, 0,
-                                GSS_C_QOP_DEFAULT, &gss_send_token,
-                                &gss_conf_state, &gss_w_token);
-
-    if(check_gss_err(data,gss_major_status,gss_minor_status,"gss_wrap")) {
-      gss_release_buffer(&gss_status, &gss_send_token);
-      gss_release_buffer(&gss_status, &gss_w_token);
-      gss_delete_sec_context(&gss_status, &gss_context, NULL);
-      failf(data, "Failed to wrap GSSAPI encryption value into token.");
-      return CURLE_COULDNT_CONNECT;
-    }
-    gss_release_buffer(&gss_status, &gss_send_token);
-
-    us_length = htons((short)gss_w_token.length);
-    memcpy(socksreq+2,&us_length,sizeof(short));
-  }
-
-  code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
-  if((code != CURLE_OK) || (4 != written)) {
-    failf(data, "Failed to send GSSAPI encryption request.");
-    gss_release_buffer(&gss_status, &gss_w_token);
-    gss_delete_sec_context(&gss_status, &gss_context, NULL);
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  if(data->set.socks5_gssapi_nec) {
-    memcpy(socksreq, &gss_enc, 1);
-    code = Curl_write_plain(conn, sock, socksreq, 1, &written);
-    if((code != CURLE_OK) || ( 1 != written)) {
-      failf(data, "Failed to send GSSAPI encryption type.");
-      gss_delete_sec_context(&gss_status, &gss_context, NULL);
-      return CURLE_COULDNT_CONNECT;
-    }
-  }
-  else {
-    code = Curl_write_plain(conn, sock, (char *)gss_w_token.value,
-                            gss_w_token.length, &written);
-    if((code != CURLE_OK) || ((ssize_t)gss_w_token.length != written)) {
-      failf(data, "Failed to send GSSAPI encryption type.");
-      gss_release_buffer(&gss_status, &gss_w_token);
-      gss_delete_sec_context(&gss_status, &gss_context, NULL);
-      return CURLE_COULDNT_CONNECT;
-    }
-    gss_release_buffer(&gss_status, &gss_w_token);
-  }
-
-  result=Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread);
-  if(result != CURLE_OK || actualread != 4) {
-    failf(data, "Failed to receive GSSAPI encryption response.");
-    gss_delete_sec_context(&gss_status, &gss_context, NULL);
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  /* ignore the first (VER) byte */
-  if(socksreq[1] == 255) { /* status / message type */
-    failf(data, "User was rejected by the SOCKS5 server (%d %d).",
-          socksreq[0], socksreq[1]);
-    gss_delete_sec_context(&gss_status, &gss_context, NULL);
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  if(socksreq[1] != 2) { /* status / messgae type */
-    failf(data, "Invalid GSSAPI encryption response type (%d %d).",
-          socksreq[0], socksreq[1]);
-    gss_delete_sec_context(&gss_status, &gss_context, NULL);
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  memcpy(&us_length, socksreq+2, sizeof(short));
-  us_length = ntohs(us_length);
-
-  gss_recv_token.length= us_length;
-  gss_recv_token.value=malloc(gss_recv_token.length);
-  if(!gss_recv_token.value) {
-    gss_delete_sec_context(&gss_status, &gss_context, NULL);
-    return CURLE_OUT_OF_MEMORY;
-  }
-  result=Curl_blockread_all(conn, sock, (char *)gss_recv_token.value,
-                            gss_recv_token.length, &actualread);
-
-  if(result != CURLE_OK || actualread != us_length) {
-    failf(data, "Failed to receive GSSAPI encryptrion type.");
-    gss_release_buffer(&gss_status, &gss_recv_token);
-    gss_delete_sec_context(&gss_status, &gss_context, NULL);
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  if(!data->set.socks5_gssapi_nec) {
-    gss_major_status = gss_unwrap(&gss_minor_status, gss_context,
-                                  &gss_recv_token, &gss_w_token,
-                                  0, GSS_C_QOP_DEFAULT);
-
-    if(check_gss_err(data,gss_major_status,gss_minor_status,"gss_unwrap")) {
-      gss_release_buffer(&gss_status, &gss_recv_token);
-      gss_release_buffer(&gss_status, &gss_w_token);
-      gss_delete_sec_context(&gss_status, &gss_context, NULL);
-      failf(data, "Failed to unwrap GSSAPI encryption value into token.");
-      return CURLE_COULDNT_CONNECT;
-    }
-    gss_release_buffer(&gss_status, &gss_recv_token);
-
-    if(gss_w_token.length != 1) {
-      failf(data, "Invalid GSSAPI encryption response length (%d).",
-            gss_w_token.length);
-      gss_release_buffer(&gss_status, &gss_w_token);
-      gss_delete_sec_context(&gss_status, &gss_context, NULL);
-      return CURLE_COULDNT_CONNECT;
-    }
-
-    memcpy(socksreq,gss_w_token.value,gss_w_token.length);
-    gss_release_buffer(&gss_status, &gss_w_token);
-  }
-  else {
-    if(gss_recv_token.length != 1) {
-      failf(data, "Invalid GSSAPI encryption response length (%d).",
-            gss_recv_token.length);
-      gss_release_buffer(&gss_status, &gss_recv_token);
-      gss_delete_sec_context(&gss_status, &gss_context, NULL);
-      return CURLE_COULDNT_CONNECT;
-    }
-
-    memcpy(socksreq,gss_recv_token.value,gss_recv_token.length);
-    gss_release_buffer(&gss_status, &gss_recv_token);
-  }
-
-  infof(data, "SOCKS5 access with%s protection granted.\n",
-        (socksreq[0]==0)?"out gssapi data":
-        ((socksreq[0]==1)?" gssapi integrity":" gssapi confidentiality"));
-
-  conn->socks5_gssapi_enctype = socksreq[0];
-  if(socksreq[0] == 0)
-    gss_delete_sec_context(&gss_status, &gss_context, NULL);
-
-  return CURLE_OK;
-}
-#endif
-
-#endif /* CURL_DISABLE_PROXY */
diff --git a/lib/socks_sspi.c b/lib/socks_sspi.c
deleted file mode 100644 (file)
index c576107..0000000
+++ /dev/null
@@ -1,591 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 2009, 2011, Markus Moeller, <markus_moeller@compuserve.com>
- * Copyright (C) 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_PROXY)
-
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_connect.h"
-#include "curl_strerror.h"
-#include "curl_timeval.h"
-#include "curl_socks.h"
-#include "curl_sspi.h"
-#include "curl_multibyte.h"
-#include "curl_warnless.h"
-
-#define _MPRINTF_REPLACE /* use the internal *printf() functions */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/*
- * Definitions required from ntsecapi.h are directly provided below this point
- * to avoid including ntsecapi.h due to a conflict with OpenSSL's safestack.h
- */
-#define KERB_WRAP_NO_ENCRYPT 0x80000001
-
-/*
- * Helper sspi error functions.
- */
-static int check_sspi_err(struct connectdata *conn,
-                          SECURITY_STATUS status,
-                          const char* function)
-{
-  if(status != SEC_E_OK &&
-     status != SEC_I_COMPLETE_AND_CONTINUE &&
-     status != SEC_I_COMPLETE_NEEDED &&
-     status != SEC_I_CONTINUE_NEEDED) {
-    failf(conn->data, "SSPI error: %s failed: %s", function,
-          Curl_sspi_strerror(conn, status));
-    return 1;
-  }
-  return 0;
-}
-
-/* This is the SSPI-using version of this function */
-CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
-                                      struct connectdata *conn)
-{
-  struct SessionHandle *data = conn->data;
-  curl_socket_t sock = conn->sock[sockindex];
-  CURLcode code;
-  ssize_t actualread;
-  ssize_t written;
-  int result;
-  /* Needs GSSAPI authentication */
-  SECURITY_STATUS status;
-  unsigned long sspi_ret_flags = 0;
-  int gss_enc;
-  SecBuffer sspi_send_token, sspi_recv_token, sspi_w_token[3];
-  SecBufferDesc input_desc, output_desc, wrap_desc;
-  SecPkgContext_Sizes sspi_sizes;
-  CredHandle cred_handle;
-  CtxtHandle sspi_context;
-  PCtxtHandle context_handle = NULL;
-  SecPkgCredentials_Names names;
-  TimeStamp expiry;
-  char *service_name = NULL;
-  unsigned short us_length;
-  unsigned long qop;
-  unsigned char socksreq[4]; /* room for gssapi exchange header only */
-  char *service = data->set.str[STRING_SOCKS5_GSSAPI_SERVICE];
-
-  /*   GSSAPI request looks like
-   * +----+------+-----+----------------+
-   * |VER | MTYP | LEN |     TOKEN      |
-   * +----+------+----------------------+
-   * | 1  |  1   |  2  | up to 2^16 - 1 |
-   * +----+------+-----+----------------+
-   */
-
-  /* prepare service name */
-  if(strchr(service, '/')) {
-    service_name = malloc(strlen(service));
-    if(!service_name)
-      return CURLE_OUT_OF_MEMORY;
-    memcpy(service_name, service, strlen(service));
-  }
-  else {
-    service_name = malloc(strlen(service) + strlen(conn->proxy.name) + 2);
-    if(!service_name)
-      return CURLE_OUT_OF_MEMORY;
-    snprintf(service_name,strlen(service) +strlen(conn->proxy.name)+2,"%s/%s",
-             service,conn->proxy.name);
-  }
-
-  input_desc.cBuffers = 1;
-  input_desc.pBuffers = &sspi_recv_token;
-  input_desc.ulVersion = SECBUFFER_VERSION;
-
-  sspi_recv_token.BufferType = SECBUFFER_TOKEN;
-  sspi_recv_token.cbBuffer = 0;
-  sspi_recv_token.pvBuffer = NULL;
-
-  output_desc.cBuffers = 1;
-  output_desc.pBuffers = &sspi_send_token;
-  output_desc.ulVersion = SECBUFFER_VERSION;
-
-  sspi_send_token.BufferType = SECBUFFER_TOKEN;
-  sspi_send_token.cbBuffer = 0;
-  sspi_send_token.pvBuffer = NULL;
-
-  wrap_desc.cBuffers = 3;
-  wrap_desc.pBuffers = sspi_w_token;
-  wrap_desc.ulVersion = SECBUFFER_VERSION;
-
-  cred_handle.dwLower = 0;
-  cred_handle.dwUpper = 0;
-
-  status = s_pSecFn->AcquireCredentialsHandle(NULL,
-                                              (TCHAR *) TEXT("Kerberos"),
-                                              SECPKG_CRED_OUTBOUND,
-                                              NULL,
-                                              NULL,
-                                              NULL,
-                                              NULL,
-                                              &cred_handle,
-                                              &expiry);
-
-  if(check_sspi_err(conn, status, "AcquireCredentialsHandle")) {
-    failf(data, "Failed to acquire credentials.");
-    Curl_safefree(service_name);
-    s_pSecFn->FreeCredentialsHandle(&cred_handle);
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  /* As long as we need to keep sending some context info, and there's no  */
-  /* errors, keep sending it...                                            */
-  for(;;) {
-    TCHAR *sname;
-
-    sname = Curl_convert_UTF8_to_tchar(service_name);
-    if(!sname)
-      return CURLE_OUT_OF_MEMORY;
-
-    status = s_pSecFn->InitializeSecurityContext(&cred_handle,
-                                                 context_handle,
-                                                 sname,
-                                                 ISC_REQ_MUTUAL_AUTH |
-                                                 ISC_REQ_ALLOCATE_MEMORY |
-                                                 ISC_REQ_CONFIDENTIALITY |
-                                                 ISC_REQ_REPLAY_DETECT,
-                                                 0,
-                                                 SECURITY_NATIVE_DREP,
-                                                 &input_desc,
-                                                 0,
-                                                 &sspi_context,
-                                                 &output_desc,
-                                                 &sspi_ret_flags,
-                                                 &expiry);
-
-    Curl_unicodefree(sname);
-
-    if(sspi_recv_token.pvBuffer) {
-      s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
-      sspi_recv_token.pvBuffer = NULL;
-      sspi_recv_token.cbBuffer = 0;
-    }
-
-    if(check_sspi_err(conn, status, "InitializeSecurityContext")) {
-      Curl_safefree(service_name);
-      s_pSecFn->FreeCredentialsHandle(&cred_handle);
-      s_pSecFn->DeleteSecurityContext(&sspi_context);
-      s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
-      failf(data, "Failed to initialise security context.");
-      return CURLE_COULDNT_CONNECT;
-    }
-
-    if(sspi_send_token.cbBuffer != 0) {
-      socksreq[0] = 1;    /* gssapi subnegotiation version */
-      socksreq[1] = 1;    /* authentication message type */
-      us_length = htons((short)sspi_send_token.cbBuffer);
-      memcpy(socksreq+2, &us_length, sizeof(short));
-
-      code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
-      if((code != CURLE_OK) || (4 != written)) {
-        failf(data, "Failed to send SSPI authentication request.");
-        Curl_safefree(service_name);
-        s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
-        s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
-        s_pSecFn->FreeCredentialsHandle(&cred_handle);
-        s_pSecFn->DeleteSecurityContext(&sspi_context);
-        return CURLE_COULDNT_CONNECT;
-      }
-
-      code = Curl_write_plain(conn, sock, (char *)sspi_send_token.pvBuffer,
-                              sspi_send_token.cbBuffer, &written);
-      if((code != CURLE_OK) || (sspi_send_token.cbBuffer != (size_t)written)) {
-        failf(data, "Failed to send SSPI authentication token.");
-        Curl_safefree(service_name);
-        s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
-        s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
-        s_pSecFn->FreeCredentialsHandle(&cred_handle);
-        s_pSecFn->DeleteSecurityContext(&sspi_context);
-        return CURLE_COULDNT_CONNECT;
-      }
-
-    }
-
-    s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
-    sspi_send_token.pvBuffer = NULL;
-    sspi_send_token.cbBuffer = 0;
-    s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
-    sspi_recv_token.pvBuffer = NULL;
-    sspi_recv_token.cbBuffer = 0;
-    if(status != SEC_I_CONTINUE_NEEDED)
-      break;
-
-    /* analyse response */
-
-    /*   GSSAPI response looks like
-     * +----+------+-----+----------------+
-     * |VER | MTYP | LEN |     TOKEN      |
-     * +----+------+----------------------+
-     * | 1  |  1   |  2  | up to 2^16 - 1 |
-     * +----+------+-----+----------------+
-     */
-
-    result = Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread);
-    if(result != CURLE_OK || actualread != 4) {
-      failf(data, "Failed to receive SSPI authentication response.");
-      Curl_safefree(service_name);
-      s_pSecFn->FreeCredentialsHandle(&cred_handle);
-      s_pSecFn->DeleteSecurityContext(&sspi_context);
-      return CURLE_COULDNT_CONNECT;
-    }
-
-    /* ignore the first (VER) byte */
-    if(socksreq[1] == 255) { /* status / message type */
-      failf(data, "User was rejected by the SOCKS5 server (%d %d).",
-            socksreq[0], socksreq[1]);
-      Curl_safefree(service_name);
-      s_pSecFn->FreeCredentialsHandle(&cred_handle);
-      s_pSecFn->DeleteSecurityContext(&sspi_context);
-      return CURLE_COULDNT_CONNECT;
-    }
-
-    if(socksreq[1] != 1) { /* status / messgae type */
-      failf(data, "Invalid SSPI authentication response type (%d %d).",
-            socksreq[0], socksreq[1]);
-      Curl_safefree(service_name);
-      s_pSecFn->FreeCredentialsHandle(&cred_handle);
-      s_pSecFn->DeleteSecurityContext(&sspi_context);
-      return CURLE_COULDNT_CONNECT;
-    }
-
-    memcpy(&us_length, socksreq+2, sizeof(short));
-    us_length = ntohs(us_length);
-
-    sspi_recv_token.cbBuffer = us_length;
-    sspi_recv_token.pvBuffer = malloc(us_length);
-
-    if(!sspi_recv_token.pvBuffer) {
-      Curl_safefree(service_name);
-      s_pSecFn->FreeCredentialsHandle(&cred_handle);
-      s_pSecFn->DeleteSecurityContext(&sspi_context);
-      return CURLE_OUT_OF_MEMORY;
-    }
-    result = Curl_blockread_all(conn, sock, (char *)sspi_recv_token.pvBuffer,
-                                sspi_recv_token.cbBuffer, &actualread);
-
-    if(result != CURLE_OK || actualread != us_length) {
-      failf(data, "Failed to receive SSPI authentication token.");
-      Curl_safefree(service_name);
-      s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
-      s_pSecFn->FreeCredentialsHandle(&cred_handle);
-      s_pSecFn->DeleteSecurityContext(&sspi_context);
-      return CURLE_COULDNT_CONNECT;
-    }
-
-    context_handle = &sspi_context;
-  }
-
-  Curl_safefree(service_name);
-
-  /* Everything is good so far, user was authenticated! */
-  status = s_pSecFn->QueryCredentialsAttributes(&cred_handle,
-                                                SECPKG_CRED_ATTR_NAMES,
-                                                &names);
-  s_pSecFn->FreeCredentialsHandle(&cred_handle);
-  if(check_sspi_err(conn, status, "QueryCredentialAttributes")) {
-    s_pSecFn->DeleteSecurityContext(&sspi_context);
-    s_pSecFn->FreeContextBuffer(names.sUserName);
-    failf(data, "Failed to determine user name.");
-    return CURLE_COULDNT_CONNECT;
-  }
-  infof(data, "SOCKS5 server authencticated user %s with gssapi.\n",
-        names.sUserName);
-  s_pSecFn->FreeContextBuffer(names.sUserName);
-
-  /* Do encryption */
-  socksreq[0] = 1;    /* gssapi subnegotiation version */
-  socksreq[1] = 2;    /* encryption message type */
-
-  gss_enc = 0; /* no data protection */
-  /* do confidentiality protection if supported */
-  if(sspi_ret_flags & ISC_REQ_CONFIDENTIALITY)
-    gss_enc = 2;
-  /* else do integrity protection */
-  else if(sspi_ret_flags & ISC_REQ_INTEGRITY)
-    gss_enc = 1;
-
-  infof(data, "SOCKS5 server supports gssapi %s data protection.\n",
-        (gss_enc==0)?"no":((gss_enc==1)?"integrity":"confidentiality") );
-  /* force to no data protection, avoid encryption/decryption for now */
-  gss_enc = 0;
-  /*
-   * Sending the encryption type in clear seems wrong. It should be
-   * protected with gss_seal()/gss_wrap(). See RFC1961 extract below
-   * The NEC reference implementations on which this is based is
-   * therefore at fault
-   *
-   *  +------+------+------+.......................+
-   *  + ver  | mtyp | len  |   token               |
-   *  +------+------+------+.......................+
-   *  + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets |
-   *  +------+------+------+.......................+
-   *
-   *   Where:
-   *
-   *  - "ver" is the protocol version number, here 1 to represent the
-   *    first version of the SOCKS/GSS-API protocol
-   *
-   *  - "mtyp" is the message type, here 2 to represent a protection
-   *    -level negotiation message
-   *
-   *  - "len" is the length of the "token" field in octets
-   *
-   *  - "token" is the GSS-API encapsulated protection level
-   *
-   * The token is produced by encapsulating an octet containing the
-   * required protection level using gss_seal()/gss_wrap() with conf_req
-   * set to FALSE.  The token is verified using gss_unseal()/
-   * gss_unwrap().
-   *
-   */
-
-  if(data->set.socks5_gssapi_nec) {
-    us_length = htons((short)1);
-    memcpy(socksreq+2, &us_length, sizeof(short));
-  }
-  else {
-    status = s_pSecFn->QueryContextAttributes(&sspi_context,
-                                              SECPKG_ATTR_SIZES,
-                                              &sspi_sizes);
-    if(check_sspi_err(conn, status, "QueryContextAttributes")) {
-      s_pSecFn->DeleteSecurityContext(&sspi_context);
-      failf(data, "Failed to query security context attributes.");
-      return CURLE_COULDNT_CONNECT;
-    }
-
-    sspi_w_token[0].cbBuffer = sspi_sizes.cbSecurityTrailer;
-    sspi_w_token[0].BufferType = SECBUFFER_TOKEN;
-    sspi_w_token[0].pvBuffer = malloc(sspi_sizes.cbSecurityTrailer);
-
-    if(!sspi_w_token[0].pvBuffer) {
-      s_pSecFn->DeleteSecurityContext(&sspi_context);
-      return CURLE_OUT_OF_MEMORY;
-    }
-
-    sspi_w_token[1].cbBuffer = 1;
-    sspi_w_token[1].pvBuffer = malloc(1);
-    if(!sspi_w_token[1].pvBuffer) {
-      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
-      s_pSecFn->DeleteSecurityContext(&sspi_context);
-      return CURLE_OUT_OF_MEMORY;
-    }
-
-    memcpy(sspi_w_token[1].pvBuffer,&gss_enc,1);
-    sspi_w_token[2].BufferType = SECBUFFER_PADDING;
-    sspi_w_token[2].cbBuffer = sspi_sizes.cbBlockSize;
-    sspi_w_token[2].pvBuffer = malloc(sspi_sizes.cbBlockSize);
-    if(!sspi_w_token[2].pvBuffer) {
-      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
-      s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
-      s_pSecFn->DeleteSecurityContext(&sspi_context);
-      return CURLE_OUT_OF_MEMORY;
-    }
-    status = s_pSecFn->EncryptMessage(&sspi_context,
-                                      KERB_WRAP_NO_ENCRYPT,
-                                      &wrap_desc,
-                                      0);
-    if(check_sspi_err(conn, status, "EncryptMessage")) {
-      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
-      s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
-      s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
-      s_pSecFn->DeleteSecurityContext(&sspi_context);
-      failf(data, "Failed to query security context attributes.");
-      return CURLE_COULDNT_CONNECT;
-    }
-    sspi_send_token.cbBuffer = sspi_w_token[0].cbBuffer
-      + sspi_w_token[1].cbBuffer
-      + sspi_w_token[2].cbBuffer;
-    sspi_send_token.pvBuffer = malloc(sspi_send_token.cbBuffer);
-    if(!sspi_send_token.pvBuffer) {
-      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
-      s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
-      s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
-      s_pSecFn->DeleteSecurityContext(&sspi_context);
-      return CURLE_OUT_OF_MEMORY;
-    }
-
-    memcpy(sspi_send_token.pvBuffer, sspi_w_token[0].pvBuffer,
-           sspi_w_token[0].cbBuffer);
-    memcpy((PUCHAR) sspi_send_token.pvBuffer +(int)sspi_w_token[0].cbBuffer,
-           sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer);
-    memcpy((PUCHAR) sspi_send_token.pvBuffer
-           +sspi_w_token[0].cbBuffer
-           +sspi_w_token[1].cbBuffer,
-           sspi_w_token[2].pvBuffer, sspi_w_token[2].cbBuffer);
-
-    s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
-    sspi_w_token[0].pvBuffer = NULL;
-    sspi_w_token[0].cbBuffer = 0;
-    s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
-    sspi_w_token[1].pvBuffer = NULL;
-    sspi_w_token[1].cbBuffer = 0;
-    s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
-    sspi_w_token[2].pvBuffer = NULL;
-    sspi_w_token[2].cbBuffer = 0;
-
-    us_length = htons((short)sspi_send_token.cbBuffer);
-    memcpy(socksreq+2,&us_length,sizeof(short));
-  }
-
-  code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
-  if((code != CURLE_OK) || (4 != written)) {
-    failf(data, "Failed to send SSPI encryption request.");
-    s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
-    s_pSecFn->DeleteSecurityContext(&sspi_context);
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  if(data->set.socks5_gssapi_nec) {
-    memcpy(socksreq,&gss_enc,1);
-    code = Curl_write_plain(conn, sock, (char *)socksreq, 1, &written);
-    if((code != CURLE_OK) || (1 != written)) {
-      failf(data, "Failed to send SSPI encryption type.");
-      s_pSecFn->DeleteSecurityContext(&sspi_context);
-      return CURLE_COULDNT_CONNECT;
-    }
-  }
-  else {
-    code = Curl_write_plain(conn, sock, (char *)sspi_send_token.pvBuffer,
-                            sspi_send_token.cbBuffer, &written);
-    if((code != CURLE_OK) || (sspi_send_token.cbBuffer != (size_t)written)) {
-      failf(data, "Failed to send SSPI encryption type.");
-      s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
-      s_pSecFn->DeleteSecurityContext(&sspi_context);
-      return CURLE_COULDNT_CONNECT;
-    }
-    s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
-  }
-
-  result = Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread);
-  if(result != CURLE_OK || actualread != 4) {
-    failf(data, "Failed to receive SSPI encryption response.");
-    s_pSecFn->DeleteSecurityContext(&sspi_context);
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  /* ignore the first (VER) byte */
-  if(socksreq[1] == 255) { /* status / message type */
-    failf(data, "User was rejected by the SOCKS5 server (%d %d).",
-          socksreq[0], socksreq[1]);
-    s_pSecFn->DeleteSecurityContext(&sspi_context);
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  if(socksreq[1] != 2) { /* status / message type */
-    failf(data, "Invalid SSPI encryption response type (%d %d).",
-          socksreq[0], socksreq[1]);
-    s_pSecFn->DeleteSecurityContext(&sspi_context);
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  memcpy(&us_length, socksreq+2, sizeof(short));
-  us_length = ntohs(us_length);
-
-  sspi_w_token[0].cbBuffer = us_length;
-  sspi_w_token[0].pvBuffer = malloc(us_length);
-  if(!sspi_w_token[0].pvBuffer) {
-    s_pSecFn->DeleteSecurityContext(&sspi_context);
-    return CURLE_OUT_OF_MEMORY;
-  }
-
-  result = Curl_blockread_all(conn, sock, (char *)sspi_w_token[0].pvBuffer,
-                              sspi_w_token[0].cbBuffer, &actualread);
-
-  if(result != CURLE_OK || actualread != us_length) {
-    failf(data, "Failed to receive SSPI encryption type.");
-    s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
-    s_pSecFn->DeleteSecurityContext(&sspi_context);
-    return CURLE_COULDNT_CONNECT;
-  }
-
-
-  if(!data->set.socks5_gssapi_nec) {
-    wrap_desc.cBuffers = 2;
-    sspi_w_token[0].BufferType = SECBUFFER_STREAM;
-    sspi_w_token[1].BufferType = SECBUFFER_DATA;
-    sspi_w_token[1].cbBuffer = 0;
-    sspi_w_token[1].pvBuffer = NULL;
-
-    status = s_pSecFn->DecryptMessage(&sspi_context,
-                                      &wrap_desc,
-                                      0,
-                                      &qop);
-
-    if(check_sspi_err(conn, status, "DecryptMessage")) {
-      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
-      s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
-      s_pSecFn->DeleteSecurityContext(&sspi_context);
-      failf(data, "Failed to query security context attributes.");
-      return CURLE_COULDNT_CONNECT;
-    }
-
-    if(sspi_w_token[1].cbBuffer != 1) {
-      failf(data, "Invalid SSPI encryption response length (%d).",
-            sspi_w_token[1].cbBuffer);
-      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
-      s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
-      s_pSecFn->DeleteSecurityContext(&sspi_context);
-      return CURLE_COULDNT_CONNECT;
-    }
-
-    memcpy(socksreq,sspi_w_token[1].pvBuffer,sspi_w_token[1].cbBuffer);
-    s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
-    s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
-  }
-  else {
-    if(sspi_w_token[0].cbBuffer != 1) {
-      failf(data, "Invalid SSPI encryption response length (%d).",
-            sspi_w_token[0].cbBuffer);
-      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
-      s_pSecFn->DeleteSecurityContext(&sspi_context);
-      return CURLE_COULDNT_CONNECT;
-    }
-    memcpy(socksreq,sspi_w_token[0].pvBuffer,sspi_w_token[0].cbBuffer);
-    s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
-  }
-
-  infof(data, "SOCKS5 access with%s protection granted.\n",
-        (socksreq[0]==0)?"out gssapi data":
-        ((socksreq[0]==1)?" gssapi integrity":" gssapi confidentiality"));
-
-  /* For later use if encryption is required
-     conn->socks5_gssapi_enctype = socksreq[0];
-     if(socksreq[0] != 0)
-       conn->socks5_sspi_context = sspi_context;
-     else {
-       s_pSecFn->DeleteSecurityContext(&sspi_context);
-       conn->socks5_sspi_context = sspi_context;
-     }
-  */
-  return CURLE_OK;
-}
-#endif
diff --git a/lib/speedcheck.c b/lib/speedcheck.c
deleted file mode 100644 (file)
index b9ce77d..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#include <curl/curl.h>
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_multiif.h"
-#include "curl_speedcheck.h"
-
-void Curl_speedinit(struct SessionHandle *data)
-{
-  memset(&data->state.keeps_speed, 0, sizeof(struct timeval));
-}
-
-CURLcode Curl_speedcheck(struct SessionHandle *data,
-                         struct timeval now)
-{
-  if((data->progress.current_speed >= 0) &&
-     data->set.low_speed_time &&
-     (Curl_tvlong(data->state.keeps_speed) != 0) &&
-     (data->progress.current_speed < data->set.low_speed_limit)) {
-    long howlong = Curl_tvdiff(now, data->state.keeps_speed);
-    long nextcheck = (data->set.low_speed_time * 1000) - howlong;
-
-    /* We are now below the "low speed limit". If we are below it
-       for "low speed time" seconds we consider that enough reason
-       to abort the download. */
-    if(nextcheck <= 0) {
-      /* we have been this slow for long enough, now die */
-      failf(data,
-            "Operation too slow. "
-            "Less than %ld bytes/sec transferred the last %ld seconds",
-            data->set.low_speed_limit,
-            data->set.low_speed_time);
-      return CURLE_OPERATION_TIMEDOUT;
-    }
-    else {
-      /* wait complete low_speed_time */
-      Curl_expire(data, nextcheck);
-    }
-  }
-  else {
-    /* we keep up the required speed all right */
-    data->state.keeps_speed = now;
-
-    if(data->set.low_speed_limit)
-      /* if there is a low speed limit enabled, we set the expire timer to
-         make this connection's speed get checked again no later than when
-         this time is up */
-      Curl_expire(data, data->set.low_speed_time*1000);
-  }
-  return CURLE_OK;
-}
diff --git a/lib/splay.c b/lib/splay.c
deleted file mode 100644 (file)
index 21f1d22..0000000
+++ /dev/null
@@ -1,288 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1997 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#include "curl_splay.h"
-
-/*
- * This macro compares two node keys i and j and returns:
- *
- *  negative value: when i is smaller than j
- *  zero          : when i is equal   to   j
- *  positive when : when i is larger  than j
- */
-#define compare(i,j) Curl_splaycomparekeys((i),(j))
-
-/*
- * Splay using the key i (which may or may not be in the tree.) The starting
- * root is t.
- */
-struct Curl_tree *Curl_splay(struct timeval i,
-                             struct Curl_tree *t)
-{
-  struct Curl_tree N, *l, *r, *y;
-  long comp;
-
-  if(t == NULL)
-    return t;
-  N.smaller = N.larger = NULL;
-  l = r = &N;
-
-  for(;;) {
-    comp = compare(i, t->key);
-    if(comp < 0) {
-      if(t->smaller == NULL)
-        break;
-      if(compare(i, t->smaller->key) < 0) {
-        y = t->smaller;                           /* rotate smaller */
-        t->smaller = y->larger;
-        y->larger = t;
-        t = y;
-        if(t->smaller == NULL)
-          break;
-      }
-      r->smaller = t;                               /* link smaller */
-      r = t;
-      t = t->smaller;
-    }
-    else if(comp > 0) {
-      if(t->larger == NULL)
-        break;
-      if(compare(i, t->larger->key) > 0) {
-        y = t->larger;                          /* rotate larger */
-        t->larger = y->smaller;
-        y->smaller = t;
-        t = y;
-        if(t->larger == NULL)
-          break;
-      }
-      l->larger = t;                              /* link larger */
-      l = t;
-      t = t->larger;
-    }
-    else
-      break;
-  }
-
-  l->larger = t->smaller;                                /* assemble */
-  r->smaller = t->larger;
-  t->smaller = N.larger;
-  t->larger = N.smaller;
-
-  return t;
-}
-
-/* Insert key i into the tree t.  Return a pointer to the resulting tree or
- * NULL if something went wrong.
- *
- * @unittest: 1309
- */
-struct Curl_tree *Curl_splayinsert(struct timeval i,
-                                   struct Curl_tree *t,
-                                   struct Curl_tree *node)
-{
-  static struct timeval KEY_NOTUSED = {-1,-1}; /* will *NEVER* appear */
-
-  if(node == NULL)
-    return t;
-
-  if(t != NULL) {
-    t = Curl_splay(i,t);
-    if(compare(i, t->key)==0) {
-      /* There already exists a node in the tree with the very same key. Build
-         a linked list of nodes. We make the new 'node' struct the new master
-         node and make the previous node the first one in the 'same' list. */
-
-      node->same = t;
-      node->key = i;
-      node->smaller = t->smaller;
-      node->larger = t->larger;
-
-      t->smaller = node; /* in the sub node for this same key, we use the
-                            smaller pointer to point back to the master
-                            node */
-
-      t->key = KEY_NOTUSED; /* and we set the key in the sub node to NOTUSED
-                               to quickly identify this node as a subnode */
-
-      return node; /* new root node */
-    }
-  }
-
-  if(t == NULL) {
-    node->smaller = node->larger = NULL;
-  }
-  else if(compare(i, t->key) < 0) {
-    node->smaller = t->smaller;
-    node->larger = t;
-    t->smaller = NULL;
-
-  }
-  else {
-    node->larger = t->larger;
-    node->smaller = t;
-    t->larger = NULL;
-  }
-  node->key = i;
-
-  node->same = NULL; /* no identical node (yet) */
-  return node;
-}
-
-/* Finds and deletes the best-fit node from the tree. Return a pointer to the
-   resulting tree.  best-fit means the node with the given or lower key */
-struct Curl_tree *Curl_splaygetbest(struct timeval i,
-                                    struct Curl_tree *t,
-                                    struct Curl_tree **removed)
-{
-  struct Curl_tree *x;
-
-  if(!t) {
-    *removed = NULL; /* none removed since there was no root */
-    return NULL;
-  }
-
-  t = Curl_splay(i,t);
-  if(compare(i, t->key) < 0) {
-    /* too big node, try the smaller chain */
-    if(t->smaller)
-      t=Curl_splay(t->smaller->key, t);
-    else {
-      /* fail */
-      *removed = NULL;
-      return t;
-    }
-  }
-
-  if(compare(i, t->key) >= 0) {               /* found it */
-    /* FIRST! Check if there is a list with identical keys */
-    x = t->same;
-    if(x) {
-      /* there is, pick one from the list */
-
-      /* 'x' is the new root node */
-
-      x->key = t->key;
-      x->larger = t->larger;
-      x->smaller = t->smaller;
-
-      *removed = t;
-      return x; /* new root */
-    }
-
-    if(t->smaller == NULL) {
-      x = t->larger;
-    }
-    else {
-      x = Curl_splay(i, t->smaller);
-      x->larger = t->larger;
-    }
-    *removed = t;
-
-    return x;
-  }
-  else {
-    *removed = NULL; /* no match */
-    return t;        /* It wasn't there */
-  }
-}
-
-
-/* Deletes the very node we point out from the tree if it's there. Stores a
- * pointer to the new resulting tree in 'newroot'.
- *
- * Returns zero on success and non-zero on errors! TODO: document error codes.
- * When returning error, it does not touch the 'newroot' pointer.
- *
- * NOTE: when the last node of the tree is removed, there's no tree left so
- * 'newroot' will be made to point to NULL.
- *
- * @unittest: 1309
- */
-int Curl_splayremovebyaddr(struct Curl_tree *t,
-                           struct Curl_tree *removenode,
-                           struct Curl_tree **newroot)
-{
-  static struct timeval KEY_NOTUSED = {-1,-1}; /* will *NEVER* appear */
-  struct Curl_tree *x;
-
-  if(!t || !removenode)
-    return 1;
-
-  if(compare(KEY_NOTUSED, removenode->key) == 0) {
-    /* Key set to NOTUSED means it is a subnode within a 'same' linked list
-       and thus we can unlink it easily. The 'smaller' link of a subnode
-       links to the parent node. */
-    if(removenode->smaller == NULL)
-      return 3;
-
-    removenode->smaller->same = removenode->same;
-    if(removenode->same)
-      removenode->same->smaller = removenode->smaller;
-
-    /* Ensures that double-remove gets caught. */
-    removenode->smaller = NULL;
-
-    /* voila, we're done! */
-    *newroot = t; /* return the same root */
-    return 0;
-  }
-
-  t = Curl_splay(removenode->key, t);
-
-  /* First make sure that we got the same root node as the one we want
-     to remove, as otherwise we might be trying to remove a node that
-     isn't actually in the tree.
-
-     We cannot just compare the keys here as a double remove in quick
-     succession of a node with key != KEY_NOTUSED && same != NULL
-     could return the same key but a different node. */
-  if(t != removenode)
-    return 2;
-
-  /* Check if there is a list with identical sizes, as then we're trying to
-     remove the root node of a list of nodes with identical keys. */
-  x = t->same;
-  if(x) {
-    /* 'x' is the new root node, we just make it use the root node's
-       smaller/larger links */
-
-    x->key = t->key;
-    x->larger = t->larger;
-    x->smaller = t->smaller;
-  }
-  else {
-    /* Remove the root node */
-    if(t->smaller == NULL)
-      x = t->larger;
-    else {
-      x = Curl_splay(removenode->key, t->smaller);
-      x->larger = t->larger;
-    }
-  }
-
-  *newroot = x; /* store new root pointer */
-
-  return 0;
-}
-
diff --git a/lib/ssh.c b/lib/ssh.c
deleted file mode 100644 (file)
index d769a04..0000000
--- a/lib/ssh.c
+++ /dev/null
@@ -1,3310 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-/* #define CURL_LIBSSH2_DEBUG */
-
-#include "curl_setup.h"
-
-#ifdef USE_LIBSSH2
-
-#ifdef HAVE_LIMITS_H
-#  include <limits.h>
-#endif
-
-#include <libssh2.h>
-#include <libssh2_sftp.h>
-
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef HAVE_UTSNAME_H
-#include <sys/utsname.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef __VMS
-#include <in.h>
-#include <inet.h>
-#endif
-
-#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
-#undef in_addr_t
-#define in_addr_t unsigned long
-#endif
-
-#include <curl/curl.h>
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_hostip.h"
-#include "curl_progress.h"
-#include "curl_transfer.h"
-#include "curl_escape.h"
-#include "curl_http.h" /* for HTTP proxy tunnel stuff */
-#include "curl_ssh.h"
-#include "curl_url.h"
-#include "curl_speedcheck.h"
-#include "curl_getinfo.h"
-
-#include "curl_strequal.h"
-#include "curl_sslgen.h"
-#include "curl_connect.h"
-#include "curl_strerror.h"
-#include "curl_inet_ntop.h"
-#include "curl_parsedate.h" /* for the week day and month names */
-#include "curl_sockaddr.h" /* required for Curl_sockaddr_storage */
-#include "curl_strtoofft.h"
-#include "curl_multiif.h"
-#include "curl_select.h"
-#include "curl_warnless.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-#ifdef WIN32
-#  undef  PATH_MAX
-#  define PATH_MAX MAX_PATH
-#endif
-
-#ifndef PATH_MAX
-#define PATH_MAX 1024 /* just an extra precaution since there are systems that
-                         have their definition hidden well */
-#endif
-
-#define sftp_libssh2_last_error(s) curlx_ultosi(libssh2_sftp_last_error(s))
-
-#define sftp_libssh2_realpath(s,p,t,m) \
-        libssh2_sftp_symlink_ex((s), (p), curlx_uztoui(strlen(p)), \
-                                (t), (m), LIBSSH2_SFTP_REALPATH)
-
-/* Local functions: */
-static const char *sftp_libssh2_strerror(int err);
-static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc);
-static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc);
-static LIBSSH2_FREE_FUNC(my_libssh2_free);
-
-static CURLcode get_pathname(const char **cpp, char **path);
-
-static CURLcode ssh_connect(struct connectdata *conn, bool *done);
-static CURLcode ssh_multi_statemach(struct connectdata *conn, bool *done);
-static CURLcode ssh_do(struct connectdata *conn, bool *done);
-
-static CURLcode ssh_getworkingpath(struct connectdata *conn,
-                                   char *homedir, /* when SFTP is used */
-                                   char **path);
-
-static CURLcode scp_done(struct connectdata *conn,
-                         CURLcode, bool premature);
-static CURLcode scp_doing(struct connectdata *conn,
-                          bool *dophase_done);
-static CURLcode scp_disconnect(struct connectdata *conn, bool dead_connection);
-
-static CURLcode sftp_done(struct connectdata *conn,
-                          CURLcode, bool premature);
-static CURLcode sftp_doing(struct connectdata *conn,
-                           bool *dophase_done);
-static CURLcode sftp_disconnect(struct connectdata *conn, bool dead);
-static
-CURLcode sftp_perform(struct connectdata *conn,
-                      bool *connected,
-                      bool *dophase_done);
-
-static int ssh_getsock(struct connectdata *conn,
-                       curl_socket_t *sock, /* points to numsocks number
-                                               of sockets */
-                       int numsocks);
-
-static int ssh_perform_getsock(const struct connectdata *conn,
-                               curl_socket_t *sock, /* points to numsocks
-                                                       number of sockets */
-                               int numsocks);
-
-/*
- * SCP protocol handler.
- */
-
-const struct Curl_handler Curl_handler_scp = {
-  "SCP",                                /* scheme */
-  ZERO_NULL,                            /* setup_connection */
-  ssh_do,                               /* do_it */
-  scp_done,                             /* done */
-  ZERO_NULL,                            /* do_more */
-  ssh_connect,                          /* connect_it */
-  ssh_multi_statemach,                  /* connecting */
-  scp_doing,                            /* doing */
-  ssh_getsock,                          /* proto_getsock */
-  ssh_getsock,                          /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ssh_perform_getsock,                  /* perform_getsock */
-  scp_disconnect,                       /* disconnect */
-  ZERO_NULL,                            /* readwrite */
-  PORT_SSH,                             /* defport */
-  CURLPROTO_SCP,                        /* protocol */
-  PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
-  | PROTOPT_NOURLQUERY                  /* flags */
-};
-
-
-/*
- * SFTP protocol handler.
- */
-
-const struct Curl_handler Curl_handler_sftp = {
-  "SFTP",                               /* scheme */
-  ZERO_NULL,                            /* setup_connection */
-  ssh_do,                               /* do_it */
-  sftp_done,                            /* done */
-  ZERO_NULL,                            /* do_more */
-  ssh_connect,                          /* connect_it */
-  ssh_multi_statemach,                  /* connecting */
-  sftp_doing,                           /* doing */
-  ssh_getsock,                          /* proto_getsock */
-  ssh_getsock,                          /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ssh_perform_getsock,                  /* perform_getsock */
-  sftp_disconnect,                      /* disconnect */
-  ZERO_NULL,                            /* readwrite */
-  PORT_SSH,                             /* defport */
-  CURLPROTO_SFTP,                       /* protocol */
-  PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
-  | PROTOPT_NOURLQUERY                  /* flags */
-};
-
-
-static void
-kbd_callback(const char *name, int name_len, const char *instruction,
-             int instruction_len, int num_prompts,
-             const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts,
-             LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses,
-             void **abstract)
-{
-  struct connectdata *conn = (struct connectdata *)*abstract;
-
-#ifdef CURL_LIBSSH2_DEBUG
-  fprintf(stderr, "name=%s\n", name);
-  fprintf(stderr, "name_len=%d\n", name_len);
-  fprintf(stderr, "instruction=%s\n", instruction);
-  fprintf(stderr, "instruction_len=%d\n", instruction_len);
-  fprintf(stderr, "num_prompts=%d\n", num_prompts);
-#else
-  (void)name;
-  (void)name_len;
-  (void)instruction;
-  (void)instruction_len;
-#endif  /* CURL_LIBSSH2_DEBUG */
-  if(num_prompts == 1) {
-    responses[0].text = strdup(conn->passwd);
-    responses[0].length = curlx_uztoui(strlen(conn->passwd));
-  }
-  (void)prompts;
-  (void)abstract;
-} /* kbd_callback */
-
-static CURLcode sftp_libssh2_error_to_CURLE(int err)
-{
-  switch (err) {
-    case LIBSSH2_FX_OK:
-      return CURLE_OK;
-
-    case LIBSSH2_FX_NO_SUCH_FILE:
-    case LIBSSH2_FX_NO_SUCH_PATH:
-      return CURLE_REMOTE_FILE_NOT_FOUND;
-
-    case LIBSSH2_FX_PERMISSION_DENIED:
-    case LIBSSH2_FX_WRITE_PROTECT:
-    case LIBSSH2_FX_LOCK_CONFlICT:
-      return CURLE_REMOTE_ACCESS_DENIED;
-
-    case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM:
-    case LIBSSH2_FX_QUOTA_EXCEEDED:
-      return CURLE_REMOTE_DISK_FULL;
-
-    case LIBSSH2_FX_FILE_ALREADY_EXISTS:
-      return CURLE_REMOTE_FILE_EXISTS;
-
-    case LIBSSH2_FX_DIR_NOT_EMPTY:
-      return CURLE_QUOTE_ERROR;
-
-    default:
-      break;
-  }
-
-  return CURLE_SSH;
-}
-
-static CURLcode libssh2_session_error_to_CURLE(int err)
-{
-  switch (err) {
-    /* Ordered by order of appearance in libssh2.h */
-    case LIBSSH2_ERROR_NONE:
-      return CURLE_OK;
-
-    case LIBSSH2_ERROR_SOCKET_NONE:
-      return CURLE_COULDNT_CONNECT;
-
-    case LIBSSH2_ERROR_ALLOC:
-      return CURLE_OUT_OF_MEMORY;
-
-    case LIBSSH2_ERROR_SOCKET_SEND:
-      return CURLE_SEND_ERROR;
-
-    case LIBSSH2_ERROR_HOSTKEY_INIT:
-    case LIBSSH2_ERROR_HOSTKEY_SIGN:
-    case LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED:
-    case LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED:
-      return CURLE_PEER_FAILED_VERIFICATION;
-
-    case LIBSSH2_ERROR_PASSWORD_EXPIRED:
-      return CURLE_LOGIN_DENIED;
-
-    case LIBSSH2_ERROR_SOCKET_TIMEOUT:
-    case LIBSSH2_ERROR_TIMEOUT:
-      return CURLE_OPERATION_TIMEDOUT;
-
-    case LIBSSH2_ERROR_EAGAIN:
-      return CURLE_AGAIN;
-  }
-
-  /* TODO: map some more of the libssh2 errors to the more appropriate CURLcode
-     error code, and possibly add a few new SSH-related one. We must however
-     not return or even depend on libssh2 errors in the public libcurl API */
-
-  return CURLE_SSH;
-}
-
-static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc)
-{
-  (void)abstract; /* arg not used */
-  return malloc(count);
-}
-
-static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc)
-{
-  (void)abstract; /* arg not used */
-  return realloc(ptr, count);
-}
-
-static LIBSSH2_FREE_FUNC(my_libssh2_free)
-{
-  (void)abstract; /* arg not used */
-  if(ptr) /* ssh2 agent sometimes call free with null ptr */
-    free(ptr);
-}
-
-/*
- * SSH State machine related code
- */
-/* This is the ONLY way to change SSH state! */
-static void state(struct connectdata *conn, sshstate nowstate)
-{
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
-  /* for debug purposes */
-  static const char * const names[] = {
-    "SSH_STOP",
-    "SSH_INIT",
-    "SSH_S_STARTUP",
-    "SSH_HOSTKEY",
-    "SSH_AUTHLIST",
-    "SSH_AUTH_PKEY_INIT",
-    "SSH_AUTH_PKEY",
-    "SSH_AUTH_PASS_INIT",
-    "SSH_AUTH_PASS",
-    "SSH_AUTH_AGENT_INIT",
-    "SSH_AUTH_AGENT_LIST",
-    "SSH_AUTH_AGENT",
-    "SSH_AUTH_HOST_INIT",
-    "SSH_AUTH_HOST",
-    "SSH_AUTH_KEY_INIT",
-    "SSH_AUTH_KEY",
-    "SSH_AUTH_DONE",
-    "SSH_SFTP_INIT",
-    "SSH_SFTP_REALPATH",
-    "SSH_SFTP_QUOTE_INIT",
-    "SSH_SFTP_POSTQUOTE_INIT",
-    "SSH_SFTP_QUOTE",
-    "SSH_SFTP_NEXT_QUOTE",
-    "SSH_SFTP_QUOTE_STAT",
-    "SSH_SFTP_QUOTE_SETSTAT",
-    "SSH_SFTP_QUOTE_SYMLINK",
-    "SSH_SFTP_QUOTE_MKDIR",
-    "SSH_SFTP_QUOTE_RENAME",
-    "SSH_SFTP_QUOTE_RMDIR",
-    "SSH_SFTP_QUOTE_UNLINK",
-    "SSH_SFTP_TRANS_INIT",
-    "SSH_SFTP_UPLOAD_INIT",
-    "SSH_SFTP_CREATE_DIRS_INIT",
-    "SSH_SFTP_CREATE_DIRS",
-    "SSH_SFTP_CREATE_DIRS_MKDIR",
-    "SSH_SFTP_READDIR_INIT",
-    "SSH_SFTP_READDIR",
-    "SSH_SFTP_READDIR_LINK",
-    "SSH_SFTP_READDIR_BOTTOM",
-    "SSH_SFTP_READDIR_DONE",
-    "SSH_SFTP_DOWNLOAD_INIT",
-    "SSH_SFTP_DOWNLOAD_STAT",
-    "SSH_SFTP_CLOSE",
-    "SSH_SFTP_SHUTDOWN",
-    "SSH_SCP_TRANS_INIT",
-    "SSH_SCP_UPLOAD_INIT",
-    "SSH_SCP_DOWNLOAD_INIT",
-    "SSH_SCP_DONE",
-    "SSH_SCP_SEND_EOF",
-    "SSH_SCP_WAIT_EOF",
-    "SSH_SCP_WAIT_CLOSE",
-    "SSH_SCP_CHANNEL_FREE",
-    "SSH_SESSION_DISCONNECT",
-    "SSH_SESSION_FREE",
-    "QUIT"
-  };
-#endif
-  struct ssh_conn *sshc = &conn->proto.sshc;
-
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
-  if(sshc->state != nowstate) {
-    infof(conn->data, "SFTP %p state change from %s to %s\n",
-          sshc, names[sshc->state], names[nowstate]);
-  }
-#endif
-
-  sshc->state = nowstate;
-}
-
-/* figure out the path to work with in this particular request */
-static CURLcode ssh_getworkingpath(struct connectdata *conn,
-                                   char *homedir,  /* when SFTP is used */
-                                   char **path) /* returns the  allocated
-                                                   real path to work with */
-{
-  struct SessionHandle *data = conn->data;
-  char *real_path = NULL;
-  char *working_path;
-  int working_path_len;
-
-  working_path = curl_easy_unescape(data, data->state.path, 0,
-                                    &working_path_len);
-  if(!working_path)
-    return CURLE_OUT_OF_MEMORY;
-
-  /* Check for /~/ , indicating relative to the user's home directory */
-  if(conn->handler->protocol & CURLPROTO_SCP) {
-    real_path = malloc(working_path_len+1);
-    if(real_path == NULL) {
-      free(working_path);
-      return CURLE_OUT_OF_MEMORY;
-    }
-    if((working_path_len > 3) && (!memcmp(working_path, "/~/", 3)))
-      /* It is referenced to the home directory, so strip the leading '/~/' */
-      memcpy(real_path, working_path+3, 4 + working_path_len-3);
-    else
-      memcpy(real_path, working_path, 1 + working_path_len);
-  }
-  else if(conn->handler->protocol & CURLPROTO_SFTP) {
-    if((working_path_len > 1) && (working_path[1] == '~')) {
-      size_t homelen = strlen(homedir);
-      real_path = malloc(homelen + working_path_len + 1);
-      if(real_path == NULL) {
-        free(working_path);
-        return CURLE_OUT_OF_MEMORY;
-      }
-      /* It is referenced to the home directory, so strip the
-         leading '/' */
-      memcpy(real_path, homedir, homelen);
-      real_path[homelen] = '/';
-      real_path[homelen+1] = '\0';
-      if(working_path_len > 3) {
-        memcpy(real_path+homelen+1, working_path + 3,
-               1 + working_path_len -3);
-      }
-    }
-    else {
-      real_path = malloc(working_path_len+1);
-      if(real_path == NULL) {
-        free(working_path);
-        return CURLE_OUT_OF_MEMORY;
-      }
-      memcpy(real_path, working_path, 1+working_path_len);
-    }
-  }
-
-  free(working_path);
-
-  /* store the pointer for the caller to receive */
-  *path = real_path;
-
-  return CURLE_OK;
-}
-
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
-static int sshkeycallback(CURL *easy,
-                          const struct curl_khkey *knownkey, /* known */
-                          const struct curl_khkey *foundkey, /* found */
-                          enum curl_khmatch match,
-                          void *clientp)
-{
-  (void)easy;
-  (void)knownkey;
-  (void)foundkey;
-  (void)clientp;
-
-  /* we only allow perfect matches, and we reject everything else */
-  return (match != CURLKHMATCH_OK)?CURLKHSTAT_REJECT:CURLKHSTAT_FINE;
-}
-#endif
-
-/*
- * Earlier libssh2 versions didn't have the ability to seek to 64bit positions
- * with 32bit size_t.
- */
-#ifdef HAVE_LIBSSH2_SFTP_SEEK64
-#define SFTP_SEEK(x,y) libssh2_sftp_seek64(x, (libssh2_uint64_t)y)
-#else
-#define SFTP_SEEK(x,y) libssh2_sftp_seek(x, (size_t)y)
-#endif
-
-/*
- * Earlier libssh2 versions didn't do SCP properly beyond 32bit sizes on 32bit
- * architectures so we check of the necessary function is present.
- */
-#ifndef HAVE_LIBSSH2_SCP_SEND64
-#define SCP_SEND(a,b,c,d) libssh2_scp_send_ex(a, b, (int)(c), (size_t)d, 0, 0)
-#else
-#define SCP_SEND(a,b,c,d) libssh2_scp_send64(a, b, (int)(c),            \
-                                             (libssh2_uint64_t)d, 0, 0)
-#endif
-
-/*
- * libssh2 1.2.8 fixed the problem with 32bit ints used for sockets on win64.
- */
-#ifdef HAVE_LIBSSH2_SESSION_HANDSHAKE
-#define libssh2_session_startup(x,y) libssh2_session_handshake(x,y)
-#endif
-
-static CURLcode ssh_knownhost(struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
-  struct SessionHandle *data = conn->data;
-
-  if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
-    /* we're asked to verify the host against a file */
-    struct ssh_conn *sshc = &conn->proto.sshc;
-    int rc;
-    int keytype;
-    size_t keylen;
-    const char *remotekey = libssh2_session_hostkey(sshc->ssh_session,
-                                                    &keylen, &keytype);
-    int keycheck = LIBSSH2_KNOWNHOST_CHECK_FAILURE;
-    int keybit = 0;
-
-    if(remotekey) {
-      /*
-       * A subject to figure out is what host name we need to pass in here.
-       * What host name does OpenSSH store in its file if an IDN name is
-       * used?
-       */
-      struct libssh2_knownhost *host;
-      enum curl_khmatch keymatch;
-      curl_sshkeycallback func =
-        data->set.ssh_keyfunc?data->set.ssh_keyfunc:sshkeycallback;
-      struct curl_khkey knownkey;
-      struct curl_khkey *knownkeyp = NULL;
-      struct curl_khkey foundkey;
-
-      keybit = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)?
-        LIBSSH2_KNOWNHOST_KEY_SSHRSA:LIBSSH2_KNOWNHOST_KEY_SSHDSS;
-
-      keycheck = libssh2_knownhost_check(sshc->kh,
-                                         conn->host.name,
-                                         remotekey, keylen,
-                                         LIBSSH2_KNOWNHOST_TYPE_PLAIN|
-                                         LIBSSH2_KNOWNHOST_KEYENC_RAW|
-                                         keybit,
-                                         &host);
-
-      infof(data, "SSH host check: %d, key: %s\n", keycheck,
-            (keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH)?
-            host->key:"<none>");
-
-      /* setup 'knownkey' */
-      if(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) {
-        knownkey.key = host->key;
-        knownkey.len = 0;
-        knownkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)?
-          CURLKHTYPE_RSA : CURLKHTYPE_DSS;
-        knownkeyp = &knownkey;
-      }
-
-      /* setup 'foundkey' */
-      foundkey.key = remotekey;
-      foundkey.len = keylen;
-      foundkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)?
-        CURLKHTYPE_RSA : CURLKHTYPE_DSS;
-
-      /*
-       * if any of the LIBSSH2_KNOWNHOST_CHECK_* defines and the
-       * curl_khmatch enum are ever modified, we need to introduce a
-       * translation table here!
-       */
-      keymatch = (enum curl_khmatch)keycheck;
-
-      /* Ask the callback how to behave */
-      rc = func(data, knownkeyp, /* from the knownhosts file */
-                &foundkey, /* from the remote host */
-                keymatch, data->set.ssh_keyfunc_userp);
-    }
-    else
-      /* no remotekey means failure! */
-      rc = CURLKHSTAT_REJECT;
-
-    switch(rc) {
-    default: /* unknown return codes will equal reject */
-    case CURLKHSTAT_REJECT:
-      state(conn, SSH_SESSION_FREE);
-    case CURLKHSTAT_DEFER:
-      /* DEFER means bail out but keep the SSH_HOSTKEY state */
-      result = sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
-      break;
-    case CURLKHSTAT_FINE:
-    case CURLKHSTAT_FINE_ADD_TO_FILE:
-      /* proceed */
-      if(keycheck != LIBSSH2_KNOWNHOST_CHECK_MATCH) {
-        /* the found host+key didn't match but has been told to be fine
-           anyway so we add it in memory */
-        int addrc = libssh2_knownhost_add(sshc->kh,
-                                          conn->host.name, NULL,
-                                          remotekey, keylen,
-                                          LIBSSH2_KNOWNHOST_TYPE_PLAIN|
-                                          LIBSSH2_KNOWNHOST_KEYENC_RAW|
-                                          keybit, NULL);
-        if(addrc)
-          infof(data, "Warning adding the known host %s failed!\n",
-                conn->host.name);
-        else if(rc == CURLKHSTAT_FINE_ADD_TO_FILE) {
-          /* now we write the entire in-memory list of known hosts to the
-             known_hosts file */
-          int wrc =
-            libssh2_knownhost_writefile(sshc->kh,
-                                        data->set.str[STRING_SSH_KNOWNHOSTS],
-                                        LIBSSH2_KNOWNHOST_FILE_OPENSSH);
-          if(wrc) {
-            infof(data, "Warning, writing %s failed!\n",
-                  data->set.str[STRING_SSH_KNOWNHOSTS]);
-          }
-        }
-      }
-      break;
-    }
-  }
-#else /* HAVE_LIBSSH2_KNOWNHOST_API */
-  (void)conn;
-#endif
-  return result;
-}
-
-static CURLcode ssh_check_fingerprint(struct connectdata *conn)
-{
-  struct ssh_conn *sshc = &conn->proto.sshc;
-  struct SessionHandle *data = conn->data;
-  const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5];
-  char md5buffer[33];
-  int i;
-
-  const char *fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
-      LIBSSH2_HOSTKEY_HASH_MD5);
-
-  if(fingerprint) {
-    /* The fingerprint points to static storage (!), don't free() it. */
-    for(i = 0; i < 16; i++)
-      snprintf(&md5buffer[i*2], 3, "%02x", (unsigned char) fingerprint[i]);
-    infof(data, "SSH MD5 fingerprint: %s\n", md5buffer);
-  }
-
-  /* Before we authenticate we check the hostkey's MD5 fingerprint
-   * against a known fingerprint, if available.
-   */
-  if(pubkey_md5 && strlen(pubkey_md5) == 32) {
-    if(!fingerprint || !strequal(md5buffer, pubkey_md5)) {
-      if(fingerprint)
-        failf(data,
-            "Denied establishing ssh session: mismatch md5 fingerprint. "
-            "Remote %s is not equal to %s", md5buffer, pubkey_md5);
-      else
-        failf(data,
-            "Denied establishing ssh session: md5 fingerprint not available");
-      state(conn, SSH_SESSION_FREE);
-      sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
-      return sshc->actualcode;
-    }
-    else {
-      infof(data, "MD5 checksum match!\n");
-      /* as we already matched, we skip the check for known hosts */
-      return CURLE_OK;
-    }
-  }
-  else
-    return ssh_knownhost(conn);
-}
-
-/*
- * ssh_statemach_act() runs the SSH state machine as far as it can without
- * blocking and without reaching the end.  The data the pointer 'block' points
- * to will be set to TRUE if the libssh2 function returns LIBSSH2_ERROR_EAGAIN
- * meaning it wants to be called again when the socket is ready
- */
-
-static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-  struct SSHPROTO *sftp_scp = data->state.proto.ssh;
-  struct ssh_conn *sshc = &conn->proto.sshc;
-  curl_socket_t sock = conn->sock[FIRSTSOCKET];
-  char *new_readdir_line;
-  int rc = LIBSSH2_ERROR_NONE;
-  int err;
-  int seekerr = CURL_SEEKFUNC_OK;
-  *block = 0; /* we're not blocking by default */
-
-  do {
-
-    switch(sshc->state) {
-    case SSH_INIT:
-      sshc->secondCreateDirs = 0;
-      sshc->nextstate = SSH_NO_STATE;
-      sshc->actualcode = CURLE_OK;
-
-      /* Set libssh2 to non-blocking, since everything internally is
-         non-blocking */
-      libssh2_session_set_blocking(sshc->ssh_session, 0);
-
-      state(conn, SSH_S_STARTUP);
-      /* fall-through */
-
-    case SSH_S_STARTUP:
-      rc = libssh2_session_startup(sshc->ssh_session, (int)sock);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      else if(rc) {
-        failf(data, "Failure establishing ssh session");
-        state(conn, SSH_SESSION_FREE);
-        sshc->actualcode = CURLE_FAILED_INIT;
-        break;
-      }
-
-      state(conn, SSH_HOSTKEY);
-
-      /* fall-through */
-    case SSH_HOSTKEY:
-      /*
-       * Before we authenticate we should check the hostkey's fingerprint
-       * against our known hosts. How that is handled (reading from file,
-       * whatever) is up to us.
-       */
-      result = ssh_check_fingerprint(conn);
-      if(result == CURLE_OK)
-        state(conn, SSH_AUTHLIST);
-      break;
-
-    case SSH_AUTHLIST:
-      /*
-       * Figure out authentication methods
-       * NB: As soon as we have provided a username to an openssh server we
-       * must never change it later. Thus, always specify the correct username
-       * here, even though the libssh2 docs kind of indicate that it should be
-       * possible to get a 'generic' list (not user-specific) of authentication
-       * methods, presumably with a blank username. That won't work in my
-       * experience.
-       * So always specify it here.
-       */
-      sshc->authlist = libssh2_userauth_list(sshc->ssh_session,
-                                             conn->user,
-                                             curlx_uztoui(strlen(conn->user)));
-
-      if(!sshc->authlist) {
-        if((err = libssh2_session_last_errno(sshc->ssh_session)) ==
-           LIBSSH2_ERROR_EAGAIN) {
-          rc = LIBSSH2_ERROR_EAGAIN;
-          break;
-        }
-        else {
-          state(conn, SSH_SESSION_FREE);
-          sshc->actualcode = libssh2_session_error_to_CURLE(err);
-          break;
-        }
-      }
-      infof(data, "SSH authentication methods available: %s\n",
-            sshc->authlist);
-
-      state(conn, SSH_AUTH_PKEY_INIT);
-      break;
-
-    case SSH_AUTH_PKEY_INIT:
-      /*
-       * Check the supported auth types in the order I feel is most secure
-       * with the requested type of authentication
-       */
-      sshc->authed = FALSE;
-
-      if((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) &&
-         (strstr(sshc->authlist, "publickey") != NULL)) {
-        char *home = NULL;
-        bool rsa_pub_empty_but_ok = FALSE;
-
-        sshc->rsa_pub = sshc->rsa = NULL;
-
-        /* To ponder about: should really the lib be messing about with the
-           HOME environment variable etc? */
-        home = curl_getenv("HOME");
-
-        if(data->set.str[STRING_SSH_PUBLIC_KEY] &&
-           !*data->set.str[STRING_SSH_PUBLIC_KEY])
-           rsa_pub_empty_but_ok = true;
-        else if(data->set.str[STRING_SSH_PUBLIC_KEY])
-          sshc->rsa_pub = aprintf("%s", data->set.str[STRING_SSH_PUBLIC_KEY]);
-        else if(home)
-          sshc->rsa_pub = aprintf("%s/.ssh/id_dsa.pub", home);
-        else
-          /* as a final resort, try current dir! */
-          sshc->rsa_pub = strdup("id_dsa.pub");
-
-        if(!rsa_pub_empty_but_ok && (sshc->rsa_pub == NULL)) {
-          Curl_safefree(home);
-          state(conn, SSH_SESSION_FREE);
-          sshc->actualcode = CURLE_OUT_OF_MEMORY;
-          break;
-        }
-
-        if(data->set.str[STRING_SSH_PRIVATE_KEY])
-          sshc->rsa = aprintf("%s", data->set.str[STRING_SSH_PRIVATE_KEY]);
-        else if(home)
-          sshc->rsa = aprintf("%s/.ssh/id_dsa", home);
-        else
-          /* as a final resort, try current dir! */
-          sshc->rsa = strdup("id_dsa");
-
-        if(sshc->rsa == NULL) {
-          Curl_safefree(home);
-          Curl_safefree(sshc->rsa_pub);
-          state(conn, SSH_SESSION_FREE);
-          sshc->actualcode = CURLE_OUT_OF_MEMORY;
-          break;
-        }
-
-        sshc->passphrase = data->set.str[STRING_KEY_PASSWD];
-        if(!sshc->passphrase)
-          sshc->passphrase = "";
-
-        Curl_safefree(home);
-
-        infof(data, "Using ssh public key file %s\n", sshc->rsa_pub);
-        infof(data, "Using ssh private key file %s\n", sshc->rsa);
-
-        state(conn, SSH_AUTH_PKEY);
-      }
-      else {
-        state(conn, SSH_AUTH_PASS_INIT);
-      }
-      break;
-
-    case SSH_AUTH_PKEY:
-      /* The function below checks if the files exists, no need to stat() here.
-       */
-      rc = libssh2_userauth_publickey_fromfile_ex(sshc->ssh_session,
-                                                  conn->user,
-                                                  curlx_uztoui(
-                                                    strlen(conn->user)),
-                                                  sshc->rsa_pub,
-                                                  sshc->rsa, sshc->passphrase);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-
-      Curl_safefree(sshc->rsa_pub);
-      Curl_safefree(sshc->rsa);
-
-      if(rc == 0) {
-        sshc->authed = TRUE;
-        infof(data, "Initialized SSH public key authentication\n");
-        state(conn, SSH_AUTH_DONE);
-      }
-      else {
-        char *err_msg;
-        (void)libssh2_session_last_error(sshc->ssh_session,
-                                         &err_msg, NULL, 0);
-        infof(data, "SSH public key authentication failed: %s\n", err_msg);
-        state(conn, SSH_AUTH_PASS_INIT);
-      }
-      break;
-
-    case SSH_AUTH_PASS_INIT:
-      if((data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) &&
-         (strstr(sshc->authlist, "password") != NULL)) {
-        state(conn, SSH_AUTH_PASS);
-      }
-      else {
-        state(conn, SSH_AUTH_HOST_INIT);
-      }
-      break;
-
-    case SSH_AUTH_PASS:
-      rc = libssh2_userauth_password_ex(sshc->ssh_session, conn->user,
-                                        curlx_uztoui(strlen(conn->user)),
-                                        conn->passwd,
-                                        curlx_uztoui(strlen(conn->passwd)),
-                                        NULL);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      else if(rc == 0) {
-        sshc->authed = TRUE;
-        infof(data, "Initialized password authentication\n");
-        state(conn, SSH_AUTH_DONE);
-      }
-      else {
-        state(conn, SSH_AUTH_HOST_INIT);
-      }
-      break;
-
-    case SSH_AUTH_HOST_INIT:
-      if((data->set.ssh_auth_types & CURLSSH_AUTH_HOST) &&
-         (strstr(sshc->authlist, "hostbased") != NULL)) {
-        state(conn, SSH_AUTH_HOST);
-      }
-      else {
-        state(conn, SSH_AUTH_AGENT_INIT);
-      }
-      break;
-
-    case SSH_AUTH_HOST:
-      state(conn, SSH_AUTH_AGENT_INIT);
-      break;
-
-    case SSH_AUTH_AGENT_INIT:
-#ifdef HAVE_LIBSSH2_AGENT_API
-      if((data->set.ssh_auth_types & CURLSSH_AUTH_AGENT)
-         && (strstr(sshc->authlist, "publickey") != NULL)) {
-
-        /* Connect to the ssh-agent */
-        /* The agent could be shared by a curl thread i believe
-           but nothing obvious as keys can be added/removed at any time */
-        if(!sshc->ssh_agent) {
-          sshc->ssh_agent = libssh2_agent_init(sshc->ssh_session);
-          if(!sshc->ssh_agent) {
-            infof(data, "Could not create agent object\n");
-
-            state(conn, SSH_AUTH_KEY_INIT);
-          }
-        }
-
-        rc = libssh2_agent_connect(sshc->ssh_agent);
-        if(rc == LIBSSH2_ERROR_EAGAIN)
-          break;
-        if(rc < 0) {
-          infof(data, "Failure connecting to agent\n");
-          state(conn, SSH_AUTH_KEY_INIT);
-        }
-        else {
-          state(conn, SSH_AUTH_AGENT_LIST);
-        }
-      }
-      else
-#endif /* HAVE_LIBSSH2_AGENT_API */
-        state(conn, SSH_AUTH_KEY_INIT);
-      break;
-
-    case SSH_AUTH_AGENT_LIST:
-#ifdef HAVE_LIBSSH2_AGENT_API
-      rc = libssh2_agent_list_identities(sshc->ssh_agent);
-
-      if(rc == LIBSSH2_ERROR_EAGAIN)
-        break;
-      if(rc < 0) {
-        infof(data, "Failure requesting identities to agent\n");
-        state(conn, SSH_AUTH_KEY_INIT);
-      }
-      else {
-        state(conn, SSH_AUTH_AGENT);
-        sshc->sshagent_prev_identity = NULL;
-      }
-#endif
-      break;
-
-    case SSH_AUTH_AGENT:
-#ifdef HAVE_LIBSSH2_AGENT_API
-      /* as prev_identity evolves only after an identity user auth finished we
-         can safely request it again as long as EAGAIN is returned here or by
-         libssh2_agent_userauth */
-      rc = libssh2_agent_get_identity(sshc->ssh_agent,
-                                      &sshc->sshagent_identity,
-                                      sshc->sshagent_prev_identity);
-      if(rc == LIBSSH2_ERROR_EAGAIN)
-        break;
-
-      if(rc == 0) {
-        rc = libssh2_agent_userauth(sshc->ssh_agent, conn->user,
-                                    sshc->sshagent_identity);
-
-        if(rc < 0) {
-          if(rc != LIBSSH2_ERROR_EAGAIN) {
-            /* tried and failed? go to next identity */
-            sshc->sshagent_prev_identity = sshc->sshagent_identity;
-          }
-          break;
-        }
-      }
-
-      if(rc < 0)
-        infof(data, "Failure requesting identities to agent\n");
-      else if(rc == 1)
-        infof(data, "No identity would match\n");
-
-      if(rc == LIBSSH2_ERROR_NONE) {
-        sshc->authed = TRUE;
-        infof(data, "Agent based authentication successful\n");
-        state(conn, SSH_AUTH_DONE);
-      }
-      else
-        state(conn, SSH_AUTH_KEY_INIT);
-#endif
-      break;
-
-    case SSH_AUTH_KEY_INIT:
-      if((data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD)
-         && (strstr(sshc->authlist, "keyboard-interactive") != NULL)) {
-        state(conn, SSH_AUTH_KEY);
-      }
-      else {
-        state(conn, SSH_AUTH_DONE);
-      }
-      break;
-
-    case SSH_AUTH_KEY:
-      /* Authentication failed. Continue with keyboard-interactive now. */
-      rc = libssh2_userauth_keyboard_interactive_ex(sshc->ssh_session,
-                                                    conn->user,
-                                                    curlx_uztoui(
-                                                      strlen(conn->user)),
-                                                    &kbd_callback);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      else if(rc == 0) {
-        sshc->authed = TRUE;
-        infof(data, "Initialized keyboard interactive authentication\n");
-      }
-      state(conn, SSH_AUTH_DONE);
-      break;
-
-    case SSH_AUTH_DONE:
-      if(!sshc->authed) {
-        failf(data, "Authentication failure");
-        state(conn, SSH_SESSION_FREE);
-        sshc->actualcode = CURLE_LOGIN_DENIED;
-        break;
-      }
-
-      /*
-       * At this point we have an authenticated ssh session.
-       */
-      infof(data, "Authentication complete\n");
-
-      Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSH is connected */
-
-      conn->sockfd = sock;
-      conn->writesockfd = CURL_SOCKET_BAD;
-
-      if(conn->handler->protocol == CURLPROTO_SFTP) {
-        state(conn, SSH_SFTP_INIT);
-        break;
-      }
-      infof(data, "SSH CONNECT phase done\n");
-      state(conn, SSH_STOP);
-      break;
-
-    case SSH_SFTP_INIT:
-      /*
-       * Start the libssh2 sftp session
-       */
-      sshc->sftp_session = libssh2_sftp_init(sshc->ssh_session);
-      if(!sshc->sftp_session) {
-        if(libssh2_session_last_errno(sshc->ssh_session) ==
-           LIBSSH2_ERROR_EAGAIN) {
-          rc = LIBSSH2_ERROR_EAGAIN;
-          break;
-        }
-        else {
-          char *err_msg;
-
-          (void)libssh2_session_last_error(sshc->ssh_session,
-                                           &err_msg, NULL, 0);
-          failf(data, "Failure initializing sftp session: %s", err_msg);
-          state(conn, SSH_SESSION_FREE);
-          sshc->actualcode = CURLE_FAILED_INIT;
-          break;
-        }
-      }
-      state(conn, SSH_SFTP_REALPATH);
-      break;
-
-    case SSH_SFTP_REALPATH:
-    {
-      char tempHome[PATH_MAX];
-
-      /*
-       * Get the "home" directory
-       */
-      rc = sftp_libssh2_realpath(sshc->sftp_session, ".",
-                                 tempHome, PATH_MAX-1);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      else if(rc > 0) {
-        /* It seems that this string is not always NULL terminated */
-        tempHome[rc] = '\0';
-        sshc->homedir = strdup(tempHome);
-        if(!sshc->homedir) {
-          state(conn, SSH_SFTP_CLOSE);
-          sshc->actualcode = CURLE_OUT_OF_MEMORY;
-          break;
-        }
-        conn->data->state.most_recent_ftp_entrypath = sshc->homedir;
-      }
-      else {
-        /* Return the error type */
-        err = sftp_libssh2_last_error(sshc->sftp_session);
-        result = sftp_libssh2_error_to_CURLE(err);
-        sshc->actualcode = result?result:CURLE_SSH;
-        DEBUGF(infof(data, "error = %d makes libcurl = %d\n",
-                     err, (int)result));
-        state(conn, SSH_STOP);
-        break;
-      }
-    }
-    /* This is the last step in the SFTP connect phase. Do note that while
-       we get the homedir here, we get the "workingpath" in the DO action
-       since the homedir will remain the same between request but the
-       working path will not. */
-    DEBUGF(infof(data, "SSH CONNECT phase done\n"));
-    state(conn, SSH_STOP);
-    break;
-
-    case SSH_SFTP_QUOTE_INIT:
-
-      result = ssh_getworkingpath(conn, sshc->homedir, &sftp_scp->path);
-      if(result) {
-        sshc->actualcode = result;
-        state(conn, SSH_STOP);
-        break;
-      }
-
-      if(data->set.quote) {
-        infof(data, "Sending quote commands\n");
-        sshc->quote_item = data->set.quote;
-        state(conn, SSH_SFTP_QUOTE);
-      }
-      else {
-        state(conn, SSH_SFTP_TRANS_INIT);
-      }
-      break;
-
-    case SSH_SFTP_POSTQUOTE_INIT:
-      if(data->set.postquote) {
-        infof(data, "Sending quote commands\n");
-        sshc->quote_item = data->set.postquote;
-        state(conn, SSH_SFTP_QUOTE);
-      }
-      else {
-        state(conn, SSH_STOP);
-      }
-      break;
-
-    case SSH_SFTP_QUOTE:
-      /* Send any quote commands */
-    {
-      const char *cp;
-
-      /*
-       * Support some of the "FTP" commands
-       */
-      char *cmd = sshc->quote_item->data;
-      sshc->acceptfail = FALSE;
-
-      /* if a command starts with an asterisk, which a legal SFTP command never
-         can, the command will be allowed to fail without it causing any
-         aborts or cancels etc. It will cause libcurl to act as if the command
-         is successful, whatever the server reponds. */
-
-      if(cmd[0] == '*') {
-        cmd++;
-        sshc->acceptfail = TRUE;
-      }
-
-      if(curl_strequal("pwd", cmd)) {
-        /* output debug output if that is requested */
-        char *tmp = aprintf("257 \"%s\" is current directory.\n",
-                            sftp_scp->path);
-        if(!tmp) {
-          result = CURLE_OUT_OF_MEMORY;
-          state(conn, SSH_SFTP_CLOSE);
-          sshc->nextstate = SSH_NO_STATE;
-          break;
-        }
-        if(data->set.verbose) {
-          Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"PWD\n", 4, conn);
-          Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp), conn);
-        }
-        /* this sends an FTP-like "header" to the header callback so that the
-           current directory can be read very similar to how it is read when
-           using ordinary FTP. */
-        result = Curl_client_write(conn, CLIENTWRITE_HEADER, tmp, strlen(tmp));
-        free(tmp);
-        state(conn, SSH_SFTP_NEXT_QUOTE);
-        break;
-      }
-      else if(cmd) {
-        /*
-         * the arguments following the command must be separated from the
-         * command with a space so we can check for it unconditionally
-         */
-        cp = strchr(cmd, ' ');
-        if(cp == NULL) {
-          failf(data, "Syntax error in SFTP command. Supply parameter(s)!");
-          state(conn, SSH_SFTP_CLOSE);
-          sshc->nextstate = SSH_NO_STATE;
-          sshc->actualcode = CURLE_QUOTE_ERROR;
-          break;
-        }
-
-        /*
-         * also, every command takes at least one argument so we get that
-         * first argument right now
-         */
-        result = get_pathname(&cp, &sshc->quote_path1);
-        if(result) {
-          if(result == CURLE_OUT_OF_MEMORY)
-            failf(data, "Out of memory");
-          else
-            failf(data, "Syntax error: Bad first parameter");
-          state(conn, SSH_SFTP_CLOSE);
-          sshc->nextstate = SSH_NO_STATE;
-          sshc->actualcode = result;
-          break;
-        }
-
-        /*
-         * SFTP is a binary protocol, so we don't send text commands to
-         * the server. Instead, we scan for commands for commands used by
-         * OpenSSH's sftp program and call the appropriate libssh2
-         * functions.
-         */
-        if(curl_strnequal(cmd, "chgrp ", 6) ||
-           curl_strnequal(cmd, "chmod ", 6) ||
-           curl_strnequal(cmd, "chown ", 6) ) {
-          /* attribute change */
-
-          /* sshc->quote_path1 contains the mode to set */
-          /* get the destination */
-          result = get_pathname(&cp, &sshc->quote_path2);
-          if(result) {
-            if(result == CURLE_OUT_OF_MEMORY)
-              failf(data, "Out of memory");
-            else
-              failf(data, "Syntax error in chgrp/chmod/chown: "
-                    "Bad second parameter");
-            Curl_safefree(sshc->quote_path1);
-            state(conn, SSH_SFTP_CLOSE);
-            sshc->nextstate = SSH_NO_STATE;
-            sshc->actualcode = result;
-            break;
-          }
-          memset(&sshc->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
-          state(conn, SSH_SFTP_QUOTE_STAT);
-          break;
-        }
-        else if(curl_strnequal(cmd, "ln ", 3) ||
-                curl_strnequal(cmd, "symlink ", 8)) {
-          /* symbolic linking */
-          /* sshc->quote_path1 is the source */
-          /* get the destination */
-          result = get_pathname(&cp, &sshc->quote_path2);
-          if(result) {
-            if(result == CURLE_OUT_OF_MEMORY)
-              failf(data, "Out of memory");
-            else
-              failf(data,
-                    "Syntax error in ln/symlink: Bad second parameter");
-            Curl_safefree(sshc->quote_path1);
-            state(conn, SSH_SFTP_CLOSE);
-            sshc->nextstate = SSH_NO_STATE;
-            sshc->actualcode = result;
-            break;
-          }
-          state(conn, SSH_SFTP_QUOTE_SYMLINK);
-          break;
-        }
-        else if(curl_strnequal(cmd, "mkdir ", 6)) {
-          /* create dir */
-          state(conn, SSH_SFTP_QUOTE_MKDIR);
-          break;
-        }
-        else if(curl_strnequal(cmd, "rename ", 7)) {
-          /* rename file */
-          /* first param is the source path */
-          /* second param is the dest. path */
-          result = get_pathname(&cp, &sshc->quote_path2);
-          if(result) {
-            if(result == CURLE_OUT_OF_MEMORY)
-              failf(data, "Out of memory");
-            else
-              failf(data, "Syntax error in rename: Bad second parameter");
-            Curl_safefree(sshc->quote_path1);
-            state(conn, SSH_SFTP_CLOSE);
-            sshc->nextstate = SSH_NO_STATE;
-            sshc->actualcode = result;
-            break;
-          }
-          state(conn, SSH_SFTP_QUOTE_RENAME);
-          break;
-        }
-        else if(curl_strnequal(cmd, "rmdir ", 6)) {
-          /* delete dir */
-          state(conn, SSH_SFTP_QUOTE_RMDIR);
-          break;
-        }
-        else if(curl_strnequal(cmd, "rm ", 3)) {
-          state(conn, SSH_SFTP_QUOTE_UNLINK);
-          break;
-        }
-
-        failf(data, "Unknown SFTP command");
-        Curl_safefree(sshc->quote_path1);
-        Curl_safefree(sshc->quote_path2);
-        state(conn, SSH_SFTP_CLOSE);
-        sshc->nextstate = SSH_NO_STATE;
-        sshc->actualcode = CURLE_QUOTE_ERROR;
-        break;
-      }
-    }
-    if(!sshc->quote_item) {
-      state(conn, SSH_SFTP_TRANS_INIT);
-    }
-    break;
-
-    case SSH_SFTP_NEXT_QUOTE:
-      Curl_safefree(sshc->quote_path1);
-      Curl_safefree(sshc->quote_path2);
-
-      sshc->quote_item = sshc->quote_item->next;
-
-      if(sshc->quote_item) {
-        state(conn, SSH_SFTP_QUOTE);
-      }
-      else {
-        if(sshc->nextstate != SSH_NO_STATE) {
-          state(conn, sshc->nextstate);
-          sshc->nextstate = SSH_NO_STATE;
-        }
-        else {
-          state(conn, SSH_SFTP_TRANS_INIT);
-        }
-      }
-      break;
-
-    case SSH_SFTP_QUOTE_STAT:
-    {
-      char *cmd = sshc->quote_item->data;
-      sshc->acceptfail = FALSE;
-
-      /* if a command starts with an asterisk, which a legal SFTP command never
-         can, the command will be allowed to fail without it causing any
-         aborts or cancels etc. It will cause libcurl to act as if the command
-         is successful, whatever the server reponds. */
-
-      if(cmd[0] == '*') {
-        cmd++;
-        sshc->acceptfail = TRUE;
-      }
-
-      if(!curl_strnequal(cmd, "chmod", 5)) {
-        /* Since chown and chgrp only set owner OR group but libssh2 wants to
-         * set them both at once, we need to obtain the current ownership
-         * first.  This takes an extra protocol round trip.
-         */
-        rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2,
-                                  curlx_uztoui(strlen(sshc->quote_path2)),
-                                  LIBSSH2_SFTP_STAT,
-                                  &sshc->quote_attrs);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else if(rc != 0 && !sshc->acceptfail) { /* get those attributes */
-          err = sftp_libssh2_last_error(sshc->sftp_session);
-          Curl_safefree(sshc->quote_path1);
-          Curl_safefree(sshc->quote_path2);
-          failf(data, "Attempt to get SFTP stats failed: %s",
-                sftp_libssh2_strerror(err));
-          state(conn, SSH_SFTP_CLOSE);
-          sshc->nextstate = SSH_NO_STATE;
-          sshc->actualcode = CURLE_QUOTE_ERROR;
-          break;
-        }
-      }
-
-      /* Now set the new attributes... */
-      if(curl_strnequal(cmd, "chgrp", 5)) {
-        sshc->quote_attrs.gid = strtoul(sshc->quote_path1, NULL, 10);
-        sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID;
-        if(sshc->quote_attrs.gid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
-           !sshc->acceptfail) {
-          Curl_safefree(sshc->quote_path1);
-          Curl_safefree(sshc->quote_path2);
-          failf(data, "Syntax error: chgrp gid not a number");
-          state(conn, SSH_SFTP_CLOSE);
-          sshc->nextstate = SSH_NO_STATE;
-          sshc->actualcode = CURLE_QUOTE_ERROR;
-          break;
-        }
-      }
-      else if(curl_strnequal(cmd, "chmod", 5)) {
-        sshc->quote_attrs.permissions = strtoul(sshc->quote_path1, NULL, 8);
-        sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;
-        /* permissions are octal */
-        if(sshc->quote_attrs.permissions == 0 &&
-           !ISDIGIT(sshc->quote_path1[0])) {
-          Curl_safefree(sshc->quote_path1);
-          Curl_safefree(sshc->quote_path2);
-          failf(data, "Syntax error: chmod permissions not a number");
-          state(conn, SSH_SFTP_CLOSE);
-          sshc->nextstate = SSH_NO_STATE;
-          sshc->actualcode = CURLE_QUOTE_ERROR;
-          break;
-        }
-      }
-      else if(curl_strnequal(cmd, "chown", 5)) {
-        sshc->quote_attrs.uid = strtoul(sshc->quote_path1, NULL, 10);
-        sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID;
-        if(sshc->quote_attrs.uid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
-           !sshc->acceptfail) {
-          Curl_safefree(sshc->quote_path1);
-          Curl_safefree(sshc->quote_path2);
-          failf(data, "Syntax error: chown uid not a number");
-          state(conn, SSH_SFTP_CLOSE);
-          sshc->nextstate = SSH_NO_STATE;
-          sshc->actualcode = CURLE_QUOTE_ERROR;
-          break;
-        }
-      }
-
-      /* Now send the completed structure... */
-      state(conn, SSH_SFTP_QUOTE_SETSTAT);
-      break;
-    }
-
-    case SSH_SFTP_QUOTE_SETSTAT:
-      rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2,
-                                curlx_uztoui(strlen(sshc->quote_path2)),
-                                LIBSSH2_SFTP_SETSTAT,
-                                &sshc->quote_attrs);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      else if(rc != 0 && !sshc->acceptfail) {
-        err = sftp_libssh2_last_error(sshc->sftp_session);
-        Curl_safefree(sshc->quote_path1);
-        Curl_safefree(sshc->quote_path2);
-        failf(data, "Attempt to set SFTP stats failed: %s",
-              sftp_libssh2_strerror(err));
-        state(conn, SSH_SFTP_CLOSE);
-        sshc->nextstate = SSH_NO_STATE;
-        sshc->actualcode = CURLE_QUOTE_ERROR;
-        break;
-      }
-      state(conn, SSH_SFTP_NEXT_QUOTE);
-      break;
-
-    case SSH_SFTP_QUOTE_SYMLINK:
-      rc = libssh2_sftp_symlink_ex(sshc->sftp_session, sshc->quote_path1,
-                                   curlx_uztoui(strlen(sshc->quote_path1)),
-                                   sshc->quote_path2,
-                                   curlx_uztoui(strlen(sshc->quote_path2)),
-                                   LIBSSH2_SFTP_SYMLINK);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      else if(rc != 0 && !sshc->acceptfail) {
-        err = sftp_libssh2_last_error(sshc->sftp_session);
-        Curl_safefree(sshc->quote_path1);
-        Curl_safefree(sshc->quote_path2);
-        failf(data, "symlink command failed: %s",
-              sftp_libssh2_strerror(err));
-        state(conn, SSH_SFTP_CLOSE);
-        sshc->nextstate = SSH_NO_STATE;
-        sshc->actualcode = CURLE_QUOTE_ERROR;
-        break;
-      }
-      state(conn, SSH_SFTP_NEXT_QUOTE);
-      break;
-
-    case SSH_SFTP_QUOTE_MKDIR:
-      rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshc->quote_path1,
-                                 curlx_uztoui(strlen(sshc->quote_path1)),
-                                 data->set.new_directory_perms);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      else if(rc != 0 && !sshc->acceptfail) {
-        err = sftp_libssh2_last_error(sshc->sftp_session);
-        Curl_safefree(sshc->quote_path1);
-        failf(data, "mkdir command failed: %s", sftp_libssh2_strerror(err));
-        state(conn, SSH_SFTP_CLOSE);
-        sshc->nextstate = SSH_NO_STATE;
-        sshc->actualcode = CURLE_QUOTE_ERROR;
-        break;
-      }
-      state(conn, SSH_SFTP_NEXT_QUOTE);
-      break;
-
-    case SSH_SFTP_QUOTE_RENAME:
-      rc = libssh2_sftp_rename_ex(sshc->sftp_session, sshc->quote_path1,
-                                  curlx_uztoui(strlen(sshc->quote_path1)),
-                                  sshc->quote_path2,
-                                  curlx_uztoui(strlen(sshc->quote_path2)),
-                                  LIBSSH2_SFTP_RENAME_OVERWRITE |
-                                  LIBSSH2_SFTP_RENAME_ATOMIC |
-                                  LIBSSH2_SFTP_RENAME_NATIVE);
-
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      else if(rc != 0 && !sshc->acceptfail) {
-        err = sftp_libssh2_last_error(sshc->sftp_session);
-        Curl_safefree(sshc->quote_path1);
-        Curl_safefree(sshc->quote_path2);
-        failf(data, "rename command failed: %s", sftp_libssh2_strerror(err));
-        state(conn, SSH_SFTP_CLOSE);
-        sshc->nextstate = SSH_NO_STATE;
-        sshc->actualcode = CURLE_QUOTE_ERROR;
-        break;
-      }
-      state(conn, SSH_SFTP_NEXT_QUOTE);
-      break;
-
-    case SSH_SFTP_QUOTE_RMDIR:
-      rc = libssh2_sftp_rmdir_ex(sshc->sftp_session, sshc->quote_path1,
-                                 curlx_uztoui(strlen(sshc->quote_path1)));
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      else if(rc != 0 && !sshc->acceptfail) {
-        err = sftp_libssh2_last_error(sshc->sftp_session);
-        Curl_safefree(sshc->quote_path1);
-        failf(data, "rmdir command failed: %s", sftp_libssh2_strerror(err));
-        state(conn, SSH_SFTP_CLOSE);
-        sshc->nextstate = SSH_NO_STATE;
-        sshc->actualcode = CURLE_QUOTE_ERROR;
-        break;
-      }
-      state(conn, SSH_SFTP_NEXT_QUOTE);
-      break;
-
-    case SSH_SFTP_QUOTE_UNLINK:
-      rc = libssh2_sftp_unlink_ex(sshc->sftp_session, sshc->quote_path1,
-                                  curlx_uztoui(strlen(sshc->quote_path1)));
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      else if(rc != 0 && !sshc->acceptfail) {
-        err = sftp_libssh2_last_error(sshc->sftp_session);
-        Curl_safefree(sshc->quote_path1);
-        failf(data, "rm command failed: %s", sftp_libssh2_strerror(err));
-        state(conn, SSH_SFTP_CLOSE);
-        sshc->nextstate = SSH_NO_STATE;
-        sshc->actualcode = CURLE_QUOTE_ERROR;
-        break;
-      }
-      state(conn, SSH_SFTP_NEXT_QUOTE);
-      break;
-
-    case SSH_SFTP_TRANS_INIT:
-      if(data->set.upload)
-        state(conn, SSH_SFTP_UPLOAD_INIT);
-      else {
-        if(data->set.opt_no_body)
-          state(conn, SSH_STOP);
-        else if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/')
-          state(conn, SSH_SFTP_READDIR_INIT);
-        else
-          state(conn, SSH_SFTP_DOWNLOAD_INIT);
-      }
-      break;
-
-    case SSH_SFTP_UPLOAD_INIT:
-    {
-      unsigned long flags;
-      /*
-       * NOTE!!!  libssh2 requires that the destination path is a full path
-       *          that includes the destination file and name OR ends in a "/"
-       *          If this is not done the destination file will be named the
-       *          same name as the last directory in the path.
-       */
-
-      if(data->state.resume_from != 0) {
-        LIBSSH2_SFTP_ATTRIBUTES attrs;
-        if(data->state.resume_from < 0) {
-          rc = libssh2_sftp_stat_ex(sshc->sftp_session, sftp_scp->path,
-                                    curlx_uztoui(strlen(sftp_scp->path)),
-                                    LIBSSH2_SFTP_STAT, &attrs);
-          if(rc == LIBSSH2_ERROR_EAGAIN) {
-            break;
-          }
-          else if(rc) {
-            data->state.resume_from = 0;
-          }
-          else {
-            curl_off_t size = attrs.filesize;
-            if(size < 0) {
-              failf(data, "Bad file size (%" FORMAT_OFF_T ")", size);
-              return CURLE_BAD_DOWNLOAD_RESUME;
-            }
-            data->state.resume_from = attrs.filesize;
-          }
-        }
-      }
-
-      if(data->set.ftp_append)
-        /* Try to open for append, but create if nonexisting */
-        flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_APPEND;
-      else if(data->state.resume_from > 0)
-        /* If we have restart position then open for append */
-        flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_APPEND;
-      else
-        /* Clear file before writing (normal behaviour) */
-        flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC;
-
-      sshc->sftp_handle =
-        libssh2_sftp_open_ex(sshc->sftp_session, sftp_scp->path,
-                             curlx_uztoui(strlen(sftp_scp->path)),
-                             flags, data->set.new_file_perms,
-                             LIBSSH2_SFTP_OPENFILE);
-
-      if(!sshc->sftp_handle) {
-        rc = libssh2_session_last_errno(sshc->ssh_session);
-
-        if(LIBSSH2_ERROR_EAGAIN == rc)
-          break;
-        else {
-          if(LIBSSH2_ERROR_SFTP_PROTOCOL == rc)
-            /* only when there was an SFTP protocol error can we extract
-               the sftp error! */
-            err = sftp_libssh2_last_error(sshc->sftp_session);
-          else
-            err = -1; /* not an sftp error at all */
-
-          if(sshc->secondCreateDirs) {
-            state(conn, SSH_SFTP_CLOSE);
-            sshc->actualcode = err>= LIBSSH2_FX_OK?
-              sftp_libssh2_error_to_CURLE(err):CURLE_SSH;
-            failf(data, "Creating the dir/file failed: %s",
-                  sftp_libssh2_strerror(err));
-            break;
-          }
-          else if(((err == LIBSSH2_FX_NO_SUCH_FILE) ||
-                   (err == LIBSSH2_FX_FAILURE) ||
-                   (err == LIBSSH2_FX_NO_SUCH_PATH)) &&
-                  (data->set.ftp_create_missing_dirs &&
-                   (strlen(sftp_scp->path) > 1))) {
-            /* try to create the path remotely */
-            sshc->secondCreateDirs = 1;
-            state(conn, SSH_SFTP_CREATE_DIRS_INIT);
-            break;
-          }
-          state(conn, SSH_SFTP_CLOSE);
-          sshc->actualcode = err>= LIBSSH2_FX_OK?
-            sftp_libssh2_error_to_CURLE(err):CURLE_SSH;
-          if(!sshc->actualcode) {
-            /* Sometimes, for some reason libssh2_sftp_last_error() returns
-               zero even though libssh2_sftp_open() failed previously! We need
-               to work around that! */
-            sshc->actualcode = CURLE_SSH;
-            err=-1;
-          }
-          failf(data, "Upload failed: %s (%d/%d)",
-                err>= LIBSSH2_FX_OK?sftp_libssh2_strerror(err):"ssh error",
-                err, rc);
-          break;
-        }
-      }
-
-      /* If we have restart point then we need to seek to the correct
-         position. */
-      if(data->state.resume_from > 0) {
-        /* Let's read off the proper amount of bytes from the input. */
-        if(conn->seek_func) {
-          seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
-                                    SEEK_SET);
-        }
-
-        if(seekerr != CURL_SEEKFUNC_OK) {
-
-          if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
-            failf(data, "Could not seek stream");
-            return CURLE_FTP_COULDNT_USE_REST;
-          }
-          /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
-          else {
-            curl_off_t passed=0;
-            do {
-              size_t readthisamountnow =
-                (data->state.resume_from - passed > CURL_OFF_T_C(BUFSIZE)) ?
-                BUFSIZE : curlx_sotouz(data->state.resume_from - passed);
-
-              size_t actuallyread =
-                conn->fread_func(data->state.buffer, 1, readthisamountnow,
-                                 conn->fread_in);
-
-              passed += actuallyread;
-              if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
-                /* this checks for greater-than only to make sure that the
-                   CURL_READFUNC_ABORT return code still aborts */
-                failf(data, "Failed to read data");
-                return CURLE_FTP_COULDNT_USE_REST;
-              }
-            } while(passed < data->state.resume_from);
-          }
-        }
-
-        /* now, decrease the size of the read */
-        if(data->set.infilesize > 0) {
-          data->set.infilesize -= data->state.resume_from;
-          data->req.size = data->set.infilesize;
-          Curl_pgrsSetUploadSize(data, data->set.infilesize);
-        }
-
-        SFTP_SEEK(sshc->sftp_handle, data->state.resume_from);
-      }
-      if(data->set.infilesize > 0) {
-        data->req.size = data->set.infilesize;
-        Curl_pgrsSetUploadSize(data, data->set.infilesize);
-      }
-      /* upload data */
-      Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
-
-      /* not set by Curl_setup_transfer to preserve keepon bits */
-      conn->sockfd = conn->writesockfd;
-
-      if(result) {
-        state(conn, SSH_SFTP_CLOSE);
-        sshc->actualcode = result;
-      }
-      else {
-        /* store this original bitmask setup to use later on if we can't
-           figure out a "real" bitmask */
-        sshc->orig_waitfor = data->req.keepon;
-
-        /* we want to use the _sending_ function even when the socket turns
-           out readable as the underlying libssh2 sftp send function will deal
-           with both accordingly */
-        conn->cselect_bits = CURL_CSELECT_OUT;
-
-        /* since we don't really wait for anything at this point, we want the
-           state machine to move on as soon as possible so we set a very short
-           timeout here */
-        Curl_expire(data, 1);
-
-        state(conn, SSH_STOP);
-      }
-      break;
-    }
-
-    case SSH_SFTP_CREATE_DIRS_INIT:
-      if(strlen(sftp_scp->path) > 1) {
-        sshc->slash_pos = sftp_scp->path + 1; /* ignore the leading '/' */
-        state(conn, SSH_SFTP_CREATE_DIRS);
-      }
-      else {
-        state(conn, SSH_SFTP_UPLOAD_INIT);
-      }
-      break;
-
-    case SSH_SFTP_CREATE_DIRS:
-      if((sshc->slash_pos = strchr(sshc->slash_pos, '/')) != NULL) {
-        *sshc->slash_pos = 0;
-
-        infof(data, "Creating directory '%s'\n", sftp_scp->path);
-        state(conn, SSH_SFTP_CREATE_DIRS_MKDIR);
-        break;
-      }
-      else {
-        state(conn, SSH_SFTP_UPLOAD_INIT);
-      }
-      break;
-
-    case SSH_SFTP_CREATE_DIRS_MKDIR:
-      /* 'mode' - parameter is preliminary - default to 0644 */
-      rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sftp_scp->path,
-                                 curlx_uztoui(strlen(sftp_scp->path)),
-                                 data->set.new_directory_perms);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      *sshc->slash_pos = '/';
-      ++sshc->slash_pos;
-      if(rc == -1) {
-        /*
-         * Abort if failure wasn't that the dir already exists or the
-         * permission was denied (creation might succeed further down the
-         * path) - retry on unspecific FAILURE also
-         */
-        err = sftp_libssh2_last_error(sshc->sftp_session);
-        if((err != LIBSSH2_FX_FILE_ALREADY_EXISTS) &&
-           (err != LIBSSH2_FX_FAILURE) &&
-           (err != LIBSSH2_FX_PERMISSION_DENIED)) {
-          result = sftp_libssh2_error_to_CURLE(err);
-          state(conn, SSH_SFTP_CLOSE);
-          sshc->actualcode = result?result:CURLE_SSH;
-          break;
-        }
-      }
-      state(conn, SSH_SFTP_CREATE_DIRS);
-      break;
-
-    case SSH_SFTP_READDIR_INIT:
-      /*
-       * This is a directory that we are trying to get, so produce a directory
-       * listing
-       */
-      sshc->sftp_handle = libssh2_sftp_open_ex(sshc->sftp_session,
-                                               sftp_scp->path,
-                                               curlx_uztoui(
-                                                 strlen(sftp_scp->path)),
-                                               0, 0, LIBSSH2_SFTP_OPENDIR);
-      if(!sshc->sftp_handle) {
-        if(libssh2_session_last_errno(sshc->ssh_session) ==
-           LIBSSH2_ERROR_EAGAIN) {
-          rc = LIBSSH2_ERROR_EAGAIN;
-          break;
-        }
-        else {
-          err = sftp_libssh2_last_error(sshc->sftp_session);
-          failf(data, "Could not open directory for reading: %s",
-                sftp_libssh2_strerror(err));
-          state(conn, SSH_SFTP_CLOSE);
-          result = sftp_libssh2_error_to_CURLE(err);
-          sshc->actualcode = result?result:CURLE_SSH;
-          break;
-        }
-      }
-      if((sshc->readdir_filename = malloc(PATH_MAX+1)) == NULL) {
-        state(conn, SSH_SFTP_CLOSE);
-        sshc->actualcode = CURLE_OUT_OF_MEMORY;
-        break;
-      }
-      if((sshc->readdir_longentry = malloc(PATH_MAX+1)) == NULL) {
-        Curl_safefree(sshc->readdir_filename);
-        state(conn, SSH_SFTP_CLOSE);
-        sshc->actualcode = CURLE_OUT_OF_MEMORY;
-        break;
-      }
-      state(conn, SSH_SFTP_READDIR);
-      break;
-
-    case SSH_SFTP_READDIR:
-      sshc->readdir_len = libssh2_sftp_readdir_ex(sshc->sftp_handle,
-                                                  sshc->readdir_filename,
-                                                  PATH_MAX,
-                                                  sshc->readdir_longentry,
-                                                  PATH_MAX,
-                                                  &sshc->readdir_attrs);
-      if(sshc->readdir_len == LIBSSH2_ERROR_EAGAIN) {
-        rc = LIBSSH2_ERROR_EAGAIN;
-        break;
-      }
-      if(sshc->readdir_len > 0) {
-        sshc->readdir_filename[sshc->readdir_len] = '\0';
-
-        if(data->set.ftp_list_only) {
-          char *tmpLine;
-
-          tmpLine = aprintf("%s\n", sshc->readdir_filename);
-          if(tmpLine == NULL) {
-            state(conn, SSH_SFTP_CLOSE);
-            sshc->actualcode = CURLE_OUT_OF_MEMORY;
-            break;
-          }
-          result = Curl_client_write(conn, CLIENTWRITE_BODY,
-                                     tmpLine, sshc->readdir_len+1);
-          Curl_safefree(tmpLine);
-
-          if(result) {
-            state(conn, SSH_STOP);
-            break;
-          }
-          /* since this counts what we send to the client, we include the
-             newline in this counter */
-          data->req.bytecount += sshc->readdir_len+1;
-
-          /* output debug output if that is requested */
-          if(data->set.verbose) {
-            Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_filename,
-                       sshc->readdir_len, conn);
-          }
-        }
-        else {
-          sshc->readdir_currLen = (int)strlen(sshc->readdir_longentry);
-          sshc->readdir_totalLen = 80 + sshc->readdir_currLen;
-          sshc->readdir_line = calloc(sshc->readdir_totalLen, 1);
-          if(!sshc->readdir_line) {
-            Curl_safefree(sshc->readdir_filename);
-            Curl_safefree(sshc->readdir_longentry);
-            state(conn, SSH_SFTP_CLOSE);
-            sshc->actualcode = CURLE_OUT_OF_MEMORY;
-            break;
-          }
-
-          memcpy(sshc->readdir_line, sshc->readdir_longentry,
-                 sshc->readdir_currLen);
-          if((sshc->readdir_attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) &&
-             ((sshc->readdir_attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
-              LIBSSH2_SFTP_S_IFLNK)) {
-            sshc->readdir_linkPath = malloc(PATH_MAX + 1);
-            if(sshc->readdir_linkPath == NULL) {
-              Curl_safefree(sshc->readdir_filename);
-              Curl_safefree(sshc->readdir_longentry);
-              state(conn, SSH_SFTP_CLOSE);
-              sshc->actualcode = CURLE_OUT_OF_MEMORY;
-              break;
-            }
-
-            snprintf(sshc->readdir_linkPath, PATH_MAX, "%s%s", sftp_scp->path,
-                     sshc->readdir_filename);
-            state(conn, SSH_SFTP_READDIR_LINK);
-            break;
-          }
-          state(conn, SSH_SFTP_READDIR_BOTTOM);
-          break;
-        }
-      }
-      else if(sshc->readdir_len == 0) {
-        Curl_safefree(sshc->readdir_filename);
-        Curl_safefree(sshc->readdir_longentry);
-        state(conn, SSH_SFTP_READDIR_DONE);
-        break;
-      }
-      else if(sshc->readdir_len <= 0) {
-        err = sftp_libssh2_last_error(sshc->sftp_session);
-        result = sftp_libssh2_error_to_CURLE(err);
-        sshc->actualcode = result?result:CURLE_SSH;
-        failf(data, "Could not open remote file for reading: %s :: %d",
-              sftp_libssh2_strerror(err),
-              libssh2_session_last_errno(sshc->ssh_session));
-        Curl_safefree(sshc->readdir_filename);
-        Curl_safefree(sshc->readdir_longentry);
-        state(conn, SSH_SFTP_CLOSE);
-        break;
-      }
-      break;
-
-    case SSH_SFTP_READDIR_LINK:
-      sshc->readdir_len =
-        libssh2_sftp_symlink_ex(sshc->sftp_session,
-                                sshc->readdir_linkPath,
-                                curlx_uztoui(strlen(sshc->readdir_linkPath)),
-                                sshc->readdir_filename,
-                                PATH_MAX, LIBSSH2_SFTP_READLINK);
-      if(sshc->readdir_len == LIBSSH2_ERROR_EAGAIN) {
-        rc = LIBSSH2_ERROR_EAGAIN;
-        break;
-      }
-      Curl_safefree(sshc->readdir_linkPath);
-
-      /* get room for the filename and extra output */
-      sshc->readdir_totalLen += 4 + sshc->readdir_len;
-      new_readdir_line = realloc(sshc->readdir_line, sshc->readdir_totalLen);
-      if(!new_readdir_line) {
-        Curl_safefree(sshc->readdir_line);
-        Curl_safefree(sshc->readdir_filename);
-        Curl_safefree(sshc->readdir_longentry);
-        state(conn, SSH_SFTP_CLOSE);
-        sshc->actualcode = CURLE_OUT_OF_MEMORY;
-        break;
-      }
-      sshc->readdir_line = new_readdir_line;
-
-      sshc->readdir_currLen += snprintf(sshc->readdir_line +
-                                        sshc->readdir_currLen,
-                                        sshc->readdir_totalLen -
-                                        sshc->readdir_currLen,
-                                        " -> %s",
-                                        sshc->readdir_filename);
-
-      state(conn, SSH_SFTP_READDIR_BOTTOM);
-      break;
-
-    case SSH_SFTP_READDIR_BOTTOM:
-      sshc->readdir_currLen += snprintf(sshc->readdir_line +
-                                        sshc->readdir_currLen,
-                                        sshc->readdir_totalLen -
-                                        sshc->readdir_currLen, "\n");
-      result = Curl_client_write(conn, CLIENTWRITE_BODY,
-                                 sshc->readdir_line,
-                                 sshc->readdir_currLen);
-
-      if(result == CURLE_OK) {
-
-        /* output debug output if that is requested */
-        if(data->set.verbose) {
-          Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_line,
-                     sshc->readdir_currLen, conn);
-        }
-        data->req.bytecount += sshc->readdir_currLen;
-      }
-      Curl_safefree(sshc->readdir_line);
-      if(result) {
-        state(conn, SSH_STOP);
-      }
-      else
-        state(conn, SSH_SFTP_READDIR);
-      break;
-
-    case SSH_SFTP_READDIR_DONE:
-      if(libssh2_sftp_closedir(sshc->sftp_handle) ==
-         LIBSSH2_ERROR_EAGAIN) {
-        rc = LIBSSH2_ERROR_EAGAIN;
-        break;
-      }
-      sshc->sftp_handle = NULL;
-      Curl_safefree(sshc->readdir_filename);
-      Curl_safefree(sshc->readdir_longentry);
-
-      /* no data to transfer */
-      Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
-      state(conn, SSH_STOP);
-      break;
-
-    case SSH_SFTP_DOWNLOAD_INIT:
-      /*
-       * Work on getting the specified file
-       */
-      sshc->sftp_handle =
-        libssh2_sftp_open_ex(sshc->sftp_session, sftp_scp->path,
-                             curlx_uztoui(strlen(sftp_scp->path)),
-                             LIBSSH2_FXF_READ, data->set.new_file_perms,
-                             LIBSSH2_SFTP_OPENFILE);
-      if(!sshc->sftp_handle) {
-        if(libssh2_session_last_errno(sshc->ssh_session) ==
-           LIBSSH2_ERROR_EAGAIN) {
-          rc = LIBSSH2_ERROR_EAGAIN;
-          break;
-        }
-        else {
-          err = sftp_libssh2_last_error(sshc->sftp_session);
-          failf(data, "Could not open remote file for reading: %s",
-                sftp_libssh2_strerror(err));
-          state(conn, SSH_SFTP_CLOSE);
-          result = sftp_libssh2_error_to_CURLE(err);
-          sshc->actualcode = result?result:CURLE_SSH;
-          break;
-        }
-      }
-      state(conn, SSH_SFTP_DOWNLOAD_STAT);
-      break;
-
-    case SSH_SFTP_DOWNLOAD_STAT:
-    {
-      LIBSSH2_SFTP_ATTRIBUTES attrs;
-
-      rc = libssh2_sftp_stat_ex(sshc->sftp_session, sftp_scp->path,
-                                curlx_uztoui(strlen(sftp_scp->path)),
-                                LIBSSH2_SFTP_STAT, &attrs);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      else if(rc) {
-        /*
-         * libssh2_sftp_open() didn't return an error, so maybe the server
-         * just doesn't support stat()
-         */
-        data->req.size = -1;
-        data->req.maxdownload = -1;
-      }
-      else {
-        curl_off_t size = attrs.filesize;
-
-        if(size < 0) {
-          failf(data, "Bad file size (%" FORMAT_OFF_T ")", size);
-          return CURLE_BAD_DOWNLOAD_RESUME;
-        }
-        if(conn->data->state.use_range) {
-          curl_off_t from, to;
-          char *ptr;
-          char *ptr2;
-
-          from=curlx_strtoofft(conn->data->state.range, &ptr, 0);
-          while(*ptr && (ISSPACE(*ptr) || (*ptr=='-')))
-            ptr++;
-          to=curlx_strtoofft(ptr, &ptr2, 0);
-          if((ptr == ptr2) /* no "to" value given */
-             || (to >= size)) {
-            to = size - 1;
-          }
-          if(from < 0) {
-            /* from is relative to end of file */
-            from += size;
-          }
-          if(from >= size) {
-            failf(data, "Offset (%"
-                  FORMAT_OFF_T ") was beyond file size (%" FORMAT_OFF_T ")",
-                  from, attrs.filesize);
-            return CURLE_BAD_DOWNLOAD_RESUME;
-          }
-          if(from > to) {
-            from = to;
-            size = 0;
-          }
-          else {
-            size = to - from + 1;
-          }
-
-          SFTP_SEEK(conn->proto.sshc.sftp_handle, from);
-        }
-        data->req.size = size;
-        data->req.maxdownload = size;
-        Curl_pgrsSetDownloadSize(data, size);
-      }
-
-      /* We can resume if we can seek to the resume position */
-      if(data->state.resume_from) {
-        if(data->state.resume_from < 0) {
-          /* We're supposed to download the last abs(from) bytes */
-          if((curl_off_t)attrs.filesize < -data->state.resume_from) {
-            failf(data, "Offset (%"
-                  FORMAT_OFF_T ") was beyond file size (%" FORMAT_OFF_T ")",
-                  data->state.resume_from, attrs.filesize);
-            return CURLE_BAD_DOWNLOAD_RESUME;
-          }
-          /* download from where? */
-          data->state.resume_from += attrs.filesize;
-        }
-        else {
-          if((curl_off_t)attrs.filesize < data->state.resume_from) {
-            failf(data, "Offset (%" FORMAT_OFF_T
-                  ") was beyond file size (%" FORMAT_OFF_T ")",
-                  data->state.resume_from, attrs.filesize);
-            return CURLE_BAD_DOWNLOAD_RESUME;
-          }
-        }
-        /* Does a completed file need to be seeked and started or closed ? */
-        /* Now store the number of bytes we are expected to download */
-        data->req.size = attrs.filesize - data->state.resume_from;
-        data->req.maxdownload = attrs.filesize - data->state.resume_from;
-        Curl_pgrsSetDownloadSize(data,
-                                 attrs.filesize - data->state.resume_from);
-        SFTP_SEEK(sshc->sftp_handle, data->state.resume_from);
-      }
-    }
-    /* Setup the actual download */
-    if(data->req.size == 0) {
-      /* no data to transfer */
-      Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
-      infof(data, "File already completely downloaded\n");
-      state(conn, SSH_STOP);
-      break;
-    }
-    else {
-      Curl_setup_transfer(conn, FIRSTSOCKET, data->req.size,
-                          FALSE, NULL, -1, NULL);
-
-      /* not set by Curl_setup_transfer to preserve keepon bits */
-      conn->writesockfd = conn->sockfd;
-
-      /* we want to use the _receiving_ function even when the socket turns
-         out writableable as the underlying libssh2 recv function will deal
-         with both accordingly */
-      conn->cselect_bits = CURL_CSELECT_IN;
-    }
-    if(result) {
-      state(conn, SSH_SFTP_CLOSE);
-      sshc->actualcode = result;
-    }
-    else {
-      state(conn, SSH_STOP);
-    }
-    break;
-
-    case SSH_SFTP_CLOSE:
-      if(sshc->sftp_handle) {
-        rc = libssh2_sftp_close(sshc->sftp_handle);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else if(rc < 0) {
-          infof(data, "Failed to close libssh2 file\n");
-        }
-        sshc->sftp_handle = NULL;
-      }
-      if(sftp_scp)
-        Curl_safefree(sftp_scp->path);
-
-      DEBUGF(infof(data, "SFTP DONE done\n"));
-
-      /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT
-         After nextstate is executed,the control should come back to
-         SSH_SFTP_CLOSE to pass the correct result back  */
-      if(sshc->nextstate != SSH_NO_STATE) {
-        state(conn, sshc->nextstate);
-        sshc->nextstate = SSH_SFTP_CLOSE;
-      }
-      else {
-        state(conn, SSH_STOP);
-        result = sshc->actualcode;
-      }
-      break;
-
-    case SSH_SFTP_SHUTDOWN:
-      /* during times we get here due to a broken transfer and then the
-         sftp_handle might not have been taken down so make sure that is done
-         before we proceed */
-
-      if(sshc->sftp_handle) {
-        rc = libssh2_sftp_close(sshc->sftp_handle);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else if(rc < 0) {
-          infof(data, "Failed to close libssh2 file\n");
-        }
-        sshc->sftp_handle = NULL;
-      }
-      if(sshc->sftp_session) {
-        rc = libssh2_sftp_shutdown(sshc->sftp_session);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else if(rc < 0) {
-          infof(data, "Failed to stop libssh2 sftp subsystem\n");
-        }
-        sshc->sftp_session = NULL;
-      }
-
-      Curl_safefree(sshc->homedir);
-      conn->data->state.most_recent_ftp_entrypath = NULL;
-
-      state(conn, SSH_SESSION_DISCONNECT);
-      break;
-
-    case SSH_SCP_TRANS_INIT:
-      result = ssh_getworkingpath(conn, sshc->homedir, &sftp_scp->path);
-      if(result) {
-        sshc->actualcode = result;
-        state(conn, SSH_STOP);
-        break;
-      }
-
-      if(data->set.upload) {
-        if(data->set.infilesize < 0) {
-          failf(data, "SCP requires a known file size for upload");
-          sshc->actualcode = CURLE_UPLOAD_FAILED;
-          state(conn, SSH_SCP_CHANNEL_FREE);
-          break;
-        }
-        state(conn, SSH_SCP_UPLOAD_INIT);
-      }
-      else {
-        state(conn, SSH_SCP_DOWNLOAD_INIT);
-      }
-      break;
-
-    case SSH_SCP_UPLOAD_INIT:
-      /*
-       * libssh2 requires that the destination path is a full path that
-       * includes the destination file and name OR ends in a "/" .  If this is
-       * not done the destination file will be named the same name as the last
-       * directory in the path.
-       */
-      sshc->ssh_channel =
-        SCP_SEND(sshc->ssh_session, sftp_scp->path, data->set.new_file_perms,
-                 data->set.infilesize);
-      if(!sshc->ssh_channel) {
-        if(libssh2_session_last_errno(sshc->ssh_session) ==
-           LIBSSH2_ERROR_EAGAIN) {
-          rc = LIBSSH2_ERROR_EAGAIN;
-          break;
-        }
-        else {
-          int ssh_err;
-          char *err_msg;
-
-          ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session,
-                                                     &err_msg, NULL, 0));
-          failf(conn->data, "%s", err_msg);
-          state(conn, SSH_SCP_CHANNEL_FREE);
-          sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err);
-          break;
-        }
-      }
-
-      /* upload data */
-      Curl_setup_transfer(conn, -1, data->req.size, FALSE, NULL,
-                          FIRSTSOCKET, NULL);
-
-      /* not set by Curl_setup_transfer to preserve keepon bits */
-      conn->sockfd = conn->writesockfd;
-
-      if(result) {
-        state(conn, SSH_SCP_CHANNEL_FREE);
-        sshc->actualcode = result;
-      }
-      else {
-        /* we want to use the _sending_ function even when the socket turns
-           out readable as the underlying libssh2 scp send function will deal
-           with both accordingly */
-        conn->cselect_bits = CURL_CSELECT_OUT;
-
-        state(conn, SSH_STOP);
-      }
-      break;
-
-    case SSH_SCP_DOWNLOAD_INIT:
-    {
-      /*
-       * We must check the remote file; if it is a directory no values will
-       * be set in sb
-       */
-      struct stat sb;
-      curl_off_t bytecount;
-
-      /* clear the struct scp recv will fill in */
-      memset(&sb, 0, sizeof(struct stat));
-
-      /* get a fresh new channel from the ssh layer */
-      sshc->ssh_channel = libssh2_scp_recv(sshc->ssh_session,
-                                           sftp_scp->path, &sb);
-      if(!sshc->ssh_channel) {
-        if(libssh2_session_last_errno(sshc->ssh_session) ==
-           LIBSSH2_ERROR_EAGAIN) {
-          rc = LIBSSH2_ERROR_EAGAIN;
-          break;
-        }
-        else {
-          int ssh_err;
-          char *err_msg;
-
-          ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session,
-                                                     &err_msg, NULL, 0));
-          failf(conn->data, "%s", err_msg);
-          state(conn, SSH_SCP_CHANNEL_FREE);
-          sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err);
-          break;
-        }
-      }
-
-      /* download data */
-      bytecount = (curl_off_t)sb.st_size;
-      data->req.maxdownload =  (curl_off_t)sb.st_size;
-      Curl_setup_transfer(conn, FIRSTSOCKET, bytecount, FALSE, NULL, -1, NULL);
-
-      /* not set by Curl_setup_transfer to preserve keepon bits */
-      conn->writesockfd = conn->sockfd;
-
-      /* we want to use the _receiving_ function even when the socket turns
-         out writableable as the underlying libssh2 recv function will deal
-         with both accordingly */
-      conn->cselect_bits = CURL_CSELECT_IN;
-
-      if(result) {
-        state(conn, SSH_SCP_CHANNEL_FREE);
-        sshc->actualcode = result;
-      }
-      else
-        state(conn, SSH_STOP);
-    }
-    break;
-
-    case SSH_SCP_DONE:
-      if(data->set.upload)
-        state(conn, SSH_SCP_SEND_EOF);
-      else
-        state(conn, SSH_SCP_CHANNEL_FREE);
-      break;
-
-    case SSH_SCP_SEND_EOF:
-      if(sshc->ssh_channel) {
-        rc = libssh2_channel_send_eof(sshc->ssh_channel);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else if(rc) {
-          infof(data, "Failed to send libssh2 channel EOF\n");
-        }
-      }
-      state(conn, SSH_SCP_WAIT_EOF);
-      break;
-
-    case SSH_SCP_WAIT_EOF:
-      if(sshc->ssh_channel) {
-        rc = libssh2_channel_wait_eof(sshc->ssh_channel);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else if(rc) {
-          infof(data, "Failed to get channel EOF: %d\n", rc);
-        }
-      }
-      state(conn, SSH_SCP_WAIT_CLOSE);
-      break;
-
-    case SSH_SCP_WAIT_CLOSE:
-      if(sshc->ssh_channel) {
-        rc = libssh2_channel_wait_closed(sshc->ssh_channel);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else if(rc) {
-          infof(data, "Channel failed to close: %d\n", rc);
-        }
-      }
-      state(conn, SSH_SCP_CHANNEL_FREE);
-      break;
-
-    case SSH_SCP_CHANNEL_FREE:
-      if(sshc->ssh_channel) {
-        rc = libssh2_channel_free(sshc->ssh_channel);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else if(rc < 0) {
-          infof(data, "Failed to free libssh2 scp subsystem\n");
-        }
-        sshc->ssh_channel = NULL;
-      }
-      DEBUGF(infof(data, "SCP DONE phase complete\n"));
-#if 0 /* PREV */
-      state(conn, SSH_SESSION_DISCONNECT);
-#endif
-      state(conn, SSH_STOP);
-      result = sshc->actualcode;
-      break;
-
-    case SSH_SESSION_DISCONNECT:
-      /* during weird times when we've been prematurely aborted, the channel
-         is still alive when we reach this state and we MUST kill the channel
-         properly first */
-      if(sshc->ssh_channel) {
-        rc = libssh2_channel_free(sshc->ssh_channel);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else if(rc < 0) {
-          infof(data, "Failed to free libssh2 scp subsystem\n");
-        }
-        sshc->ssh_channel = NULL;
-      }
-
-      if(sshc->ssh_session) {
-        rc = libssh2_session_disconnect(sshc->ssh_session, "Shutdown");
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else if(rc < 0) {
-          infof(data, "Failed to disconnect libssh2 session\n");
-        }
-      }
-
-      Curl_safefree(sshc->homedir);
-      conn->data->state.most_recent_ftp_entrypath = NULL;
-
-      state(conn, SSH_SESSION_FREE);
-      break;
-
-    case SSH_SESSION_FREE:
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
-      if(sshc->kh) {
-        libssh2_knownhost_free(sshc->kh);
-        sshc->kh = NULL;
-      }
-#endif
-
-#ifdef HAVE_LIBSSH2_AGENT_API
-      if(sshc->ssh_agent) {
-        rc = libssh2_agent_disconnect(sshc->ssh_agent);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else if(rc < 0) {
-          infof(data, "Failed to disconnect from libssh2 agent\n");
-        }
-        libssh2_agent_free (sshc->ssh_agent);
-        sshc->ssh_agent = NULL;
-
-        /* NB: there is no need to free identities, they are part of internal
-           agent stuff */
-        sshc->sshagent_identity = NULL;
-        sshc->sshagent_prev_identity = NULL;
-      }
-#endif
-
-      if(sshc->ssh_session) {
-        rc = libssh2_session_free(sshc->ssh_session);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else if(rc < 0) {
-          infof(data, "Failed to free libssh2 session\n");
-        }
-        sshc->ssh_session = NULL;
-      }
-
-      /* worst-case scenario cleanup */
-
-      DEBUGASSERT(sshc->ssh_session == NULL);
-      DEBUGASSERT(sshc->ssh_channel == NULL);
-      DEBUGASSERT(sshc->sftp_session == NULL);
-      DEBUGASSERT(sshc->sftp_handle == NULL);
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
-      DEBUGASSERT(sshc->kh == NULL);
-#endif
-#ifdef HAVE_LIBSSH2_AGENT_API
-      DEBUGASSERT(sshc->ssh_agent == NULL);
-#endif
-
-      Curl_safefree(sshc->rsa_pub);
-      Curl_safefree(sshc->rsa);
-
-      Curl_safefree(sshc->quote_path1);
-      Curl_safefree(sshc->quote_path2);
-
-      Curl_safefree(sshc->homedir);
-
-      Curl_safefree(sshc->readdir_filename);
-      Curl_safefree(sshc->readdir_longentry);
-      Curl_safefree(sshc->readdir_line);
-      Curl_safefree(sshc->readdir_linkPath);
-
-      /* the code we are about to return */
-      result = sshc->actualcode;
-
-      memset(sshc, 0, sizeof(struct ssh_conn));
-
-      conn->bits.close = TRUE;
-      sshc->state = SSH_SESSION_FREE; /* current */
-      sshc->nextstate = SSH_NO_STATE;
-      state(conn, SSH_STOP);
-      break;
-
-    case SSH_QUIT:
-      /* fallthrough, just stop! */
-    default:
-      /* internal error */
-      sshc->nextstate = SSH_NO_STATE;
-      state(conn, SSH_STOP);
-      break;
-    }
-
-  } while(!rc && (sshc->state != SSH_STOP));
-
-  if(rc == LIBSSH2_ERROR_EAGAIN) {
-    /* we would block, we need to wait for the socket to be ready (in the
-       right direction too)! */
-    *block = TRUE;
-  }
-
-  return result;
-}
-
-/* called by the multi interface to figure out what socket(s) to wait for and
-   for what actions in the DO_DONE, PERFORM and WAITPERFORM states */
-static int ssh_perform_getsock(const struct connectdata *conn,
-                               curl_socket_t *sock, /* points to numsocks
-                                                       number of sockets */
-                               int numsocks)
-{
-#ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
-  int bitmap = GETSOCK_BLANK;
-  (void)numsocks;
-
-  sock[0] = conn->sock[FIRSTSOCKET];
-
-  if(conn->waitfor & KEEP_RECV)
-    bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
-
-  if(conn->waitfor & KEEP_SEND)
-    bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
-
-  return bitmap;
-#else
-  /* if we don't know the direction we can use the generic *_getsock()
-     function even for the protocol_connect and doing states */
-  return Curl_single_getsock(conn, sock, numsocks);
-#endif
-}
-
-/* Generic function called by the multi interface to figure out what socket(s)
-   to wait for and for what actions during the DOING and PROTOCONNECT states*/
-static int ssh_getsock(struct connectdata *conn,
-                       curl_socket_t *sock, /* points to numsocks number
-                                               of sockets */
-                       int numsocks)
-{
-#ifndef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
-  (void)conn;
-  (void)sock;
-  (void)numsocks;
-  /* if we don't know any direction we can just play along as we used to and
-     not provide any sensible info */
-  return GETSOCK_BLANK;
-#else
-  /* if we know the direction we can use the generic *_getsock() function even
-     for the protocol_connect and doing states */
-  return ssh_perform_getsock(conn, sock, numsocks);
-#endif
-}
-
-#ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
-/*
- * When one of the libssh2 functions has returned LIBSSH2_ERROR_EAGAIN this
- * function is used to figure out in what direction and stores this info so
- * that the multi interface can take advantage of it. Make sure to call this
- * function in all cases so that when it _doesn't_ return EAGAIN we can
- * restore the default wait bits.
- */
-static void ssh_block2waitfor(struct connectdata *conn, bool block)
-{
-  struct ssh_conn *sshc = &conn->proto.sshc;
-  int dir;
-  if(!block)
-    conn->waitfor = 0;
-  else if((dir = libssh2_session_block_directions(sshc->ssh_session))) {
-    /* translate the libssh2 define bits into our own bit defines */
-    conn->waitfor = ((dir&LIBSSH2_SESSION_BLOCK_INBOUND)?KEEP_RECV:0) |
-      ((dir&LIBSSH2_SESSION_BLOCK_OUTBOUND)?KEEP_SEND:0);
-  }
-  else
-    /* It didn't block or libssh2 didn't reveal in which direction, put back
-       the original set */
-    conn->waitfor = sshc->orig_waitfor;
-}
-#else
-  /* no libssh2 directional support so we simply don't know */
-#define ssh_block2waitfor(x,y) Curl_nop_stmt
-#endif
-
-/* called repeatedly until done from curl_multi.c */
-static CURLcode ssh_multi_statemach(struct connectdata *conn, bool *done)
-{
-  struct ssh_conn *sshc = &conn->proto.sshc;
-  CURLcode result = CURLE_OK;
-  bool block; /* we store the status and use that to provide a ssh_getsock()
-                 implementation */
-
-  result = ssh_statemach_act(conn, &block);
-  *done = (sshc->state == SSH_STOP) ? TRUE : FALSE;
-  ssh_block2waitfor(conn, block);
-
-  return result;
-}
-
-static CURLcode ssh_easy_statemach(struct connectdata *conn,
-                                   bool duringconnect)
-{
-  struct ssh_conn *sshc = &conn->proto.sshc;
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  while((sshc->state != SSH_STOP) && !result) {
-    bool block;
-    long left;
-
-    result = ssh_statemach_act(conn, &block);
-    if(result)
-      break;
-
-    if(Curl_pgrsUpdate(conn))
-      return CURLE_ABORTED_BY_CALLBACK;
-    else {
-      struct timeval now = Curl_tvnow();
-      result = Curl_speedcheck(data, now);
-      if(result)
-        break;
-    }
-
-    left = Curl_timeleft(data, NULL, duringconnect);
-    if(left < 0) {
-      failf(data, "Operation timed out");
-      return CURLE_OPERATION_TIMEDOUT;
-    }
-
-#ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
-    if((CURLE_OK == result) && block) {
-      int dir = libssh2_session_block_directions(sshc->ssh_session);
-      curl_socket_t sock = conn->sock[FIRSTSOCKET];
-      curl_socket_t fd_read = CURL_SOCKET_BAD;
-      curl_socket_t fd_write = CURL_SOCKET_BAD;
-      if(LIBSSH2_SESSION_BLOCK_INBOUND & dir)
-        fd_read = sock;
-      if(LIBSSH2_SESSION_BLOCK_OUTBOUND & dir)
-        fd_write = sock;
-      /* wait for the socket to become ready */
-      Curl_socket_ready(fd_read, fd_write,
-                        left>1000?1000:left); /* ignore result */
-    }
-#endif
-
-  }
-
-  return result;
-}
-
-/*
- * SSH setup and connection
- */
-static CURLcode ssh_init(struct connectdata *conn)
-{
-  struct SessionHandle *data = conn->data;
-  struct SSHPROTO *ssh;
-  struct ssh_conn *sshc = &conn->proto.sshc;
-
-  sshc->actualcode = CURLE_OK; /* reset error code */
-  sshc->secondCreateDirs =0;   /* reset the create dir attempt state
-                                  variable */
-
-  if(data->state.proto.ssh)
-    return CURLE_OK;
-
-  ssh = calloc(1, sizeof(struct SSHPROTO));
-  if(!ssh)
-    return CURLE_OUT_OF_MEMORY;
-
-  data->state.proto.ssh = ssh;
-
-  return CURLE_OK;
-}
-
-static Curl_recv scp_recv, sftp_recv;
-static Curl_send scp_send, sftp_send;
-
-/*
- * Curl_ssh_connect() gets called from Curl_protocol_connect() to allow us to
- * do protocol-specific actions at connect-time.
- */
-static CURLcode ssh_connect(struct connectdata *conn, bool *done)
-{
-#ifdef CURL_LIBSSH2_DEBUG
-  curl_socket_t sock;
-#endif
-  struct ssh_conn *ssh;
-  CURLcode result;
-  struct SessionHandle *data = conn->data;
-
-  /* We default to persistent connections. We set this already in this connect
-     function to make the re-use checks properly be able to check this bit. */
-  conn->bits.close = FALSE;
-
-  /* If there already is a protocol-specific struct allocated for this
-     sessionhandle, deal with it */
-  Curl_reset_reqproto(conn);
-
-  result = ssh_init(conn);
-  if(result)
-    return result;
-
-  if(conn->handler->protocol & CURLPROTO_SCP) {
-    conn->recv[FIRSTSOCKET] = scp_recv;
-    conn->send[FIRSTSOCKET] = scp_send;
-  }
-  else {
-    conn->recv[FIRSTSOCKET] = sftp_recv;
-    conn->send[FIRSTSOCKET] = sftp_send;
-  }
-  ssh = &conn->proto.sshc;
-
-#ifdef CURL_LIBSSH2_DEBUG
-  if(conn->user) {
-    infof(data, "User: %s\n", conn->user);
-  }
-  if(conn->passwd) {
-    infof(data, "Password: %s\n", conn->passwd);
-  }
-  sock = conn->sock[FIRSTSOCKET];
-#endif /* CURL_LIBSSH2_DEBUG */
-
-  ssh->ssh_session = libssh2_session_init_ex(my_libssh2_malloc,
-                                             my_libssh2_free,
-                                             my_libssh2_realloc, conn);
-  if(ssh->ssh_session == NULL) {
-    failf(data, "Failure initialising ssh session");
-    return CURLE_FAILED_INIT;
-  }
-
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
-  if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
-    int rc;
-    ssh->kh = libssh2_knownhost_init(ssh->ssh_session);
-    if(!ssh->kh) {
-      /* eeek. TODO: free the ssh_session! */
-      return CURLE_FAILED_INIT;
-    }
-
-    /* read all known hosts from there */
-    rc = libssh2_knownhost_readfile(ssh->kh,
-                                    data->set.str[STRING_SSH_KNOWNHOSTS],
-                                    LIBSSH2_KNOWNHOST_FILE_OPENSSH);
-    if(rc < 0)
-      infof(data, "Failed to read known hosts from %s\n",
-            data->set.str[STRING_SSH_KNOWNHOSTS]);
-  }
-#endif /* HAVE_LIBSSH2_KNOWNHOST_API */
-
-#ifdef CURL_LIBSSH2_DEBUG
-  libssh2_trace(ssh->ssh_session, ~0);
-  infof(data, "SSH socket: %d\n", (int)sock);
-#endif /* CURL_LIBSSH2_DEBUG */
-
-  state(conn, SSH_INIT);
-
-  if(data->state.used_interface == Curl_if_multi)
-    result = ssh_multi_statemach(conn, done);
-  else {
-    result = ssh_easy_statemach(conn, TRUE);
-    if(!result)
-      *done = TRUE;
-  }
-
-  return result;
-}
-
-/*
- ***********************************************************************
- *
- * scp_perform()
- *
- * This is the actual DO function for SCP. Get a file according to
- * the options previously setup.
- */
-
-static
-CURLcode scp_perform(struct connectdata *conn,
-                      bool *connected,
-                      bool *dophase_done)
-{
-  CURLcode result = CURLE_OK;
-
-  DEBUGF(infof(conn->data, "DO phase starts\n"));
-
-  *dophase_done = FALSE; /* not done yet */
-
-  /* start the first command in the DO phase */
-  state(conn, SSH_SCP_TRANS_INIT);
-
-  /* run the state-machine */
-  if(conn->data->state.used_interface == Curl_if_multi) {
-    result = ssh_multi_statemach(conn, dophase_done);
-  }
-  else {
-    result = ssh_easy_statemach(conn, FALSE);
-    *dophase_done = TRUE; /* with the easy interface we are done here */
-  }
-  *connected = conn->bits.tcpconnect[FIRSTSOCKET];
-
-  if(*dophase_done) {
-    DEBUGF(infof(conn->data, "DO phase is complete\n"));
-  }
-
-  return result;
-}
-
-/* called from curl_multi.c while DOing */
-static CURLcode scp_doing(struct connectdata *conn,
-                               bool *dophase_done)
-{
-  CURLcode result;
-  result = ssh_multi_statemach(conn, dophase_done);
-
-  if(*dophase_done) {
-    DEBUGF(infof(conn->data, "DO phase is complete\n"));
-  }
-  return result;
-}
-
-/*
- * The DO function is generic for both protocols. There was previously two
- * separate ones but this way means less duplicated code.
- */
-
-static CURLcode ssh_do(struct connectdata *conn, bool *done)
-{
-  CURLcode res;
-  bool connected = 0;
-  struct SessionHandle *data = conn->data;
-
-  *done = FALSE; /* default to false */
-
-  /*
-    Since connections can be re-used between SessionHandles, this might be a
-    connection already existing but on a fresh SessionHandle struct so we must
-    make sure we have a good 'struct SSHPROTO' to play with. For new
-    connections, the struct SSHPROTO is allocated and setup in the
-    ssh_connect() function.
-  */
-  Curl_reset_reqproto(conn);
-  res = ssh_init(conn);
-  if(res)
-    return res;
-
-  data->req.size = -1; /* make sure this is unknown at this point */
-
-  Curl_pgrsSetUploadCounter(data, 0);
-  Curl_pgrsSetDownloadCounter(data, 0);
-  Curl_pgrsSetUploadSize(data, 0);
-  Curl_pgrsSetDownloadSize(data, 0);
-
-  if(conn->handler->protocol & CURLPROTO_SCP)
-    res = scp_perform(conn, &connected,  done);
-  else
-    res = sftp_perform(conn, &connected,  done);
-
-  return res;
-}
-
-/* BLOCKING, but the function is using the state machine so the only reason
-   this is still blocking is that the multi interface code has no support for
-   disconnecting operations that takes a while */
-static CURLcode scp_disconnect(struct connectdata *conn, bool dead_connection)
-{
-  CURLcode result = CURLE_OK;
-  struct ssh_conn *ssh = &conn->proto.sshc;
-  (void) dead_connection;
-
-  Curl_safefree(conn->data->state.proto.ssh);
-
-  if(ssh->ssh_session) {
-    /* only if there's a session still around to use! */
-
-    state(conn, SSH_SESSION_DISCONNECT);
-
-    result = ssh_easy_statemach(conn, FALSE);
-  }
-
-  return result;
-}
-
-/* generic done function for both SCP and SFTP called from their specific
-   done functions */
-static CURLcode ssh_done(struct connectdata *conn, CURLcode status)
-{
-  CURLcode result = CURLE_OK;
-  struct SSHPROTO *sftp_scp = conn->data->state.proto.ssh;
-
-  if(status == CURLE_OK) {
-    /* run the state-machine
-
-       TODO: when the multi interface is used, this _really_ should be using
-       the ssh_multi_statemach function but we have no general support for
-       non-blocking DONE operations, not in the multi state machine and with
-       Curl_done() invokes on several places in the code!
-    */
-    result = ssh_easy_statemach(conn, FALSE);
-  }
-  else
-    result = status;
-
-  if(sftp_scp)
-    Curl_safefree(sftp_scp->path);
-  if(Curl_pgrsDone(conn))
-    return CURLE_ABORTED_BY_CALLBACK;
-
-  conn->data->req.keepon = 0; /* clear all bits */
-  return result;
-}
-
-
-static CURLcode scp_done(struct connectdata *conn, CURLcode status,
-                         bool premature)
-{
-  (void)premature; /* not used */
-
-  if(status == CURLE_OK)
-    state(conn, SSH_SCP_DONE);
-
-  return ssh_done(conn, status);
-
-}
-
-/* return number of received (decrypted) bytes */
-static ssize_t scp_send(struct connectdata *conn, int sockindex,
-                        const void *mem, size_t len, CURLcode *err)
-{
-  ssize_t nwrite;
-  (void)sockindex; /* we only support SCP on the fixed known primary socket */
-
-  /* libssh2_channel_write() returns int! */
-  nwrite = (ssize_t)
-    libssh2_channel_write(conn->proto.sshc.ssh_channel, mem, len);
-
-  ssh_block2waitfor(conn, (nwrite == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE);
-
-  if(nwrite == LIBSSH2_ERROR_EAGAIN) {
-    *err = CURLE_AGAIN;
-    nwrite = 0;
-  }
-  else if(nwrite < LIBSSH2_ERROR_NONE) {
-    *err = libssh2_session_error_to_CURLE((int)nwrite);
-    nwrite = -1;
-  }
-
-  return nwrite;
-}
-
-/*
- * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
- * a regular CURLcode value.
- */
-static ssize_t scp_recv(struct connectdata *conn, int sockindex,
-                        char *mem, size_t len, CURLcode *err)
-{
-  ssize_t nread;
-  (void)sockindex; /* we only support SCP on the fixed known primary socket */
-
-  /* libssh2_channel_read() returns int */
-  nread = (ssize_t)
-    libssh2_channel_read(conn->proto.sshc.ssh_channel, mem, len);
-
-  ssh_block2waitfor(conn, (nread == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE);
-  if(nread == LIBSSH2_ERROR_EAGAIN) {
-    *err = CURLE_AGAIN;
-    nread = -1;
-  }
-
-  return nread;
-}
-
-/*
- * =============== SFTP ===============
- */
-
-/*
- ***********************************************************************
- *
- * sftp_perform()
- *
- * This is the actual DO function for SFTP. Get a file/directory according to
- * the options previously setup.
- */
-
-static
-CURLcode sftp_perform(struct connectdata *conn,
-                      bool *connected,
-                      bool *dophase_done)
-{
-  CURLcode result = CURLE_OK;
-
-  DEBUGF(infof(conn->data, "DO phase starts\n"));
-
-  *dophase_done = FALSE; /* not done yet */
-
-  /* start the first command in the DO phase */
-  state(conn, SSH_SFTP_QUOTE_INIT);
-
-  /* run the state-machine */
-  if(conn->data->state.used_interface == Curl_if_multi) {
-    result = ssh_multi_statemach(conn, dophase_done);
-  }
-  else {
-    result = ssh_easy_statemach(conn, FALSE);
-    *dophase_done = TRUE; /* with the easy interface we are done here */
-  }
-  *connected = conn->bits.tcpconnect[FIRSTSOCKET];
-
-  if(*dophase_done) {
-    DEBUGF(infof(conn->data, "DO phase is complete\n"));
-  }
-
-  return result;
-}
-
-/* called from curl_multi.c while DOing */
-static CURLcode sftp_doing(struct connectdata *conn,
-                           bool *dophase_done)
-{
-  CURLcode result;
-  result = ssh_multi_statemach(conn, dophase_done);
-
-  if(*dophase_done) {
-    DEBUGF(infof(conn->data, "DO phase is complete\n"));
-  }
-  return result;
-}
-
-/* BLOCKING, but the function is using the state machine so the only reason
-   this is still blocking is that the multi interface code has no support for
-   disconnecting operations that takes a while */
-static CURLcode sftp_disconnect(struct connectdata *conn, bool dead_connection)
-{
-  CURLcode result = CURLE_OK;
-  (void) dead_connection;
-
-  DEBUGF(infof(conn->data, "SSH DISCONNECT starts now\n"));
-
-  Curl_safefree(conn->data->state.proto.ssh);
-
-  if(conn->proto.sshc.ssh_session) {
-    /* only if there's a session still around to use! */
-    state(conn, SSH_SFTP_SHUTDOWN);
-    result = ssh_easy_statemach(conn, FALSE);
-  }
-
-  DEBUGF(infof(conn->data, "SSH DISCONNECT is done\n"));
-
-  return result;
-
-}
-
-static CURLcode sftp_done(struct connectdata *conn, CURLcode status,
-                               bool premature)
-{
-  struct ssh_conn *sshc = &conn->proto.sshc;
-
-  if(status == CURLE_OK) {
-    /* Post quote commands are executed after the SFTP_CLOSE state to avoid
-       errors that could happen due to open file handles during POSTQUOTE
-       operation */
-    if(!status && !premature && conn->data->set.postquote) {
-      sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT;
-      state(conn, SSH_SFTP_CLOSE);
-    }
-    else
-      state(conn, SSH_SFTP_CLOSE);
-  }
-  return ssh_done(conn, status);
-}
-
-/* return number of sent bytes */
-static ssize_t sftp_send(struct connectdata *conn, int sockindex,
-                         const void *mem, size_t len, CURLcode *err)
-{
-  ssize_t nwrite;   /* libssh2_sftp_write() used to return size_t in 0.14
-                       but is changed to ssize_t in 0.15. These days we don't
-                       support libssh2 0.15*/
-  (void)sockindex;
-
-  nwrite = libssh2_sftp_write(conn->proto.sshc.sftp_handle, mem, len);
-
-  ssh_block2waitfor(conn, (nwrite == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE);
-
-  if(nwrite == LIBSSH2_ERROR_EAGAIN) {
-    *err = CURLE_AGAIN;
-    nwrite = 0;
-  }
-  else if(nwrite < LIBSSH2_ERROR_NONE) {
-    *err = libssh2_session_error_to_CURLE((int)nwrite);
-    nwrite = -1;
-  }
-
-  return nwrite;
-}
-
-/*
- * Return number of received (decrypted) bytes
- */
-static ssize_t sftp_recv(struct connectdata *conn, int sockindex,
-                         char *mem, size_t len, CURLcode *err)
-{
-  ssize_t nread;
-  (void)sockindex;
-
-  nread = libssh2_sftp_read(conn->proto.sshc.sftp_handle, mem, len);
-
-  ssh_block2waitfor(conn, (nread == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE);
-
-  if(nread == LIBSSH2_ERROR_EAGAIN) {
-    *err = CURLE_AGAIN;
-    nread = -1;
-  }
-  return nread;
-}
-
-/* The get_pathname() function is being borrowed from OpenSSH-sftp.c
-   version 4.6p1. */
-/*
- * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-static CURLcode
-get_pathname(const char **cpp, char **path)
-{
-  const char *cp = *cpp, *end;
-  char quot;
-  unsigned int i, j;
-  static const char WHITESPACE[] = " \t\r\n";
-
-  cp += strspn(cp, WHITESPACE);
-  if(!*cp) {
-    *cpp = cp;
-    *path = NULL;
-    return CURLE_QUOTE_ERROR;
-  }
-
-  *path = malloc(strlen(cp) + 1);
-  if(*path == NULL)
-    return CURLE_OUT_OF_MEMORY;
-
-  /* Check for quoted filenames */
-  if(*cp == '\"' || *cp == '\'') {
-    quot = *cp++;
-
-    /* Search for terminating quote, unescape some chars */
-    for(i = j = 0; i <= strlen(cp); i++) {
-      if(cp[i] == quot) {  /* Found quote */
-        i++;
-        (*path)[j] = '\0';
-        break;
-      }
-      if(cp[i] == '\0') {  /* End of string */
-        /*error("Unterminated quote");*/
-        goto fail;
-      }
-      if(cp[i] == '\\') {  /* Escaped characters */
-        i++;
-        if(cp[i] != '\'' && cp[i] != '\"' &&
-            cp[i] != '\\') {
-          /*error("Bad escaped character '\\%c'",
-              cp[i]);*/
-          goto fail;
-        }
-      }
-      (*path)[j++] = cp[i];
-    }
-
-    if(j == 0) {
-      /*error("Empty quotes");*/
-      goto fail;
-    }
-    *cpp = cp + i + strspn(cp + i, WHITESPACE);
-  }
-  else {
-    /* Read to end of filename */
-    end = strpbrk(cp, WHITESPACE);
-    if(end == NULL)
-      end = strchr(cp, '\0');
-    *cpp = end + strspn(end, WHITESPACE);
-
-    memcpy(*path, cp, end - cp);
-    (*path)[end - cp] = '\0';
-  }
-  return CURLE_OK;
-
-  fail:
-    Curl_safefree(*path);
-    return CURLE_QUOTE_ERROR;
-}
-
-
-static const char *sftp_libssh2_strerror(int err)
-{
-  switch (err) {
-    case LIBSSH2_FX_NO_SUCH_FILE:
-      return "No such file or directory";
-
-    case LIBSSH2_FX_PERMISSION_DENIED:
-      return "Permission denied";
-
-    case LIBSSH2_FX_FAILURE:
-      return "Operation failed";
-
-    case LIBSSH2_FX_BAD_MESSAGE:
-      return "Bad message from SFTP server";
-
-    case LIBSSH2_FX_NO_CONNECTION:
-      return "Not connected to SFTP server";
-
-    case LIBSSH2_FX_CONNECTION_LOST:
-      return "Connection to SFTP server lost";
-
-    case LIBSSH2_FX_OP_UNSUPPORTED:
-      return "Operation not supported by SFTP server";
-
-    case LIBSSH2_FX_INVALID_HANDLE:
-      return "Invalid handle";
-
-    case LIBSSH2_FX_NO_SUCH_PATH:
-      return "No such file or directory";
-
-    case LIBSSH2_FX_FILE_ALREADY_EXISTS:
-      return "File already exists";
-
-    case LIBSSH2_FX_WRITE_PROTECT:
-      return "File is write protected";
-
-    case LIBSSH2_FX_NO_MEDIA:
-      return "No media";
-
-    case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM:
-      return "Disk full";
-
-    case LIBSSH2_FX_QUOTA_EXCEEDED:
-      return "User quota exceeded";
-
-    case LIBSSH2_FX_UNKNOWN_PRINCIPLE:
-      return "Unknown principle";
-
-    case LIBSSH2_FX_LOCK_CONFlICT:
-      return "File lock conflict";
-
-    case LIBSSH2_FX_DIR_NOT_EMPTY:
-      return "Directory not empty";
-
-    case LIBSSH2_FX_NOT_A_DIRECTORY:
-      return "Not a directory";
-
-    case LIBSSH2_FX_INVALID_FILENAME:
-      return "Invalid filename";
-
-    case LIBSSH2_FX_LINK_LOOP:
-      return "Link points to itself";
-  }
-  return "Unknown error in libssh2";
-}
-
-#endif /* USE_LIBSSH2 */
diff --git a/lib/sslgen.c b/lib/sslgen.c
deleted file mode 100644 (file)
index d85ba8a..0000000
+++ /dev/null
@@ -1,541 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-/* This file is for implementing all "generic" SSL functions that all libcurl
-   internals should use. It is then responsible for calling the proper
-   "backend" function.
-
-   SSL-functions in libcurl should call functions in this source file, and not
-   to any specific SSL-layer.
-
-   Curl_ssl_ - prefix for generic ones
-   Curl_ossl_ - prefix for OpenSSL ones
-   Curl_gtls_ - prefix for GnuTLS ones
-   Curl_nss_ - prefix for NSS ones
-   Curl_polarssl_ - prefix for PolarSSL ones
-   Curl_cyassl_ - prefix for CyaSSL ones
-   Curl_schannel_ - prefix for Schannel SSPI ones
-   Curl_darwinssl_ - prefix for SecureTransport (Darwin) ones
-
-   Note that this source code uses curlssl_* functions, and they are all
-   defines/macros #defined by the lib-specific header files.
-
-   "SSL/TLS Strong Encryption: An Introduction"
-   http://httpd.apache.org/docs-2.0/ssl/ssl_intro.html
-*/
-
-#include "curl_setup.h"
-
-#include "curl_urldata.h"
-#define SSLGEN_C
-#include "curl_sslgen.h" /* generic SSL protos etc */
-#include "curl_ssluse.h" /* OpenSSL versions */
-#include "curl_gtls.h"   /* GnuTLS versions */
-#include "curl_nssg.h"   /* NSS versions */
-#include "curl_qssl.h"   /* QSOSSL versions */
-#include "curl_polarssl.h" /* PolarSSL versions */
-#include "curl_axtls.h"  /* axTLS versions */
-#include "curl_cyassl.h"  /* CyaSSL versions */
-#include "curl_schannel.h" /* Schannel SSPI version */
-#include "curl_darwinssl.h" /* SecureTransport (Darwin) version */
-#include "curl_sendf.h"
-#include "curl_rawstr.h"
-#include "curl_url.h"
-#include "curl_memory.h"
-#include "curl_progress.h"
-#include "curl_share.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/* convenience macro to check if this handle is using a shared SSL session */
-#define SSLSESSION_SHARED(data) (data->share &&                        \
-                                 (data->share->specifier &             \
-                                  (1<<CURL_LOCK_DATA_SSL_SESSION)))
-
-static bool safe_strequal(char* str1, char* str2)
-{
-  if(str1 && str2)
-    /* both pointers point to something then compare them */
-    return (0 != Curl_raw_equal(str1, str2)) ? TRUE : FALSE;
-  else
-    /* if both pointers are NULL then treat them as equal */
-    return (!str1 && !str2) ? TRUE : FALSE;
-}
-
-bool
-Curl_ssl_config_matches(struct ssl_config_data* data,
-                        struct ssl_config_data* needle)
-{
-  if((data->version == needle->version) &&
-     (data->verifypeer == needle->verifypeer) &&
-     (data->verifyhost == needle->verifyhost) &&
-     safe_strequal(data->CApath, needle->CApath) &&
-     safe_strequal(data->CAfile, needle->CAfile) &&
-     safe_strequal(data->random_file, needle->random_file) &&
-     safe_strequal(data->egdsocket, needle->egdsocket) &&
-     safe_strequal(data->cipher_list, needle->cipher_list))
-    return TRUE;
-
-  return FALSE;
-}
-
-bool
-Curl_clone_ssl_config(struct ssl_config_data *source,
-                      struct ssl_config_data *dest)
-{
-  dest->sessionid = source->sessionid;
-  dest->verifyhost = source->verifyhost;
-  dest->verifypeer = source->verifypeer;
-  dest->version = source->version;
-
-  if(source->CAfile) {
-    dest->CAfile = strdup(source->CAfile);
-    if(!dest->CAfile)
-      return FALSE;
-  }
-  else
-    dest->CAfile = NULL;
-
-  if(source->CApath) {
-    dest->CApath = strdup(source->CApath);
-    if(!dest->CApath)
-      return FALSE;
-  }
-  else
-    dest->CApath = NULL;
-
-  if(source->cipher_list) {
-    dest->cipher_list = strdup(source->cipher_list);
-    if(!dest->cipher_list)
-      return FALSE;
-  }
-  else
-    dest->cipher_list = NULL;
-
-  if(source->egdsocket) {
-    dest->egdsocket = strdup(source->egdsocket);
-    if(!dest->egdsocket)
-      return FALSE;
-  }
-  else
-    dest->egdsocket = NULL;
-
-  if(source->random_file) {
-    dest->random_file = strdup(source->random_file);
-    if(!dest->random_file)
-      return FALSE;
-  }
-  else
-    dest->random_file = NULL;
-
-  return TRUE;
-}
-
-void Curl_free_ssl_config(struct ssl_config_data* sslc)
-{
-  Curl_safefree(sslc->CAfile);
-  Curl_safefree(sslc->CApath);
-  Curl_safefree(sslc->cipher_list);
-  Curl_safefree(sslc->egdsocket);
-  Curl_safefree(sslc->random_file);
-}
-
-#ifdef USE_SSL
-
-/* "global" init done? */
-static bool init_ssl=FALSE;
-
-/**
- * Global SSL init
- *
- * @retval 0 error initializing SSL
- * @retval 1 SSL initialized successfully
- */
-int Curl_ssl_init(void)
-{
-  /* make sure this is only done once */
-  if(init_ssl)
-    return 1;
-  init_ssl = TRUE; /* never again */
-
-  return curlssl_init();
-}
-
-
-/* Global cleanup */
-void Curl_ssl_cleanup(void)
-{
-  if(init_ssl) {
-    /* only cleanup if we did a previous init */
-    curlssl_cleanup();
-    init_ssl = FALSE;
-  }
-}
-
-CURLcode
-Curl_ssl_connect(struct connectdata *conn, int sockindex)
-{
-  CURLcode res;
-  /* mark this is being ssl-enabled from here on. */
-  conn->ssl[sockindex].use = TRUE;
-  conn->ssl[sockindex].state = ssl_connection_negotiating;
-
-  res = curlssl_connect(conn, sockindex);
-
-  if(!res)
-    Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSL is connected */
-
-  return res;
-}
-
-CURLcode
-Curl_ssl_connect_nonblocking(struct connectdata *conn, int sockindex,
-                             bool *done)
-{
-  CURLcode res;
-  /* mark this is being ssl requested from here on. */
-  conn->ssl[sockindex].use = TRUE;
-#ifdef curlssl_connect_nonblocking
-  res = curlssl_connect_nonblocking(conn, sockindex, done);
-#else
-  *done = TRUE; /* fallback to BLOCKING */
-  res = curlssl_connect(conn, sockindex);
-#endif /* non-blocking connect support */
-  if(!res && *done)
-    Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSL is connected */
-  return res;
-}
-
-/*
- * Check if there's a session ID for the given connection in the cache, and if
- * there's one suitable, it is provided. Returns TRUE when no entry matched.
- */
-int Curl_ssl_getsessionid(struct connectdata *conn,
-                          void **ssl_sessionid,
-                          size_t *idsize) /* set 0 if unknown */
-{
-  struct curl_ssl_session *check;
-  struct SessionHandle *data = conn->data;
-  size_t i;
-  long *general_age;
-  bool no_match = TRUE;
-
-  *ssl_sessionid = NULL;
-
-  if(!conn->ssl_config.sessionid)
-    /* session ID re-use is disabled */
-    return TRUE;
-
-  /* Lock if shared */
-  if(SSLSESSION_SHARED(data)) {
-    Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
-    general_age = &data->share->sessionage;
-  }
-  else
-    general_age = &data->state.sessionage;
-
-  for(i = 0; i < data->set.ssl.max_ssl_sessions; i++) {
-    check = &data->state.session[i];
-    if(!check->sessionid)
-      /* not session ID means blank entry */
-      continue;
-    if(Curl_raw_equal(conn->host.name, check->name) &&
-       (conn->remote_port == check->remote_port) &&
-       Curl_ssl_config_matches(&conn->ssl_config, &check->ssl_config)) {
-      /* yes, we have a session ID! */
-      (*general_age)++;          /* increase general age */
-      check->age = *general_age; /* set this as used in this age */
-      *ssl_sessionid = check->sessionid;
-      if(idsize)
-        *idsize = check->idsize;
-      no_match = FALSE;
-      break;
-    }
-  }
-
-  /* Unlock */
-  if(SSLSESSION_SHARED(data))
-    Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
-
-  return no_match;
-}
-
-/*
- * Kill a single session ID entry in the cache.
- */
-void Curl_ssl_kill_session(struct curl_ssl_session *session)
-{
-  if(session->sessionid) {
-    /* defensive check */
-
-    /* free the ID the SSL-layer specific way */
-    curlssl_session_free(session->sessionid);
-
-    session->sessionid = NULL;
-    session->age = 0; /* fresh */
-
-    Curl_free_ssl_config(&session->ssl_config);
-
-    Curl_safefree(session->name);
-  }
-}
-
-/*
- * Delete the given session ID from the cache.
- */
-void Curl_ssl_delsessionid(struct connectdata *conn, void *ssl_sessionid)
-{
-  size_t i;
-  struct SessionHandle *data=conn->data;
-
-  if(SSLSESSION_SHARED(data))
-    Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
-
-  for(i = 0; i < data->set.ssl.max_ssl_sessions; i++) {
-    struct curl_ssl_session *check = &data->state.session[i];
-
-    if(check->sessionid == ssl_sessionid) {
-      Curl_ssl_kill_session(check);
-      break;
-    }
-  }
-
-  if(SSLSESSION_SHARED(data))
-    Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
-}
-
-/*
- * Store session id in the session cache. The ID passed on to this function
- * must already have been extracted and allocated the proper way for the SSL
- * layer. Curl_XXXX_session_free() will be called to free/kill the session ID
- * later on.
- */
-CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
-                               void *ssl_sessionid,
-                               size_t idsize)
-{
-  size_t i;
-  struct SessionHandle *data=conn->data; /* the mother of all structs */
-  struct curl_ssl_session *store = &data->state.session[0];
-  long oldest_age=data->state.session[0].age; /* zero if unused */
-  char *clone_host;
-  long *general_age;
-
-  /* Even though session ID re-use might be disabled, that only disables USING
-     IT. We still store it here in case the re-using is again enabled for an
-     upcoming transfer */
-
-  clone_host = strdup(conn->host.name);
-  if(!clone_host)
-    return CURLE_OUT_OF_MEMORY; /* bail out */
-
-  /* Now we should add the session ID and the host name to the cache, (remove
-     the oldest if necessary) */
-
-  /* If using shared SSL session, lock! */
-  if(SSLSESSION_SHARED(data)) {
-    Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
-    general_age = &data->share->sessionage;
-  }
-  else {
-    general_age = &data->state.sessionage;
-  }
-
-  /* find an empty slot for us, or find the oldest */
-  for(i = 1; (i < data->set.ssl.max_ssl_sessions) &&
-        data->state.session[i].sessionid; i++) {
-    if(data->state.session[i].age < oldest_age) {
-      oldest_age = data->state.session[i].age;
-      store = &data->state.session[i];
-    }
-  }
-  if(i == data->set.ssl.max_ssl_sessions)
-    /* cache is full, we must "kill" the oldest entry! */
-    Curl_ssl_kill_session(store);
-  else
-    store = &data->state.session[i]; /* use this slot */
-
-  /* now init the session struct wisely */
-  store->sessionid = ssl_sessionid;
-  store->idsize = idsize;
-  store->age = *general_age;    /* set current age */
-  if(store->name)
-    /* free it if there's one already present */
-    free(store->name);
-  store->name = clone_host;               /* clone host name */
-  store->remote_port = conn->remote_port; /* port number */
-
-
-  /* Unlock */
-  if(SSLSESSION_SHARED(data))
-    Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
-
-  if(!Curl_clone_ssl_config(&conn->ssl_config, &store->ssl_config)) {
-    store->sessionid = NULL; /* let caller free sessionid */
-    free(clone_host);
-    return CURLE_OUT_OF_MEMORY;
-  }
-
-  return CURLE_OK;
-}
-
-
-void Curl_ssl_close_all(struct SessionHandle *data)
-{
-  size_t i;
-  /* kill the session ID cache if not shared */
-  if(data->state.session && !SSLSESSION_SHARED(data)) {
-    for(i = 0; i < data->set.ssl.max_ssl_sessions; i++)
-      /* the single-killer function handles empty table slots */
-      Curl_ssl_kill_session(&data->state.session[i]);
-
-    /* free the cache data */
-    Curl_safefree(data->state.session);
-  }
-
-  curlssl_close_all(data);
-}
-
-void Curl_ssl_close(struct connectdata *conn, int sockindex)
-{
-  DEBUGASSERT((sockindex <= 1) && (sockindex >= -1));
-  curlssl_close(conn, sockindex);
-}
-
-CURLcode Curl_ssl_shutdown(struct connectdata *conn, int sockindex)
-{
-  if(curlssl_shutdown(conn, sockindex))
-    return CURLE_SSL_SHUTDOWN_FAILED;
-
-  conn->ssl[sockindex].use = FALSE; /* get back to ordinary socket usage */
-  conn->ssl[sockindex].state = ssl_connection_none;
-
-  conn->recv[sockindex] = Curl_recv_plain;
-  conn->send[sockindex] = Curl_send_plain;
-
-  return CURLE_OK;
-}
-
-/* Selects an SSL crypto engine
- */
-CURLcode Curl_ssl_set_engine(struct SessionHandle *data, const char *engine)
-{
-  return curlssl_set_engine(data, engine);
-}
-
-/* Selects the default SSL crypto engine
- */
-CURLcode Curl_ssl_set_engine_default(struct SessionHandle *data)
-{
-  return curlssl_set_engine_default(data);
-}
-
-/* Return list of OpenSSL crypto engine names. */
-struct curl_slist *Curl_ssl_engines_list(struct SessionHandle *data)
-{
-  return curlssl_engines_list(data);
-}
-
-/*
- * This sets up a session ID cache to the specified size. Make sure this code
- * is agnostic to what underlying SSL technology we use.
- */
-CURLcode Curl_ssl_initsessions(struct SessionHandle *data, size_t amount)
-{
-  struct curl_ssl_session *session;
-
-  if(data->state.session)
-    /* this is just a precaution to prevent multiple inits */
-    return CURLE_OK;
-
-  session = calloc(amount, sizeof(struct curl_ssl_session));
-  if(!session)
-    return CURLE_OUT_OF_MEMORY;
-
-  /* store the info in the SSL section */
-  data->set.ssl.max_ssl_sessions = amount;
-  data->state.session = session;
-  data->state.sessionage = 1; /* this is brand new */
-  return CURLE_OK;
-}
-
-size_t Curl_ssl_version(char *buffer, size_t size)
-{
-  return curlssl_version(buffer, size);
-}
-
-/*
- * This function tries to determine connection status.
- *
- * Return codes:
- *     1 means the connection is still in place
- *     0 means the connection has been closed
- *    -1 means the connection status is unknown
- */
-int Curl_ssl_check_cxn(struct connectdata *conn)
-{
-  return curlssl_check_cxn(conn);
-}
-
-bool Curl_ssl_data_pending(const struct connectdata *conn,
-                           int connindex)
-{
-  return curlssl_data_pending(conn, connindex);
-}
-
-void Curl_ssl_free_certinfo(struct SessionHandle *data)
-{
-  int i;
-  struct curl_certinfo *ci = &data->info.certs;
-  if(ci->num_of_certs) {
-    /* free all individual lists used */
-    for(i=0; i<ci->num_of_certs; i++) {
-      curl_slist_free_all(ci->certinfo[i]);
-      ci->certinfo[i] = NULL;
-    }
-    free(ci->certinfo); /* free the actual array too */
-    ci->certinfo = NULL;
-    ci->num_of_certs = 0;
-  }
-}
-
-#if defined(USE_SSLEAY) || defined(USE_GNUTLS) || defined(USE_NSS) || \
-    defined(USE_DARWINSSL)
-/* these functions are only used by some SSL backends */
-
-void Curl_ssl_random(struct SessionHandle *data,
-                     unsigned char *entropy,
-                     size_t length)
-{
-  curlssl_random(data, entropy, length);
-}
-
-void Curl_ssl_md5sum(unsigned char *tmp, /* input */
-                     size_t tmplen,
-                     unsigned char *md5sum, /* output */
-                     size_t md5len)
-{
-  curlssl_md5sum(tmp, tmplen, md5sum, md5len);
-}
-#endif /* USE_SSLEAY || USE_GNUTLS || USE_NSS || USE_DARWINSSL */
-
-#endif /* USE_SSL */
diff --git a/lib/ssluse.c b/lib/ssluse.c
deleted file mode 100644 (file)
index 0809d46..0000000
+++ /dev/null
@@ -1,2736 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-/*
- * Source file for all OpenSSL-specific code for the TLS/SSL layer. No code
- * but curl_sslgen.c should ever call or use these functions.
- */
-
-/*
- * The original SSLeay-using code for curl was written by Linas Vepstas and
- * Sampo Kellomaki 1998.
- */
-
-#include "curl_setup.h"
-
-#ifdef HAVE_LIMITS_H
-#include <limits.h>
-#endif
-
-#include "curl_urldata.h"
-#include "curl_sendf.h"
-#include "curl_formdata.h" /* for the boundary function */
-#include "curl_url.h" /* for the ssl config check function */
-#include "curl_inet_pton.h"
-#include "curl_ssluse.h"
-#include "curl_connect.h"
-#include "curl_strequal.h"
-#include "curl_select.h"
-#include "curl_sslgen.h"
-#include "curl_rawstr.h"
-#include "curl_hostcheck.h"
-
-#define _MPRINTF_REPLACE /* use the internal *printf() functions */
-#include <curl/mprintf.h>
-
-#ifdef USE_SSLEAY
-
-#ifdef USE_OPENSSL
-#include <openssl/rand.h>
-#include <openssl/x509v3.h>
-#include <openssl/dsa.h>
-#include <openssl/dh.h>
-#include <openssl/err.h>
-#include <openssl/md5.h>
-#else
-#include <rand.h>
-#include <x509v3.h>
-#include <md5.h>
-#endif
-
-#include "curl_warnless.h"
-#include "curl_memory.h"
-#include "curl_non_ascii.h" /* for Curl_convert_from_utf8 prototype */
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-#ifndef OPENSSL_VERSION_NUMBER
-#error "OPENSSL_VERSION_NUMBER not defined"
-#endif
-
-#if OPENSSL_VERSION_NUMBER >= 0x0090581fL
-#define HAVE_SSL_GET1_SESSION 1
-#else
-#undef HAVE_SSL_GET1_SESSION
-#endif
-
-#if OPENSSL_VERSION_NUMBER >= 0x00904100L
-#define HAVE_USERDATA_IN_PWD_CALLBACK 1
-#else
-#undef HAVE_USERDATA_IN_PWD_CALLBACK
-#endif
-
-#if OPENSSL_VERSION_NUMBER >= 0x00907001L
-/* ENGINE_load_private_key() takes four arguments */
-#define HAVE_ENGINE_LOAD_FOUR_ARGS
-#include <openssl/ui.h>
-#else
-/* ENGINE_load_private_key() takes three arguments */
-#undef HAVE_ENGINE_LOAD_FOUR_ARGS
-#endif
-
-#if (OPENSSL_VERSION_NUMBER >= 0x00903001L) && defined(HAVE_OPENSSL_PKCS12_H)
-/* OpenSSL has PKCS 12 support */
-#define HAVE_PKCS12_SUPPORT
-#else
-/* OpenSSL/SSLEay does not have PKCS12 support */
-#undef HAVE_PKCS12_SUPPORT
-#endif
-
-#if OPENSSL_VERSION_NUMBER >= 0x00906001L
-#define HAVE_ERR_ERROR_STRING_N 1
-#endif
-
-#if OPENSSL_VERSION_NUMBER >= 0x00909000L
-#define SSL_METHOD_QUAL const
-#else
-#define SSL_METHOD_QUAL
-#endif
-
-#if OPENSSL_VERSION_NUMBER >= 0x00907000L
-/* 0.9.6 didn't have X509_STORE_set_flags() */
-#define HAVE_X509_STORE_SET_FLAGS 1
-#else
-#define X509_STORE_set_flags(x,y) Curl_nop_stmt
-#endif
-
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
-#define HAVE_ERR_REMOVE_THREAD_STATE 1
-#endif
-
-#ifndef HAVE_SSLV2_CLIENT_METHOD
-#undef OPENSSL_NO_SSL2 /* undef first to avoid compiler warnings */
-#define OPENSSL_NO_SSL2
-#endif
-
-/*
- * Number of bytes to read from the random number seed file. This must be
- * a finite value (because some entropy "files" like /dev/urandom have
- * an infinite length), but must be large enough to provide enough
- * entopy to properly seed OpenSSL's PRNG.
- */
-#define RAND_LOAD_LENGTH 1024
-
-#ifndef HAVE_USERDATA_IN_PWD_CALLBACK
-static char global_passwd[64];
-#endif
-
-static int passwd_callback(char *buf, int num, int encrypting
-#ifdef HAVE_USERDATA_IN_PWD_CALLBACK
-                           /* This was introduced in 0.9.4, we can set this
-                              using SSL_CTX_set_default_passwd_cb_userdata()
-                              */
-                           , void *global_passwd
-#endif
-                           )
-{
-  DEBUGASSERT(0 == encrypting);
-
-  if(!encrypting) {
-    int klen = curlx_uztosi(strlen((char *)global_passwd));
-    if(num > klen) {
-      memcpy(buf, global_passwd, klen+1);
-      return klen;
-    }
-  }
-  return 0;
-}
-
-/*
- * rand_enough() is a function that returns TRUE if we have seeded the random
- * engine properly. We use some preprocessor magic to provide a seed_enough()
- * macro to use, just to prevent a compiler warning on this function if we
- * pass in an argument that is never used.
- */
-
-#ifdef HAVE_RAND_STATUS
-#define seed_enough(x) rand_enough()
-static bool rand_enough(void)
-{
-  return (0 != RAND_status()) ? TRUE : FALSE;
-}
-#else
-#define seed_enough(x) rand_enough(x)
-static bool rand_enough(int nread)
-{
-  /* this is a very silly decision to make */
-  return (nread > 500) ? TRUE : FALSE;
-}
-#endif
-
-static int ossl_seed(struct SessionHandle *data)
-{
-  char *buf = data->state.buffer; /* point to the big buffer */
-  int nread=0;
-
-  /* Q: should we add support for a random file name as a libcurl option?
-     A: Yes, it is here */
-
-#ifndef RANDOM_FILE
-  /* if RANDOM_FILE isn't defined, we only perform this if an option tells
-     us to! */
-  if(data->set.ssl.random_file)
-#define RANDOM_FILE "" /* doesn't matter won't be used */
-#endif
-  {
-    /* let the option override the define */
-    nread += RAND_load_file((data->set.str[STRING_SSL_RANDOM_FILE]?
-                             data->set.str[STRING_SSL_RANDOM_FILE]:
-                             RANDOM_FILE),
-                            RAND_LOAD_LENGTH);
-    if(seed_enough(nread))
-      return nread;
-  }
-
-#if defined(HAVE_RAND_EGD)
-  /* only available in OpenSSL 0.9.5 and later */
-  /* EGD_SOCKET is set at configure time or not at all */
-#ifndef EGD_SOCKET
-  /* If we don't have the define set, we only do this if the egd-option
-     is set */
-  if(data->set.str[STRING_SSL_EGDSOCKET])
-#define EGD_SOCKET "" /* doesn't matter won't be used */
-#endif
-  {
-    /* If there's an option and a define, the option overrides the
-       define */
-    int ret = RAND_egd(data->set.str[STRING_SSL_EGDSOCKET]?
-                       data->set.str[STRING_SSL_EGDSOCKET]:EGD_SOCKET);
-    if(-1 != ret) {
-      nread += ret;
-      if(seed_enough(nread))
-        return nread;
-    }
-  }
-#endif
-
-  /* If we get here, it means we need to seed the PRNG using a "silly"
-     approach! */
-  {
-    int len;
-    char *area;
-
-    /* Changed call to RAND_seed to use the underlying RAND_add implementation
-     * directly.  Do this in a loop, with the amount of additional entropy
-     * being dependent upon the algorithm used by Curl_FormBoundary(): N bytes
-     * of a 7-bit ascii set. -- Richard Gorton, March 11 2003.
-     */
-
-    do {
-      area = Curl_FormBoundary();
-      if(!area)
-        return 3; /* out of memory */
-
-      len = curlx_uztosi(strlen(area));
-      RAND_add(area, len, (len >> 1));
-
-      free(area); /* now remove the random junk */
-    } while(!RAND_status());
-  }
-
-  /* generates a default path for the random seed file */
-  buf[0]=0; /* blank it first */
-  RAND_file_name(buf, BUFSIZE);
-  if(buf[0]) {
-    /* we got a file name to try */
-    nread += RAND_load_file(buf, RAND_LOAD_LENGTH);
-    if(seed_enough(nread))
-      return nread;
-  }
-
-  infof(data, "libcurl is now using a weak random seed!\n");
-  return nread;
-}
-
-int Curl_ossl_seed(struct SessionHandle *data)
-{
-  /* we have the "SSL is seeded" boolean static to prevent multiple
-     time-consuming seedings in vain */
-  static bool ssl_seeded = FALSE;
-
-  if(!ssl_seeded || data->set.str[STRING_SSL_RANDOM_FILE] ||
-     data->set.str[STRING_SSL_EGDSOCKET]) {
-    ossl_seed(data);
-    ssl_seeded = TRUE;
-  }
-  return 0;
-}
-
-
-#ifndef SSL_FILETYPE_ENGINE
-#define SSL_FILETYPE_ENGINE 42
-#endif
-#ifndef SSL_FILETYPE_PKCS12
-#define SSL_FILETYPE_PKCS12 43
-#endif
-static int do_file_type(const char *type)
-{
-  if(!type || !type[0])
-    return SSL_FILETYPE_PEM;
-  if(Curl_raw_equal(type, "PEM"))
-    return SSL_FILETYPE_PEM;
-  if(Curl_raw_equal(type, "DER"))
-    return SSL_FILETYPE_ASN1;
-  if(Curl_raw_equal(type, "ENG"))
-    return SSL_FILETYPE_ENGINE;
-  if(Curl_raw_equal(type, "P12"))
-    return SSL_FILETYPE_PKCS12;
-  return -1;
-}
-
-static
-int cert_stuff(struct connectdata *conn,
-               SSL_CTX* ctx,
-               char *cert_file,
-               const char *cert_type,
-               char *key_file,
-               const char *key_type)
-{
-  struct SessionHandle *data = conn->data;
-
-  int file_type = do_file_type(cert_type);
-
-  if(cert_file != NULL || file_type == SSL_FILETYPE_ENGINE) {
-    SSL *ssl;
-    X509 *x509;
-    int cert_done = 0;
-
-    if(data->set.str[STRING_KEY_PASSWD]) {
-#ifndef HAVE_USERDATA_IN_PWD_CALLBACK
-      /*
-       * If password has been given, we store that in the global
-       * area (*shudder*) for a while:
-       */
-      size_t len = strlen(data->set.str[STRING_KEY_PASSWD]);
-      if(len < sizeof(global_passwd))
-        memcpy(global_passwd, data->set.str[STRING_KEY_PASSWD], len+1);
-      else
-        global_passwd[0] = '\0';
-#else
-      /*
-       * We set the password in the callback userdata
-       */
-      SSL_CTX_set_default_passwd_cb_userdata(ctx,
-                                             data->set.str[STRING_KEY_PASSWD]);
-#endif
-      /* Set passwd callback: */
-      SSL_CTX_set_default_passwd_cb(ctx, passwd_callback);
-    }
-
-
-#define SSL_CLIENT_CERT_ERR \
-    "unable to use client certificate (no key found or wrong pass phrase?)"
-
-    switch(file_type) {
-    case SSL_FILETYPE_PEM:
-      /* SSL_CTX_use_certificate_chain_file() only works on PEM files */
-      if(SSL_CTX_use_certificate_chain_file(ctx,
-                                            cert_file) != 1) {
-        failf(data, SSL_CLIENT_CERT_ERR);
-        return 0;
-      }
-      break;
-
-    case SSL_FILETYPE_ASN1:
-      /* SSL_CTX_use_certificate_file() works with either PEM or ASN1, but
-         we use the case above for PEM so this can only be performed with
-         ASN1 files. */
-      if(SSL_CTX_use_certificate_file(ctx,
-                                      cert_file,
-                                      file_type) != 1) {
-        failf(data, SSL_CLIENT_CERT_ERR);
-        return 0;
-      }
-      break;
-    case SSL_FILETYPE_ENGINE:
-#if defined(HAVE_OPENSSL_ENGINE_H) && defined(ENGINE_CTRL_GET_CMD_FROM_NAME)
-      {
-        if(data->state.engine) {
-          const char *cmd_name = "LOAD_CERT_CTRL";
-          struct {
-            const char *cert_id;
-            X509 *cert;
-          } params;
-
-          params.cert_id = cert_file;
-          params.cert = NULL;
-
-          /* Does the engine supports LOAD_CERT_CTRL ? */
-          if(!ENGINE_ctrl(data->state.engine, ENGINE_CTRL_GET_CMD_FROM_NAME,
-                          0, (void *)cmd_name, NULL)) {
-            failf(data, "ssl engine does not support loading certificates");
-            return 0;
-          }
-
-          /* Load the certificate from the engine */
-          if(!ENGINE_ctrl_cmd(data->state.engine, cmd_name,
-                              0, &params, NULL, 1)) {
-            failf(data, "ssl engine cannot load client cert with id"
-                  " '%s' [%s]", cert_file,
-                  ERR_error_string(ERR_get_error(), NULL));
-            return 0;
-          }
-
-          if(!params.cert) {
-            failf(data, "ssl engine didn't initialized the certificate "
-                  "properly.");
-            return 0;
-          }
-
-          if(SSL_CTX_use_certificate(ctx, params.cert) != 1) {
-            failf(data, "unable to set client certificate");
-            X509_free(params.cert);
-            return 0;
-          }
-          X509_free(params.cert); /* we don't need the handle any more... */
-        }
-        else {
-          failf(data, "crypto engine not set, can't load certificate");
-          return 0;
-        }
-      }
-      break;
-#else
-      failf(data, "file type ENG for certificate not implemented");
-      return 0;
-#endif
-
-    case SSL_FILETYPE_PKCS12:
-    {
-#ifdef HAVE_PKCS12_SUPPORT
-      FILE *f;
-      PKCS12 *p12;
-      EVP_PKEY *pri;
-      STACK_OF(X509) *ca = NULL;
-      int i;
-
-      f = fopen(cert_file,"rb");
-      if(!f) {
-        failf(data, "could not open PKCS12 file '%s'", cert_file);
-        return 0;
-      }
-      p12 = d2i_PKCS12_fp(f, NULL);
-      fclose(f);
-
-      if(!p12) {
-        failf(data, "error reading PKCS12 file '%s'", cert_file );
-        return 0;
-      }
-
-      PKCS12_PBE_add();
-
-      if(!PKCS12_parse(p12, data->set.str[STRING_KEY_PASSWD], &pri, &x509,
-                        &ca)) {
-        failf(data,
-              "could not parse PKCS12 file, check password, OpenSSL error %s",
-              ERR_error_string(ERR_get_error(), NULL) );
-        PKCS12_free(p12);
-        return 0;
-      }
-
-      PKCS12_free(p12);
-
-      if(SSL_CTX_use_certificate(ctx, x509) != 1) {
-        failf(data, SSL_CLIENT_CERT_ERR);
-        EVP_PKEY_free(pri);
-        X509_free(x509);
-        sk_X509_pop_free(ca, X509_free);
-        return 0;
-      }
-
-      if(SSL_CTX_use_PrivateKey(ctx, pri) != 1) {
-        failf(data, "unable to use private key from PKCS12 file '%s'",
-              cert_file);
-        EVP_PKEY_free(pri);
-        X509_free(x509);
-        sk_X509_pop_free(ca, X509_free);
-        return 0;
-      }
-
-      if(!SSL_CTX_check_private_key (ctx)) {
-        failf(data, "private key from PKCS12 file '%s' "
-              "does not match certificate in same file", cert_file);
-        EVP_PKEY_free(pri);
-        X509_free(x509);
-        sk_X509_pop_free(ca, X509_free);
-        return 0;
-      }
-      /* Set Certificate Verification chain */
-      if(ca && sk_X509_num(ca)) {
-        for(i = 0; i < sk_X509_num(ca); i++) {
-          if(!SSL_CTX_add_extra_chain_cert(ctx,sk_X509_value(ca, i))) {
-            failf(data, "cannot add certificate to certificate chain");
-            EVP_PKEY_free(pri);
-            X509_free(x509);
-            sk_X509_pop_free(ca, X509_free);
-            return 0;
-          }
-          if(!SSL_CTX_add_client_CA(ctx, sk_X509_value(ca, i))) {
-            failf(data, "cannot add certificate to client CA list");
-            EVP_PKEY_free(pri);
-            X509_free(x509);
-            sk_X509_pop_free(ca, X509_free);
-            return 0;
-          }
-        }
-      }
-
-      EVP_PKEY_free(pri);
-      X509_free(x509);
-      sk_X509_pop_free(ca, X509_free);
-      cert_done = 1;
-      break;
-#else
-      failf(data, "file type P12 for certificate not supported");
-      return 0;
-#endif
-    }
-    default:
-      failf(data, "not supported file type '%s' for certificate", cert_type);
-      return 0;
-    }
-
-    file_type = do_file_type(key_type);
-
-    switch(file_type) {
-    case SSL_FILETYPE_PEM:
-      if(cert_done)
-        break;
-      if(key_file == NULL)
-        /* cert & key can only be in PEM case in the same file */
-        key_file=cert_file;
-    case SSL_FILETYPE_ASN1:
-      if(SSL_CTX_use_PrivateKey_file(ctx, key_file, file_type) != 1) {
-        failf(data, "unable to set private key file: '%s' type %s",
-              key_file, key_type?key_type:"PEM");
-        return 0;
-      }
-      break;
-    case SSL_FILETYPE_ENGINE:
-#ifdef HAVE_OPENSSL_ENGINE_H
-      {                         /* XXXX still needs some work */
-        EVP_PKEY *priv_key = NULL;
-        if(data->state.engine) {
-#ifdef HAVE_ENGINE_LOAD_FOUR_ARGS
-          UI_METHOD *ui_method = UI_OpenSSL();
-#endif
-          /* the typecast below was added to please mingw32 */
-          priv_key = (EVP_PKEY *)
-            ENGINE_load_private_key(data->state.engine,key_file,
-#ifdef HAVE_ENGINE_LOAD_FOUR_ARGS
-                                    ui_method,
-#endif
-                                    data->set.str[STRING_KEY_PASSWD]);
-          if(!priv_key) {
-            failf(data, "failed to load private key from crypto engine");
-            return 0;
-          }
-          if(SSL_CTX_use_PrivateKey(ctx, priv_key) != 1) {
-            failf(data, "unable to set private key");
-            EVP_PKEY_free(priv_key);
-            return 0;
-          }
-          EVP_PKEY_free(priv_key);  /* we don't need the handle any more... */
-        }
-        else {
-          failf(data, "crypto engine not set, can't load private key");
-          return 0;
-        }
-      }
-      break;
-#else
-      failf(data, "file type ENG for private key not supported");
-      return 0;
-#endif
-    case SSL_FILETYPE_PKCS12:
-      if(!cert_done) {
-        failf(data, "file type P12 for private key not supported");
-        return 0;
-      }
-      break;
-    default:
-      failf(data, "not supported file type for private key");
-      return 0;
-    }
-
-    ssl=SSL_new(ctx);
-    if(NULL == ssl) {
-      failf(data,"unable to create an SSL structure");
-      return 0;
-    }
-
-    x509=SSL_get_certificate(ssl);
-
-    /* This version was provided by Evan Jordan and is supposed to not
-       leak memory as the previous version: */
-    if(x509 != NULL) {
-      EVP_PKEY *pktmp = X509_get_pubkey(x509);
-      EVP_PKEY_copy_parameters(pktmp,SSL_get_privatekey(ssl));
-      EVP_PKEY_free(pktmp);
-    }
-
-    SSL_free(ssl);
-
-    /* If we are using DSA, we can copy the parameters from
-     * the private key */
-
-
-    /* Now we know that a key and cert have been set against
-     * the SSL context */
-    if(!SSL_CTX_check_private_key(ctx)) {
-      failf(data, "Private key does not match the certificate public key");
-      return 0;
-    }
-#ifndef HAVE_USERDATA_IN_PWD_CALLBACK
-    /* erase it now */
-    memset(global_passwd, 0, sizeof(global_passwd));
-#endif
-  }
-  return 1;
-}
-
-/* returns non-zero on failure */
-static int x509_name_oneline(X509_NAME *a, char *buf, size_t size)
-{
-#if 0
-  return X509_NAME_oneline(a, buf, size);
-#else
-  BIO *bio_out = BIO_new(BIO_s_mem());
-  BUF_MEM *biomem;
-  int rc;
-
-  if(!bio_out)
-    return 1; /* alloc failed! */
-
-  rc = X509_NAME_print_ex(bio_out, a, 0, XN_FLAG_SEP_SPLUS_SPC);
-  BIO_get_mem_ptr(bio_out, &biomem);
-
-  if((size_t)biomem->length < size)
-    size = biomem->length;
-  else
-    size--; /* don't overwrite the buffer end */
-
-  memcpy(buf, biomem->data, size);
-  buf[size]=0;
-
-  BIO_free(bio_out);
-
-  return !rc;
-#endif
-}
-
-static
-int cert_verify_callback(int ok, X509_STORE_CTX *ctx)
-{
-  X509 *err_cert;
-  char buf[256];
-
-  err_cert=X509_STORE_CTX_get_current_cert(ctx);
-  (void)x509_name_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
-  return ok;
-}
-
-/* Return error string for last OpenSSL error
- */
-static char *SSL_strerror(unsigned long error, char *buf, size_t size)
-{
-#ifdef HAVE_ERR_ERROR_STRING_N
-  /* OpenSSL 0.9.6 and later has a function named
-     ERRO_error_string_n() that takes the size of the buffer as a
-     third argument */
-  ERR_error_string_n(error, buf, size);
-#else
-  (void) size;
-  ERR_error_string(error, buf);
-#endif
-  return buf;
-}
-
-#endif /* USE_SSLEAY */
-
-#ifdef USE_SSLEAY
-/**
- * Global SSL init
- *
- * @retval 0 error initializing SSL
- * @retval 1 SSL initialized successfully
- */
-int Curl_ossl_init(void)
-{
-#ifdef HAVE_ENGINE_LOAD_BUILTIN_ENGINES
-  ENGINE_load_builtin_engines();
-#endif
-
-  /* Lets get nice error messages */
-  SSL_load_error_strings();
-
-  /* Init the global ciphers and digests */
-  if(!SSLeay_add_ssl_algorithms())
-    return 0;
-
-  OpenSSL_add_all_algorithms();
-
-  return 1;
-}
-
-#endif /* USE_SSLEAY */
-
-#ifdef USE_SSLEAY
-
-/* Global cleanup */
-void Curl_ossl_cleanup(void)
-{
-  /* Free ciphers and digests lists */
-  EVP_cleanup();
-
-#ifdef HAVE_ENGINE_CLEANUP
-  /* Free engine list */
-  ENGINE_cleanup();
-#endif
-
-#ifdef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA
-  /* Free OpenSSL ex_data table */
-  CRYPTO_cleanup_all_ex_data();
-#endif
-
-  /* Free OpenSSL error strings */
-  ERR_free_strings();
-
-  /* Free thread local error state, destroying hash upon zero refcount */
-#ifdef HAVE_ERR_REMOVE_THREAD_STATE
-  ERR_remove_thread_state(NULL);
-#else
-  ERR_remove_state(0);
-#endif
-}
-
-/*
- * This function uses SSL_peek to determine connection status.
- *
- * Return codes:
- *     1 means the connection is still in place
- *     0 means the connection has been closed
- *    -1 means the connection status is unknown
- */
-int Curl_ossl_check_cxn(struct connectdata *conn)
-{
-  int rc;
-  char buf;
-
-  rc = SSL_peek(conn->ssl[FIRSTSOCKET].handle, (void*)&buf, 1);
-  if(rc > 0)
-    return 1; /* connection still in place */
-
-  if(rc == 0)
-    return 0; /* connection has been closed */
-
-  return -1; /* connection status unknown */
-}
-
-/* Selects an OpenSSL crypto engine
- */
-CURLcode Curl_ossl_set_engine(struct SessionHandle *data, const char *engine)
-{
-#if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H)
-  ENGINE *e;
-
-#if OPENSSL_VERSION_NUMBER >= 0x00909000L
-  e = ENGINE_by_id(engine);
-#else
-  /* avoid memory leak */
-  for(e = ENGINE_get_first(); e; e = ENGINE_get_next(e)) {
-    const char *e_id = ENGINE_get_id(e);
-    if(!strcmp(engine, e_id))
-      break;
-  }
-#endif
-
-  if(!e) {
-    failf(data, "SSL Engine '%s' not found", engine);
-    return CURLE_SSL_ENGINE_NOTFOUND;
-  }
-
-  if(data->state.engine) {
-    ENGINE_finish(data->state.engine);
-    ENGINE_free(data->state.engine);
-    data->state.engine = NULL;
-  }
-  if(!ENGINE_init(e)) {
-    char buf[256];
-
-    ENGINE_free(e);
-    failf(data, "Failed to initialise SSL Engine '%s':\n%s",
-          engine, SSL_strerror(ERR_get_error(), buf, sizeof(buf)));
-    return CURLE_SSL_ENGINE_INITFAILED;
-  }
-  data->state.engine = e;
-  return CURLE_OK;
-#else
-  (void)engine;
-  failf(data, "SSL Engine not supported");
-  return CURLE_SSL_ENGINE_NOTFOUND;
-#endif
-}
-
-/* Sets engine as default for all SSL operations
- */
-CURLcode Curl_ossl_set_engine_default(struct SessionHandle *data)
-{
-#ifdef HAVE_OPENSSL_ENGINE_H
-  if(data->state.engine) {
-    if(ENGINE_set_default(data->state.engine, ENGINE_METHOD_ALL) > 0) {
-      infof(data,"set default crypto engine '%s'\n",
-            ENGINE_get_id(data->state.engine));
-    }
-    else {
-      failf(data, "set default crypto engine '%s' failed",
-            ENGINE_get_id(data->state.engine));
-      return CURLE_SSL_ENGINE_SETFAILED;
-    }
-  }
-#else
-  (void) data;
-#endif
-  return CURLE_OK;
-}
-
-/* Return list of OpenSSL crypto engine names.
- */
-struct curl_slist *Curl_ossl_engines_list(struct SessionHandle *data)
-{
-  struct curl_slist *list = NULL;
-#if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H)
-  struct curl_slist *beg;
-  ENGINE *e;
-
-  for(e = ENGINE_get_first(); e; e = ENGINE_get_next(e)) {
-    beg = curl_slist_append(list, ENGINE_get_id(e));
-    if(!beg) {
-      curl_slist_free_all(list);
-      return NULL;
-    }
-    list = beg;
-  }
-#endif
-  (void) data;
-  return list;
-}
-
-
-/*
- * This function is called when an SSL connection is closed.
- */
-void Curl_ossl_close(struct connectdata *conn, int sockindex)
-{
-  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-
-  if(connssl->handle) {
-    (void)SSL_shutdown(connssl->handle);
-    SSL_set_connect_state(connssl->handle);
-
-    SSL_free (connssl->handle);
-    connssl->handle = NULL;
-  }
-  if(connssl->ctx) {
-    SSL_CTX_free (connssl->ctx);
-    connssl->ctx = NULL;
-  }
-}
-
-/*
- * This function is called to shut down the SSL layer but keep the
- * socket open (CCC - Clear Command Channel)
- */
-int Curl_ossl_shutdown(struct connectdata *conn, int sockindex)
-{
-  int retval = 0;
-  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-  struct SessionHandle *data = conn->data;
-  char buf[120]; /* We will use this for the OpenSSL error buffer, so it has
-                    to be at least 120 bytes long. */
-  unsigned long sslerror;
-  ssize_t nread;
-  int buffsize;
-  int err;
-  int done = 0;
-
-  /* This has only been tested on the proftpd server, and the mod_tls code
-     sends a close notify alert without waiting for a close notify alert in
-     response. Thus we wait for a close notify alert from the server, but
-     we do not send one. Let's hope other servers do the same... */
-
-  if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE)
-      (void)SSL_shutdown(connssl->handle);
-
-  if(connssl->handle) {
-    buffsize = (int)sizeof(buf);
-    while(!done) {
-      int what = Curl_socket_ready(conn->sock[sockindex],
-                                   CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT);
-      if(what > 0) {
-        ERR_clear_error();
-
-        /* Something to read, let's do it and hope that it is the close
-           notify alert from the server */
-        nread = (ssize_t)SSL_read(conn->ssl[sockindex].handle, buf,
-                                  buffsize);
-        err = SSL_get_error(conn->ssl[sockindex].handle, (int)nread);
-
-        switch(err) {
-        case SSL_ERROR_NONE: /* this is not an error */
-        case SSL_ERROR_ZERO_RETURN: /* no more data */
-          /* This is the expected response. There was no data but only
-             the close notify alert */
-          done = 1;
-          break;
-        case SSL_ERROR_WANT_READ:
-          /* there's data pending, re-invoke SSL_read() */
-          infof(data, "SSL_ERROR_WANT_READ\n");
-          break;
-        case SSL_ERROR_WANT_WRITE:
-          /* SSL wants a write. Really odd. Let's bail out. */
-          infof(data, "SSL_ERROR_WANT_WRITE\n");
-          done = 1;
-          break;
-        default:
-          /* openssl/ssl.h says "look at error stack/return value/errno" */
-          sslerror = ERR_get_error();
-          failf(conn->data, "SSL read: %s, errno %d",
-                ERR_error_string(sslerror, buf),
-                SOCKERRNO);
-          done = 1;
-          break;
-        }
-      }
-      else if(0 == what) {
-        /* timeout */
-        failf(data, "SSL shutdown timeout");
-        done = 1;
-      }
-      else {
-        /* anything that gets here is fatally bad */
-        failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
-        retval = -1;
-        done = 1;
-      }
-    } /* while()-loop for the select() */
-
-    if(data->set.verbose) {
-#ifdef HAVE_SSL_GET_SHUTDOWN
-      switch(SSL_get_shutdown(connssl->handle)) {
-      case SSL_SENT_SHUTDOWN:
-        infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN\n");
-        break;
-      case SSL_RECEIVED_SHUTDOWN:
-        infof(data, "SSL_get_shutdown() returned SSL_RECEIVED_SHUTDOWN\n");
-        break;
-      case SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN:
-        infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN|"
-              "SSL_RECEIVED__SHUTDOWN\n");
-        break;
-      }
-#endif
-    }
-
-    SSL_free (connssl->handle);
-    connssl->handle = NULL;
-  }
-  return retval;
-}
-
-void Curl_ossl_session_free(void *ptr)
-{
-  /* free the ID */
-  SSL_SESSION_free(ptr);
-}
-
-/*
- * This function is called when the 'data' struct is going away. Close
- * down everything and free all resources!
- */
-int Curl_ossl_close_all(struct SessionHandle *data)
-{
-#ifdef HAVE_OPENSSL_ENGINE_H
-  if(data->state.engine) {
-    ENGINE_finish(data->state.engine);
-    ENGINE_free(data->state.engine);
-    data->state.engine = NULL;
-  }
-#else
-  (void)data;
-#endif
-  return 0;
-}
-
-static int asn1_output(const ASN1_UTCTIME *tm,
-                       char *buf,
-                       size_t sizeofbuf)
-{
-  const char *asn1_string;
-  int gmt=FALSE;
-  int i;
-  int year=0,month=0,day=0,hour=0,minute=0,second=0;
-
-  i=tm->length;
-  asn1_string=(const char *)tm->data;
-
-  if(i < 10)
-    return 1;
-  if(asn1_string[i-1] == 'Z')
-    gmt=TRUE;
-  for(i=0; i<10; i++)
-    if((asn1_string[i] > '9') || (asn1_string[i] < '0'))
-      return 2;
-
-  year= (asn1_string[0]-'0')*10+(asn1_string[1]-'0');
-  if(year < 50)
-    year+=100;
-
-  month= (asn1_string[2]-'0')*10+(asn1_string[3]-'0');
-  if((month > 12) || (month < 1))
-    return 3;
-
-  day= (asn1_string[4]-'0')*10+(asn1_string[5]-'0');
-  hour= (asn1_string[6]-'0')*10+(asn1_string[7]-'0');
-  minute=  (asn1_string[8]-'0')*10+(asn1_string[9]-'0');
-
-  if((asn1_string[10] >= '0') && (asn1_string[10] <= '9') &&
-     (asn1_string[11] >= '0') && (asn1_string[11] <= '9'))
-    second= (asn1_string[10]-'0')*10+(asn1_string[11]-'0');
-
-  snprintf(buf, sizeofbuf,
-           "%04d-%02d-%02d %02d:%02d:%02d %s",
-           year+1900, month, day, hour, minute, second, (gmt?"GMT":""));
-
-  return 0;
-}
-
-/* ====================================================== */
-
-
-/* Quote from RFC2818 section 3.1 "Server Identity"
-
-   If a subjectAltName extension of type dNSName is present, that MUST
-   be used as the identity. Otherwise, the (most specific) Common Name
-   field in the Subject field of the certificate MUST be used. Although
-   the use of the Common Name is existing practice, it is deprecated and
-   Certification Authorities are encouraged to use the dNSName instead.
-
-   Matching is performed using the matching rules specified by
-   [RFC2459].  If more than one identity of a given type is present in
-   the certificate (e.g., more than one dNSName name, a match in any one
-   of the set is considered acceptable.) Names may contain the wildcard
-   character * which is considered to match any single domain name
-   component or component fragment. E.g., *.a.com matches foo.a.com but
-   not bar.foo.a.com. f*.com matches foo.com but not bar.com.
-
-   In some cases, the URI is specified as an IP address rather than a
-   hostname. In this case, the iPAddress subjectAltName must be present
-   in the certificate and must exactly match the IP in the URI.
-
-*/
-static CURLcode verifyhost(struct connectdata *conn,
-                           X509 *server_cert)
-{
-  int matched = -1; /* -1 is no alternative match yet, 1 means match and 0
-                       means mismatch */
-  int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */
-  size_t addrlen = 0;
-  struct SessionHandle *data = conn->data;
-  STACK_OF(GENERAL_NAME) *altnames;
-#ifdef ENABLE_IPV6
-  struct in6_addr addr;
-#else
-  struct in_addr addr;
-#endif
-  CURLcode res = CURLE_OK;
-
-#ifdef ENABLE_IPV6
-  if(conn->bits.ipv6_ip &&
-     Curl_inet_pton(AF_INET6, conn->host.name, &addr)) {
-    target = GEN_IPADD;
-    addrlen = sizeof(struct in6_addr);
-  }
-  else
-#endif
-    if(Curl_inet_pton(AF_INET, conn->host.name, &addr)) {
-      target = GEN_IPADD;
-      addrlen = sizeof(struct in_addr);
-    }
-
-  /* get a "list" of alternative names */
-  altnames = X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL);
-
-  if(altnames) {
-    int numalts;
-    int i;
-
-    /* get amount of alternatives, RFC2459 claims there MUST be at least
-       one, but we don't depend on it... */
-    numalts = sk_GENERAL_NAME_num(altnames);
-
-    /* loop through all alternatives while none has matched */
-    for(i=0; (i<numalts) && (matched != 1); i++) {
-      /* get a handle to alternative name number i */
-      const GENERAL_NAME *check = sk_GENERAL_NAME_value(altnames, i);
-
-      /* only check alternatives of the same type the target is */
-      if(check->type == target) {
-        /* get data and length */
-        const char *altptr = (char *)ASN1_STRING_data(check->d.ia5);
-        size_t altlen = (size_t) ASN1_STRING_length(check->d.ia5);
-
-        switch(target) {
-        case GEN_DNS: /* name/pattern comparison */
-          /* The OpenSSL man page explicitly says: "In general it cannot be
-             assumed that the data returned by ASN1_STRING_data() is null
-             terminated or does not contain embedded nulls." But also that
-             "The actual format of the data will depend on the actual string
-             type itself: for example for and IA5String the data will be ASCII"
-
-             Gisle researched the OpenSSL sources:
-             "I checked the 0.9.6 and 0.9.8 sources before my patch and
-             it always 0-terminates an IA5String."
-          */
-          if((altlen == strlen(altptr)) &&
-             /* if this isn't true, there was an embedded zero in the name
-                string and we cannot match it. */
-             Curl_cert_hostcheck(altptr, conn->host.name))
-            matched = 1;
-          else
-            matched = 0;
-          break;
-
-        case GEN_IPADD: /* IP address comparison */
-          /* compare alternative IP address if the data chunk is the same size
-             our server IP address is */
-          if((altlen == addrlen) && !memcmp(altptr, &addr, altlen))
-            matched = 1;
-          else
-            matched = 0;
-          break;
-        }
-      }
-    }
-    GENERAL_NAMES_free(altnames);
-  }
-
-  if(matched == 1)
-    /* an alternative name matched the server hostname */
-    infof(data, "\t subjectAltName: %s matched\n", conn->host.dispname);
-  else if(matched == 0) {
-    /* an alternative name field existed, but didn't match and then
-       we MUST fail */
-    infof(data, "\t subjectAltName does not match %s\n", conn->host.dispname);
-    res = CURLE_PEER_FAILED_VERIFICATION;
-  }
-  else {
-    /* we have to look to the last occurrence of a commonName in the
-       distinguished one to get the most significant one. */
-    int j,i=-1 ;
-
-/* The following is done because of a bug in 0.9.6b */
-
-    unsigned char *nulstr = (unsigned char *)"";
-    unsigned char *peer_CN = nulstr;
-
-    X509_NAME *name = X509_get_subject_name(server_cert) ;
-    if(name)
-      while((j = X509_NAME_get_index_by_NID(name, NID_commonName, i))>=0)
-        i=j;
-
-    /* we have the name entry and we will now convert this to a string
-       that we can use for comparison. Doing this we support BMPstring,
-       UTF8 etc. */
-
-    if(i>=0) {
-      ASN1_STRING *tmp = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name,i));
-
-      /* In OpenSSL 0.9.7d and earlier, ASN1_STRING_to_UTF8 fails if the input
-         is already UTF-8 encoded. We check for this case and copy the raw
-         string manually to avoid the problem. This code can be made
-         conditional in the future when OpenSSL has been fixed. Work-around
-         brought by Alexis S. L. Carvalho. */
-      if(tmp) {
-        if(ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) {
-          j = ASN1_STRING_length(tmp);
-          if(j >= 0) {
-            peer_CN = OPENSSL_malloc(j+1);
-            if(peer_CN) {
-              memcpy(peer_CN, ASN1_STRING_data(tmp), j);
-              peer_CN[j] = '\0';
-            }
-          }
-        }
-        else /* not a UTF8 name */
-          j = ASN1_STRING_to_UTF8(&peer_CN, tmp);
-
-        if(peer_CN && (curlx_uztosi(strlen((char *)peer_CN)) != j)) {
-          /* there was a terminating zero before the end of string, this
-             cannot match and we return failure! */
-          failf(data, "SSL: illegal cert name field");
-          res = CURLE_PEER_FAILED_VERIFICATION;
-        }
-      }
-    }
-
-    if(peer_CN == nulstr)
-       peer_CN = NULL;
-    else {
-      /* convert peer_CN from UTF8 */
-      CURLcode rc = Curl_convert_from_utf8(data, peer_CN, strlen(peer_CN));
-      /* Curl_convert_from_utf8 calls failf if unsuccessful */
-      if(rc) {
-        OPENSSL_free(peer_CN);
-        return rc;
-      }
-    }
-
-    if(res)
-      /* error already detected, pass through */
-      ;
-    else if(!peer_CN) {
-      failf(data,
-            "SSL: unable to obtain common name from peer certificate");
-      res = CURLE_PEER_FAILED_VERIFICATION;
-    }
-    else if(!Curl_cert_hostcheck((const char *)peer_CN, conn->host.name)) {
-      failf(data, "SSL: certificate subject name '%s' does not match "
-            "target host name '%s'", peer_CN, conn->host.dispname);
-      res = CURLE_PEER_FAILED_VERIFICATION;
-    }
-    else {
-      infof(data, "\t common name: %s (matched)\n", peer_CN);
-    }
-    if(peer_CN)
-      OPENSSL_free(peer_CN);
-  }
-  return res;
-}
-#endif /* USE_SSLEAY */
-
-/* The SSL_CTRL_SET_MSG_CALLBACK doesn't exist in ancient OpenSSL versions
-   and thus this cannot be done there. */
-#ifdef SSL_CTRL_SET_MSG_CALLBACK
-
-static const char *ssl_msg_type(int ssl_ver, int msg)
-{
-  if(ssl_ver == SSL2_VERSION_MAJOR) {
-    switch (msg) {
-      case SSL2_MT_ERROR:
-        return "Error";
-      case SSL2_MT_CLIENT_HELLO:
-        return "Client hello";
-      case SSL2_MT_CLIENT_MASTER_KEY:
-        return "Client key";
-      case SSL2_MT_CLIENT_FINISHED:
-        return "Client finished";
-      case SSL2_MT_SERVER_HELLO:
-        return "Server hello";
-      case SSL2_MT_SERVER_VERIFY:
-        return "Server verify";
-      case SSL2_MT_SERVER_FINISHED:
-        return "Server finished";
-      case SSL2_MT_REQUEST_CERTIFICATE:
-        return "Request CERT";
-      case SSL2_MT_CLIENT_CERTIFICATE:
-        return "Client CERT";
-    }
-  }
-  else if(ssl_ver == SSL3_VERSION_MAJOR) {
-    switch (msg) {
-      case SSL3_MT_HELLO_REQUEST:
-        return "Hello request";
-      case SSL3_MT_CLIENT_HELLO:
-        return "Client hello";
-      case SSL3_MT_SERVER_HELLO:
-        return "Server hello";
-      case SSL3_MT_CERTIFICATE:
-        return "CERT";
-      case SSL3_MT_SERVER_KEY_EXCHANGE:
-        return "Server key exchange";
-      case SSL3_MT_CLIENT_KEY_EXCHANGE:
-        return "Client key exchange";
-      case SSL3_MT_CERTIFICATE_REQUEST:
-        return "Request CERT";
-      case SSL3_MT_SERVER_DONE:
-        return "Server finished";
-      case SSL3_MT_CERTIFICATE_VERIFY:
-        return "CERT verify";
-      case SSL3_MT_FINISHED:
-        return "Finished";
-    }
-  }
-  return "Unknown";
-}
-
-static const char *tls_rt_type(int type)
-{
-  return (
-    type == SSL3_RT_CHANGE_CIPHER_SPEC ? "TLS change cipher, " :
-    type == SSL3_RT_ALERT              ? "TLS alert, "         :
-    type == SSL3_RT_HANDSHAKE          ? "TLS handshake, "     :
-    type == SSL3_RT_APPLICATION_DATA   ? "TLS app data, "      :
-                                         "TLS Unknown, ");
-}
-
-
-/*
- * Our callback from the SSL/TLS layers.
- */
-static void ssl_tls_trace(int direction, int ssl_ver, int content_type,
-                          const void *buf, size_t len, const SSL *ssl,
-                          struct connectdata *conn)
-{
-  struct SessionHandle *data;
-  const char *msg_name, *tls_rt_name;
-  char ssl_buf[1024];
-  int  ver, msg_type, txt_len;
-
-  if(!conn || !conn->data || !conn->data->set.fdebug ||
-     (direction != 0 && direction != 1))
-    return;
-
-  data = conn->data;
-  ssl_ver >>= 8;
-  ver = (ssl_ver == SSL2_VERSION_MAJOR ? '2' :
-         ssl_ver == SSL3_VERSION_MAJOR ? '3' : '?');
-
-  /* SSLv2 doesn't seem to have TLS record-type headers, so OpenSSL
-   * always pass-up content-type as 0. But the interesting message-type
-   * is at 'buf[0]'.
-   */
-  if(ssl_ver == SSL3_VERSION_MAJOR && content_type != 0)
-    tls_rt_name = tls_rt_type(content_type);
-  else
-    tls_rt_name = "";
-
-  msg_type = *(char*)buf;
-  msg_name = ssl_msg_type(ssl_ver, msg_type);
-
-  txt_len = snprintf(ssl_buf, sizeof(ssl_buf), "SSLv%c, %s%s (%d):\n",
-                     ver, tls_rt_name, msg_name, msg_type);
-  Curl_debug(data, CURLINFO_TEXT, ssl_buf, (size_t)txt_len, NULL);
-
-  Curl_debug(data, (direction == 1) ? CURLINFO_SSL_DATA_OUT :
-             CURLINFO_SSL_DATA_IN, (char *)buf, len, NULL);
-  (void) ssl;
-}
-#endif
-
-#ifdef USE_SSLEAY
-/* ====================================================== */
-
-#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
-#  define use_sni(x)  sni = (x)
-#else
-#  define use_sni(x)  Curl_nop_stmt
-#endif
-
-static CURLcode
-ossl_connect_step1(struct connectdata *conn,
-                   int sockindex)
-{
-  CURLcode retcode = CURLE_OK;
-
-  struct SessionHandle *data = conn->data;
-  SSL_METHOD_QUAL SSL_METHOD *req_method=NULL;
-  void *ssl_sessionid=NULL;
-  X509_LOOKUP *lookup=NULL;
-  curl_socket_t sockfd = conn->sock[sockindex];
-  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-  long ctx_options;
-#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
-  bool sni;
-#ifdef ENABLE_IPV6
-  struct in6_addr addr;
-#else
-  struct in_addr addr;
-#endif
-#endif
-
-  DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
-
-  /* Make funny stuff to get random input */
-  Curl_ossl_seed(data);
-
-  /* check to see if we've been told to use an explicit SSL/TLS version */
-
-  switch(data->set.ssl.version) {
-  default:
-  case CURL_SSLVERSION_DEFAULT:
-#ifdef USE_TLS_SRP
-    if(data->set.ssl.authtype == CURL_TLSAUTH_SRP) {
-      infof(data, "Set version TLSv1 for SRP authorisation\n");
-      req_method = TLSv1_client_method() ;
-    }
-    else
-#endif
-    /* we try to figure out version */
-    req_method = SSLv23_client_method();
-    use_sni(TRUE);
-    break;
-  case CURL_SSLVERSION_TLSv1:
-    req_method = TLSv1_client_method();
-    use_sni(TRUE);
-    break;
-  case CURL_SSLVERSION_SSLv2:
-#ifdef OPENSSL_NO_SSL2
-    failf(data, "OpenSSL was built without SSLv2 support");
-    return CURLE_NOT_BUILT_IN;
-#else
-#ifdef USE_TLS_SRP
-    if(data->set.ssl.authtype == CURL_TLSAUTH_SRP)
-      return CURLE_SSL_CONNECT_ERROR;
-#endif
-    req_method = SSLv2_client_method();
-    use_sni(FALSE);
-    break;
-#endif
-  case CURL_SSLVERSION_SSLv3:
-#ifdef USE_TLS_SRP
-    if(data->set.ssl.authtype == CURL_TLSAUTH_SRP)
-      return CURLE_SSL_CONNECT_ERROR;
-#endif
-    req_method = SSLv3_client_method();
-    use_sni(FALSE);
-    break;
-  }
-
-  if(connssl->ctx)
-    SSL_CTX_free(connssl->ctx);
-  connssl->ctx = SSL_CTX_new(req_method);
-
-  if(!connssl->ctx) {
-    failf(data, "SSL: couldn't create a context: %s",
-          ERR_error_string(ERR_peek_error(), NULL));
-    return CURLE_OUT_OF_MEMORY;
-  }
-
-#ifdef SSL_MODE_RELEASE_BUFFERS
-  SSL_CTX_set_mode(connssl->ctx, SSL_MODE_RELEASE_BUFFERS);
-#endif
-
-#ifdef SSL_CTRL_SET_MSG_CALLBACK
-  if(data->set.fdebug && data->set.verbose) {
-    /* the SSL trace callback is only used for verbose logging so we only
-       inform about failures of setting it */
-    if(!SSL_CTX_callback_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK,
-                               (void (*)(void))ssl_tls_trace)) {
-      infof(data, "SSL: couldn't set callback!\n");
-    }
-    else if(!SSL_CTX_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK_ARG, 0,
-                          conn)) {
-      infof(data, "SSL: couldn't set callback argument!\n");
-    }
-  }
-#endif
-
-  /* OpenSSL contains code to work-around lots of bugs and flaws in various
-     SSL-implementations. SSL_CTX_set_options() is used to enabled those
-     work-arounds. The man page for this option states that SSL_OP_ALL enables
-     all the work-arounds and that "It is usually safe to use SSL_OP_ALL to
-     enable the bug workaround options if compatibility with somewhat broken
-     implementations is desired."
-
-     The "-no_ticket" option was introduced in Openssl0.9.8j. It's a flag to
-     disable "rfc4507bis session ticket support".  rfc4507bis was later turned
-     into the proper RFC5077 it seems: http://tools.ietf.org/html/rfc5077
-
-     The enabled extension concerns the session management. I wonder how often
-     libcurl stops a connection and then resumes a TLS session. also, sending
-     the session data is some overhead. .I suggest that you just use your
-     proposed patch (which explicitly disables TICKET).
-
-     If someone writes an application with libcurl and openssl who wants to
-     enable the feature, one can do this in the SSL callback.
-
-     SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG option enabling allowed proper
-     interoperability with web server Netscape Enterprise Server 2.0.1 which
-     was released back in 1996.
-
-     Due to CVE-2010-4180, option SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG has
-     become ineffective as of OpenSSL 0.9.8q and 1.0.0c. In order to mitigate
-     CVE-2010-4180 when using previous OpenSSL versions we no longer enable
-     this option regardless of OpenSSL version and SSL_OP_ALL definition.
-
-     OpenSSL added a work-around for a SSL 3.0/TLS 1.0 CBC vulnerability
-     (http://www.openssl.org/~bodo/tls-cbc.txt). In 0.9.6e they added a bit to
-     SSL_OP_ALL that _disables_ that work-around despite the fact that
-     SSL_OP_ALL is documented to do "rather harmless" workarounds. In order to
-     keep the secure work-around, the SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS bit
-     must not be set.
-  */
-
-  ctx_options = SSL_OP_ALL;
-
-#ifdef SSL_OP_NO_TICKET
-  ctx_options |= SSL_OP_NO_TICKET;
-#endif
-
-#ifdef SSL_OP_NO_COMPRESSION
-  ctx_options |= SSL_OP_NO_COMPRESSION;
-#endif
-
-#ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
-  /* mitigate CVE-2010-4180 */
-  ctx_options &= ~SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG;
-#endif
-
-#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
-  /* unless the user explicitly ask to allow the protocol vulnerability we
-     use the work-around */
-  if(!conn->data->set.ssl_enable_beast)
-    ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
-#endif
-
-  /* disable SSLv2 in the default case (i.e. allow SSLv3 and TLSv1) */
-  if(data->set.ssl.version == CURL_SSLVERSION_DEFAULT)
-    ctx_options |= SSL_OP_NO_SSLv2;
-
-  SSL_CTX_set_options(connssl->ctx, ctx_options);
-
-#if 0
-  /*
-   * Not sure it's needed to tell SSL_connect() that socket is
-   * non-blocking. It doesn't seem to care, but just return with
-   * SSL_ERROR_WANT_x.
-   */
-  if(data->state.used_interface == Curl_if_multi)
-    SSL_CTX_ctrl(connssl->ctx, BIO_C_SET_NBIO, 1, NULL);
-#endif
-
-  if(data->set.str[STRING_CERT] || data->set.str[STRING_CERT_TYPE]) {
-    if(!cert_stuff(conn,
-                   connssl->ctx,
-                   data->set.str[STRING_CERT],
-                   data->set.str[STRING_CERT_TYPE],
-                   data->set.str[STRING_KEY],
-                   data->set.str[STRING_KEY_TYPE])) {
-      /* failf() is already done in cert_stuff() */
-      return CURLE_SSL_CERTPROBLEM;
-    }
-  }
-
-  if(data->set.str[STRING_SSL_CIPHER_LIST]) {
-    if(!SSL_CTX_set_cipher_list(connssl->ctx,
-                                data->set.str[STRING_SSL_CIPHER_LIST])) {
-      failf(data, "failed setting cipher list");
-      return CURLE_SSL_CIPHER;
-    }
-  }
-
-#ifdef USE_TLS_SRP
-  if(data->set.ssl.authtype == CURL_TLSAUTH_SRP) {
-    infof(data, "Using TLS-SRP username: %s\n", data->set.ssl.username);
-
-    if(!SSL_CTX_set_srp_username(connssl->ctx, data->set.ssl.username)) {
-      failf(data, "Unable to set SRP user name");
-      return CURLE_BAD_FUNCTION_ARGUMENT;
-    }
-    if(!SSL_CTX_set_srp_password(connssl->ctx,data->set.ssl.password)) {
-      failf(data, "failed setting SRP password");
-      return CURLE_BAD_FUNCTION_ARGUMENT;
-    }
-    if(!data->set.str[STRING_SSL_CIPHER_LIST]) {
-      infof(data, "Setting cipher list SRP\n");
-
-      if(!SSL_CTX_set_cipher_list(connssl->ctx, "SRP")) {
-        failf(data, "failed setting SRP cipher list");
-        return CURLE_SSL_CIPHER;
-      }
-    }
-  }
-#endif
-  if(data->set.str[STRING_SSL_CAFILE] || data->set.str[STRING_SSL_CAPATH]) {
-    /* tell SSL where to find CA certificates that are used to verify
-       the servers certificate. */
-    if(!SSL_CTX_load_verify_locations(connssl->ctx,
-                                       data->set.str[STRING_SSL_CAFILE],
-                                       data->set.str[STRING_SSL_CAPATH])) {
-      if(data->set.ssl.verifypeer) {
-        /* Fail if we insist on successfully verifying the server. */
-        failf(data,"error setting certificate verify locations:\n"
-              "  CAfile: %s\n  CApath: %s",
-              data->set.str[STRING_SSL_CAFILE]?
-              data->set.str[STRING_SSL_CAFILE]: "none",
-              data->set.str[STRING_SSL_CAPATH]?
-              data->set.str[STRING_SSL_CAPATH] : "none");
-        return CURLE_SSL_CACERT_BADFILE;
-      }
-      else {
-        /* Just continue with a warning if no strict  certificate verification
-           is required. */
-        infof(data, "error setting certificate verify locations,"
-              " continuing anyway:\n");
-      }
-    }
-    else {
-      /* Everything is fine. */
-      infof(data, "successfully set certificate verify locations:\n");
-    }
-    infof(data,
-          "  CAfile: %s\n"
-          "  CApath: %s\n",
-          data->set.str[STRING_SSL_CAFILE] ? data->set.str[STRING_SSL_CAFILE]:
-          "none",
-          data->set.str[STRING_SSL_CAPATH] ? data->set.str[STRING_SSL_CAPATH]:
-          "none");
-  }
-
-  if(data->set.str[STRING_SSL_CRLFILE]) {
-    /* tell SSL where to find CRL file that is used to check certificate
-     * revocation */
-    lookup=X509_STORE_add_lookup(SSL_CTX_get_cert_store(connssl->ctx),
-                                 X509_LOOKUP_file());
-    if(!lookup ||
-       (!X509_load_crl_file(lookup,data->set.str[STRING_SSL_CRLFILE],
-                            X509_FILETYPE_PEM)) ) {
-      failf(data,"error loading CRL file: %s",
-            data->set.str[STRING_SSL_CRLFILE]);
-      return CURLE_SSL_CRL_BADFILE;
-    }
-    else {
-      /* Everything is fine. */
-      infof(data, "successfully load CRL file:\n");
-      X509_STORE_set_flags(SSL_CTX_get_cert_store(connssl->ctx),
-                           X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
-    }
-    infof(data,
-          "  CRLfile: %s\n", data->set.str[STRING_SSL_CRLFILE] ?
-          data->set.str[STRING_SSL_CRLFILE]: "none");
-  }
-
-  /* SSL always tries to verify the peer, this only says whether it should
-   * fail to connect if the verification fails, or if it should continue
-   * anyway. In the latter case the result of the verification is checked with
-   * SSL_get_verify_result() below. */
-  SSL_CTX_set_verify(connssl->ctx,
-                     data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE,
-                     cert_verify_callback);
-
-  /* give application a chance to interfere with SSL set up. */
-  if(data->set.ssl.fsslctx) {
-    retcode = (*data->set.ssl.fsslctx)(data, connssl->ctx,
-                                       data->set.ssl.fsslctxp);
-    if(retcode) {
-      failf(data,"error signaled by ssl ctx callback");
-      return retcode;
-    }
-  }
-
-  /* Lets make an SSL structure */
-  if(connssl->handle)
-    SSL_free(connssl->handle);
-  connssl->handle = SSL_new(connssl->ctx);
-  if(!connssl->handle) {
-    failf(data, "SSL: couldn't create a context (handle)!");
-    return CURLE_OUT_OF_MEMORY;
-  }
-  SSL_set_connect_state(connssl->handle);
-
-  connssl->server_cert = 0x0;
-
-#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
-  if((0 == Curl_inet_pton(AF_INET, conn->host.name, &addr)) &&
-#ifdef ENABLE_IPV6
-     (0 == Curl_inet_pton(AF_INET6, conn->host.name, &addr)) &&
-#endif
-     sni &&
-     !SSL_set_tlsext_host_name(connssl->handle, conn->host.name))
-    infof(data, "WARNING: failed to configure server name indication (SNI) "
-          "TLS extension\n");
-#endif
-
-  /* Check if there's a cached ID we can/should use here! */
-  if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) {
-    /* we got a session id, use it! */
-    if(!SSL_set_session(connssl->handle, ssl_sessionid)) {
-      failf(data, "SSL: SSL_set_session failed: %s",
-            ERR_error_string(ERR_get_error(),NULL));
-      return CURLE_SSL_CONNECT_ERROR;
-    }
-    /* Informational message */
-    infof (data, "SSL re-using session ID\n");
-  }
-
-  /* pass the raw socket into the SSL layers */
-  if(!SSL_set_fd(connssl->handle, (int)sockfd)) {
-    failf(data, "SSL: SSL_set_fd failed: %s",
-          ERR_error_string(ERR_get_error(),NULL));
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-
-  connssl->connecting_state = ssl_connect_2;
-  return CURLE_OK;
-}
-
-static CURLcode
-ossl_connect_step2(struct connectdata *conn, int sockindex)
-{
-  struct SessionHandle *data = conn->data;
-  int err;
-  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-
-  DEBUGASSERT(ssl_connect_2 == connssl->connecting_state
-             || ssl_connect_2_reading == connssl->connecting_state
-             || ssl_connect_2_writing == connssl->connecting_state);
-
-  ERR_clear_error();
-
-  err = SSL_connect(connssl->handle);
-
-  /* 1  is fine
-     0  is "not successful but was shut down controlled"
-     <0 is "handshake was not successful, because a fatal error occurred" */
-  if(1 != err) {
-    int detail = SSL_get_error(connssl->handle, err);
-
-    if(SSL_ERROR_WANT_READ == detail) {
-      connssl->connecting_state = ssl_connect_2_reading;
-      return CURLE_OK;
-    }
-    else if(SSL_ERROR_WANT_WRITE == detail) {
-      connssl->connecting_state = ssl_connect_2_writing;
-      return CURLE_OK;
-    }
-    else {
-      /* untreated error */
-      unsigned long errdetail;
-      char error_buffer[256]; /* OpenSSL documents that this must be at least
-                                 256 bytes long. */
-      CURLcode rc;
-      const char *cert_problem = NULL;
-      long lerr;
-
-      connssl->connecting_state = ssl_connect_2; /* the connection failed,
-                                                    we're not waiting for
-                                                    anything else. */
-
-      errdetail = ERR_get_error(); /* Gets the earliest error code from the
-                                      thread's error queue and removes the
-                                      entry. */
-
-      switch(errdetail) {
-      case 0x1407E086:
-        /* 1407E086:
-           SSL routines:
-           SSL2_SET_CERTIFICATE:
-           certificate verify failed */
-        /* fall-through */
-      case 0x14090086:
-        /* 14090086:
-           SSL routines:
-           SSL3_GET_SERVER_CERTIFICATE:
-           certificate verify failed */
-        rc = CURLE_SSL_CACERT;
-
-        lerr = SSL_get_verify_result(connssl->handle);
-        if(lerr != X509_V_OK) {
-          snprintf(error_buffer, sizeof(error_buffer),
-                   "SSL certificate problem: %s",
-                   X509_verify_cert_error_string(lerr));
-        }
-        else
-          cert_problem = "SSL certificate problem, verify that the CA cert is"
-            " OK.";
-
-        break;
-      default:
-        rc = CURLE_SSL_CONNECT_ERROR;
-        SSL_strerror(errdetail, error_buffer, sizeof(error_buffer));
-        break;
-      }
-
-      /* detail is already set to the SSL error above */
-
-      /* If we e.g. use SSLv2 request-method and the server doesn't like us
-       * (RST connection etc.), OpenSSL gives no explanation whatsoever and
-       * the SO_ERROR is also lost.
-       */
-      if(CURLE_SSL_CONNECT_ERROR == rc && errdetail == 0) {
-        failf(data, "Unknown SSL protocol error in connection to %s:%ld ",
-              conn->host.name, conn->port);
-        return rc;
-      }
-      /* Could be a CERT problem */
-
-      failf(data, "%s%s", cert_problem ? cert_problem : "", error_buffer);
-      return rc;
-    }
-  }
-  else {
-    /* we have been connected fine, we're not waiting for anything else. */
-    connssl->connecting_state = ssl_connect_3;
-
-    /* Informational message */
-    infof (data, "SSL connection using %s\n",
-           SSL_get_cipher(connssl->handle));
-
-    return CURLE_OK;
-  }
-}
-
-static int asn1_object_dump(ASN1_OBJECT *a, char *buf, size_t len)
-{
-  int i, ilen;
-
-  if((ilen = (int)len) < 0)
-    return 1; /* buffer too big */
-
-  i = i2t_ASN1_OBJECT(buf, ilen, a);
-
-  if(i >= ilen)
-    return 1; /* buffer too small */
-
-  return 0;
-}
-
-static CURLcode push_certinfo_len(struct SessionHandle *data,
-                                  int certnum,
-                                  const char *label,
-                                  const char *value,
-                                  size_t valuelen)
-{
-  struct curl_certinfo *ci = &data->info.certs;
-  char *output;
-  struct curl_slist *nl;
-  CURLcode res = CURLE_OK;
-  size_t labellen = strlen(label);
-  size_t outlen = labellen + 1 + valuelen + 1; /* label:value\0 */
-
-  output = malloc(outlen);
-  if(!output)
-    return CURLE_OUT_OF_MEMORY;
-
-  /* sprintf the label and colon */
-  snprintf(output, outlen, "%s:", label);
-
-  /* memcpy the value (it might not be zero terminated) */
-  memcpy(&output[labellen+1], value, valuelen);
-
-  /* zero terminate the output */
-  output[labellen + 1 + valuelen] = 0;
-
-  /* TODO: we should rather introduce an internal API that can do the
-     equivalent of curl_slist_append but doesn't strdup() the given data as
-     like in this place the extra malloc/free is totally pointless */
-  nl = curl_slist_append(ci->certinfo[certnum], output);
-  free(output);
-  if(!nl) {
-    curl_slist_free_all(ci->certinfo[certnum]);
-    ci->certinfo[certnum] = NULL;
-    res = CURLE_OUT_OF_MEMORY;
-  }
-  else
-    ci->certinfo[certnum] = nl;
-
-  return res;
-}
-
-/* this is a convenience function for push_certinfo_len that takes a zero
-   terminated value */
-static CURLcode push_certinfo(struct SessionHandle *data,
-                              int certnum,
-                              const char *label,
-                              const char *value)
-{
-  size_t valuelen = strlen(value);
-
-  return push_certinfo_len(data, certnum, label, value, valuelen);
-}
-
-static void pubkey_show(struct SessionHandle *data,
-                        int num,
-                        const char *type,
-                        const char *name,
-                        unsigned char *raw,
-                        int len)
-{
-  size_t left;
-  int i;
-  char namebuf[32];
-  char *buffer;
-
-  left = len*3 + 1;
-  buffer = malloc(left);
-  if(buffer) {
-    char *ptr=buffer;
-    snprintf(namebuf, sizeof(namebuf), "%s(%s)", type, name);
-    for(i=0; i< len; i++) {
-      snprintf(ptr, left, "%02x:", raw[i]);
-      ptr += 3;
-      left -= 3;
-    }
-    infof(data, "   %s: %s\n", namebuf, buffer);
-    push_certinfo(data, num, namebuf, buffer);
-    free(buffer);
-  }
-}
-
-#define print_pubkey_BN(_type, _name, _num)    \
-do {                              \
-  if(pubkey->pkey._type->_name != NULL) { \
-    int len = BN_num_bytes(pubkey->pkey._type->_name);  \
-    if(len < CERTBUFFERSIZE) {                                    \
-      BN_bn2bin(pubkey->pkey._type->_name, (unsigned char*)bufp); \
-      bufp[len] = 0;                                                    \
-      pubkey_show(data, _num, #_type, #_name, (unsigned char*)bufp, len); \
-    } \
-  } \
-} WHILE_FALSE
-
-static int X509V3_ext(struct SessionHandle *data,
-                      int certnum,
-                      STACK_OF(X509_EXTENSION) *exts)
-{
-  int i;
-  size_t j;
-
-  if(sk_X509_EXTENSION_num(exts) <= 0)
-    /* no extensions, bail out */
-    return 1;
-
-  for(i=0; i<sk_X509_EXTENSION_num(exts); i++) {
-    ASN1_OBJECT *obj;
-    X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i);
-    BUF_MEM *biomem;
-    char buf[512];
-    char *ptr=buf;
-    char namebuf[128];
-    BIO *bio_out = BIO_new(BIO_s_mem());
-
-    if(!bio_out)
-      return 1;
-
-    obj = X509_EXTENSION_get_object(ext);
-
-    asn1_object_dump(obj, namebuf, sizeof(namebuf));
-
-    infof(data, "%s: %s\n", namebuf,
-          X509_EXTENSION_get_critical(ext)?"(critical)":"");
-
-    if(!X509V3_EXT_print(bio_out, ext, 0, 0))
-      M_ASN1_OCTET_STRING_print(bio_out, ext->value);
-
-    BIO_get_mem_ptr(bio_out, &biomem);
-
-    /* biomem->length bytes at biomem->data, this little loop here is only
-       done for the infof() call, we send the "raw" data to the certinfo
-       function */
-    for(j=0; j<(size_t)biomem->length; j++) {
-      const char *sep="";
-      if(biomem->data[j] == '\n') {
-        sep=", ";
-        j++; /* skip the newline */
-      };
-      while((biomem->data[j] == ' ') && (j<(size_t)biomem->length))
-        j++;
-      if(j<(size_t)biomem->length)
-        ptr+=snprintf(ptr, sizeof(buf)-(ptr-buf), "%s%c", sep,
-                      biomem->data[j]);
-    }
-    infof(data, "  %s\n", buf);
-
-    push_certinfo(data, certnum, namebuf, buf);
-
-    BIO_free(bio_out);
-
-  }
-  return 0; /* all is fine */
-}
-
-
-static void X509_signature(struct SessionHandle *data,
-                           int numcert,
-                           ASN1_STRING *sig)
-{
-  char buf[1024];
-  char *ptr = buf;
-  int i;
-  for(i=0; i<sig->length; i++)
-    ptr+=snprintf(ptr, sizeof(buf)-(ptr-buf), "%02x:", sig->data[i]);
-
-  infof(data, " Signature: %s\n", buf);
-  push_certinfo(data, numcert, "Signature", buf);
-}
-
-static void dumpcert(struct SessionHandle *data, X509 *x, int numcert)
-{
-  BIO *bio_out = BIO_new(BIO_s_mem());
-  BUF_MEM *biomem;
-
-  /* this outputs the cert in this 64 column wide style with newlines and
-     -----BEGIN CERTIFICATE----- texts and more */
-  PEM_write_bio_X509(bio_out, x);
-
-  BIO_get_mem_ptr(bio_out, &biomem);
-
-  infof(data, "%s\n", biomem->data);
-
-  push_certinfo_len(data, numcert, "Cert", biomem->data, biomem->length);
-
-  BIO_free(bio_out);
-
-}
-
-
-static int init_certinfo(struct SessionHandle *data,
-                         int num)
-{
-  struct curl_certinfo *ci = &data->info.certs;
-  struct curl_slist **table;
-
-  Curl_ssl_free_certinfo(data);
-
-  ci->num_of_certs = num;
-  table = calloc((size_t)num, sizeof(struct curl_slist *));
-  if(!table)
-    return 1;
-
-  ci->certinfo = table;
-  return 0;
-}
-
-/*
- * This size was previously 512 which has been reported "too small" without
- * any specifics, so it was enlarged to allow more data to get shown uncut.
- * The "perfect" size is yet to figure out.
- */
-#define CERTBUFFERSIZE 8192
-
-static CURLcode get_cert_chain(struct connectdata *conn,
-                               struct ssl_connect_data *connssl)
-
-{
-  STACK_OF(X509) *sk;
-  int i;
-  char *bufp;
-  struct SessionHandle *data = conn->data;
-  int numcerts;
-
-  bufp = malloc(CERTBUFFERSIZE);
-  if(!bufp)
-    return CURLE_OUT_OF_MEMORY;
-
-  sk = SSL_get_peer_cert_chain(connssl->handle);
-  if(!sk) {
-    free(bufp);
-    return CURLE_OUT_OF_MEMORY;
-  }
-
-  numcerts = sk_X509_num(sk);
-  if(init_certinfo(data, numcerts)) {
-    free(bufp);
-    return CURLE_OUT_OF_MEMORY;
-  }
-
-  infof(data, "--- Certificate chain\n");
-  for(i=0; i<numcerts; i++) {
-    long value;
-    ASN1_INTEGER *num;
-    ASN1_TIME *certdate;
-
-    /* get the certs in "importance order" */
-#if 0
-    X509 *x = sk_X509_value(sk, numcerts - i - 1);
-#else
-    X509 *x = sk_X509_value(sk, i);
-#endif
-
-    X509_CINF *cinf;
-    EVP_PKEY *pubkey=NULL;
-    int j;
-    char *ptr;
-
-    (void)x509_name_oneline(X509_get_subject_name(x), bufp, CERTBUFFERSIZE);
-    infof(data, "%2d Subject: %s\n", i, bufp);
-    push_certinfo(data, i, "Subject", bufp);
-
-    (void)x509_name_oneline(X509_get_issuer_name(x), bufp, CERTBUFFERSIZE);
-    infof(data, "   Issuer: %s\n", bufp);
-    push_certinfo(data, i, "Issuer", bufp);
-
-    value = X509_get_version(x);
-    infof(data, "   Version: %lu (0x%lx)\n", value+1, value);
-    snprintf(bufp, CERTBUFFERSIZE, "%lx", value);
-    push_certinfo(data, i, "Version", bufp); /* hex */
-
-    num=X509_get_serialNumber(x);
-    if(num->length <= 4) {
-      value = ASN1_INTEGER_get(num);
-      infof(data,"   Serial Number: %ld (0x%lx)\n", value, value);
-      snprintf(bufp, CERTBUFFERSIZE, "%lx", value);
-    }
-    else {
-      int left = CERTBUFFERSIZE;
-
-      ptr = bufp;
-      *ptr++ = 0;
-      if(num->type == V_ASN1_NEG_INTEGER)
-        *ptr++='-';
-
-      for(j=0; (j<num->length) && (left>=4); j++) {
-        /* TODO: length restrictions */
-        snprintf(ptr, 3, "%02x%c",num->data[j],
-                 ((j+1 == num->length)?'\n':':'));
-        ptr += 3;
-        left-=4;
-      }
-      if(num->length)
-        infof(data,"   Serial Number: %s\n", bufp);
-      else
-        bufp[0]=0;
-    }
-    if(bufp[0])
-      push_certinfo(data, i, "Serial Number", bufp); /* hex */
-
-    cinf = x->cert_info;
-
-    j = asn1_object_dump(cinf->signature->algorithm, bufp, CERTBUFFERSIZE);
-    if(!j) {
-      infof(data, "   Signature Algorithm: %s\n", bufp);
-      push_certinfo(data, i, "Signature Algorithm", bufp);
-    }
-
-    certdate = X509_get_notBefore(x);
-    asn1_output(certdate, bufp, CERTBUFFERSIZE);
-    infof(data, "   Start date: %s\n", bufp);
-    push_certinfo(data, i, "Start date", bufp);
-
-    certdate = X509_get_notAfter(x);
-    asn1_output(certdate, bufp, CERTBUFFERSIZE);
-    infof(data, "   Expire date: %s\n", bufp);
-    push_certinfo(data, i, "Expire date", bufp);
-
-    j = asn1_object_dump(cinf->key->algor->algorithm, bufp, CERTBUFFERSIZE);
-    if(!j) {
-      infof(data, "   Public Key Algorithm: %s\n", bufp);
-      push_certinfo(data, i, "Public Key Algorithm", bufp);
-    }
-
-    pubkey = X509_get_pubkey(x);
-    if(!pubkey)
-      infof(data, "   Unable to load public key\n");
-    else {
-      switch(pubkey->type) {
-      case EVP_PKEY_RSA:
-        infof(data,  "   RSA Public Key (%d bits)\n",
-              BN_num_bits(pubkey->pkey.rsa->n));
-        snprintf(bufp, CERTBUFFERSIZE, "%d", BN_num_bits(pubkey->pkey.rsa->n));
-        push_certinfo(data, i, "RSA Public Key", bufp);
-
-        print_pubkey_BN(rsa, n, i);
-        print_pubkey_BN(rsa, e, i);
-        print_pubkey_BN(rsa, d, i);
-        print_pubkey_BN(rsa, p, i);
-        print_pubkey_BN(rsa, q, i);
-        print_pubkey_BN(rsa, dmp1, i);
-        print_pubkey_BN(rsa, dmq1, i);
-        print_pubkey_BN(rsa, iqmp, i);
-        break;
-      case EVP_PKEY_DSA:
-        print_pubkey_BN(dsa, p, i);
-        print_pubkey_BN(dsa, q, i);
-        print_pubkey_BN(dsa, g, i);
-        print_pubkey_BN(dsa, priv_key, i);
-        print_pubkey_BN(dsa, pub_key, i);
-        break;
-      case EVP_PKEY_DH:
-        print_pubkey_BN(dh, p, i);
-        print_pubkey_BN(dh, g, i);
-        print_pubkey_BN(dh, priv_key, i);
-        print_pubkey_BN(dh, pub_key, i);
-        break;
-#if 0
-      case EVP_PKEY_EC: /* symbol not present in OpenSSL 0.9.6 */
-        /* left TODO */
-        break;
-#endif
-      }
-      EVP_PKEY_free(pubkey);
-    }
-
-    X509V3_ext(data, i, cinf->extensions);
-
-    X509_signature(data, i, x->signature);
-
-    dumpcert(data, x, i);
-  }
-
-  free(bufp);
-
-  return CURLE_OK;
-}
-
-/*
- * Get the server cert, verify it and show it etc, only call failf() if the
- * 'strict' argument is TRUE as otherwise all this is for informational
- * purposes only!
- *
- * We check certificates to authenticate the server; otherwise we risk
- * man-in-the-middle attack.
- */
-static CURLcode servercert(struct connectdata *conn,
-                           struct ssl_connect_data *connssl,
-                           bool strict)
-{
-  CURLcode retcode = CURLE_OK;
-  int rc;
-  long lerr;
-  ASN1_TIME *certdate;
-  struct SessionHandle *data = conn->data;
-  X509 *issuer;
-  FILE *fp;
-  char *buffer = data->state.buffer;
-
-  if(data->set.ssl.certinfo)
-    /* we've been asked to gather certificate info! */
-    (void)get_cert_chain(conn, connssl);
-
-  data->set.ssl.certverifyresult = !X509_V_OK;
-
-  connssl->server_cert = SSL_get_peer_certificate(connssl->handle);
-  if(!connssl->server_cert) {
-    if(strict)
-      failf(data, "SSL: couldn't get peer certificate!");
-    return CURLE_PEER_FAILED_VERIFICATION;
-  }
-  infof (data, "Server certificate:\n");
-
-  rc = x509_name_oneline(X509_get_subject_name(connssl->server_cert),
-                         buffer, BUFSIZE);
-  if(rc) {
-    if(strict)
-      failf(data, "SSL: couldn't get X509-subject!");
-    X509_free(connssl->server_cert);
-    connssl->server_cert = NULL;
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-  infof(data, "\t subject: %s\n", buffer);
-
-  certdate = X509_get_notBefore(connssl->server_cert);
-  asn1_output(certdate, buffer, BUFSIZE);
-  infof(data, "\t start date: %s\n", buffer);
-
-  certdate = X509_get_notAfter(connssl->server_cert);
-  asn1_output(certdate, buffer, BUFSIZE);
-  infof(data, "\t expire date: %s\n", buffer);
-
-  if(data->set.ssl.verifyhost) {
-    retcode = verifyhost(conn, connssl->server_cert);
-    if(retcode) {
-      X509_free(connssl->server_cert);
-      connssl->server_cert = NULL;
-      return retcode;
-    }
-  }
-
-  rc = x509_name_oneline(X509_get_issuer_name(connssl->server_cert),
-                         buffer, BUFSIZE);
-  if(rc) {
-    if(strict)
-      failf(data, "SSL: couldn't get X509-issuer name!");
-    retcode = CURLE_SSL_CONNECT_ERROR;
-  }
-  else {
-    infof(data, "\t issuer: %s\n", buffer);
-
-    /* We could do all sorts of certificate verification stuff here before
-       deallocating the certificate. */
-
-    /* e.g. match issuer name with provided issuer certificate */
-    if(data->set.str[STRING_SSL_ISSUERCERT]) {
-      fp=fopen(data->set.str[STRING_SSL_ISSUERCERT],"r");
-      if(!fp) {
-        if(strict)
-          failf(data, "SSL: Unable to open issuer cert (%s)",
-                data->set.str[STRING_SSL_ISSUERCERT]);
-        X509_free(connssl->server_cert);
-        connssl->server_cert = NULL;
-        return CURLE_SSL_ISSUER_ERROR;
-      }
-      issuer = PEM_read_X509(fp,NULL,ZERO_NULL,NULL);
-      if(!issuer) {
-        if(strict)
-          failf(data, "SSL: Unable to read issuer cert (%s)",
-                data->set.str[STRING_SSL_ISSUERCERT]);
-        X509_free(connssl->server_cert);
-        X509_free(issuer);
-        fclose(fp);
-        return CURLE_SSL_ISSUER_ERROR;
-      }
-      fclose(fp);
-      if(X509_check_issued(issuer,connssl->server_cert) != X509_V_OK) {
-        if(strict)
-          failf(data, "SSL: Certificate issuer check failed (%s)",
-                data->set.str[STRING_SSL_ISSUERCERT]);
-        X509_free(connssl->server_cert);
-        X509_free(issuer);
-        connssl->server_cert = NULL;
-        return CURLE_SSL_ISSUER_ERROR;
-      }
-      infof(data, "\t SSL certificate issuer check ok (%s)\n",
-            data->set.str[STRING_SSL_ISSUERCERT]);
-      X509_free(issuer);
-    }
-
-    lerr = data->set.ssl.certverifyresult=
-      SSL_get_verify_result(connssl->handle);
-    if(data->set.ssl.certverifyresult != X509_V_OK) {
-      if(data->set.ssl.verifypeer) {
-        /* We probably never reach this, because SSL_connect() will fail
-           and we return earlier if verifypeer is set? */
-        if(strict)
-          failf(data, "SSL certificate verify result: %s (%ld)",
-                X509_verify_cert_error_string(lerr), lerr);
-        retcode = CURLE_PEER_FAILED_VERIFICATION;
-      }
-      else
-        infof(data, "\t SSL certificate verify result: %s (%ld),"
-              " continuing anyway.\n",
-              X509_verify_cert_error_string(lerr), lerr);
-    }
-    else
-      infof(data, "\t SSL certificate verify ok.\n");
-  }
-
-  X509_free(connssl->server_cert);
-  connssl->server_cert = NULL;
-  connssl->connecting_state = ssl_connect_done;
-
-  return retcode;
-}
-
-
-static CURLcode
-ossl_connect_step3(struct connectdata *conn,
-                   int sockindex)
-{
-  CURLcode retcode = CURLE_OK;
-  void *old_ssl_sessionid=NULL;
-  struct SessionHandle *data = conn->data;
-  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-  int incache;
-  SSL_SESSION *our_ssl_sessionid;
-
-  DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
-
-#ifdef HAVE_SSL_GET1_SESSION
-  our_ssl_sessionid = SSL_get1_session(connssl->handle);
-
-  /* SSL_get1_session() will increment the reference
-     count and the session will stay in memory until explicitly freed with
-     SSL_SESSION_free(3), regardless of its state.
-     This function was introduced in openssl 0.9.5a. */
-#else
-  our_ssl_sessionid = SSL_get_session(connssl->handle);
-
-  /* if SSL_get1_session() is unavailable, use SSL_get_session().
-     This is an inferior option because the session can be flushed
-     at any time by openssl. It is included only so curl compiles
-     under versions of openssl < 0.9.5a.
-
-     WARNING: How curl behaves if it's session is flushed is
-     untested.
-  */
-#endif
-
-  incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL));
-  if(incache) {
-    if(old_ssl_sessionid != our_ssl_sessionid) {
-      infof(data, "old SSL session ID is stale, removing\n");
-      Curl_ssl_delsessionid(conn, old_ssl_sessionid);
-      incache = FALSE;
-    }
-  }
-  if(!incache) {
-    retcode = Curl_ssl_addsessionid(conn, our_ssl_sessionid,
-                                    0 /* unknown size */);
-    if(retcode) {
-      failf(data, "failed to store ssl session");
-      return retcode;
-    }
-  }
-#ifdef HAVE_SSL_GET1_SESSION
-  else {
-    /* Session was incache, so refcount already incremented earlier.
-     * Avoid further increments with each SSL_get1_session() call.
-     * This does not free the session as refcount remains > 0
-     */
-    SSL_SESSION_free(our_ssl_sessionid);
-  }
-#endif
-
-  /*
-   * We check certificates to authenticate the server; otherwise we risk
-   * man-in-the-middle attack; NEVERTHELESS, if we're told explicitly not to
-   * verify the peer ignore faults and failures from the server cert
-   * operations.
-   */
-
-  if(!data->set.ssl.verifypeer)
-    (void)servercert(conn, connssl, FALSE);
-  else
-    retcode = servercert(conn, connssl, TRUE);
-
-  if(CURLE_OK == retcode)
-    connssl->connecting_state = ssl_connect_done;
-  return retcode;
-}
-
-static Curl_recv ossl_recv;
-static Curl_send ossl_send;
-
-static CURLcode
-ossl_connect_common(struct connectdata *conn,
-                    int sockindex,
-                    bool nonblocking,
-                    bool *done)
-{
-  CURLcode retcode;
-  struct SessionHandle *data = conn->data;
-  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-  curl_socket_t sockfd = conn->sock[sockindex];
-  long timeout_ms;
-  int what;
-
-  /* check if the connection has already been established */
-  if(ssl_connection_complete == connssl->state) {
-    *done = TRUE;
-    return CURLE_OK;
-  }
-
-  if(ssl_connect_1==connssl->connecting_state) {
-    /* Find out how much more time we're allowed */
-    timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
-    if(timeout_ms < 0) {
-      /* no need to continue if time already is up */
-      failf(data, "SSL connection timeout");
-      return CURLE_OPERATION_TIMEDOUT;
-    }
-    retcode = ossl_connect_step1(conn, sockindex);
-    if(retcode)
-      return retcode;
-  }
-
-  while(ssl_connect_2 == connssl->connecting_state ||
-        ssl_connect_2_reading == connssl->connecting_state ||
-        ssl_connect_2_writing == connssl->connecting_state) {
-
-    /* check allowed time left */
-    timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
-    if(timeout_ms < 0) {
-      /* no need to continue if time already is up */
-      failf(data, "SSL connection timeout");
-      return CURLE_OPERATION_TIMEDOUT;
-    }
-
-    /* if ssl is expecting something, check if it's available. */
-    if(connssl->connecting_state == ssl_connect_2_reading
-        || connssl->connecting_state == ssl_connect_2_writing) {
-
-      curl_socket_t writefd = ssl_connect_2_writing==
-        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
-      curl_socket_t readfd = ssl_connect_2_reading==
-        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
-
-      what = Curl_socket_ready(readfd, writefd, nonblocking?0:timeout_ms);
-      if(what < 0) {
-        /* fatal error */
-        failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
-        return CURLE_SSL_CONNECT_ERROR;
-      }
-      else if(0 == what) {
-        if(nonblocking) {
-          *done = FALSE;
-          return CURLE_OK;
-        }
-        else {
-          /* timeout */
-          failf(data, "SSL connection timeout");
-          return CURLE_OPERATION_TIMEDOUT;
-        }
-      }
-      /* socket is readable or writable */
-    }
-
-    /* Run transaction, and return to the caller if it failed or if this
-     * connection is done nonblocking and this loop would execute again. This
-     * permits the owner of a multi handle to abort a connection attempt
-     * before step2 has completed while ensuring that a client using select()
-     * or epoll() will always have a valid fdset to wait on.
-     */
-    retcode = ossl_connect_step2(conn, sockindex);
-    if(retcode || (nonblocking &&
-                   (ssl_connect_2 == connssl->connecting_state ||
-                    ssl_connect_2_reading == connssl->connecting_state ||
-                    ssl_connect_2_writing == connssl->connecting_state)))
-      return retcode;
-
-  } /* repeat step2 until all transactions are done. */
-
-
-  if(ssl_connect_3==connssl->connecting_state) {
-    retcode = ossl_connect_step3(conn, sockindex);
-    if(retcode)
-      return retcode;
-  }
-
-  if(ssl_connect_done==connssl->connecting_state) {
-    connssl->state = ssl_connection_complete;
-    conn->recv[sockindex] = ossl_recv;
-    conn->send[sockindex] = ossl_send;
-    *done = TRUE;
-  }
-  else
-    *done = FALSE;
-
-  /* Reset our connect state machine */
-  connssl->connecting_state = ssl_connect_1;
-
-  return CURLE_OK;
-}
-
-CURLcode
-Curl_ossl_connect_nonblocking(struct connectdata *conn,
-                              int sockindex,
-                              bool *done)
-{
-  return ossl_connect_common(conn, sockindex, TRUE, done);
-}
-
-CURLcode
-Curl_ossl_connect(struct connectdata *conn,
-                  int sockindex)
-{
-  CURLcode retcode;
-  bool done = FALSE;
-
-  retcode = ossl_connect_common(conn, sockindex, FALSE, &done);
-  if(retcode)
-    return retcode;
-
-  DEBUGASSERT(done);
-
-  return CURLE_OK;
-}
-
-bool Curl_ossl_data_pending(const struct connectdata *conn,
-                            int connindex)
-{
-  if(conn->ssl[connindex].handle)
-    /* SSL is in use */
-    return (0 != SSL_pending(conn->ssl[connindex].handle)) ? TRUE : FALSE;
-  else
-    return FALSE;
-}
-
-static ssize_t ossl_send(struct connectdata *conn,
-                         int sockindex,
-                         const void *mem,
-                         size_t len,
-                         CURLcode *curlcode)
-{
-  /* SSL_write() is said to return 'int' while write() and send() returns
-     'size_t' */
-  int err;
-  char error_buffer[120]; /* OpenSSL documents that this must be at least 120
-                             bytes long. */
-  unsigned long sslerror;
-  int memlen;
-  int rc;
-
-  ERR_clear_error();
-
-  memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
-  rc = SSL_write(conn->ssl[sockindex].handle, mem, memlen);
-
-  if(rc < 0) {
-    err = SSL_get_error(conn->ssl[sockindex].handle, rc);
-
-    switch(err) {
-    case SSL_ERROR_WANT_READ:
-    case SSL_ERROR_WANT_WRITE:
-      /* The operation did not complete; the same TLS/SSL I/O function
-         should be called again later. This is basically an EWOULDBLOCK
-         equivalent. */
-      *curlcode = CURLE_AGAIN;
-      return -1;
-    case SSL_ERROR_SYSCALL:
-      failf(conn->data, "SSL_write() returned SYSCALL, errno = %d",
-            SOCKERRNO);
-      *curlcode = CURLE_SEND_ERROR;
-      return -1;
-    case SSL_ERROR_SSL:
-      /*  A failure in the SSL library occurred, usually a protocol error.
-          The OpenSSL error queue contains more information on the error. */
-      sslerror = ERR_get_error();
-      failf(conn->data, "SSL_write() error: %s",
-            ERR_error_string(sslerror, error_buffer));
-      *curlcode = CURLE_SEND_ERROR;
-      return -1;
-    }
-    /* a true error */
-    failf(conn->data, "SSL_write() return error %d", err);
-    *curlcode = CURLE_SEND_ERROR;
-    return -1;
-  }
-  return (ssize_t)rc; /* number of bytes */
-}
-
-static ssize_t ossl_recv(struct connectdata *conn, /* connection data */
-                         int num,                  /* socketindex */
-                         char *buf,                /* store read data here */
-                         size_t buffersize,        /* max amount to read */
-                         CURLcode *curlcode)
-{
-  char error_buffer[120]; /* OpenSSL documents that this must be at
-                             least 120 bytes long. */
-  unsigned long sslerror;
-  ssize_t nread;
-  int buffsize;
-
-  ERR_clear_error();
-
-  buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
-  nread = (ssize_t)SSL_read(conn->ssl[num].handle, buf, buffsize);
-  if(nread < 0) {
-    /* failed SSL_read */
-    int err = SSL_get_error(conn->ssl[num].handle, (int)nread);
-
-    switch(err) {
-    case SSL_ERROR_NONE: /* this is not an error */
-    case SSL_ERROR_ZERO_RETURN: /* no more data */
-      break;
-    case SSL_ERROR_WANT_READ:
-    case SSL_ERROR_WANT_WRITE:
-      /* there's data pending, re-invoke SSL_read() */
-      *curlcode = CURLE_AGAIN;
-      return -1;
-    default:
-      /* openssl/ssl.h says "look at error stack/return value/errno" */
-      sslerror = ERR_get_error();
-      failf(conn->data, "SSL read: %s, errno %d",
-            ERR_error_string(sslerror, error_buffer),
-            SOCKERRNO);
-      *curlcode = CURLE_RECV_ERROR;
-      return -1;
-    }
-  }
-  return nread;
-}
-
-size_t Curl_ossl_version(char *buffer, size_t size)
-{
-#ifdef YASSL_VERSION
-  /* yassl provides an OpenSSL API compatibility layer so it looks identical
-     to OpenSSL in all other aspects */
-  return snprintf(buffer, size, "yassl/%s", YASSL_VERSION);
-#else /* YASSL_VERSION */
-
-#if(SSLEAY_VERSION_NUMBER >= 0x905000)
-  {
-    char sub[2];
-    unsigned long ssleay_value;
-    sub[1]='\0';
-    ssleay_value=SSLeay();
-    if(ssleay_value < 0x906000) {
-      ssleay_value=SSLEAY_VERSION_NUMBER;
-      sub[0]='\0';
-    }
-    else {
-      if(ssleay_value&0xff0) {
-        sub[0]=(char)(((ssleay_value>>4)&0xff) + 'a' -1);
-      }
-      else
-        sub[0]='\0';
-    }
-
-    return snprintf(buffer, size, "OpenSSL/%lx.%lx.%lx%s",
-                    (ssleay_value>>28)&0xf,
-                    (ssleay_value>>20)&0xff,
-                    (ssleay_value>>12)&0xff,
-                    sub);
-  }
-
-#else /* SSLEAY_VERSION_NUMBER is less than 0.9.5 */
-
-#if(SSLEAY_VERSION_NUMBER >= 0x900000)
-  return snprintf(buffer, size, "OpenSSL/%lx.%lx.%lx",
-                  (SSLEAY_VERSION_NUMBER>>28)&0xff,
-                  (SSLEAY_VERSION_NUMBER>>20)&0xff,
-                  (SSLEAY_VERSION_NUMBER>>12)&0xf);
-
-#else /* (SSLEAY_VERSION_NUMBER >= 0x900000) */
-  {
-    char sub[2];
-    sub[1]='\0';
-    if(SSLEAY_VERSION_NUMBER&0x0f) {
-      sub[0]=(SSLEAY_VERSION_NUMBER&0x0f) + 'a' -1;
-    }
-    else
-      sub[0]='\0';
-
-    return snprintf(buffer, size, "SSL/%x.%x.%x%s",
-                    (SSLEAY_VERSION_NUMBER>>12)&0xff,
-                    (SSLEAY_VERSION_NUMBER>>8)&0xf,
-                    (SSLEAY_VERSION_NUMBER>>4)&0xf, sub);
-  }
-#endif /* (SSLEAY_VERSION_NUMBER >= 0x900000) */
-#endif /* SSLEAY_VERSION_NUMBER is less than 0.9.5 */
-
-#endif /* YASSL_VERSION */
-}
-
-void Curl_ossl_random(struct SessionHandle *data, unsigned char *entropy,
-                      size_t length)
-{
-  Curl_ossl_seed(data); /* Initiate the seed if not already done */
-  RAND_bytes(entropy, curlx_uztosi(length));
-}
-
-void Curl_ossl_md5sum(unsigned char *tmp, /* input */
-                      size_t tmplen,
-                      unsigned char *md5sum /* output */,
-                      size_t unused)
-{
-  MD5_CTX MD5pw;
-  (void)unused;
-  MD5_Init(&MD5pw);
-  MD5_Update(&MD5pw, tmp, tmplen);
-  MD5_Final(md5sum, &MD5pw);
-}
-#endif /* USE_SSLEAY */
diff --git a/lib/strdup.c b/lib/strdup.c
deleted file mode 100644 (file)
index 8dcaa67..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-/*
- * This file is 'mem-include-scan' clean. See test 1132.
- */
-#include "curl_setup.h"
-
-#include "curl_strdup.h"
-
-#ifndef HAVE_STRDUP
-char *curlx_strdup(const char *str)
-{
-  size_t len;
-  char *newstr;
-
-  if(!str)
-    return (char *)NULL;
-
-  len = strlen(str);
-
-  if(len >= ((size_t)-1) / sizeof(char))
-    return (char *)NULL;
-
-  newstr = malloc((len+1)*sizeof(char));
-  if(!newstr)
-    return (char *)NULL;
-
-  memcpy(newstr,str,(len+1)*sizeof(char));
-
-  return newstr;
-
-}
-#endif
diff --git a/lib/strequal.c b/lib/strequal.c
deleted file mode 100644 (file)
index 5d370c8..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef HAVE_STRINGS_H
-#include <strings.h>
-#endif
-
-#include "curl_strequal.h"
-
-/*
- * @unittest: 1301
- */
-int curl_strequal(const char *first, const char *second)
-{
-#if defined(HAVE_STRCASECMP)
-  return !(strcasecmp)(first, second);
-#elif defined(HAVE_STRCMPI)
-  return !(strcmpi)(first, second);
-#elif defined(HAVE_STRICMP)
-  return !(stricmp)(first, second);
-#else
-  while(*first && *second) {
-    if(toupper(*first) != toupper(*second)) {
-      break;
-    }
-    first++;
-    second++;
-  }
-  return toupper(*first) == toupper(*second);
-#endif
-}
-
-/*
- * @unittest: 1301
- */
-int curl_strnequal(const char *first, const char *second, size_t max)
-{
-#if defined(HAVE_STRNCASECMP)
-  return !strncasecmp(first, second, max);
-#elif defined(HAVE_STRNCMPI)
-  return !strncmpi(first, second, max);
-#elif defined(HAVE_STRNICMP)
-  return !strnicmp(first, second, max);
-#else
-  while(*first && *second && max) {
-    if(toupper(*first) != toupper(*second)) {
-      break;
-    }
-    max--;
-    first++;
-    second++;
-  }
-  if(0 == max)
-    return 1; /* they are equal this far */
-
-  return toupper(*first) == toupper(*second);
-#endif
-}
-
-#ifndef HAVE_STRLCAT
-/*
- * The strlcat() function appends the NUL-terminated string src to the end
- * of dst. It will append at most size - strlen(dst) - 1 bytes, NUL-termi-
- * nating the result.
- *
- * The strlcpy() and strlcat() functions return the total length of the
- * string they tried to create.  For strlcpy() that means the length of src.
- * For strlcat() that means the initial length of dst plus the length of
- * src. While this may seem somewhat confusing it was done to make trunca-
- * tion detection simple.
- *
- *
- */
-size_t Curl_strlcat(char *dst, const char *src, size_t siz)
-{
-  char *d = dst;
-  const char *s = src;
-  size_t n = siz;
-  union {
-    ssize_t sig;
-     size_t uns;
-  } dlen;
-
-  /* Find the end of dst and adjust bytes left but don't go past end */
-  while(n-- != 0 && *d != '\0')
-    d++;
-  dlen.sig = d - dst;
-  n = siz - dlen.uns;
-
-  if(n == 0)
-    return(dlen.uns + strlen(s));
-  while(*s != '\0') {
-    if(n != 1) {
-      *d++ = *s;
-      n--;
-    }
-    s++;
-  }
-  *d = '\0';
-
-  return(dlen.uns + (s - src));     /* count does not include NUL */
-}
-#endif
diff --git a/lib/strerror.c b/lib/strerror.c
deleted file mode 100644 (file)
index 27567a1..0000000
+++ /dev/null
@@ -1,1119 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 2004 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef HAVE_STRERROR_R
-#  if (!defined(HAVE_POSIX_STRERROR_R) && \
-       !defined(HAVE_GLIBC_STRERROR_R) && \
-       !defined(HAVE_VXWORKS_STRERROR_R)) || \
-      (defined(HAVE_POSIX_STRERROR_R) && defined(HAVE_VXWORKS_STRERROR_R)) || \
-      (defined(HAVE_GLIBC_STRERROR_R) && defined(HAVE_VXWORKS_STRERROR_R)) || \
-      (defined(HAVE_POSIX_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R))
-#    error "strerror_r MUST be either POSIX, glibc or vxworks-style"
-#  endif
-#endif
-
-#include <curl/curl.h>
-
-#ifdef USE_LIBIDN
-#include <idna.h>
-#endif
-
-#include "curl_strerror.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-const char *
-curl_easy_strerror(CURLcode error)
-{
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-  switch (error) {
-  case CURLE_OK:
-    return "No error";
-
-  case CURLE_UNSUPPORTED_PROTOCOL:
-    return "Unsupported protocol";
-
-  case CURLE_FAILED_INIT:
-    return "Failed initialization";
-
-  case CURLE_URL_MALFORMAT:
-    return "URL using bad/illegal format or missing URL";
-
-  case CURLE_NOT_BUILT_IN:
-    return "A requested feature, protocol or option was not found built-in in"
-      " this libcurl due to a build-time decision.";
-
-  case CURLE_COULDNT_RESOLVE_PROXY:
-    return "Couldn't resolve proxy name";
-
-  case CURLE_COULDNT_RESOLVE_HOST:
-    return "Couldn't resolve host name";
-
-  case CURLE_COULDNT_CONNECT:
-    return "Couldn't connect to server";
-
-  case CURLE_FTP_WEIRD_SERVER_REPLY:
-    return "FTP: weird server reply";
-
-  case CURLE_REMOTE_ACCESS_DENIED:
-    return "Access denied to remote resource";
-
-  case CURLE_FTP_ACCEPT_FAILED:
-    return "FTP: The server failed to connect to data port";
-
-  case CURLE_FTP_ACCEPT_TIMEOUT:
-    return "FTP: Accepting server connect has timed out";
-
-  case CURLE_FTP_PRET_FAILED:
-    return "FTP: The server did not accept the PRET command.";
-
-  case CURLE_FTP_WEIRD_PASS_REPLY:
-    return "FTP: unknown PASS reply";
-
-  case CURLE_FTP_WEIRD_PASV_REPLY:
-    return "FTP: unknown PASV reply";
-
-  case CURLE_FTP_WEIRD_227_FORMAT:
-    return "FTP: unknown 227 response format";
-
-  case CURLE_FTP_CANT_GET_HOST:
-    return "FTP: can't figure out the host in the PASV response";
-
-  case CURLE_FTP_COULDNT_SET_TYPE:
-    return "FTP: couldn't set file type";
-
-  case CURLE_PARTIAL_FILE:
-    return "Transferred a partial file";
-
-  case CURLE_FTP_COULDNT_RETR_FILE:
-    return "FTP: couldn't retrieve (RETR failed) the specified file";
-
-  case CURLE_QUOTE_ERROR:
-    return "Quote command returned error";
-
-  case CURLE_HTTP_RETURNED_ERROR:
-    return "HTTP response code said error";
-
-  case CURLE_WRITE_ERROR:
-    return "Failed writing received data to disk/application";
-
-  case CURLE_UPLOAD_FAILED:
-    return "Upload failed (at start/before it took off)";
-
-  case CURLE_READ_ERROR:
-    return "Failed to open/read local data from file/application";
-
-  case CURLE_OUT_OF_MEMORY:
-    return "Out of memory";
-
-  case CURLE_OPERATION_TIMEDOUT:
-    return "Timeout was reached";
-
-  case CURLE_FTP_PORT_FAILED:
-    return "FTP: command PORT failed";
-
-  case CURLE_FTP_COULDNT_USE_REST:
-    return "FTP: command REST failed";
-
-  case CURLE_RANGE_ERROR:
-    return "Requested range was not delivered by the server";
-
-  case CURLE_HTTP_POST_ERROR:
-    return "Internal problem setting up the POST";
-
-  case CURLE_SSL_CONNECT_ERROR:
-    return "SSL connect error";
-
-  case CURLE_BAD_DOWNLOAD_RESUME:
-    return "Couldn't resume download";
-
-  case CURLE_FILE_COULDNT_READ_FILE:
-    return "Couldn't read a file:// file";
-
-  case CURLE_LDAP_CANNOT_BIND:
-    return "LDAP: cannot bind";
-
-  case CURLE_LDAP_SEARCH_FAILED:
-    return "LDAP: search failed";
-
-  case CURLE_FUNCTION_NOT_FOUND:
-    return "A required function in the library was not found";
-
-  case CURLE_ABORTED_BY_CALLBACK:
-    return "Operation was aborted by an application callback";
-
-  case CURLE_BAD_FUNCTION_ARGUMENT:
-    return "A libcurl function was given a bad argument";
-
-  case CURLE_INTERFACE_FAILED:
-    return "Failed binding local connection end";
-
-  case CURLE_TOO_MANY_REDIRECTS :
-    return "Number of redirects hit maximum amount";
-
-  case CURLE_UNKNOWN_OPTION:
-    return "An unknown option was passed in to libcurl";
-
-  case CURLE_TELNET_OPTION_SYNTAX :
-    return "Malformed telnet option";
-
-  case CURLE_PEER_FAILED_VERIFICATION:
-    return "SSL peer certificate or SSH remote key was not OK";
-
-  case CURLE_GOT_NOTHING:
-    return "Server returned nothing (no headers, no data)";
-
-  case CURLE_SSL_ENGINE_NOTFOUND:
-    return "SSL crypto engine not found";
-
-  case CURLE_SSL_ENGINE_SETFAILED:
-    return "Can not set SSL crypto engine as default";
-
-  case CURLE_SSL_ENGINE_INITFAILED:
-    return "Failed to initialise SSL crypto engine";
-
-  case CURLE_SEND_ERROR:
-    return "Failed sending data to the peer";
-
-  case CURLE_RECV_ERROR:
-    return "Failure when receiving data from the peer";
-
-  case CURLE_SSL_CERTPROBLEM:
-    return "Problem with the local SSL certificate";
-
-  case CURLE_SSL_CIPHER:
-    return "Couldn't use specified SSL cipher";
-
-  case CURLE_SSL_CACERT:
-    return "Peer certificate cannot be authenticated with given CA "
-      "certificates";
-
-  case CURLE_SSL_CACERT_BADFILE:
-    return "Problem with the SSL CA cert (path? access rights?)";
-
-  case CURLE_BAD_CONTENT_ENCODING:
-    return "Unrecognized or bad HTTP Content or Transfer-Encoding";
-
-  case CURLE_LDAP_INVALID_URL:
-    return "Invalid LDAP URL";
-
-  case CURLE_FILESIZE_EXCEEDED:
-    return "Maximum file size exceeded";
-
-  case CURLE_USE_SSL_FAILED:
-    return "Requested SSL level failed";
-
-  case CURLE_SSL_SHUTDOWN_FAILED:
-    return "Failed to shut down the SSL connection";
-
-  case CURLE_SSL_CRL_BADFILE:
-    return "Failed to load CRL file (path? access rights?, format?)";
-
-  case CURLE_SSL_ISSUER_ERROR:
-    return "Issuer check against peer certificate failed";
-
-  case CURLE_SEND_FAIL_REWIND:
-    return "Send failed since rewinding of the data stream failed";
-
-  case CURLE_LOGIN_DENIED:
-    return "Login denied";
-
-  case CURLE_TFTP_NOTFOUND:
-    return "TFTP: File Not Found";
-
-  case CURLE_TFTP_PERM:
-    return "TFTP: Access Violation";
-
-  case CURLE_REMOTE_DISK_FULL:
-    return "Disk full or allocation exceeded";
-
-  case CURLE_TFTP_ILLEGAL:
-    return "TFTP: Illegal operation";
-
-  case CURLE_TFTP_UNKNOWNID:
-    return "TFTP: Unknown transfer ID";
-
-  case CURLE_REMOTE_FILE_EXISTS:
-    return "Remote file already exists";
-
-  case CURLE_TFTP_NOSUCHUSER:
-    return "TFTP: No such user";
-
-  case CURLE_CONV_FAILED:
-    return "Conversion failed";
-
-  case CURLE_CONV_REQD:
-    return "Caller must register CURLOPT_CONV_ callback options";
-
-  case CURLE_REMOTE_FILE_NOT_FOUND:
-    return "Remote file not found";
-
-  case CURLE_SSH:
-    return "Error in the SSH layer";
-
-  case CURLE_AGAIN:
-    return "Socket not ready for send/recv";
-
-  case CURLE_RTSP_CSEQ_ERROR:
-    return "RTSP CSeq mismatch or invalid CSeq";
-
-  case CURLE_RTSP_SESSION_ERROR:
-    return "RTSP session error";
-
-  case CURLE_FTP_BAD_FILE_LIST:
-    return "Unable to parse FTP file list";
-
-  case CURLE_CHUNK_FAILED:
-    return "Chunk callback failed";
-
-    /* error codes not used by current libcurl */
-  case CURLE_OBSOLETE16:
-  case CURLE_OBSOLETE20:
-  case CURLE_OBSOLETE24:
-  case CURLE_OBSOLETE29:
-  case CURLE_OBSOLETE32:
-  case CURLE_OBSOLETE40:
-  case CURLE_OBSOLETE44:
-  case CURLE_OBSOLETE46:
-  case CURLE_OBSOLETE50:
-  case CURLE_OBSOLETE57:
-  case CURL_LAST:
-    break;
-  }
-  /*
-   * By using a switch, gcc -Wall will complain about enum values
-   * which do not appear, helping keep this function up-to-date.
-   * By using gcc -Wall -Werror, you can't forget.
-   *
-   * A table would not have the same benefit.  Most compilers will
-   * generate code very similar to a table in any case, so there
-   * is little performance gain from a table.  And something is broken
-   * for the user's application, anyways, so does it matter how fast
-   * it _doesn't_ work?
-   *
-   * The line number for the error will be near this comment, which
-   * is why it is here, and not at the start of the switch.
-   */
-  return "Unknown error";
-#else
-  if(error == CURLE_OK)
-    return "No error";
-  else
-    return "Error";
-#endif
-}
-
-const char *
-curl_multi_strerror(CURLMcode error)
-{
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-  switch (error) {
-  case CURLM_CALL_MULTI_PERFORM:
-    return "Please call curl_multi_perform() soon";
-
-  case CURLM_OK:
-    return "No error";
-
-  case CURLM_BAD_HANDLE:
-    return "Invalid multi handle";
-
-  case CURLM_BAD_EASY_HANDLE:
-    return "Invalid easy handle";
-
-  case CURLM_OUT_OF_MEMORY:
-    return "Out of memory";
-
-  case CURLM_INTERNAL_ERROR:
-    return "Internal error";
-
-  case CURLM_BAD_SOCKET:
-    return "Invalid socket argument";
-
-  case CURLM_UNKNOWN_OPTION:
-    return "Unknown option";
-
-  case CURLM_LAST:
-    break;
-  }
-
-  return "Unknown error";
-#else
-  if(error == CURLM_OK)
-    return "No error";
-  else
-    return "Error";
-#endif
-}
-
-const char *
-curl_share_strerror(CURLSHcode error)
-{
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-  switch (error) {
-  case CURLSHE_OK:
-    return "No error";
-
-  case CURLSHE_BAD_OPTION:
-    return "Unknown share option";
-
-  case CURLSHE_IN_USE:
-    return "Share currently in use";
-
-  case CURLSHE_INVALID:
-    return "Invalid share handle";
-
-  case CURLSHE_NOMEM:
-    return "Out of memory";
-
-  case CURLSHE_NOT_BUILT_IN:
-    return "Feature not enabled in this library";
-
-  case CURLSHE_LAST:
-    break;
-  }
-
-  return "CURLSHcode unknown";
-#else
-  if(error == CURLSHE_OK)
-    return "No error";
-  else
-    return "Error";
-#endif
-}
-
-#ifdef USE_WINSOCK
-
-/* This function handles most / all (?) Winsock errors cURL is able to produce.
- */
-static const char *
-get_winsock_error (int err, char *buf, size_t len)
-{
-  const char *p;
-
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-  switch (err) {
-  case WSAEINTR:
-    p = "Call interrupted";
-    break;
-  case WSAEBADF:
-    p = "Bad file";
-    break;
-  case WSAEACCES:
-    p = "Bad access";
-    break;
-  case WSAEFAULT:
-    p = "Bad argument";
-    break;
-  case WSAEINVAL:
-    p = "Invalid arguments";
-    break;
-  case WSAEMFILE:
-    p = "Out of file descriptors";
-    break;
-  case WSAEWOULDBLOCK:
-    p = "Call would block";
-    break;
-  case WSAEINPROGRESS:
-  case WSAEALREADY:
-    p = "Blocking call in progress";
-    break;
-  case WSAENOTSOCK:
-    p = "Descriptor is not a socket";
-    break;
-  case WSAEDESTADDRREQ:
-    p = "Need destination address";
-    break;
-  case WSAEMSGSIZE:
-    p = "Bad message size";
-    break;
-  case WSAEPROTOTYPE:
-    p = "Bad protocol";
-    break;
-  case WSAENOPROTOOPT:
-    p = "Protocol option is unsupported";
-    break;
-  case WSAEPROTONOSUPPORT:
-    p = "Protocol is unsupported";
-    break;
-  case WSAESOCKTNOSUPPORT:
-    p = "Socket is unsupported";
-    break;
-  case WSAEOPNOTSUPP:
-    p = "Operation not supported";
-    break;
-  case WSAEAFNOSUPPORT:
-    p = "Address family not supported";
-    break;
-  case WSAEPFNOSUPPORT:
-    p = "Protocol family not supported";
-    break;
-  case WSAEADDRINUSE:
-    p = "Address already in use";
-    break;
-  case WSAEADDRNOTAVAIL:
-    p = "Address not available";
-    break;
-  case WSAENETDOWN:
-    p = "Network down";
-    break;
-  case WSAENETUNREACH:
-    p = "Network unreachable";
-    break;
-  case WSAENETRESET:
-    p = "Network has been reset";
-    break;
-  case WSAECONNABORTED:
-    p = "Connection was aborted";
-    break;
-  case WSAECONNRESET:
-    p = "Connection was reset";
-    break;
-  case WSAENOBUFS:
-    p = "No buffer space";
-    break;
-  case WSAEISCONN:
-    p = "Socket is already connected";
-    break;
-  case WSAENOTCONN:
-    p = "Socket is not connected";
-    break;
-  case WSAESHUTDOWN:
-    p = "Socket has been shut down";
-    break;
-  case WSAETOOMANYREFS:
-    p = "Too many references";
-    break;
-  case WSAETIMEDOUT:
-    p = "Timed out";
-    break;
-  case WSAECONNREFUSED:
-    p = "Connection refused";
-    break;
-  case WSAELOOP:
-    p = "Loop??";
-    break;
-  case WSAENAMETOOLONG:
-    p = "Name too long";
-    break;
-  case WSAEHOSTDOWN:
-    p = "Host down";
-    break;
-  case WSAEHOSTUNREACH:
-    p = "Host unreachable";
-    break;
-  case WSAENOTEMPTY:
-    p = "Not empty";
-    break;
-  case WSAEPROCLIM:
-    p = "Process limit reached";
-    break;
-  case WSAEUSERS:
-    p = "Too many users";
-    break;
-  case WSAEDQUOT:
-    p = "Bad quota";
-    break;
-  case WSAESTALE:
-    p = "Something is stale";
-    break;
-  case WSAEREMOTE:
-    p = "Remote error";
-    break;
-#ifdef WSAEDISCON  /* missing in SalfordC! */
-  case WSAEDISCON:
-    p = "Disconnected";
-    break;
-#endif
-    /* Extended Winsock errors */
-  case WSASYSNOTREADY:
-    p = "Winsock library is not ready";
-    break;
-  case WSANOTINITIALISED:
-    p = "Winsock library not initialised";
-    break;
-  case WSAVERNOTSUPPORTED:
-    p = "Winsock version not supported";
-    break;
-
-    /* getXbyY() errors (already handled in herrmsg):
-     * Authoritative Answer: Host not found */
-  case WSAHOST_NOT_FOUND:
-    p = "Host not found";
-    break;
-
-    /* Non-Authoritative: Host not found, or SERVERFAIL */
-  case WSATRY_AGAIN:
-    p = "Host not found, try again";
-    break;
-
-    /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */
-  case WSANO_RECOVERY:
-    p = "Unrecoverable error in call to nameserver";
-    break;
-
-    /* Valid name, no data record of requested type */
-  case WSANO_DATA:
-    p = "No data record of requested type";
-    break;
-
-  default:
-    return NULL;
-  }
-#else
-  if(err == CURLE_OK)
-    return NULL;
-  else
-    p = "error";
-#endif
-  strncpy (buf, p, len);
-  buf [len-1] = '\0';
-  return buf;
-}
-#endif   /* USE_WINSOCK */
-
-/*
- * Our thread-safe and smart strerror() replacement.
- *
- * The 'err' argument passed in to this function MUST be a true errno number
- * as reported on this system. We do no range checking on the number before
- * we pass it to the "number-to-message" conversion function and there might
- * be systems that don't do proper range checking in there themselves.
- *
- * We don't do range checking (on systems other than Windows) since there is
- * no good reliable and portable way to do it.
- */
-const char *Curl_strerror(struct connectdata *conn, int err)
-{
-  char *buf, *p;
-  size_t max;
-  int old_errno = ERRNO;
-
-  DEBUGASSERT(conn);
-  DEBUGASSERT(err >= 0);
-
-  buf = conn->syserr_buf;
-  max = sizeof(conn->syserr_buf)-1;
-  *buf = '\0';
-
-#ifdef USE_WINSOCK
-
-#ifdef _WIN32_WCE
-  {
-    wchar_t wbuf[256];
-    wbuf[0] = L'\0';
-
-    FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
-                  LANG_NEUTRAL, wbuf, sizeof(wbuf)/sizeof(wchar_t), NULL);
-    wcstombs(buf,wbuf,max);
-  }
-#else
-  /* 'sys_nerr' is the maximum errno number, it is not widely portable */
-  if(err >= 0 && err < sys_nerr)
-    strncpy(buf, strerror(err), max);
-  else {
-    if(!get_winsock_error(err, buf, max) &&
-       !FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
-                       LANG_NEUTRAL, buf, (DWORD)max, NULL))
-      snprintf(buf, max, "Unknown error %d (%#x)", err, err);
-  }
-#endif
-
-#else /* not USE_WINSOCK coming up */
-
-#if defined(HAVE_STRERROR_R) && defined(HAVE_POSIX_STRERROR_R)
- /*
-  * The POSIX-style strerror_r() may set errno to ERANGE if insufficient
-  * storage is supplied via 'strerrbuf' and 'buflen' to hold the generated
-  * message string, or EINVAL if 'errnum' is not a valid error number.
-  */
-  if(0 != strerror_r(err, buf, max)) {
-    if('\0' == buf[0])
-      snprintf(buf, max, "Unknown error %d", err);
-  }
-#elif defined(HAVE_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R)
- /*
-  * The glibc-style strerror_r() only *might* use the buffer we pass to
-  * the function, but it always returns the error message as a pointer,
-  * so we must copy that string unconditionally (if non-NULL).
-  */
-  {
-    char buffer[256];
-    char *msg = strerror_r(err, buffer, sizeof(buffer));
-    if(msg)
-      strncpy(buf, msg, max);
-    else
-      snprintf(buf, max, "Unknown error %d", err);
-  }
-#elif defined(HAVE_STRERROR_R) && defined(HAVE_VXWORKS_STRERROR_R)
- /*
-  * The vxworks-style strerror_r() does use the buffer we pass to the function.
-  * The buffer size should be at least MAXERRSTR_SIZE (150) defined in rtsold.h
-  */
-  {
-    char buffer[256];
-    if(OK == strerror_r(err, buffer))
-      strncpy(buf, buffer, max);
-    else
-      snprintf(buf, max, "Unknown error %d", err);
-  }
-#else
-  {
-    char *msg = strerror(err);
-    if(msg)
-      strncpy(buf, msg, max);
-    else
-      snprintf(buf, max, "Unknown error %d", err);
-  }
-#endif
-
-#endif /* end of ! USE_WINSOCK */
-
-  buf[max] = '\0'; /* make sure the string is zero terminated */
-
-  /* strip trailing '\r\n' or '\n'. */
-  if((p = strrchr(buf,'\n')) != NULL && (p - buf) >= 2)
-     *p = '\0';
-  if((p = strrchr(buf,'\r')) != NULL && (p - buf) >= 1)
-     *p = '\0';
-
-  if(old_errno != ERRNO)
-    SET_ERRNO(old_errno);
-
-  return buf;
-}
-
-#ifdef USE_LIBIDN
-/*
- * Return error-string for libidn status as returned from idna_to_ascii_lz().
- */
-const char *Curl_idn_strerror (struct connectdata *conn, int err)
-{
-#ifdef HAVE_IDNA_STRERROR
-  (void)conn;
-  return idna_strerror((Idna_rc) err);
-#else
-  const char *str;
-  char *buf;
-  size_t max;
-
-  DEBUGASSERT(conn);
-
-  buf = conn->syserr_buf;
-  max = sizeof(conn->syserr_buf)-1;
-  *buf = '\0';
-
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-  switch ((Idna_rc)err) {
-    case IDNA_SUCCESS:
-      str = "No error";
-      break;
-    case IDNA_STRINGPREP_ERROR:
-      str = "Error in string preparation";
-      break;
-    case IDNA_PUNYCODE_ERROR:
-      str = "Error in Punycode operation";
-      break;
-    case IDNA_CONTAINS_NON_LDH:
-      str = "Illegal ASCII characters";
-      break;
-    case IDNA_CONTAINS_MINUS:
-      str = "Contains minus";
-      break;
-    case IDNA_INVALID_LENGTH:
-      str = "Invalid output length";
-      break;
-    case IDNA_NO_ACE_PREFIX:
-      str = "No ACE prefix (\"xn--\")";
-      break;
-    case IDNA_ROUNDTRIP_VERIFY_ERROR:
-      str = "Round trip verify error";
-      break;
-    case IDNA_CONTAINS_ACE_PREFIX:
-      str = "Already have ACE prefix (\"xn--\")";
-      break;
-    case IDNA_ICONV_ERROR:
-      str = "Locale conversion failed";
-      break;
-    case IDNA_MALLOC_ERROR:
-      str = "Allocation failed";
-      break;
-    case IDNA_DLOPEN_ERROR:
-      str = "dlopen() error";
-      break;
-    default:
-      snprintf(buf, max, "error %d", err);
-      str = NULL;
-      break;
-  }
-#else
-  if((Idna_rc)err == IDNA_SUCCESS)
-    str = "No error";
-  else
-    str = "Error";
-#endif
-  if(str)
-    strncpy(buf, str, max);
-  buf[max] = '\0';
-  return (buf);
-#endif
-}
-#endif  /* USE_LIBIDN */
-
-#ifdef USE_WINDOWS_SSPI
-const char *Curl_sspi_strerror (struct connectdata *conn, int err)
-{
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-  char txtbuf[80];
-  char msgbuf[sizeof(conn->syserr_buf)];
-  char *p, *str, *msg = NULL;
-  bool msg_formatted = FALSE;
-  int old_errno;
-#endif
-  const char *txt;
-  char *outbuf;
-  size_t outmax;
-
-  DEBUGASSERT(conn);
-
-  outbuf = conn->syserr_buf;
-  outmax = sizeof(conn->syserr_buf)-1;
-  *outbuf = '\0';
-
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-
-  old_errno = ERRNO;
-
-  switch (err) {
-    case SEC_E_OK:
-      txt = "No error";
-      break;
-    case SEC_E_ALGORITHM_MISMATCH:
-      txt = "SEC_E_ALGORITHM_MISMATCH";
-      break;
-    case SEC_E_BAD_BINDINGS:
-      txt = "SEC_E_BAD_BINDINGS";
-      break;
-    case SEC_E_BAD_PKGID:
-      txt = "SEC_E_BAD_PKGID";
-      break;
-    case SEC_E_BUFFER_TOO_SMALL:
-      txt = "SEC_E_BUFFER_TOO_SMALL";
-      break;
-    case SEC_E_CANNOT_INSTALL:
-      txt = "SEC_E_CANNOT_INSTALL";
-      break;
-    case SEC_E_CANNOT_PACK:
-      txt = "SEC_E_CANNOT_PACK";
-      break;
-    case SEC_E_CERT_EXPIRED:
-      txt = "SEC_E_CERT_EXPIRED";
-      break;
-    case SEC_E_CERT_UNKNOWN:
-      txt = "SEC_E_CERT_UNKNOWN";
-      break;
-    case SEC_E_CERT_WRONG_USAGE:
-      txt = "SEC_E_CERT_WRONG_USAGE";
-      break;
-    case SEC_E_CONTEXT_EXPIRED:
-      txt = "SEC_E_CONTEXT_EXPIRED";
-      break;
-    case SEC_E_CROSSREALM_DELEGATION_FAILURE:
-      txt = "SEC_E_CROSSREALM_DELEGATION_FAILURE";
-      break;
-    case SEC_E_CRYPTO_SYSTEM_INVALID:
-      txt = "SEC_E_CRYPTO_SYSTEM_INVALID";
-      break;
-    case SEC_E_DECRYPT_FAILURE:
-      txt = "SEC_E_DECRYPT_FAILURE";
-      break;
-    case SEC_E_DELEGATION_POLICY:
-      txt = "SEC_E_DELEGATION_POLICY";
-      break;
-    case SEC_E_DELEGATION_REQUIRED:
-      txt = "SEC_E_DELEGATION_REQUIRED";
-      break;
-    case SEC_E_DOWNGRADE_DETECTED:
-      txt = "SEC_E_DOWNGRADE_DETECTED";
-      break;
-    case SEC_E_ENCRYPT_FAILURE:
-      txt = "SEC_E_ENCRYPT_FAILURE";
-      break;
-    case SEC_E_ILLEGAL_MESSAGE:
-      txt = "SEC_E_ILLEGAL_MESSAGE";
-      break;
-    case SEC_E_INCOMPLETE_CREDENTIALS:
-      txt = "SEC_E_INCOMPLETE_CREDENTIALS";
-      break;
-    case SEC_E_INCOMPLETE_MESSAGE:
-      txt = "SEC_E_INCOMPLETE_MESSAGE";
-      break;
-    case SEC_E_INSUFFICIENT_MEMORY:
-      txt = "SEC_E_INSUFFICIENT_MEMORY";
-      break;
-    case SEC_E_INTERNAL_ERROR:
-      txt = "SEC_E_INTERNAL_ERROR";
-      break;
-    case SEC_E_INVALID_HANDLE:
-      txt = "SEC_E_INVALID_HANDLE";
-      break;
-    case SEC_E_INVALID_PARAMETER:
-      txt = "SEC_E_INVALID_PARAMETER";
-      break;
-    case SEC_E_INVALID_TOKEN:
-      txt = "SEC_E_INVALID_TOKEN";
-      break;
-    case SEC_E_ISSUING_CA_UNTRUSTED:
-      txt = "SEC_E_ISSUING_CA_UNTRUSTED";
-      break;
-    case SEC_E_ISSUING_CA_UNTRUSTED_KDC:
-      txt = "SEC_E_ISSUING_CA_UNTRUSTED_KDC";
-      break;
-    case SEC_E_KDC_CERT_EXPIRED:
-      txt = "SEC_E_KDC_CERT_EXPIRED";
-      break;
-    case SEC_E_KDC_CERT_REVOKED:
-      txt = "SEC_E_KDC_CERT_REVOKED";
-      break;
-    case SEC_E_KDC_INVALID_REQUEST:
-      txt = "SEC_E_KDC_INVALID_REQUEST";
-      break;
-    case SEC_E_KDC_UNABLE_TO_REFER:
-      txt = "SEC_E_KDC_UNABLE_TO_REFER";
-      break;
-    case SEC_E_KDC_UNKNOWN_ETYPE:
-      txt = "SEC_E_KDC_UNKNOWN_ETYPE";
-      break;
-    case SEC_E_LOGON_DENIED:
-      txt = "SEC_E_LOGON_DENIED";
-      break;
-    case SEC_E_MAX_REFERRALS_EXCEEDED:
-      txt = "SEC_E_MAX_REFERRALS_EXCEEDED";
-      break;
-    case SEC_E_MESSAGE_ALTERED:
-      txt = "SEC_E_MESSAGE_ALTERED";
-      break;
-    case SEC_E_MULTIPLE_ACCOUNTS:
-      txt = "SEC_E_MULTIPLE_ACCOUNTS";
-      break;
-    case SEC_E_MUST_BE_KDC:
-      txt = "SEC_E_MUST_BE_KDC";
-      break;
-    case SEC_E_NOT_OWNER:
-      txt = "SEC_E_NOT_OWNER";
-      break;
-    case SEC_E_NO_AUTHENTICATING_AUTHORITY:
-      txt = "SEC_E_NO_AUTHENTICATING_AUTHORITY";
-      break;
-    case SEC_E_NO_CREDENTIALS:
-      txt = "SEC_E_NO_CREDENTIALS";
-      break;
-    case SEC_E_NO_IMPERSONATION:
-      txt = "SEC_E_NO_IMPERSONATION";
-      break;
-    case SEC_E_NO_IP_ADDRESSES:
-      txt = "SEC_E_NO_IP_ADDRESSES";
-      break;
-    case SEC_E_NO_KERB_KEY:
-      txt = "SEC_E_NO_KERB_KEY";
-      break;
-    case SEC_E_NO_PA_DATA:
-      txt = "SEC_E_NO_PA_DATA";
-      break;
-    case SEC_E_NO_S4U_PROT_SUPPORT:
-      txt = "SEC_E_NO_S4U_PROT_SUPPORT";
-      break;
-    case SEC_E_NO_TGT_REPLY:
-      txt = "SEC_E_NO_TGT_REPLY";
-      break;
-    case SEC_E_OUT_OF_SEQUENCE:
-      txt = "SEC_E_OUT_OF_SEQUENCE";
-      break;
-    case SEC_E_PKINIT_CLIENT_FAILURE:
-      txt = "SEC_E_PKINIT_CLIENT_FAILURE";
-      break;
-    case SEC_E_PKINIT_NAME_MISMATCH:
-      txt = "SEC_E_PKINIT_NAME_MISMATCH";
-      break;
-    case SEC_E_POLICY_NLTM_ONLY:
-      txt = "SEC_E_POLICY_NLTM_ONLY";
-      break;
-    case SEC_E_QOP_NOT_SUPPORTED:
-      txt = "SEC_E_QOP_NOT_SUPPORTED";
-      break;
-    case SEC_E_REVOCATION_OFFLINE_C:
-      txt = "SEC_E_REVOCATION_OFFLINE_C";
-      break;
-    case SEC_E_REVOCATION_OFFLINE_KDC:
-      txt = "SEC_E_REVOCATION_OFFLINE_KDC";
-      break;
-    case SEC_E_SECPKG_NOT_FOUND:
-      txt = "SEC_E_SECPKG_NOT_FOUND";
-      break;
-    case SEC_E_SECURITY_QOS_FAILED:
-      txt = "SEC_E_SECURITY_QOS_FAILED";
-      break;
-    case SEC_E_SHUTDOWN_IN_PROGRESS:
-      txt = "SEC_E_SHUTDOWN_IN_PROGRESS";
-      break;
-    case SEC_E_SMARTCARD_CERT_EXPIRED:
-      txt = "SEC_E_SMARTCARD_CERT_EXPIRED";
-      break;
-    case SEC_E_SMARTCARD_CERT_REVOKED:
-      txt = "SEC_E_SMARTCARD_CERT_REVOKED";
-      break;
-    case SEC_E_SMARTCARD_LOGON_REQUIRED:
-      txt = "SEC_E_SMARTCARD_LOGON_REQUIRED";
-      break;
-    case SEC_E_STRONG_CRYPTO_NOT_SUPPORTED:
-      txt = "SEC_E_STRONG_CRYPTO_NOT_SUPPORTED";
-      break;
-    case SEC_E_TARGET_UNKNOWN:
-      txt = "SEC_E_TARGET_UNKNOWN";
-      break;
-    case SEC_E_TIME_SKEW:
-      txt = "SEC_E_TIME_SKEW";
-      break;
-    case SEC_E_TOO_MANY_PRINCIPALS:
-      txt = "SEC_E_TOO_MANY_PRINCIPALS";
-      break;
-    case SEC_E_UNFINISHED_CONTEXT_DELETED:
-      txt = "SEC_E_UNFINISHED_CONTEXT_DELETED";
-      break;
-    case SEC_E_UNKNOWN_CREDENTIALS:
-      txt = "SEC_E_UNKNOWN_CREDENTIALS";
-      break;
-    case SEC_E_UNSUPPORTED_FUNCTION:
-      txt = "SEC_E_UNSUPPORTED_FUNCTION";
-      break;
-    case SEC_E_UNSUPPORTED_PREAUTH:
-      txt = "SEC_E_UNSUPPORTED_PREAUTH";
-      break;
-    case SEC_E_UNTRUSTED_ROOT:
-      txt = "SEC_E_UNTRUSTED_ROOT";
-      break;
-    case SEC_E_WRONG_CREDENTIAL_HANDLE:
-      txt = "SEC_E_WRONG_CREDENTIAL_HANDLE";
-      break;
-    case SEC_E_WRONG_PRINCIPAL:
-      txt = "SEC_E_WRONG_PRINCIPAL";
-      break;
-    case SEC_I_COMPLETE_AND_CONTINUE:
-      txt = "SEC_I_COMPLETE_AND_CONTINUE";
-      break;
-    case SEC_I_COMPLETE_NEEDED:
-      txt = "SEC_I_COMPLETE_NEEDED";
-      break;
-    case SEC_I_CONTEXT_EXPIRED:
-      txt = "SEC_I_CONTEXT_EXPIRED";
-      break;
-    case SEC_I_CONTINUE_NEEDED:
-      txt = "SEC_I_CONTINUE_NEEDED";
-      break;
-    case SEC_I_INCOMPLETE_CREDENTIALS:
-      txt = "SEC_I_INCOMPLETE_CREDENTIALS";
-      break;
-    case SEC_I_LOCAL_LOGON:
-      txt = "SEC_I_LOCAL_LOGON";
-      break;
-    case SEC_I_NO_LSA_CONTEXT:
-      txt = "SEC_I_NO_LSA_CONTEXT";
-      break;
-    case SEC_I_RENEGOTIATE:
-      txt = "SEC_I_RENEGOTIATE";
-      break;
-    case SEC_I_SIGNATURE_NEEDED:
-      txt = "SEC_I_SIGNATURE_NEEDED";
-      break;
-    default:
-      txt = "Unknown error";
-  }
-
-  if(err == SEC_E_OK)
-    strncpy(outbuf, txt, outmax);
-  else {
-    str = txtbuf;
-    snprintf(txtbuf, sizeof(txtbuf), "%s (0x%04X%04X)",
-             txt, (err >> 16) & 0xffff, err & 0xffff);
-    txtbuf[sizeof(txtbuf)-1] = '\0';
-
-#ifdef _WIN32_WCE
-    {
-      wchar_t wbuf[256];
-      wbuf[0] = L'\0';
-
-      if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
-                       FORMAT_MESSAGE_IGNORE_INSERTS,
-                       NULL, err, LANG_NEUTRAL,
-                       wbuf, sizeof(wbuf)/sizeof(wchar_t), NULL)) {
-        wcstombs(msgbuf,wbuf,sizeof(msgbuf)-1);
-        msg_formatted = TRUE;
-      }
-    }
-#else
-    if(FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
-                      FORMAT_MESSAGE_IGNORE_INSERTS,
-                      NULL, err, LANG_NEUTRAL,
-                      msgbuf, sizeof(msgbuf)-1, NULL)) {
-      msg_formatted = TRUE;
-    }
-#endif
-    if(msg_formatted) {
-      msgbuf[sizeof(msgbuf)-1] = '\0';
-      /* strip trailing '\r\n' or '\n' */
-      if((p = strrchr(msgbuf,'\n')) != NULL && (p - msgbuf) >= 2)
-         *p = '\0';
-      if((p = strrchr(msgbuf,'\r')) != NULL && (p - msgbuf) >= 1)
-         *p = '\0';
-      msg = msgbuf;
-    }
-    if(msg)
-      snprintf(outbuf, outmax, "%s - %s", str, msg);
-    else
-      strncpy(outbuf, str, outmax);
-  }
-
-  if(old_errno != ERRNO)
-    SET_ERRNO(old_errno);
-
-#else
-
-  if(err == SEC_E_OK)
-    txt = "No error";
-  else
-    txt = "Error";
-
-  strncpy(outbuf, txt, outmax);
-
-#endif
-
-  outbuf[outmax] = '\0';
-
-  return outbuf;
-}
-#endif /* USE_WINDOWS_SSPI */
diff --git a/lib/strtok.c b/lib/strtok.c
deleted file mode 100644 (file)
index 33bdd96..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifndef HAVE_STRTOK_R
-#include <stddef.h>
-
-#include "curl_strtok.h"
-
-char *
-Curl_strtok_r(char *ptr, const char *sep, char **end)
-{
-  if(!ptr)
-    /* we got NULL input so then we get our last position instead */
-    ptr = *end;
-
-  /* pass all letters that are including in the separator string */
-  while(*ptr && strchr(sep, *ptr))
-    ++ptr;
-
-  if(*ptr) {
-    /* so this is where the next piece of string starts */
-    char *start = ptr;
-
-    /* set the end pointer to the first byte after the start */
-    *end = start + 1;
-
-    /* scan through the string to find where it ends, it ends on a
-       null byte or a character that exists in the separator string */
-    while(**end && !strchr(sep, **end))
-      ++*end;
-
-    if(**end) {
-      /* the end is not a null byte */
-      **end = '\0';  /* zero terminate it! */
-      ++*end;        /* advance the last pointer to beyond the null byte */
-    }
-
-    return start; /* return the position where the string starts */
-  }
-
-  /* we ended up on a null byte, there are no more strings to find! */
-  return NULL;
-}
-
-#endif /* this was only compiled if strtok_r wasn't present */
diff --git a/lib/strtoofft.c b/lib/strtoofft.c
deleted file mode 100644 (file)
index d203d9c..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#include "curl_strtoofft.h"
-
-/*
- * NOTE:
- *
- * In the ISO C standard (IEEE Std 1003.1), there is a strtoimax() function we
- * could use in case strtoll() doesn't exist...  See
- * http://www.opengroup.org/onlinepubs/009695399/functions/strtoimax.html
- */
-
-#ifdef NEED_CURL_STRTOLL
-
-/* Range tests can be used for alphanum decoding if characters are consecutive,
-   like in ASCII. Else an array is scanned. Determine this condition now. */
-
-#if('9' - '0') != 9 || ('Z' - 'A') != 25 || ('z' - 'a') != 25
-
-#define NO_RANGE_TEST
-
-static const char valchars[] =
-            "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
-#endif
-
-static int get_char(char c, int base);
-
-/**
- * Emulated version of the strtoll function.  This extracts a long long
- * value from the given input string and returns it.
- */
-curl_off_t
-curlx_strtoll(const char *nptr, char **endptr, int base)
-{
-  char *end;
-  int is_negative = 0;
-  int overflow;
-  int i;
-  curl_off_t value = 0;
-  curl_off_t newval;
-
-  /* Skip leading whitespace. */
-  end = (char *)nptr;
-  while(ISSPACE(end[0])) {
-    end++;
-  }
-
-  /* Handle the sign, if any. */
-  if(end[0] == '-') {
-    is_negative = 1;
-    end++;
-  }
-  else if(end[0] == '+') {
-    end++;
-  }
-  else if(end[0] == '\0') {
-    /* We had nothing but perhaps some whitespace -- there was no number. */
-    if(endptr) {
-      *endptr = end;
-    }
-    return 0;
-  }
-
-  /* Handle special beginnings, if present and allowed. */
-  if(end[0] == '0' && end[1] == 'x') {
-    if(base == 16 || base == 0) {
-      end += 2;
-      base = 16;
-    }
-  }
-  else if(end[0] == '0') {
-    if(base == 8 || base == 0) {
-      end++;
-      base = 8;
-    }
-  }
-
-  /* Matching strtol, if the base is 0 and it doesn't look like
-   * the number is octal or hex, we assume it's base 10.
-   */
-  if(base == 0) {
-    base = 10;
-  }
-
-  /* Loop handling digits. */
-  value = 0;
-  overflow = 0;
-  for(i = get_char(end[0], base);
-      i != -1;
-      end++, i = get_char(end[0], base)) {
-    newval = base * value + i;
-    if(newval < value) {
-      /* We've overflowed. */
-      overflow = 1;
-      break;
-    }
-    else
-      value = newval;
-  }
-
-  if(!overflow) {
-    if(is_negative) {
-      /* Fix the sign. */
-      value *= -1;
-    }
-  }
-  else {
-    if(is_negative)
-      value = CURL_OFF_T_MIN;
-    else
-      value = CURL_OFF_T_MAX;
-
-    SET_ERRNO(ERANGE);
-  }
-
-  if(endptr)
-    *endptr = end;
-
-  return value;
-}
-
-/**
- * Returns the value of c in the given base, or -1 if c cannot
- * be interpreted properly in that base (i.e., is out of range,
- * is a null, etc.).
- *
- * @param c     the character to interpret according to base
- * @param base  the base in which to interpret c
- *
- * @return  the value of c in base, or -1 if c isn't in range
- */
-static int get_char(char c, int base)
-{
-#ifndef NO_RANGE_TEST
-  int value = -1;
-  if(c <= '9' && c >= '0') {
-    value = c - '0';
-  }
-  else if(c <= 'Z' && c >= 'A') {
-    value = c - 'A' + 10;
-  }
-  else if(c <= 'z' && c >= 'a') {
-    value = c - 'a' + 10;
-  }
-#else
-  const char * cp;
-  int value;
-
-  cp = memchr(valchars, c, 10 + 26 + 26);
-
-  if(!cp)
-    return -1;
-
-  value = cp - valchars;
-
-  if(value >= 10 + 26)
-    value -= 26;                /* Lowercase. */
-#endif
-
-  if(value >= base) {
-    value = -1;
-  }
-
-  return value;
-}
-#endif  /* Only present if we need strtoll, but don't have it. */
diff --git a/lib/telnet.c b/lib/telnet.c
deleted file mode 100644 (file)
index 54eab1c..0000000
+++ /dev/null
@@ -1,1678 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifndef CURL_DISABLE_TELNET
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef HAVE_NET_IF_H
-#include <net/if.h>
-#endif
-#ifdef HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-
-#ifdef HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-
-#include "curl_urldata.h"
-#include <curl/curl.h>
-#include "curl_transfer.h"
-#include "curl_sendf.h"
-#include "curl_telnet.h"
-#include "curl_connect.h"
-#include "curl_progress.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#define  TELOPTS
-#define  TELCMDS
-
-#include "curl_arpa_telnet.h"
-#include "curl_memory.h"
-#include "curl_select.h"
-#include "curl_strequal.h"
-#include "curl_rawstr.h"
-#include "curl_warnless.h"
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-#define SUBBUFSIZE 512
-
-#define CURL_SB_CLEAR(x)  x->subpointer = x->subbuffer
-#define CURL_SB_TERM(x)                                 \
-  do {                                                  \
-    x->subend = x->subpointer;                          \
-    CURL_SB_CLEAR(x);                                   \
-  } WHILE_FALSE
-#define CURL_SB_ACCUM(x,c)                                   \
-  do {                                                       \
-    if(x->subpointer < (x->subbuffer+sizeof x->subbuffer))   \
-      *x->subpointer++ = (c);                                \
-  } WHILE_FALSE
-
-#define  CURL_SB_GET(x) ((*x->subpointer++)&0xff)
-#define  CURL_SB_PEEK(x)   ((*x->subpointer)&0xff)
-#define  CURL_SB_EOF(x) (x->subpointer >= x->subend)
-#define  CURL_SB_LEN(x) (x->subend - x->subpointer)
-
-#ifdef CURL_DISABLE_VERBOSE_STRINGS
-#define printoption(a,b,c,d)  Curl_nop_stmt
-#endif
-
-#ifdef USE_WINSOCK
-typedef FARPROC WSOCK2_FUNC;
-static CURLcode check_wsock2 ( struct SessionHandle *data );
-#endif
-
-static
-CURLcode telrcv(struct connectdata *,
-                const unsigned char *inbuf, /* Data received from socket */
-                ssize_t count);             /* Number of bytes received */
-
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-static void printoption(struct SessionHandle *data,
-                        const char *direction,
-                        int cmd, int option);
-#endif
-
-static void negotiate(struct connectdata *);
-static void send_negotiation(struct connectdata *, int cmd, int option);
-static void set_local_option(struct connectdata *, int cmd, int option);
-static void set_remote_option(struct connectdata *, int cmd, int option);
-
-static void printsub(struct SessionHandle *data,
-                     int direction, unsigned char *pointer,
-                     size_t length);
-static void suboption(struct connectdata *);
-static void sendsuboption(struct connectdata *conn, int option);
-
-static CURLcode telnet_do(struct connectdata *conn, bool *done);
-static CURLcode telnet_done(struct connectdata *conn,
-                                 CURLcode, bool premature);
-static CURLcode send_telnet_data(struct connectdata *conn,
-                                 char *buffer, ssize_t nread);
-
-/* For negotiation compliant to RFC 1143 */
-#define CURL_NO          0
-#define CURL_YES         1
-#define CURL_WANTYES     2
-#define CURL_WANTNO      3
-
-#define CURL_EMPTY       0
-#define CURL_OPPOSITE    1
-
-/*
- * Telnet receiver states for fsm
- */
-typedef enum
-{
-   CURL_TS_DATA = 0,
-   CURL_TS_IAC,
-   CURL_TS_WILL,
-   CURL_TS_WONT,
-   CURL_TS_DO,
-   CURL_TS_DONT,
-   CURL_TS_CR,
-   CURL_TS_SB,   /* sub-option collection */
-   CURL_TS_SE   /* looking for sub-option end */
-} TelnetReceive;
-
-struct TELNET {
-  int please_negotiate;
-  int already_negotiated;
-  int us[256];
-  int usq[256];
-  int us_preferred[256];
-  int him[256];
-  int himq[256];
-  int him_preferred[256];
-  int subnegotiation[256];
-  char subopt_ttype[32];             /* Set with suboption TTYPE */
-  char subopt_xdisploc[128];         /* Set with suboption XDISPLOC */
-  unsigned short subopt_wsx;         /* Set with suboption NAWS */
-  unsigned short subopt_wsy;         /* Set with suboption NAWS */
-  struct curl_slist *telnet_vars;    /* Environment variables */
-
-  /* suboptions */
-  unsigned char subbuffer[SUBBUFSIZE];
-  unsigned char *subpointer, *subend;      /* buffer for sub-options */
-
-  TelnetReceive telrcv_state;
-};
-
-
-/*
- * TELNET protocol handler.
- */
-
-const struct Curl_handler Curl_handler_telnet = {
-  "TELNET",                             /* scheme */
-  ZERO_NULL,                            /* setup_connection */
-  telnet_do,                            /* do_it */
-  telnet_done,                          /* done */
-  ZERO_NULL,                            /* do_more */
-  ZERO_NULL,                            /* connect_it */
-  ZERO_NULL,                            /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
-  ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* readwrite */
-  PORT_TELNET,                          /* defport */
-  CURLPROTO_TELNET,                     /* protocol */
-  PROTOPT_NONE | PROTOPT_NOURLQUERY     /* flags */
-};
-
-
-#ifdef USE_WINSOCK
-static CURLcode
-check_wsock2 ( struct SessionHandle *data )
-{
-  int err;
-  WORD wVersionRequested;
-  WSADATA wsaData;
-
-  DEBUGASSERT(data);
-
-  /* telnet requires at least WinSock 2.0 so ask for it. */
-  wVersionRequested = MAKEWORD(2, 0);
-
-  err = WSAStartup(wVersionRequested, &wsaData);
-
-  /* We must've called this once already, so this call */
-  /* should always succeed.  But, just in case... */
-  if(err != 0) {
-    failf(data,"WSAStartup failed (%d)",err);
-    return CURLE_FAILED_INIT;
-  }
-
-  /* We have to have a WSACleanup call for every successful */
-  /* WSAStartup call. */
-  WSACleanup();
-
-  /* Check that our version is supported */
-  if(LOBYTE(wsaData.wVersion) != LOBYTE(wVersionRequested) ||
-      HIBYTE(wsaData.wVersion) != HIBYTE(wVersionRequested)) {
-      /* Our version isn't supported */
-      failf(data,"insufficient winsock version to support "
-            "telnet");
-      return CURLE_FAILED_INIT;
-  }
-
-  /* Our version is supported */
-  return CURLE_OK;
-}
-#endif
-
-static
-CURLcode init_telnet(struct connectdata *conn)
-{
-  struct TELNET *tn;
-
-  tn = calloc(1, sizeof(struct TELNET));
-  if(!tn)
-    return CURLE_OUT_OF_MEMORY;
-
-  conn->data->state.proto.telnet = (void *)tn; /* make us known */
-
-  tn->telrcv_state = CURL_TS_DATA;
-
-  /* Init suboptions */
-  CURL_SB_CLEAR(tn);
-
-  /* Set the options we want by default */
-  tn->us_preferred[CURL_TELOPT_SGA] = CURL_YES;
-  tn->him_preferred[CURL_TELOPT_SGA] = CURL_YES;
-
-  /* To be compliant with previous releases of libcurl
-     we enable this option by default. This behaviour
-         can be changed thanks to the "BINARY" option in
-         CURLOPT_TELNETOPTIONS
-  */
-  tn->us_preferred[CURL_TELOPT_BINARY] = CURL_YES;
-  tn->him_preferred[CURL_TELOPT_BINARY] = CURL_YES;
-
-  /* We must allow the server to echo what we sent
-         but it is not necessary to request the server
-         to do so (it might forces the server to close
-         the connection). Hence, we ignore ECHO in the
-         negotiate function
-  */
-  tn->him_preferred[CURL_TELOPT_ECHO] = CURL_YES;
-
-  /* Set the subnegotiation fields to send information
-    just after negotiation passed (do/will)
-
-     Default values are (0,0) initialized by calloc.
-     According to the RFC1013 it is valid:
-     A value equal to zero is acceptable for the width (or height),
-         and means that no character width (or height) is being sent.
-         In this case, the width (or height) that will be assumed by the
-         Telnet server is operating system specific (it will probably be
-         based upon the terminal type information that may have been sent
-         using the TERMINAL TYPE Telnet option). */
-  tn->subnegotiation[CURL_TELOPT_NAWS] = CURL_YES;
-  return CURLE_OK;
-}
-
-static void negotiate(struct connectdata *conn)
-{
-  int i;
-  struct TELNET *tn = (struct TELNET *) conn->data->state.proto.telnet;
-
-  for(i = 0;i < CURL_NTELOPTS;i++) {
-    if(i==CURL_TELOPT_ECHO)
-      continue;
-
-    if(tn->us_preferred[i] == CURL_YES)
-      set_local_option(conn, i, CURL_YES);
-
-    if(tn->him_preferred[i] == CURL_YES)
-      set_remote_option(conn, i, CURL_YES);
-  }
-}
-
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-static void printoption(struct SessionHandle *data,
-                        const char *direction, int cmd, int option)
-{
-  const char *fmt;
-  const char *opt;
-
-  if(data->set.verbose) {
-    if(cmd == CURL_IAC) {
-      if(CURL_TELCMD_OK(option))
-        infof(data, "%s IAC %s\n", direction, CURL_TELCMD(option));
-      else
-        infof(data, "%s IAC %d\n", direction, option);
-    }
-    else {
-      fmt = (cmd == CURL_WILL) ? "WILL" : (cmd == CURL_WONT) ? "WONT" :
-        (cmd == CURL_DO) ? "DO" : (cmd == CURL_DONT) ? "DONT" : 0;
-      if(fmt) {
-        if(CURL_TELOPT_OK(option))
-          opt = CURL_TELOPT(option);
-        else if(option == CURL_TELOPT_EXOPL)
-          opt = "EXOPL";
-        else
-          opt = NULL;
-
-        if(opt)
-          infof(data, "%s %s %s\n", direction, fmt, opt);
-        else
-          infof(data, "%s %s %d\n", direction, fmt, option);
-      }
-      else
-        infof(data, "%s %d %d\n", direction, cmd, option);
-    }
-  }
-}
-#endif
-
-static void send_negotiation(struct connectdata *conn, int cmd, int option)
-{
-   unsigned char buf[3];
-   ssize_t bytes_written;
-   int err;
-   struct SessionHandle *data = conn->data;
-
-   buf[0] = CURL_IAC;
-   buf[1] = (unsigned char)cmd;
-   buf[2] = (unsigned char)option;
-
-   bytes_written = swrite(conn->sock[FIRSTSOCKET], buf, 3);
-   if(bytes_written < 0) {
-     err = SOCKERRNO;
-     failf(data,"Sending data failed (%d)",err);
-   }
-
-   printoption(conn->data, "SENT", cmd, option);
-}
-
-static
-void set_remote_option(struct connectdata *conn, int option, int newstate)
-{
-  struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
-  if(newstate == CURL_YES) {
-    switch(tn->him[option]) {
-    case CURL_NO:
-      tn->him[option] = CURL_WANTYES;
-      send_negotiation(conn, CURL_DO, option);
-      break;
-
-    case CURL_YES:
-      /* Already enabled */
-      break;
-
-    case CURL_WANTNO:
-      switch(tn->himq[option]) {
-      case CURL_EMPTY:
-        /* Already negotiating for CURL_YES, queue the request */
-        tn->himq[option] = CURL_OPPOSITE;
-        break;
-      case CURL_OPPOSITE:
-        /* Error: already queued an enable request */
-        break;
-      }
-      break;
-
-    case CURL_WANTYES:
-      switch(tn->himq[option]) {
-      case CURL_EMPTY:
-        /* Error: already negotiating for enable */
-        break;
-      case CURL_OPPOSITE:
-        tn->himq[option] = CURL_EMPTY;
-        break;
-      }
-      break;
-    }
-  }
-  else { /* NO */
-    switch(tn->him[option]) {
-    case CURL_NO:
-      /* Already disabled */
-      break;
-
-    case CURL_YES:
-      tn->him[option] = CURL_WANTNO;
-      send_negotiation(conn, CURL_DONT, option);
-      break;
-
-    case CURL_WANTNO:
-      switch(tn->himq[option]) {
-      case CURL_EMPTY:
-        /* Already negotiating for NO */
-        break;
-      case CURL_OPPOSITE:
-        tn->himq[option] = CURL_EMPTY;
-        break;
-      }
-      break;
-
-    case CURL_WANTYES:
-      switch(tn->himq[option]) {
-      case CURL_EMPTY:
-        tn->himq[option] = CURL_OPPOSITE;
-        break;
-      case CURL_OPPOSITE:
-        break;
-      }
-      break;
-    }
-  }
-}
-
-static
-void rec_will(struct connectdata *conn, int option)
-{
-  struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
-  switch(tn->him[option]) {
-  case CURL_NO:
-    if(tn->him_preferred[option] == CURL_YES) {
-      tn->him[option] = CURL_YES;
-      send_negotiation(conn, CURL_DO, option);
-    }
-    else
-      send_negotiation(conn, CURL_DONT, option);
-
-    break;
-
-  case CURL_YES:
-    /* Already enabled */
-    break;
-
-  case CURL_WANTNO:
-    switch(tn->himq[option]) {
-    case CURL_EMPTY:
-      /* Error: DONT answered by WILL */
-      tn->him[option] = CURL_NO;
-      break;
-    case CURL_OPPOSITE:
-      /* Error: DONT answered by WILL */
-      tn->him[option] = CURL_YES;
-      tn->himq[option] = CURL_EMPTY;
-      break;
-    }
-    break;
-
-  case CURL_WANTYES:
-    switch(tn->himq[option]) {
-    case CURL_EMPTY:
-      tn->him[option] = CURL_YES;
-      break;
-    case CURL_OPPOSITE:
-      tn->him[option] = CURL_WANTNO;
-      tn->himq[option] = CURL_EMPTY;
-      send_negotiation(conn, CURL_DONT, option);
-      break;
-    }
-    break;
-  }
-}
-
-static
-void rec_wont(struct connectdata *conn, int option)
-{
-  struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
-  switch(tn->him[option]) {
-  case CURL_NO:
-    /* Already disabled */
-    break;
-
-  case CURL_YES:
-    tn->him[option] = CURL_NO;
-    send_negotiation(conn, CURL_DONT, option);
-    break;
-
-  case CURL_WANTNO:
-    switch(tn->himq[option]) {
-    case CURL_EMPTY:
-      tn->him[option] = CURL_NO;
-      break;
-
-    case CURL_OPPOSITE:
-      tn->him[option] = CURL_WANTYES;
-      tn->himq[option] = CURL_EMPTY;
-      send_negotiation(conn, CURL_DO, option);
-      break;
-    }
-    break;
-
-  case CURL_WANTYES:
-    switch(tn->himq[option]) {
-    case CURL_EMPTY:
-      tn->him[option] = CURL_NO;
-      break;
-    case CURL_OPPOSITE:
-      tn->him[option] = CURL_NO;
-      tn->himq[option] = CURL_EMPTY;
-      break;
-    }
-    break;
-  }
-}
-
-static void
-set_local_option(struct connectdata *conn, int option, int newstate)
-{
-  struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
-  if(newstate == CURL_YES) {
-    switch(tn->us[option]) {
-    case CURL_NO:
-      tn->us[option] = CURL_WANTYES;
-      send_negotiation(conn, CURL_WILL, option);
-      break;
-
-    case CURL_YES:
-      /* Already enabled */
-      break;
-
-    case CURL_WANTNO:
-      switch(tn->usq[option]) {
-      case CURL_EMPTY:
-        /* Already negotiating for CURL_YES, queue the request */
-        tn->usq[option] = CURL_OPPOSITE;
-        break;
-      case CURL_OPPOSITE:
-        /* Error: already queued an enable request */
-        break;
-      }
-      break;
-
-    case CURL_WANTYES:
-      switch(tn->usq[option]) {
-      case CURL_EMPTY:
-        /* Error: already negotiating for enable */
-        break;
-      case CURL_OPPOSITE:
-        tn->usq[option] = CURL_EMPTY;
-        break;
-      }
-      break;
-    }
-  }
-  else { /* NO */
-    switch(tn->us[option]) {
-    case CURL_NO:
-      /* Already disabled */
-      break;
-
-    case CURL_YES:
-      tn->us[option] = CURL_WANTNO;
-      send_negotiation(conn, CURL_WONT, option);
-      break;
-
-    case CURL_WANTNO:
-      switch(tn->usq[option]) {
-      case CURL_EMPTY:
-        /* Already negotiating for NO */
-        break;
-      case CURL_OPPOSITE:
-        tn->usq[option] = CURL_EMPTY;
-        break;
-      }
-      break;
-
-    case CURL_WANTYES:
-      switch(tn->usq[option]) {
-      case CURL_EMPTY:
-        tn->usq[option] = CURL_OPPOSITE;
-        break;
-      case CURL_OPPOSITE:
-        break;
-      }
-      break;
-    }
-  }
-}
-
-static
-void rec_do(struct connectdata *conn, int option)
-{
-  struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
-  switch(tn->us[option]) {
-  case CURL_NO:
-    if(tn->us_preferred[option] == CURL_YES) {
-      tn->us[option] = CURL_YES;
-      send_negotiation(conn, CURL_WILL, option);
-      if(tn->subnegotiation[option] == CURL_YES)
-        /* transmission of data option */
-        sendsuboption(conn, option);
-    }
-    else if(tn->subnegotiation[option] == CURL_YES) {
-      /* send information to achieve this option*/
-      tn->us[option] = CURL_YES;
-      send_negotiation(conn, CURL_WILL, option);
-      sendsuboption(conn, option);
-    }
-    else
-      send_negotiation(conn, CURL_WONT, option);
-    break;
-
-  case CURL_YES:
-    /* Already enabled */
-    break;
-
-  case CURL_WANTNO:
-    switch(tn->usq[option]) {
-    case CURL_EMPTY:
-      /* Error: DONT answered by WILL */
-      tn->us[option] = CURL_NO;
-      break;
-    case CURL_OPPOSITE:
-      /* Error: DONT answered by WILL */
-      tn->us[option] = CURL_YES;
-      tn->usq[option] = CURL_EMPTY;
-      break;
-    }
-    break;
-
-  case CURL_WANTYES:
-    switch(tn->usq[option]) {
-    case CURL_EMPTY:
-      tn->us[option] = CURL_YES;
-      if(tn->subnegotiation[option] == CURL_YES) {
-        /* transmission of data option */
-        sendsuboption(conn, option);
-      }
-      break;
-    case CURL_OPPOSITE:
-      tn->us[option] = CURL_WANTNO;
-      tn->himq[option] = CURL_EMPTY;
-      send_negotiation(conn, CURL_WONT, option);
-      break;
-    }
-    break;
-  }
-}
-
-static
-void rec_dont(struct connectdata *conn, int option)
-{
-  struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
-  switch(tn->us[option]) {
-  case CURL_NO:
-    /* Already disabled */
-    break;
-
-  case CURL_YES:
-    tn->us[option] = CURL_NO;
-    send_negotiation(conn, CURL_WONT, option);
-    break;
-
-  case CURL_WANTNO:
-    switch(tn->usq[option]) {
-    case CURL_EMPTY:
-      tn->us[option] = CURL_NO;
-      break;
-
-    case CURL_OPPOSITE:
-      tn->us[option] = CURL_WANTYES;
-      tn->usq[option] = CURL_EMPTY;
-      send_negotiation(conn, CURL_WILL, option);
-      break;
-    }
-    break;
-
-  case CURL_WANTYES:
-    switch(tn->usq[option]) {
-    case CURL_EMPTY:
-      tn->us[option] = CURL_NO;
-      break;
-    case CURL_OPPOSITE:
-      tn->us[option] = CURL_NO;
-      tn->usq[option] = CURL_EMPTY;
-      break;
-    }
-    break;
-  }
-}
-
-
-static void printsub(struct SessionHandle *data,
-                     int direction,             /* '<' or '>' */
-                     unsigned char *pointer,    /* where suboption data is */
-                     size_t length)             /* length of suboption data */
-{
-  unsigned int i = 0;
-  unsigned short *pval;
-
-  if(data->set.verbose) {
-    if(direction) {
-      infof(data, "%s IAC SB ", (direction == '<')? "RCVD":"SENT");
-      if(length >= 3) {
-        int j;
-
-        i = pointer[length-2];
-        j = pointer[length-1];
-
-        if(i != CURL_IAC || j != CURL_SE) {
-          infof(data, "(terminated by ");
-          if(CURL_TELOPT_OK(i))
-            infof(data, "%s ", CURL_TELOPT(i));
-          else if(CURL_TELCMD_OK(i))
-            infof(data, "%s ", CURL_TELCMD(i));
-          else
-            infof(data, "%u ", i);
-          if(CURL_TELOPT_OK(j))
-            infof(data, "%s", CURL_TELOPT(j));
-          else if(CURL_TELCMD_OK(j))
-            infof(data, "%s", CURL_TELCMD(j));
-          else
-            infof(data, "%d", j);
-          infof(data, ", not IAC SE!) ");
-        }
-      }
-      length -= 2;
-    }
-    if(length < 1) {
-      infof(data, "(Empty suboption?)");
-      return;
-    }
-
-    if(CURL_TELOPT_OK(pointer[0])) {
-      switch(pointer[0]) {
-      case CURL_TELOPT_TTYPE:
-      case CURL_TELOPT_XDISPLOC:
-      case CURL_TELOPT_NEW_ENVIRON:
-      case CURL_TELOPT_NAWS:
-        infof(data, "%s", CURL_TELOPT(pointer[0]));
-        break;
-      default:
-        infof(data, "%s (unsupported)", CURL_TELOPT(pointer[0]));
-        break;
-      }
-    }
-    else
-      infof(data, "%d (unknown)", pointer[i]);
-
-    switch(pointer[0]) {
-    case CURL_TELOPT_NAWS:
-      pval = (unsigned short*)(pointer+1);
-      infof(data, "Width: %hu ; Height: %hu",
-            ntohs(pval[0]), ntohs(pval[1]));
-      break;
-    default:
-      switch(pointer[1]) {
-      case CURL_TELQUAL_IS:
-        infof(data, " IS");
-        break;
-      case CURL_TELQUAL_SEND:
-        infof(data, " SEND");
-        break;
-      case CURL_TELQUAL_INFO:
-        infof(data, " INFO/REPLY");
-        break;
-      case CURL_TELQUAL_NAME:
-        infof(data, " NAME");
-        break;
-      }
-
-      switch(pointer[0]) {
-      case CURL_TELOPT_TTYPE:
-      case CURL_TELOPT_XDISPLOC:
-        pointer[length] = 0;
-        infof(data, " \"%s\"", &pointer[2]);
-        break;
-      case CURL_TELOPT_NEW_ENVIRON:
-        if(pointer[1] == CURL_TELQUAL_IS) {
-          infof(data, " ");
-          for(i = 3;i < length;i++) {
-            switch(pointer[i]) {
-            case CURL_NEW_ENV_VAR:
-              infof(data, ", ");
-              break;
-            case CURL_NEW_ENV_VALUE:
-              infof(data, " = ");
-              break;
-            default:
-              infof(data, "%c", pointer[i]);
-              break;
-            }
-          }
-        }
-        break;
-      default:
-        for(i = 2; i < length; i++)
-          infof(data, " %.2x", pointer[i]);
-        break;
-      }
-    }
-    if(direction)
-      infof(data, "\n");
-  }
-}
-
-static CURLcode check_telnet_options(struct connectdata *conn)
-{
-  struct curl_slist *head;
-  struct curl_slist *beg;
-  char option_keyword[128];
-  char option_arg[256];
-  struct SessionHandle *data = conn->data;
-  struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
-  CURLcode result = CURLE_OK;
-  int binary_option;
-
-  /* Add the user name as an environment variable if it
-     was given on the command line */
-  if(conn->bits.user_passwd) {
-    snprintf(option_arg, sizeof(option_arg), "USER,%s", conn->user);
-    beg = curl_slist_append(tn->telnet_vars, option_arg);
-    if(!beg) {
-      curl_slist_free_all(tn->telnet_vars);
-      tn->telnet_vars = NULL;
-      return CURLE_OUT_OF_MEMORY;
-    }
-    tn->telnet_vars = beg;
-    tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
-  }
-
-  for(head = data->set.telnet_options; head; head=head->next) {
-    if(sscanf(head->data, "%127[^= ]%*[ =]%255s",
-              option_keyword, option_arg) == 2) {
-
-      /* Terminal type */
-      if(Curl_raw_equal(option_keyword, "TTYPE")) {
-        strncpy(tn->subopt_ttype, option_arg, 31);
-        tn->subopt_ttype[31] = 0; /* String termination */
-        tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES;
-        continue;
-      }
-
-      /* Display variable */
-      if(Curl_raw_equal(option_keyword, "XDISPLOC")) {
-        strncpy(tn->subopt_xdisploc, option_arg, 127);
-        tn->subopt_xdisploc[127] = 0; /* String termination */
-        tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES;
-        continue;
-      }
-
-      /* Environment variable */
-      if(Curl_raw_equal(option_keyword, "NEW_ENV")) {
-        beg = curl_slist_append(tn->telnet_vars, option_arg);
-        if(!beg) {
-          result = CURLE_OUT_OF_MEMORY;
-          break;
-        }
-        tn->telnet_vars = beg;
-        tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
-        continue;
-      }
-
-          /* Window Size */
-      if(Curl_raw_equal(option_keyword, "WS")) {
-        if(sscanf(option_arg, "%hu%*[xX]%hu",
-                  &tn->subopt_wsx, &tn->subopt_wsy) == 2)
-          tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES;
-        else {
-          failf(data, "Syntax error in telnet option: %s", head->data);
-          result = CURLE_TELNET_OPTION_SYNTAX;
-          break;
-        }
-        continue;
-      }
-
-      /* To take care or not of the 8th bit in data exchange */
-      if(Curl_raw_equal(option_keyword, "BINARY")) {
-        binary_option=atoi(option_arg);
-        if(binary_option!=1) {
-          tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO;
-          tn->him_preferred[CURL_TELOPT_BINARY] = CURL_NO;
-        }
-        continue;
-      }
-
-      failf(data, "Unknown telnet option %s", head->data);
-      result = CURLE_UNKNOWN_TELNET_OPTION;
-      break;
-    }
-    else {
-      failf(data, "Syntax error in telnet option: %s", head->data);
-      result = CURLE_TELNET_OPTION_SYNTAX;
-      break;
-    }
-  }
-
-  if(result) {
-    curl_slist_free_all(tn->telnet_vars);
-    tn->telnet_vars = NULL;
-  }
-
-  return result;
-}
-
-/*
- * suboption()
- *
- * Look at the sub-option buffer, and try to be helpful to the other
- * side.
- */
-
-static void suboption(struct connectdata *conn)
-{
-  struct curl_slist *v;
-  unsigned char temp[2048];
-  ssize_t bytes_written;
-  size_t len;
-  size_t tmplen;
-  int err;
-  char varname[128];
-  char varval[128];
-  struct SessionHandle *data = conn->data;
-  struct TELNET *tn = (struct TELNET *)data->state.proto.telnet;
-
-  printsub(data, '<', (unsigned char *)tn->subbuffer, CURL_SB_LEN(tn)+2);
-  switch (CURL_SB_GET(tn)) {
-    case CURL_TELOPT_TTYPE:
-      len = strlen(tn->subopt_ttype) + 4 + 2;
-      snprintf((char *)temp, sizeof(temp),
-               "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_TTYPE,
-               CURL_TELQUAL_IS, tn->subopt_ttype, CURL_IAC, CURL_SE);
-      bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
-      if(bytes_written < 0) {
-        err = SOCKERRNO;
-        failf(data,"Sending data failed (%d)",err);
-      }
-      printsub(data, '>', &temp[2], len-2);
-      break;
-    case CURL_TELOPT_XDISPLOC:
-      len = strlen(tn->subopt_xdisploc) + 4 + 2;
-      snprintf((char *)temp, sizeof(temp),
-               "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_XDISPLOC,
-               CURL_TELQUAL_IS, tn->subopt_xdisploc, CURL_IAC, CURL_SE);
-      bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
-      if(bytes_written < 0) {
-        err = SOCKERRNO;
-        failf(data,"Sending data failed (%d)",err);
-      }
-      printsub(data, '>', &temp[2], len-2);
-      break;
-    case CURL_TELOPT_NEW_ENVIRON:
-      snprintf((char *)temp, sizeof(temp),
-               "%c%c%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_NEW_ENVIRON,
-               CURL_TELQUAL_IS);
-      len = 4;
-
-      for(v = tn->telnet_vars;v;v = v->next) {
-        tmplen = (strlen(v->data) + 1);
-        /* Add the variable only if it fits */
-        if(len + tmplen < (int)sizeof(temp)-6) {
-          sscanf(v->data, "%127[^,],%127s", varname, varval);
-          snprintf((char *)&temp[len], sizeof(temp) - len,
-                   "%c%s%c%s", CURL_NEW_ENV_VAR, varname,
-                   CURL_NEW_ENV_VALUE, varval);
-          len += tmplen;
-        }
-      }
-      snprintf((char *)&temp[len], sizeof(temp) - len,
-               "%c%c", CURL_IAC, CURL_SE);
-      len += 2;
-      bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
-      if(bytes_written < 0) {
-        err = SOCKERRNO;
-        failf(data,"Sending data failed (%d)",err);
-      }
-      printsub(data, '>', &temp[2], len-2);
-      break;
-  }
-  return;
-}
-
-
-/*
- * sendsuboption()
- *
- * Send suboption information to the server side.
- */
-
-static void sendsuboption(struct connectdata *conn, int option)
-{
-  ssize_t bytes_written;
-  int err;
-  unsigned short x, y;
-  unsigned char*uc1, *uc2;
-
-  struct SessionHandle *data = conn->data;
-  struct TELNET *tn = (struct TELNET *)data->state.proto.telnet;
-
-  switch (option) {
-  case CURL_TELOPT_NAWS:
-    /* We prepare data to be sent */
-    CURL_SB_CLEAR(tn);
-    CURL_SB_ACCUM(tn, CURL_IAC);
-    CURL_SB_ACCUM(tn, CURL_SB);
-    CURL_SB_ACCUM(tn, CURL_TELOPT_NAWS);
-    /* We must deal either with litte or big endien processors */
-    /* Window size must be sent according to the 'network order' */
-    x=htons(tn->subopt_wsx);
-    y=htons(tn->subopt_wsy);
-    uc1 = (unsigned char*)&x;
-    uc2 = (unsigned char*)&y;
-    CURL_SB_ACCUM(tn, uc1[0]);
-    CURL_SB_ACCUM(tn, uc1[1]);
-    CURL_SB_ACCUM(tn, uc2[0]);
-    CURL_SB_ACCUM(tn, uc2[1]);
-
-    CURL_SB_ACCUM(tn, CURL_IAC);
-    CURL_SB_ACCUM(tn, CURL_SE);
-    CURL_SB_TERM(tn);
-    /* data suboption is now ready */
-
-    printsub(data, '>', (unsigned char *)tn->subbuffer+2,
-             CURL_SB_LEN(tn)-2);
-
-    /* we send the header of the suboption... */
-    bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer, 3);
-    if(bytes_written < 0) {
-      err = SOCKERRNO;
-      failf(data, "Sending data failed (%d)", err);
-    }
-    /* ... then the window size with the send_telnet_data() function
-       to deal with 0xFF cases ... */
-    send_telnet_data(conn, (char *)tn->subbuffer+3, 4);
-    /* ... and the footer */
-    bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer+7, 2);
-    if(bytes_written < 0) {
-      err = SOCKERRNO;
-      failf(data, "Sending data failed (%d)", err);
-    }
-    break;
-  }
-}
-
-
-static
-CURLcode telrcv(struct connectdata *conn,
-                const unsigned char *inbuf, /* Data received from socket */
-                ssize_t count)              /* Number of bytes received */
-{
-  unsigned char c;
-  CURLcode result;
-  int in = 0;
-  int startwrite=-1;
-  struct SessionHandle *data = conn->data;
-  struct TELNET *tn = (struct TELNET *)data->state.proto.telnet;
-
-#define startskipping()                                       \
-  if(startwrite >= 0) {                                       \
-    result = Curl_client_write(conn,                          \
-                               CLIENTWRITE_BODY,              \
-                               (char *)&inbuf[startwrite],    \
-                               in-startwrite);                \
-    if(result != CURLE_OK)                                    \
-      return result;                                          \
-  }                                                           \
-  startwrite = -1
-
-#define writebyte() \
-    if(startwrite < 0) \
-      startwrite = in
-
-#define bufferflush() startskipping()
-
-  while(count--) {
-    c = inbuf[in];
-
-    switch (tn->telrcv_state) {
-    case CURL_TS_CR:
-      tn->telrcv_state = CURL_TS_DATA;
-      if(c == '\0') {
-        startskipping();
-        break;   /* Ignore \0 after CR */
-      }
-      writebyte();
-      break;
-
-    case CURL_TS_DATA:
-      if(c == CURL_IAC) {
-        tn->telrcv_state = CURL_TS_IAC;
-        startskipping();
-        break;
-      }
-      else if(c == '\r')
-        tn->telrcv_state = CURL_TS_CR;
-      writebyte();
-      break;
-
-    case CURL_TS_IAC:
-    process_iac:
-      DEBUGASSERT(startwrite < 0);
-      switch (c) {
-      case CURL_WILL:
-        tn->telrcv_state = CURL_TS_WILL;
-        break;
-      case CURL_WONT:
-        tn->telrcv_state = CURL_TS_WONT;
-        break;
-      case CURL_DO:
-        tn->telrcv_state = CURL_TS_DO;
-        break;
-      case CURL_DONT:
-        tn->telrcv_state = CURL_TS_DONT;
-        break;
-      case CURL_SB:
-        CURL_SB_CLEAR(tn);
-        tn->telrcv_state = CURL_TS_SB;
-        break;
-      case CURL_IAC:
-        tn->telrcv_state = CURL_TS_DATA;
-        writebyte();
-        break;
-      case CURL_DM:
-      case CURL_NOP:
-      case CURL_GA:
-      default:
-        tn->telrcv_state = CURL_TS_DATA;
-        printoption(data, "RCVD", CURL_IAC, c);
-        break;
-      }
-      break;
-
-      case CURL_TS_WILL:
-        printoption(data, "RCVD", CURL_WILL, c);
-        tn->please_negotiate = 1;
-        rec_will(conn, c);
-        tn->telrcv_state = CURL_TS_DATA;
-        break;
-
-      case CURL_TS_WONT:
-        printoption(data, "RCVD", CURL_WONT, c);
-        tn->please_negotiate = 1;
-        rec_wont(conn, c);
-        tn->telrcv_state = CURL_TS_DATA;
-        break;
-
-      case CURL_TS_DO:
-        printoption(data, "RCVD", CURL_DO, c);
-        tn->please_negotiate = 1;
-        rec_do(conn, c);
-        tn->telrcv_state = CURL_TS_DATA;
-        break;
-
-      case CURL_TS_DONT:
-        printoption(data, "RCVD", CURL_DONT, c);
-        tn->please_negotiate = 1;
-        rec_dont(conn, c);
-        tn->telrcv_state = CURL_TS_DATA;
-        break;
-
-      case CURL_TS_SB:
-        if(c == CURL_IAC)
-          tn->telrcv_state = CURL_TS_SE;
-        else
-          CURL_SB_ACCUM(tn,c);
-        break;
-
-      case CURL_TS_SE:
-        if(c != CURL_SE) {
-          if(c != CURL_IAC) {
-            /*
-             * This is an error.  We only expect to get "IAC IAC" or "IAC SE".
-             * Several things may have happened.  An IAC was not doubled, the
-             * IAC SE was left off, or another option got inserted into the
-             * suboption are all possibilities.  If we assume that the IAC was
-             * not doubled, and really the IAC SE was left off, we could get
-             * into an infinate loop here.  So, instead, we terminate the
-             * suboption, and process the partial suboption if we can.
-             */
-            CURL_SB_ACCUM(tn, CURL_IAC);
-            CURL_SB_ACCUM(tn, c);
-            tn->subpointer -= 2;
-            CURL_SB_TERM(tn);
-
-            printoption(data, "In SUBOPTION processing, RCVD", CURL_IAC, c);
-            suboption(conn);   /* handle sub-option */
-            tn->telrcv_state = CURL_TS_IAC;
-            goto process_iac;
-          }
-          CURL_SB_ACCUM(tn,c);
-          tn->telrcv_state = CURL_TS_SB;
-        }
-        else
-        {
-          CURL_SB_ACCUM(tn, CURL_IAC);
-          CURL_SB_ACCUM(tn, CURL_SE);
-          tn->subpointer -= 2;
-          CURL_SB_TERM(tn);
-          suboption(conn);   /* handle sub-option */
-          tn->telrcv_state = CURL_TS_DATA;
-        }
-        break;
-    }
-    ++in;
-  }
-  bufferflush();
-  return CURLE_OK;
-}
-
-/* Escape and send a telnet data block */
-/* TODO: write large chunks of data instead of one byte at a time */
-static CURLcode send_telnet_data(struct connectdata *conn,
-                                 char *buffer, ssize_t nread)
-{
-  unsigned char outbuf[2];
-  ssize_t bytes_written, total_written;
-  int out_count;
-  CURLcode rc = CURLE_OK;
-
-  while(rc == CURLE_OK && nread--) {
-    outbuf[0] = *buffer++;
-    out_count = 1;
-    if(outbuf[0] == CURL_IAC)
-      outbuf[out_count++] = CURL_IAC;
-
-    total_written = 0;
-    do {
-      /* Make sure socket is writable to avoid EWOULDBLOCK condition */
-      struct pollfd pfd[1];
-      pfd[0].fd = conn->sock[FIRSTSOCKET];
-      pfd[0].events = POLLOUT;
-      switch (Curl_poll(pfd, 1, -1)) {
-        case -1:                    /* error, abort writing */
-        case 0:                     /* timeout (will never happen) */
-          rc = CURLE_SEND_ERROR;
-          break;
-        default:                    /* write! */
-          bytes_written = 0;
-          rc = Curl_write(conn, conn->sock[FIRSTSOCKET], outbuf+total_written,
-                          out_count-total_written, &bytes_written);
-          total_written += bytes_written;
-          break;
-      }
-    /* handle partial write */
-    } while(rc == CURLE_OK && total_written < out_count);
-  }
-  return rc;
-}
-
-static CURLcode telnet_done(struct connectdata *conn,
-                                 CURLcode status, bool premature)
-{
-  struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
-  (void)status; /* unused */
-  (void)premature; /* not used */
-
-  if(!tn)
-    return CURLE_OK;
-
-  curl_slist_free_all(tn->telnet_vars);
-  tn->telnet_vars = NULL;
-
-  Curl_safefree(conn->data->state.proto.telnet);
-
-  return CURLE_OK;
-}
-
-static CURLcode telnet_do(struct connectdata *conn, bool *done)
-{
-  CURLcode code;
-  struct SessionHandle *data = conn->data;
-  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
-#ifdef USE_WINSOCK
-  HMODULE wsock2;
-  WSOCK2_FUNC close_event_func;
-  WSOCK2_FUNC create_event_func;
-  WSOCK2_FUNC event_select_func;
-  WSOCK2_FUNC enum_netevents_func;
-  WSAEVENT event_handle;
-  WSANETWORKEVENTS events;
-  HANDLE stdin_handle;
-  HANDLE objs[2];
-  DWORD  obj_count;
-  DWORD  wait_timeout;
-  DWORD waitret;
-  DWORD readfile_read;
-  int err;
-#else
-  int interval_ms;
-  struct pollfd pfd[2];
-  int poll_cnt;
-  curl_off_t total_dl = 0;
-  curl_off_t total_ul = 0;
-#endif
-  ssize_t nread;
-  struct timeval now;
-  bool keepon = TRUE;
-  char *buf = data->state.buffer;
-  struct TELNET *tn;
-
-  *done = TRUE; /* unconditionally */
-
-  code = init_telnet(conn);
-  if(code)
-    return code;
-
-  tn = (struct TELNET *)data->state.proto.telnet;
-
-  code = check_telnet_options(conn);
-  if(code)
-    return code;
-
-#ifdef USE_WINSOCK
-  /*
-  ** This functionality only works with WinSock >= 2.0.  So,
-  ** make sure have it.
-  */
-  code = check_wsock2(data);
-  if(code)
-    return code;
-
-  /* OK, so we have WinSock 2.0.  We need to dynamically */
-  /* load ws2_32.dll and get the function pointers we need. */
-  wsock2 = LoadLibrary(TEXT("WS2_32.DLL"));
-  if(wsock2 == NULL) {
-    failf(data,"failed to load WS2_32.DLL (%d)", ERRNO);
-    return CURLE_FAILED_INIT;
-  }
-
-  /* Grab a pointer to WSACreateEvent */
-  create_event_func = GetProcAddress(wsock2,"WSACreateEvent");
-  if(create_event_func == NULL) {
-    failf(data,"failed to find WSACreateEvent function (%d)",
-          ERRNO);
-    FreeLibrary(wsock2);
-    return CURLE_FAILED_INIT;
-  }
-
-  /* And WSACloseEvent */
-  close_event_func = GetProcAddress(wsock2,"WSACloseEvent");
-  if(close_event_func == NULL) {
-    failf(data,"failed to find WSACloseEvent function (%d)",
-          ERRNO);
-    FreeLibrary(wsock2);
-    return CURLE_FAILED_INIT;
-  }
-
-  /* And WSAEventSelect */
-  event_select_func = GetProcAddress(wsock2,"WSAEventSelect");
-  if(event_select_func == NULL) {
-    failf(data,"failed to find WSAEventSelect function (%d)",
-          ERRNO);
-    FreeLibrary(wsock2);
-    return CURLE_FAILED_INIT;
-  }
-
-  /* And WSAEnumNetworkEvents */
-  enum_netevents_func = GetProcAddress(wsock2,"WSAEnumNetworkEvents");
-  if(enum_netevents_func == NULL) {
-    failf(data,"failed to find WSAEnumNetworkEvents function (%d)",
-          ERRNO);
-    FreeLibrary(wsock2);
-    return CURLE_FAILED_INIT;
-  }
-
-  /* We want to wait for both stdin and the socket. Since
-  ** the select() function in winsock only works on sockets
-  ** we have to use the WaitForMultipleObjects() call.
-  */
-
-  /* First, create a sockets event object */
-  event_handle = (WSAEVENT)create_event_func();
-  if(event_handle == WSA_INVALID_EVENT) {
-    failf(data,"WSACreateEvent failed (%d)", SOCKERRNO);
-    FreeLibrary(wsock2);
-    return CURLE_FAILED_INIT;
-  }
-
-  /* Tell winsock what events we want to listen to */
-  if(event_select_func(sockfd, event_handle, FD_READ|FD_CLOSE) ==
-     SOCKET_ERROR) {
-    close_event_func(event_handle);
-    FreeLibrary(wsock2);
-    return CURLE_OK;
-  }
-
-  /* The get the Windows file handle for stdin */
-  stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
-
-  /* Create the list of objects to wait for */
-  objs[0] = event_handle;
-  objs[1] = stdin_handle;
-
-  /* If stdin_handle is a pipe, use PeekNamedPipe() method to check it,
-     else use the old WaitForMultipleObjects() way */
-  if(GetFileType(stdin_handle) == FILE_TYPE_PIPE ||
-     data->set.is_fread_set) {
-    /* Don't wait for stdin_handle, just wait for event_handle */
-    obj_count = 1;
-    /* Check stdin_handle per 100 milliseconds */
-    wait_timeout = 100;
-  }
-  else {
-    obj_count = 2;
-    wait_timeout = 1000;
-  }
-
-  /* Keep on listening and act on events */
-  while(keepon) {
-    waitret = WaitForMultipleObjects(obj_count, objs, FALSE, wait_timeout);
-    switch(waitret) {
-    case WAIT_TIMEOUT:
-    {
-      for(;;) {
-        if(obj_count == 1) {
-          /* read from user-supplied method */
-          code = (int)conn->fread_func(buf, 1, BUFSIZE - 1, conn->fread_in);
-          if(code == CURL_READFUNC_ABORT) {
-            keepon = FALSE;
-            code = CURLE_READ_ERROR;
-            break;
-          }
-
-          if(code == CURL_READFUNC_PAUSE)
-            break;
-
-          if(code == 0)                        /* no bytes */
-            break;
-
-          readfile_read = code; /* fall thru with number of bytes read */
-        }
-        else {
-          /* read from stdin */
-          if(!PeekNamedPipe(stdin_handle, NULL, 0, NULL,
-                            &readfile_read, NULL)) {
-            keepon = FALSE;
-            code = CURLE_READ_ERROR;
-            break;
-          }
-
-          if(!readfile_read)
-            break;
-
-          if(!ReadFile(stdin_handle, buf, sizeof(data->state.buffer),
-                       &readfile_read, NULL)) {
-            keepon = FALSE;
-            code = CURLE_READ_ERROR;
-            break;
-          }
-        }
-
-        code = send_telnet_data(conn, buf, readfile_read);
-        if(code) {
-          keepon = FALSE;
-          break;
-        }
-      }
-    }
-    break;
-
-    case WAIT_OBJECT_0 + 1:
-    {
-      if(!ReadFile(stdin_handle, buf, sizeof(data->state.buffer),
-                   &readfile_read, NULL)) {
-        keepon = FALSE;
-        code = CURLE_READ_ERROR;
-        break;
-      }
-
-      code = send_telnet_data(conn, buf, readfile_read);
-      if(code) {
-        keepon = FALSE;
-        break;
-      }
-    }
-    break;
-
-    case WAIT_OBJECT_0:
-
-      if(SOCKET_ERROR == enum_netevents_func(sockfd, event_handle, &events)) {
-        if((err = SOCKERRNO) != EINPROGRESS) {
-          infof(data,"WSAEnumNetworkEvents failed (%d)", err);
-          keepon = FALSE;
-          code = CURLE_READ_ERROR;
-        }
-        break;
-      }
-      if(events.lNetworkEvents & FD_READ) {
-        /* read data from network */
-        code = Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread);
-        /* read would've blocked. Loop again */
-        if(code == CURLE_AGAIN)
-          break;
-        /* returned not-zero, this an error */
-        else if(code) {
-          keepon = FALSE;
-          break;
-        }
-        /* returned zero but actually received 0 or less here,
-           the server closed the connection and we bail out */
-        else if(nread <= 0) {
-          keepon = FALSE;
-          break;
-        }
-
-        code = telrcv(conn, (unsigned char *)buf, nread);
-        if(code) {
-          keepon = FALSE;
-          break;
-        }
-
-        /* Negotiate if the peer has started negotiating,
-           otherwise don't. We don't want to speak telnet with
-           non-telnet servers, like POP or SMTP. */
-        if(tn->please_negotiate && !tn->already_negotiated) {
-          negotiate(conn);
-          tn->already_negotiated = 1;
-        }
-      }
-      if(events.lNetworkEvents & FD_CLOSE) {
-        keepon = FALSE;
-      }
-      break;
-
-    }
-
-    if(data->set.timeout) {
-      now = Curl_tvnow();
-      if(Curl_tvdiff(now, conn->created) >= data->set.timeout) {
-        failf(data, "Time-out");
-        code = CURLE_OPERATION_TIMEDOUT;
-        keepon = FALSE;
-      }
-    }
-  }
-
-  /* We called WSACreateEvent, so call WSACloseEvent */
-  if(!close_event_func(event_handle)) {
-    infof(data,"WSACloseEvent failed (%d)", SOCKERRNO);
-  }
-
-  /* "Forget" pointers into the library we're about to free */
-  create_event_func = NULL;
-  close_event_func = NULL;
-  event_select_func = NULL;
-  enum_netevents_func = NULL;
-
-  /* We called LoadLibrary, so call FreeLibrary */
-  if(!FreeLibrary(wsock2))
-    infof(data,"FreeLibrary(wsock2) failed (%d)", ERRNO);
-#else
-  pfd[0].fd = sockfd;
-  pfd[0].events = POLLIN;
-
-  if(conn->fread_func != (curl_read_callback)fread) {
-    poll_cnt = 1;
-    interval_ms = 100; /* poll user-supplied read function */
-  }
-  else {
-    /* really using fread, so infile is a FILE* */
-    pfd[1].fd = fileno((FILE *)conn->fread_in);
-    pfd[1].events = POLLIN;
-    poll_cnt = 2;
-    interval_ms = 1 * 1000;
-  }
-
-  while(keepon) {
-    switch (Curl_poll(pfd, poll_cnt, interval_ms)) {
-    case -1:                    /* error, stop reading */
-      keepon = FALSE;
-      continue;
-    case 0:                     /* timeout */
-      pfd[0].revents = 0;
-      pfd[1].revents = 0;
-      /* fall through */
-    default:                    /* read! */
-      if(pfd[0].revents & POLLIN) {
-        /* read data from network */
-        code = Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread);
-        /* read would've blocked. Loop again */
-        if(code == CURLE_AGAIN)
-          break;
-        /* returned not-zero, this an error */
-        else if(code) {
-          keepon = FALSE;
-          break;
-        }
-        /* returned zero but actually received 0 or less here,
-           the server closed the connection and we bail out */
-        else if(nread <= 0) {
-          keepon = FALSE;
-          break;
-        }
-
-        total_dl += nread;
-        Curl_pgrsSetDownloadCounter(data, total_dl);
-        code = telrcv(conn, (unsigned char *)buf, nread);
-        if(code) {
-          keepon = FALSE;
-          break;
-        }
-
-        /* Negotiate if the peer has started negotiating,
-           otherwise don't. We don't want to speak telnet with
-           non-telnet servers, like POP or SMTP. */
-        if(tn->please_negotiate && !tn->already_negotiated) {
-          negotiate(conn);
-          tn->already_negotiated = 1;
-        }
-      }
-
-      nread = 0;
-      if(poll_cnt == 2) {
-        if(pfd[1].revents & POLLIN) { /* read from in file */
-          nread = read(pfd[1].fd, buf, BUFSIZE - 1);
-        }
-      }
-      else {
-        /* read from user-supplied method */
-        nread = (int)conn->fread_func(buf, 1, BUFSIZE - 1, conn->fread_in);
-        if(nread == CURL_READFUNC_ABORT) {
-          keepon = FALSE;
-          break;
-        }
-        if(nread == CURL_READFUNC_PAUSE)
-          break;
-      }
-
-      if(nread > 0) {
-        code = send_telnet_data(conn, buf, nread);
-        if(code) {
-          keepon = FALSE;
-          break;
-        }
-        total_ul += nread;
-        Curl_pgrsSetUploadCounter(data, total_ul);
-      }
-      else if(nread < 0)
-        keepon = FALSE;
-
-      break;
-    } /* poll switch statement */
-
-    if(data->set.timeout) {
-      now = Curl_tvnow();
-      if(Curl_tvdiff(now, conn->created) >= data->set.timeout) {
-        failf(data, "Time-out");
-        code = CURLE_OPERATION_TIMEDOUT;
-        keepon = FALSE;
-      }
-    }
-
-    if(Curl_pgrsUpdate(conn)) {
-      code = CURLE_ABORTED_BY_CALLBACK;
-      break;
-    }
-  }
-#endif
-  /* mark this as "no further transfer wanted" */
-  Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
-
-  return code;
-}
-#endif
diff --git a/lib/tftp.c b/lib/tftp.c
deleted file mode 100644 (file)
index 1af246e..0000000
+++ /dev/null
@@ -1,1500 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifndef CURL_DISABLE_TFTP
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef HAVE_NET_IF_H
-#include <net/if.h>
-#endif
-#ifdef HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-
-#ifdef HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-
-#include "curl_urldata.h"
-#include <curl/curl.h>
-#include "curl_transfer.h"
-#include "curl_sendf.h"
-#include "curl_tftp.h"
-#include "curl_progress.h"
-#include "curl_connect.h"
-#include "curl_strerror.h"
-#include "curl_sockaddr.h" /* required for Curl_sockaddr_storage */
-#include "curl_multiif.h"
-#include "curl_url.h"
-#include "curl_rawstr.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-#include "curl_select.h"
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/* RFC2348 allows the block size to be negotiated */
-#define TFTP_BLKSIZE_DEFAULT 512
-#define TFTP_BLKSIZE_MIN 8
-#define TFTP_BLKSIZE_MAX 65464
-#define TFTP_OPTION_BLKSIZE "blksize"
-
-/* from RFC2349: */
-#define TFTP_OPTION_TSIZE    "tsize"
-#define TFTP_OPTION_INTERVAL "timeout"
-
-typedef enum {
-  TFTP_MODE_NETASCII=0,
-  TFTP_MODE_OCTET
-} tftp_mode_t;
-
-typedef enum {
-  TFTP_STATE_START=0,
-  TFTP_STATE_RX,
-  TFTP_STATE_TX,
-  TFTP_STATE_FIN
-} tftp_state_t;
-
-typedef enum {
-  TFTP_EVENT_NONE = -1,
-  TFTP_EVENT_INIT = 0,
-  TFTP_EVENT_RRQ = 1,
-  TFTP_EVENT_WRQ = 2,
-  TFTP_EVENT_DATA = 3,
-  TFTP_EVENT_ACK = 4,
-  TFTP_EVENT_ERROR = 5,
-  TFTP_EVENT_OACK = 6,
-  TFTP_EVENT_TIMEOUT
-} tftp_event_t;
-
-typedef enum {
-  TFTP_ERR_UNDEF=0,
-  TFTP_ERR_NOTFOUND,
-  TFTP_ERR_PERM,
-  TFTP_ERR_DISKFULL,
-  TFTP_ERR_ILLEGAL,
-  TFTP_ERR_UNKNOWNID,
-  TFTP_ERR_EXISTS,
-  TFTP_ERR_NOSUCHUSER,  /* This will never be triggered by this code */
-
-  /* The remaining error codes are internal to curl */
-  TFTP_ERR_NONE = -100,
-  TFTP_ERR_TIMEOUT,
-  TFTP_ERR_NORESPONSE
-} tftp_error_t;
-
-typedef struct tftp_packet {
-  unsigned char *data;
-} tftp_packet_t;
-
-typedef struct tftp_state_data {
-  tftp_state_t    state;
-  tftp_mode_t     mode;
-  tftp_error_t    error;
-  tftp_event_t    event;
-  struct connectdata      *conn;
-  curl_socket_t   sockfd;
-  int             retries;
-  int             retry_time;
-  int             retry_max;
-  time_t          start_time;
-  time_t          max_time;
-  time_t          rx_time;
-  unsigned short  block;
-  struct Curl_sockaddr_storage   local_addr;
-  struct Curl_sockaddr_storage   remote_addr;
-  curl_socklen_t  remote_addrlen;
-  int             rbytes;
-  int             sbytes;
-  int             blksize;
-  int             requested_blksize;
-  tftp_packet_t   rpacket;
-  tftp_packet_t   spacket;
-} tftp_state_data_t;
-
-
-/* Forward declarations */
-static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event) ;
-static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event) ;
-static CURLcode tftp_connect(struct connectdata *conn, bool *done);
-static CURLcode tftp_disconnect(struct connectdata *conn,
-                                bool dead_connection);
-static CURLcode tftp_do(struct connectdata *conn, bool *done);
-static CURLcode tftp_done(struct connectdata *conn,
-                          CURLcode, bool premature);
-static CURLcode tftp_setup_connection(struct connectdata * conn);
-static CURLcode tftp_multi_statemach(struct connectdata *conn, bool *done);
-static CURLcode tftp_doing(struct connectdata *conn, bool *dophase_done);
-static int tftp_getsock(struct connectdata *conn, curl_socket_t *socks,
-                        int numsocks);
-static CURLcode tftp_translate_code(tftp_error_t error);
-
-
-/*
- * TFTP protocol handler.
- */
-
-const struct Curl_handler Curl_handler_tftp = {
-  "TFTP",                               /* scheme */
-  tftp_setup_connection,                /* setup_connection */
-  tftp_do,                              /* do_it */
-  tftp_done,                            /* done */
-  ZERO_NULL,                            /* do_more */
-  tftp_connect,                         /* connect_it */
-  tftp_multi_statemach,                 /* connecting */
-  tftp_doing,                           /* doing */
-  tftp_getsock,                         /* proto_getsock */
-  tftp_getsock,                         /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
-  tftp_disconnect,                      /* disconnect */
-  ZERO_NULL,                            /* readwrite */
-  PORT_TFTP,                            /* defport */
-  CURLPROTO_TFTP,                       /* protocol */
-  PROTOPT_NONE | PROTOPT_NOURLQUERY     /* flags */
-};
-
-/**********************************************************
- *
- * tftp_set_timeouts -
- *
- * Set timeouts based on state machine state.
- * Use user provided connect timeouts until DATA or ACK
- * packet is received, then use user-provided transfer timeouts
- *
- *
- **********************************************************/
-static CURLcode tftp_set_timeouts(tftp_state_data_t *state)
-{
-  time_t maxtime, timeout;
-  long timeout_ms;
-  bool start = (state->state == TFTP_STATE_START) ? TRUE : FALSE;
-
-  time(&state->start_time);
-
-  /* Compute drop-dead time */
-  timeout_ms = Curl_timeleft(state->conn->data, NULL, start);
-
-  if(timeout_ms < 0) {
-    /* time-out, bail out, go home */
-    failf(state->conn->data, "Connection time-out");
-    return CURLE_OPERATION_TIMEDOUT;
-  }
-
-  if(start) {
-
-    maxtime = (time_t)(timeout_ms + 500) / 1000;
-    state->max_time = state->start_time+maxtime;
-
-    /* Set per-block timeout to total */
-    timeout = maxtime ;
-
-    /* Average restart after 5 seconds */
-    state->retry_max = (int)timeout/5;
-
-    if(state->retry_max < 1)
-      /* avoid division by zero below */
-      state->retry_max = 1;
-
-    /* Compute the re-start interval to suit the timeout */
-    state->retry_time = (int)timeout/state->retry_max;
-    if(state->retry_time<1)
-      state->retry_time=1;
-
-  }
-  else {
-    if(timeout_ms > 0)
-      maxtime = (time_t)(timeout_ms + 500) / 1000;
-    else
-      maxtime = 3600;
-
-    state->max_time = state->start_time+maxtime;
-
-    /* Set per-block timeout to total */
-    timeout = maxtime;
-
-    /* Average reposting an ACK after 5 seconds */
-    state->retry_max = (int)timeout/5;
-  }
-  /* But bound the total number */
-  if(state->retry_max<3)
-    state->retry_max=3;
-
-  if(state->retry_max>50)
-    state->retry_max=50;
-
-  /* Compute the re-ACK interval to suit the timeout */
-  state->retry_time = (int)(timeout/state->retry_max);
-  if(state->retry_time<1)
-    state->retry_time=1;
-
-  infof(state->conn->data,
-        "set timeouts for state %d; Total %ld, retry %d maxtry %d\n",
-        (int)state->state, (long)(state->max_time-state->start_time),
-        state->retry_time, state->retry_max);
-
-  /* init RX time */
-  time(&state->rx_time);
-
-  return CURLE_OK;
-}
-
-/**********************************************************
- *
- * tftp_set_send_first
- *
- * Event handler for the START state
- *
- **********************************************************/
-
-static void setpacketevent(tftp_packet_t *packet, unsigned short num)
-{
-  packet->data[0] = (unsigned char)(num >> 8);
-  packet->data[1] = (unsigned char)(num & 0xff);
-}
-
-
-static void setpacketblock(tftp_packet_t *packet, unsigned short num)
-{
-  packet->data[2] = (unsigned char)(num >> 8);
-  packet->data[3] = (unsigned char)(num & 0xff);
-}
-
-static unsigned short getrpacketevent(const tftp_packet_t *packet)
-{
-  return (unsigned short)((packet->data[0] << 8) | packet->data[1]);
-}
-
-static unsigned short getrpacketblock(const tftp_packet_t *packet)
-{
-  return (unsigned short)((packet->data[2] << 8) | packet->data[3]);
-}
-
-static size_t Curl_strnlen(const char *string, size_t maxlen)
-{
-  const char *end = memchr (string, '\0', maxlen);
-  return end ? (size_t) (end - string) : maxlen;
-}
-
-static const char *tftp_option_get(const char *buf, size_t len,
-                                   const char **option, const char **value)
-{
-  size_t loc;
-
-  loc = Curl_strnlen( buf, len );
-  loc++; /* NULL term */
-
-  if(loc >= len)
-    return NULL;
-  *option = buf;
-
-  loc += Curl_strnlen( buf+loc, len-loc );
-  loc++; /* NULL term */
-
-  if(loc > len)
-    return NULL;
-  *value = &buf[strlen(*option) + 1];
-
-  return &buf[loc];
-}
-
-static CURLcode tftp_parse_option_ack(tftp_state_data_t *state,
-                                      const char *ptr, int len)
-{
-  const char *tmp = ptr;
-  struct SessionHandle *data = state->conn->data;
-
-  /* if OACK doesn't contain blksize option, the default (512) must be used */
-  state->blksize = TFTP_BLKSIZE_DEFAULT;
-
-  while(tmp < ptr + len) {
-    const char *option, *value;
-
-    tmp = tftp_option_get(tmp, ptr + len - tmp, &option, &value);
-    if(tmp == NULL) {
-      failf(data, "Malformed ACK packet, rejecting");
-      return CURLE_TFTP_ILLEGAL;
-    }
-
-    infof(data, "got option=(%s) value=(%s)\n", option, value);
-
-    if(checkprefix(option, TFTP_OPTION_BLKSIZE)) {
-      long blksize;
-
-      blksize = strtol( value, NULL, 10 );
-
-      if(!blksize) {
-        failf(data, "invalid blocksize value in OACK packet");
-        return CURLE_TFTP_ILLEGAL;
-      }
-      else if(blksize > TFTP_BLKSIZE_MAX) {
-        failf(data, "%s (%d)", "blksize is larger than max supported",
-              TFTP_BLKSIZE_MAX);
-        return CURLE_TFTP_ILLEGAL;
-      }
-      else if(blksize < TFTP_BLKSIZE_MIN) {
-        failf(data, "%s (%d)", "blksize is smaller than min supported",
-              TFTP_BLKSIZE_MIN);
-        return CURLE_TFTP_ILLEGAL;
-      }
-      else if(blksize > state->requested_blksize) {
-        /* could realloc pkt buffers here, but the spec doesn't call out
-         * support for the server requesting a bigger blksize than the client
-         * requests */
-        failf(data, "%s (%ld)",
-              "server requested blksize larger than allocated", blksize);
-        return CURLE_TFTP_ILLEGAL;
-      }
-
-      state->blksize = (int)blksize;
-      infof(data, "%s (%d) %s (%d)\n", "blksize parsed from OACK",
-            state->blksize, "requested", state->requested_blksize);
-    }
-    else if(checkprefix(option, TFTP_OPTION_TSIZE)) {
-      long tsize = 0;
-
-      tsize = strtol( value, NULL, 10 );
-      infof(data, "%s (%ld)\n", "tsize parsed from OACK", tsize);
-
-      /* tsize should be ignored on upload: Who cares about the size of the
-         remote file? */
-      if(!data->set.upload) {
-        if(!tsize) {
-          failf(data, "invalid tsize -:%s:- value in OACK packet", value);
-          return CURLE_TFTP_ILLEGAL;
-        }
-        Curl_pgrsSetDownloadSize(data, tsize);
-      }
-    }
-  }
-
-  return CURLE_OK;
-}
-
-static size_t tftp_option_add(tftp_state_data_t *state, size_t csize,
-                              char *buf, const char *option)
-{
-  if(( strlen(option) + csize + 1 ) > (size_t)state->blksize)
-    return 0;
-  strcpy(buf, option);
-  return( strlen(option) + 1 );
-}
-
-static CURLcode tftp_connect_for_tx(tftp_state_data_t *state,
-                                    tftp_event_t event)
-{
-  CURLcode res;
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-  struct SessionHandle *data = state->conn->data;
-
-  infof(data, "%s\n", "Connected for transmit");
-#endif
-  state->state = TFTP_STATE_TX;
-  res = tftp_set_timeouts(state);
-  if(res != CURLE_OK)
-    return(res);
-  return tftp_tx(state, event);
-}
-
-static CURLcode tftp_connect_for_rx(tftp_state_data_t *state,
-                                    tftp_event_t event)
-{
-  CURLcode res;
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-  struct SessionHandle *data = state->conn->data;
-
-  infof(data, "%s\n", "Connected for receive");
-#endif
-  state->state = TFTP_STATE_RX;
-  res = tftp_set_timeouts(state);
-  if(res != CURLE_OK)
-    return(res);
-  return tftp_rx(state, event);
-}
-
-static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event)
-{
-  size_t sbytes;
-  ssize_t senddata;
-  const char *mode = "octet";
-  char *filename;
-  char buf[64];
-  struct SessionHandle *data = state->conn->data;
-  CURLcode res = CURLE_OK;
-
-  /* Set ascii mode if -B flag was used */
-  if(data->set.prefer_ascii)
-    mode = "netascii";
-
-  switch(event) {
-
-  case TFTP_EVENT_INIT:    /* Send the first packet out */
-  case TFTP_EVENT_TIMEOUT: /* Resend the first packet out */
-    /* Increment the retry counter, quit if over the limit */
-    state->retries++;
-    if(state->retries>state->retry_max) {
-      state->error = TFTP_ERR_NORESPONSE;
-      state->state = TFTP_STATE_FIN;
-      return res;
-    }
-
-    if(data->set.upload) {
-      /* If we are uploading, send an WRQ */
-      setpacketevent(&state->spacket, TFTP_EVENT_WRQ);
-      state->conn->data->req.upload_fromhere =
-        (char *)state->spacket.data+4;
-      if(data->set.infilesize != -1)
-        Curl_pgrsSetUploadSize(data, data->set.infilesize);
-    }
-    else {
-      /* If we are downloading, send an RRQ */
-      setpacketevent(&state->spacket, TFTP_EVENT_RRQ);
-    }
-    /* As RFC3617 describes the separator slash is not actually part of the
-       file name so we skip the always-present first letter of the path
-       string. */
-    filename = curl_easy_unescape(data, &state->conn->data->state.path[1], 0,
-                                  NULL);
-    if(!filename)
-      return CURLE_OUT_OF_MEMORY;
-
-    snprintf((char *)state->spacket.data+2,
-             state->blksize,
-             "%s%c%s%c", filename, '\0',  mode, '\0');
-    sbytes = 4 + strlen(filename) + strlen(mode);
-
-    /* add tsize option */
-    if(data->set.upload && (data->set.infilesize != -1))
-      snprintf( buf, sizeof(buf), "%" FORMAT_OFF_T, data->set.infilesize );
-    else
-      strcpy(buf, "0"); /* the destination is large enough */
-
-    sbytes += tftp_option_add(state, sbytes,
-                              (char *)state->spacket.data+sbytes,
-                              TFTP_OPTION_TSIZE);
-    sbytes += tftp_option_add(state, sbytes,
-                              (char *)state->spacket.data+sbytes, buf);
-    /* add blksize option */
-    snprintf( buf, sizeof(buf), "%d", state->requested_blksize );
-    sbytes += tftp_option_add(state, sbytes,
-                              (char *)state->spacket.data+sbytes,
-                              TFTP_OPTION_BLKSIZE);
-    sbytes += tftp_option_add(state, sbytes,
-                              (char *)state->spacket.data+sbytes, buf );
-
-    /* add timeout option */
-    snprintf( buf, sizeof(buf), "%d", state->retry_time);
-    sbytes += tftp_option_add(state, sbytes,
-                              (char *)state->spacket.data+sbytes,
-                              TFTP_OPTION_INTERVAL);
-    sbytes += tftp_option_add(state, sbytes,
-                              (char *)state->spacket.data+sbytes, buf );
-
-    /* the typecase for the 3rd argument is mostly for systems that do
-       not have a size_t argument, like older unixes that want an 'int' */
-    senddata = sendto(state->sockfd, (void *)state->spacket.data,
-                      (SEND_TYPE_ARG3)sbytes, 0,
-                      state->conn->ip_addr->ai_addr,
-                      state->conn->ip_addr->ai_addrlen);
-    if(senddata != (ssize_t)sbytes) {
-      failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
-    }
-    Curl_safefree(filename);
-    break;
-
-  case TFTP_EVENT_OACK:
-    if(data->set.upload) {
-      res = tftp_connect_for_tx(state, event);
-    }
-    else {
-      res = tftp_connect_for_rx(state, event);
-    }
-    break;
-
-  case TFTP_EVENT_ACK: /* Connected for transmit */
-    res = tftp_connect_for_tx(state, event);
-    break;
-
-  case TFTP_EVENT_DATA: /* Connected for receive */
-    res = tftp_connect_for_rx(state, event);
-    break;
-
-  case TFTP_EVENT_ERROR:
-    state->state = TFTP_STATE_FIN;
-    break;
-
-  default:
-    failf(state->conn->data, "tftp_send_first: internal error");
-    break;
-  }
-  return res;
-}
-
-/* the next blocknum is x + 1 but it needs to wrap at an unsigned 16bit
-   boundary */
-#define NEXT_BLOCKNUM(x) (((x)+1)&0xffff)
-
-/**********************************************************
- *
- * tftp_rx
- *
- * Event handler for the RX state
- *
- **********************************************************/
-static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event)
-{
-  ssize_t sbytes;
-  int rblock;
-  struct SessionHandle *data = state->conn->data;
-
-  switch(event) {
-
-  case TFTP_EVENT_DATA:
-    /* Is this the block we expect? */
-    rblock = getrpacketblock(&state->rpacket);
-    if(NEXT_BLOCKNUM(state->block) == rblock) {
-      /* This is the expected block.  Reset counters and ACK it. */
-      state->retries = 0;
-    }
-    else if(state->block == rblock) {
-      /* This is the last recently received block again. Log it and ACK it
-         again. */
-      infof(data, "Received last DATA packet block %d again.\n", rblock);
-    }
-    else {
-      /* totally unexpected, just log it */
-      infof(data,
-            "Received unexpected DATA packet block %d, expecting block %d\n",
-            rblock, NEXT_BLOCKNUM(state->block));
-      break;
-    }
-
-    /* ACK this block. */
-    state->block = (unsigned short)rblock;
-    setpacketevent(&state->spacket, TFTP_EVENT_ACK);
-    setpacketblock(&state->spacket, state->block);
-    sbytes = sendto(state->sockfd, (void *)state->spacket.data,
-                    4, SEND_4TH_ARG,
-                    (struct sockaddr *)&state->remote_addr,
-                    state->remote_addrlen);
-    if(sbytes < 0) {
-      failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
-      return CURLE_SEND_ERROR;
-    }
-
-    /* Check if completed (That is, a less than full packet is received) */
-    if(state->rbytes < (ssize_t)state->blksize+4) {
-      state->state = TFTP_STATE_FIN;
-    }
-    else {
-      state->state = TFTP_STATE_RX;
-    }
-    time(&state->rx_time);
-    break;
-
-  case TFTP_EVENT_OACK:
-    /* ACK option acknowledgement so we can move on to data */
-    state->block = 0;
-    state->retries = 0;
-    setpacketevent(&state->spacket, TFTP_EVENT_ACK);
-    setpacketblock(&state->spacket, state->block);
-    sbytes = sendto(state->sockfd, (void *)state->spacket.data,
-                    4, SEND_4TH_ARG,
-                    (struct sockaddr *)&state->remote_addr,
-                    state->remote_addrlen);
-    if(sbytes < 0) {
-      failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
-      return CURLE_SEND_ERROR;
-    }
-
-    /* we're ready to RX data */
-    state->state = TFTP_STATE_RX;
-    time(&state->rx_time);
-    break;
-
-  case TFTP_EVENT_TIMEOUT:
-    /* Increment the retry count and fail if over the limit */
-    state->retries++;
-    infof(data,
-          "Timeout waiting for block %d ACK.  Retries = %d\n",
-          NEXT_BLOCKNUM(state->block), state->retries);
-    if(state->retries > state->retry_max) {
-      state->error = TFTP_ERR_TIMEOUT;
-      state->state = TFTP_STATE_FIN;
-    }
-    else {
-      /* Resend the previous ACK */
-      sbytes = sendto(state->sockfd, (void *)state->spacket.data,
-                      4, SEND_4TH_ARG,
-                      (struct sockaddr *)&state->remote_addr,
-                      state->remote_addrlen);
-      if(sbytes<0) {
-        failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
-        return CURLE_SEND_ERROR;
-      }
-    }
-    break;
-
-  case TFTP_EVENT_ERROR:
-    setpacketevent(&state->spacket, TFTP_EVENT_ERROR);
-    setpacketblock(&state->spacket, state->block);
-    (void)sendto(state->sockfd, (void *)state->spacket.data,
-                 4, SEND_4TH_ARG,
-                 (struct sockaddr *)&state->remote_addr,
-                 state->remote_addrlen);
-    /* don't bother with the return code, but if the socket is still up we
-     * should be a good TFTP client and let the server know we're done */
-    state->state = TFTP_STATE_FIN;
-    break;
-
-  default:
-    failf(data, "%s", "tftp_rx: internal error");
-    return CURLE_TFTP_ILLEGAL; /* not really the perfect return code for
-                                  this */
-  }
-  return CURLE_OK;
-}
-
-/**********************************************************
- *
- * tftp_tx
- *
- * Event handler for the TX state
- *
- **********************************************************/
-static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event)
-{
-  struct SessionHandle *data = state->conn->data;
-  ssize_t sbytes;
-  int rblock;
-  CURLcode res = CURLE_OK;
-  struct SingleRequest *k = &data->req;
-
-  switch(event) {
-
-  case TFTP_EVENT_ACK:
-  case TFTP_EVENT_OACK:
-    if(event == TFTP_EVENT_ACK) {
-      /* Ack the packet */
-      rblock = getrpacketblock(&state->rpacket);
-
-      if(rblock != state->block &&
-         /* There's a bug in tftpd-hpa that causes it to send us an ack for
-          * 65535 when the block number wraps to 0. So when we're expecting
-          * 0, also accept 65535. See
-          * http://syslinux.zytor.com/archives/2010-September/015253.html
-          * */
-         !(state->block == 0 && rblock == 65535)) {
-        /* This isn't the expected block.  Log it and up the retry counter */
-        infof(data, "Received ACK for block %d, expecting %d\n",
-              rblock, state->block);
-        state->retries++;
-        /* Bail out if over the maximum */
-        if(state->retries>state->retry_max) {
-          failf(data, "tftp_tx: giving up waiting for block %d ack",
-                state->block);
-          res = CURLE_SEND_ERROR;
-        }
-        else {
-          /* Re-send the data packet */
-          sbytes = sendto(state->sockfd, (void *)state->spacket.data,
-                          4+state->sbytes, SEND_4TH_ARG,
-                          (struct sockaddr *)&state->remote_addr,
-                          state->remote_addrlen);
-          /* Check all sbytes were sent */
-          if(sbytes<0) {
-            failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
-            res = CURLE_SEND_ERROR;
-          }
-        }
-        return res;
-      }
-      /* This is the expected packet.  Reset the counters and send the next
-         block */
-      time(&state->rx_time);
-      state->block++;
-    }
-    else
-      state->block = 1; /* first data block is 1 when using OACK */
-
-    state->retries = 0;
-    setpacketevent(&state->spacket, TFTP_EVENT_DATA);
-    setpacketblock(&state->spacket, state->block);
-    if(state->block > 1 && state->sbytes < (int)state->blksize) {
-      state->state = TFTP_STATE_FIN;
-      return CURLE_OK;
-    }
-    res = Curl_fillreadbuffer(state->conn, state->blksize, &state->sbytes);
-    if(res)
-      return res;
-    sbytes = sendto(state->sockfd, (void *)state->spacket.data,
-                    4+state->sbytes, SEND_4TH_ARG,
-                    (struct sockaddr *)&state->remote_addr,
-                    state->remote_addrlen);
-    /* Check all sbytes were sent */
-    if(sbytes<0) {
-      failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
-      return CURLE_SEND_ERROR;
-    }
-    /* Update the progress meter */
-    k->writebytecount += state->sbytes;
-    Curl_pgrsSetUploadCounter(data, k->writebytecount);
-    break;
-
-  case TFTP_EVENT_TIMEOUT:
-    /* Increment the retry counter and log the timeout */
-    state->retries++;
-    infof(data, "Timeout waiting for block %d ACK. "
-          " Retries = %d\n", NEXT_BLOCKNUM(state->block), state->retries);
-    /* Decide if we've had enough */
-    if(state->retries > state->retry_max) {
-      state->error = TFTP_ERR_TIMEOUT;
-      state->state = TFTP_STATE_FIN;
-    }
-    else {
-      /* Re-send the data packet */
-      sbytes = sendto(state->sockfd, (void *)state->spacket.data,
-                      4+state->sbytes, SEND_4TH_ARG,
-                      (struct sockaddr *)&state->remote_addr,
-                      state->remote_addrlen);
-      /* Check all sbytes were sent */
-      if(sbytes<0) {
-        failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
-        return CURLE_SEND_ERROR;
-      }
-      /* since this was a re-send, we remain at the still byte position */
-      Curl_pgrsSetUploadCounter(data, k->writebytecount);
-    }
-    break;
-
-  case TFTP_EVENT_ERROR:
-    state->state = TFTP_STATE_FIN;
-    setpacketevent(&state->spacket, TFTP_EVENT_ERROR);
-    setpacketblock(&state->spacket, state->block);
-    (void)sendto(state->sockfd, (void *)state->spacket.data, 4, SEND_4TH_ARG,
-                 (struct sockaddr *)&state->remote_addr,
-                 state->remote_addrlen);
-    /* don't bother with the return code, but if the socket is still up we
-     * should be a good TFTP client and let the server know we're done */
-    state->state = TFTP_STATE_FIN;
-    break;
-
-  default:
-    failf(data, "tftp_tx: internal error, event: %i", (int)(event));
-    break;
-  }
-
-  return res;
-}
-
-/**********************************************************
- *
- * tftp_translate_code
- *
- * Translate internal error codes to CURL error codes
- *
- **********************************************************/
-static CURLcode tftp_translate_code(tftp_error_t error)
-{
-  CURLcode code = CURLE_OK;
-
-  if(error != TFTP_ERR_NONE) {
-    switch(error) {
-    case TFTP_ERR_NOTFOUND:
-      code = CURLE_TFTP_NOTFOUND;
-      break;
-    case TFTP_ERR_PERM:
-      code = CURLE_TFTP_PERM;
-      break;
-    case TFTP_ERR_DISKFULL:
-      code = CURLE_REMOTE_DISK_FULL;
-      break;
-    case TFTP_ERR_UNDEF:
-    case TFTP_ERR_ILLEGAL:
-      code = CURLE_TFTP_ILLEGAL;
-      break;
-    case TFTP_ERR_UNKNOWNID:
-      code = CURLE_TFTP_UNKNOWNID;
-      break;
-    case TFTP_ERR_EXISTS:
-      code = CURLE_REMOTE_FILE_EXISTS;
-      break;
-    case TFTP_ERR_NOSUCHUSER:
-      code = CURLE_TFTP_NOSUCHUSER;
-      break;
-    case TFTP_ERR_TIMEOUT:
-      code = CURLE_OPERATION_TIMEDOUT;
-      break;
-    case TFTP_ERR_NORESPONSE:
-      code = CURLE_COULDNT_CONNECT;
-      break;
-    default:
-      code= CURLE_ABORTED_BY_CALLBACK;
-      break;
-    }
-  }
-  else {
-    code = CURLE_OK;
-  }
-
-  return(code);
-}
-
-/**********************************************************
- *
- * tftp_state_machine
- *
- * The tftp state machine event dispatcher
- *
- **********************************************************/
-static CURLcode tftp_state_machine(tftp_state_data_t *state,
-                                   tftp_event_t event)
-{
-  CURLcode res = CURLE_OK;
-  struct SessionHandle *data = state->conn->data;
-  switch(state->state) {
-  case TFTP_STATE_START:
-    DEBUGF(infof(data, "TFTP_STATE_START\n"));
-    res = tftp_send_first(state, event);
-    break;
-  case TFTP_STATE_RX:
-    DEBUGF(infof(data, "TFTP_STATE_RX\n"));
-    res = tftp_rx(state, event);
-    break;
-  case TFTP_STATE_TX:
-    DEBUGF(infof(data, "TFTP_STATE_TX\n"));
-    res = tftp_tx(state, event);
-    break;
-  case TFTP_STATE_FIN:
-    infof(data, "%s\n", "TFTP finished");
-    break;
-  default:
-    DEBUGF(infof(data, "STATE: %d\n", state->state));
-    failf(data, "%s", "Internal state machine error");
-    res = CURLE_TFTP_ILLEGAL;
-    break;
-  }
-  return res;
-}
-
-/**********************************************************
- *
- * tftp_disconnect
- *
- * The disconnect callback
- *
- **********************************************************/
-static CURLcode tftp_disconnect(struct connectdata *conn, bool dead_connection)
-{
-  tftp_state_data_t *state = conn->proto.tftpc;
-  (void) dead_connection;
-
-  /* done, free dynamically allocated pkt buffers */
-  if(state) {
-    Curl_safefree(state->rpacket.data);
-    Curl_safefree(state->spacket.data);
-    free(state);
-  }
-
-  return CURLE_OK;
-}
-
-/**********************************************************
- *
- * tftp_connect
- *
- * The connect callback
- *
- **********************************************************/
-static CURLcode tftp_connect(struct connectdata *conn, bool *done)
-{
-  CURLcode code;
-  tftp_state_data_t *state;
-  int blksize, rc;
-
-  blksize = TFTP_BLKSIZE_DEFAULT;
-
-  /* If there already is a protocol-specific struct allocated for this
-     sessionhandle, deal with it */
-  Curl_reset_reqproto(conn);
-
-  state = conn->proto.tftpc = calloc(1, sizeof(tftp_state_data_t));
-  if(!state)
-    return CURLE_OUT_OF_MEMORY;
-
-  /* alloc pkt buffers based on specified blksize */
-  if(conn->data->set.tftp_blksize) {
-    blksize = (int)conn->data->set.tftp_blksize;
-    if(blksize > TFTP_BLKSIZE_MAX || blksize < TFTP_BLKSIZE_MIN )
-      return CURLE_TFTP_ILLEGAL;
-  }
-
-  if(!state->rpacket.data) {
-    state->rpacket.data = calloc(1, blksize + 2 + 2);
-
-    if(!state->rpacket.data)
-      return CURLE_OUT_OF_MEMORY;
-  }
-
-  if(!state->spacket.data) {
-    state->spacket.data = calloc(1, blksize + 2 + 2);
-
-    if(!state->spacket.data)
-      return CURLE_OUT_OF_MEMORY;
-  }
-
-  conn->bits.close = TRUE; /* we don't keep TFTP connections up bascially
-                              because there's none or very little gain for UDP
-                           */
-
-  state->conn = conn;
-  state->sockfd = state->conn->sock[FIRSTSOCKET];
-  state->state = TFTP_STATE_START;
-  state->error = TFTP_ERR_NONE;
-  state->blksize = TFTP_BLKSIZE_DEFAULT;
-  state->requested_blksize = blksize;
-
-  ((struct sockaddr *)&state->local_addr)->sa_family =
-    (unsigned short)(conn->ip_addr->ai_family);
-
-  tftp_set_timeouts(state);
-
-  if(!conn->bits.bound) {
-    /* If not already bound, bind to any interface, random UDP port. If it is
-     * reused or a custom local port was desired, this has already been done!
-     *
-     * We once used the size of the local_addr struct as the third argument
-     * for bind() to better work with IPv6 or whatever size the struct could
-     * have, but we learned that at least Tru64, AIX and IRIX *requires* the
-     * size of that argument to match the exact size of a 'sockaddr_in' struct
-     * when running IPv4-only.
-     *
-     * Therefore we use the size from the address we connected to, which we
-     * assume uses the same IP version and thus hopefully this works for both
-     * IPv4 and IPv6...
-     */
-    rc = bind(state->sockfd, (struct sockaddr *)&state->local_addr,
-              conn->ip_addr->ai_addrlen);
-    if(rc) {
-      failf(conn->data, "bind() failed; %s",
-            Curl_strerror(conn, SOCKERRNO));
-      return CURLE_COULDNT_CONNECT;
-    }
-    conn->bits.bound = TRUE;
-  }
-
-  Curl_pgrsStartNow(conn->data);
-
-  *done = TRUE;
-  code = CURLE_OK;
-  return(code);
-}
-
-/**********************************************************
- *
- * tftp_done
- *
- * The done callback
- *
- **********************************************************/
-static CURLcode tftp_done(struct connectdata *conn, CURLcode status,
-                          bool premature)
-{
-  CURLcode code = CURLE_OK;
-  tftp_state_data_t *state = (tftp_state_data_t *)conn->proto.tftpc;
-
-  (void)status; /* unused */
-  (void)premature; /* not used */
-
-  if(Curl_pgrsDone(conn))
-    return CURLE_ABORTED_BY_CALLBACK;
-
-  /* If we have encountered an error */
-  code = tftp_translate_code(state->error);
-
-  return code;
-}
-
-/**********************************************************
- *
- * tftp_getsock
- *
- * The getsock callback
- *
- **********************************************************/
-static int tftp_getsock(struct connectdata *conn, curl_socket_t *socks,
-                        int numsocks)
-{
-  if(!numsocks)
-    return GETSOCK_BLANK;
-
-  socks[0] = conn->sock[FIRSTSOCKET];
-
-  return GETSOCK_READSOCK(0);
-}
-
-/**********************************************************
- *
- * tftp_receive_packet
- *
- * Called once select fires and data is ready on the socket
- *
- **********************************************************/
-static CURLcode tftp_receive_packet(struct connectdata *conn)
-{
-  struct Curl_sockaddr_storage fromaddr;
-  curl_socklen_t        fromlen;
-  CURLcode              result = CURLE_OK;
-  struct SessionHandle  *data = conn->data;
-  tftp_state_data_t     *state = (tftp_state_data_t *)conn->proto.tftpc;
-  struct SingleRequest  *k = &data->req;
-
-  /* Receive the packet */
-  fromlen = sizeof(fromaddr);
-  state->rbytes = (int)recvfrom(state->sockfd,
-                                (void *)state->rpacket.data,
-                                state->blksize+4,
-                                0,
-                                (struct sockaddr *)&fromaddr,
-                                &fromlen);
-  if(state->remote_addrlen==0) {
-    memcpy(&state->remote_addr, &fromaddr, fromlen);
-    state->remote_addrlen = fromlen;
-  }
-
-  /* Sanity check packet length */
-  if(state->rbytes < 4) {
-    failf(data, "Received too short packet");
-    /* Not a timeout, but how best to handle it? */
-    state->event = TFTP_EVENT_TIMEOUT;
-  }
-  else {
-    /* The event is given by the TFTP packet time */
-    state->event = (tftp_event_t)getrpacketevent(&state->rpacket);
-
-    switch(state->event) {
-    case TFTP_EVENT_DATA:
-      /* Don't pass to the client empty or retransmitted packets */
-      if(state->rbytes > 4 &&
-         (NEXT_BLOCKNUM(state->block) == getrpacketblock(&state->rpacket))) {
-        result = Curl_client_write(conn, CLIENTWRITE_BODY,
-                                   (char *)state->rpacket.data+4,
-                                   state->rbytes-4);
-        if(result) {
-          tftp_state_machine(state, TFTP_EVENT_ERROR);
-          return result;
-        }
-        k->bytecount += state->rbytes-4;
-        Curl_pgrsSetDownloadCounter(data, (curl_off_t) k->bytecount);
-      }
-      break;
-    case TFTP_EVENT_ERROR:
-      state->error = (tftp_error_t)getrpacketblock(&state->rpacket);
-      infof(data, "%s\n", (const char *)state->rpacket.data+4);
-      break;
-    case TFTP_EVENT_ACK:
-      break;
-    case TFTP_EVENT_OACK:
-      result = tftp_parse_option_ack(state,
-                                     (const char *)state->rpacket.data+2,
-                                     state->rbytes-2);
-      if(result)
-        return result;
-      break;
-    case TFTP_EVENT_RRQ:
-    case TFTP_EVENT_WRQ:
-    default:
-      failf(data, "%s", "Internal error: Unexpected packet");
-      break;
-    }
-
-    /* Update the progress meter */
-    if(Curl_pgrsUpdate(conn)) {
-      tftp_state_machine(state, TFTP_EVENT_ERROR);
-      return CURLE_ABORTED_BY_CALLBACK;
-    }
-  }
-  return result;
-}
-
-/**********************************************************
- *
- * tftp_state_timeout
- *
- * Check if timeouts have been reached
- *
- **********************************************************/
-static long tftp_state_timeout(struct connectdata *conn, tftp_event_t *event)
-{
-  time_t                current;
-  tftp_state_data_t     *state = (tftp_state_data_t *)conn->proto.tftpc;
-
-  if(event)
-    *event = TFTP_EVENT_NONE;
-
-  time(&current);
-  if(current > state->max_time) {
-    DEBUGF(infof(conn->data, "timeout: %ld > %ld\n",
-                 (long)current, (long)state->max_time));
-    state->error = TFTP_ERR_TIMEOUT;
-    state->state = TFTP_STATE_FIN;
-    return 0;
-  }
-  else if(current > state->rx_time+state->retry_time) {
-    if(event)
-      *event = TFTP_EVENT_TIMEOUT;
-    time(&state->rx_time); /* update even though we received nothing */
-  }
-
-  /* there's a typecast below here since 'time_t' may in fact be larger than
-     'long', but we estimate that a 'long' will still be able to hold number
-     of seconds even if "only" 32 bit */
-  return (long)(state->max_time - current);
-}
-
-
-/**********************************************************
- *
- * tftp_easy_statemach
- *
- * Handle easy request until completion
- *
- **********************************************************/
-static CURLcode tftp_easy_statemach(struct connectdata *conn)
-{
-  int                   rc;
-  int                   check_time = 0;
-  CURLcode              result = CURLE_OK;
-  struct SessionHandle  *data = conn->data;
-  tftp_state_data_t     *state = (tftp_state_data_t *)conn->proto.tftpc;
-  curl_socket_t         fd_read;
-  long                  timeout_ms;
-  struct SingleRequest  *k = &data->req;
-  struct timeval        transaction_start = Curl_tvnow();
-
-  k->start = transaction_start;
-  k->now = transaction_start;
-
-  /* Run the TFTP State Machine */
-  for(; (state->state != TFTP_STATE_FIN) && (result == CURLE_OK); ) {
-
-    timeout_ms = state->retry_time * 1000;
-
-    if(data->set.upload) {
-      if(data->set.max_send_speed &&
-          (data->progress.ulspeed > data->set.max_send_speed)) {
-        fd_read = CURL_SOCKET_BAD;
-        timeout_ms = Curl_sleep_time(data->set.max_send_speed,
-                                     data->progress.ulspeed, state->blksize);
-      }
-      else {
-        fd_read = state->sockfd;
-      }
-    }
-    else {
-      if(data->set.max_recv_speed &&
-         (data->progress.dlspeed > data->set.max_recv_speed)) {
-        fd_read = CURL_SOCKET_BAD;
-        timeout_ms = Curl_sleep_time(data->set.max_recv_speed,
-                                     data->progress.dlspeed, state->blksize);
-      }
-      else
-        fd_read = state->sockfd;
-    }
-
-    if(data->set.timeout) {
-      timeout_ms = data->set.timeout - Curl_tvdiff(k->now, k->start);
-      if(timeout_ms > state->retry_time * 1000)
-        timeout_ms = state->retry_time * 1000;
-      else if(timeout_ms < 0)
-        timeout_ms = 0;
-    }
-
-
-    /* Wait until ready to read or timeout occurs */
-    rc = Curl_socket_ready(fd_read, CURL_SOCKET_BAD, timeout_ms);
-
-    k->now = Curl_tvnow();
-
-    /* Force a progress callback if it's been too long */
-    if(Curl_tvdiff(k->now, k->start) >= data->set.timeout) {
-      if(Curl_pgrsUpdate(conn)) {
-        tftp_state_machine(state, TFTP_EVENT_ERROR);
-        return CURLE_ABORTED_BY_CALLBACK;
-      }
-      k->start = k->now;
-    }
-
-    if(rc == -1) {
-      /* bail out */
-      int error = SOCKERRNO;
-      failf(data, "%s", Curl_strerror(conn, error));
-      state->event = TFTP_EVENT_ERROR;
-    }
-    else {
-
-      if(rc==0) {
-        /* A timeout occurred, but our timeout is variable, so maybe
-           just continue? */
-        long rtms = state->retry_time * 1000;
-        if(Curl_tvdiff(k->now, transaction_start) > rtms) {
-          state->event = TFTP_EVENT_TIMEOUT;
-          /* Force a look at transfer timeouts */
-          check_time = 1;
-        }
-        else {
-          continue; /* skip state machine */
-        }
-      }
-      else {
-        result = tftp_receive_packet(conn);
-        if(result == CURLE_OK)
-          transaction_start = Curl_tvnow();
-
-        if(k->bytecountp)
-          *k->bytecountp = k->bytecount; /* read count */
-        if(k->writebytecountp)
-          *k->writebytecountp = k->writebytecount; /* write count */
-      }
-    }
-
-    if(check_time) {
-      tftp_state_timeout(conn, NULL);
-      check_time = 0;
-    }
-
-    if(result)
-      return(result);
-
-    result = tftp_state_machine(state, state->event);
-  }
-
-  /* Tell curl we're done */
-  Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
-
-  return(result);
-}
-
-/**********************************************************
- *
- * tftp_multi_statemach
- *
- * Handle single RX socket event and return
- *
- **********************************************************/
-static CURLcode tftp_multi_statemach(struct connectdata *conn, bool *done)
-{
-  int                   rc;
-  tftp_event_t          event;
-  CURLcode              result = CURLE_OK;
-  struct SessionHandle  *data = conn->data;
-  tftp_state_data_t     *state = (tftp_state_data_t *)conn->proto.tftpc;
-  long                  timeout_ms = tftp_state_timeout(conn, &event);
-
-  *done = FALSE;
-
-  if(timeout_ms <= 0) {
-    failf(data, "TFTP response timeout");
-    return CURLE_OPERATION_TIMEDOUT;
-  }
-  else if(event != TFTP_EVENT_NONE) {
-    result = tftp_state_machine(state, event);
-    if(result != CURLE_OK)
-      return(result);
-    *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE;
-    if(*done)
-      /* Tell curl we're done */
-      Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
-  }
-  else {
-    /* no timeouts to handle, check our socket */
-    rc = Curl_socket_ready(state->sockfd, CURL_SOCKET_BAD, 0);
-
-    if(rc == -1) {
-      /* bail out */
-      int error = SOCKERRNO;
-      failf(data, "%s", Curl_strerror(conn, error));
-      state->event = TFTP_EVENT_ERROR;
-    }
-    else if(rc != 0) {
-      result = tftp_receive_packet(conn);
-      if(result != CURLE_OK)
-        return(result);
-      result = tftp_state_machine(state, state->event);
-      if(result != CURLE_OK)
-        return(result);
-      *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE;
-      if(*done)
-        /* Tell curl we're done */
-        Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
-    }
-    /* if rc == 0, then select() timed out */
-  }
-
-  return result;
-}
-
-/**********************************************************
- *
- * tftp_doing
- *
- * Called from curl_multi.c while DOing
- *
- **********************************************************/
-static CURLcode tftp_doing(struct connectdata *conn, bool *dophase_done)
-{
-  CURLcode result;
-  result = tftp_multi_statemach(conn, dophase_done);
-
-  if(*dophase_done) {
-    DEBUGF(infof(conn->data, "DO phase is complete\n"));
-  }
-  return result;
-}
-
-/**********************************************************
- *
- * tftp_peform
- *
- * Entry point for transfer from tftp_do, sarts state mach
- *
- **********************************************************/
-static CURLcode tftp_perform(struct connectdata *conn, bool *dophase_done)
-{
-  CURLcode              result = CURLE_OK;
-  tftp_state_data_t     *state = (tftp_state_data_t *)conn->proto.tftpc;
-
-  *dophase_done = FALSE;
-
-  result = tftp_state_machine(state, TFTP_EVENT_INIT);
-
-  if(state->state == TFTP_STATE_FIN || result != CURLE_OK)
-    return(result);
-
-  if(conn->data->state.used_interface == Curl_if_multi)
-    tftp_multi_statemach(conn, dophase_done);
-  else {
-    result = tftp_easy_statemach(conn);
-    *dophase_done = TRUE; /* with the easy interface we are done here */
-  }
-
-  if(*dophase_done)
-    DEBUGF(infof(conn->data, "DO phase is complete\n"));
-
-  return result;
-}
-
-
-/**********************************************************
- *
- * tftp_do
- *
- * The do callback
- *
- * This callback initiates the TFTP transfer
- *
- **********************************************************/
-
-static CURLcode tftp_do(struct connectdata *conn, bool *done)
-{
-  tftp_state_data_t     *state;
-  CURLcode              code;
-
-  *done = FALSE;
-
-  /*
-    Since connections can be re-used between SessionHandles, this might be a
-    connection already existing but on a fresh SessionHandle struct so we must
-    make sure we have a good 'struct TFTP' to play with. For new connections,
-    the struct TFTP is allocated and setup in the tftp_connect() function.
-  */
-  Curl_reset_reqproto(conn);
-
-  if(!conn->proto.tftpc) {
-    code = tftp_connect(conn, done);
-    if(code)
-      return code;
-  }
-  state = (tftp_state_data_t *)conn->proto.tftpc;
-
-  code = tftp_perform(conn, done);
-
-  /* If tftp_perform() returned an error, use that for return code. If it
-     was OK, see if tftp_translate_code() has an error. */
-  if(code == CURLE_OK)
-    /* If we have encountered an internal tftp error, translate it. */
-    code = tftp_translate_code(state->error);
-
-  return code;
-}
-
-static CURLcode tftp_setup_connection(struct connectdata * conn)
-{
-  struct SessionHandle *data = conn->data;
-  char * type;
-  char command;
-
-  conn->socktype = SOCK_DGRAM;   /* UDP datagram based */
-
-  /* TFTP URLs support an extension like ";mode=<typecode>" that
-   * we'll try to get now! */
-  type = strstr(data->state.path, ";mode=");
-
-  if(!type)
-    type = strstr(conn->host.rawalloc, ";mode=");
-
-  if(type) {
-    *type = 0;                   /* it was in the middle of the hostname */
-    command = Curl_raw_toupper(type[6]);
-
-    switch (command) {
-    case 'A': /* ASCII mode */
-    case 'N': /* NETASCII mode */
-      data->set.prefer_ascii = TRUE;
-      break;
-
-    case 'O': /* octet mode */
-    case 'I': /* binary mode */
-    default:
-      /* switch off ASCII */
-      data->set.prefer_ascii = FALSE;
-      break;
-    }
-  }
-
-  return CURLE_OK;
-}
-#endif
diff --git a/lib/timeval.c b/lib/timeval.c
deleted file mode 100644 (file)
index 8e4c7bd..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_timeval.h"
-
-#if defined(WIN32) && !defined(MSDOS)
-
-struct timeval curlx_tvnow(void)
-{
-  /*
-  ** GetTickCount() is available on _all_ Windows versions from W95 up
-  ** to nowadays. Returns milliseconds elapsed since last system boot,
-  ** increases monotonically and wraps once 49.7 days have elapsed.
-  */
-  struct timeval now;
-  DWORD milliseconds = GetTickCount();
-  now.tv_sec = milliseconds / 1000;
-  now.tv_usec = (milliseconds % 1000) * 1000;
-  return now;
-}
-
-#elif defined(HAVE_CLOCK_GETTIME_MONOTONIC)
-
-struct timeval curlx_tvnow(void)
-{
-  /*
-  ** clock_gettime() is granted to be increased monotonically when the
-  ** monotonic clock is queried. Time starting point is unspecified, it
-  ** could be the system start-up time, the Epoch, or something else,
-  ** in any case the time starting point does not change once that the
-  ** system has started up.
-  */
-  struct timeval now;
-  struct timespec tsnow;
-  if(0 == clock_gettime(CLOCK_MONOTONIC, &tsnow)) {
-    now.tv_sec = tsnow.tv_sec;
-    now.tv_usec = tsnow.tv_nsec / 1000;
-  }
-  /*
-  ** Even when the configure process has truly detected monotonic clock
-  ** availability, it might happen that it is not actually available at
-  ** run-time. When this occurs simply fallback to other time source.
-  */
-#ifdef HAVE_GETTIMEOFDAY
-  else
-    (void)gettimeofday(&now, NULL);
-#else
-  else {
-    now.tv_sec = (long)time(NULL);
-    now.tv_usec = 0;
-  }
-#endif
-  return now;
-}
-
-#elif defined(HAVE_GETTIMEOFDAY)
-
-struct timeval curlx_tvnow(void)
-{
-  /*
-  ** gettimeofday() is not granted to be increased monotonically, due to
-  ** clock drifting and external source time synchronization it can jump
-  ** forward or backward in time.
-  */
-  struct timeval now;
-  (void)gettimeofday(&now, NULL);
-  return now;
-}
-
-#else
-
-struct timeval curlx_tvnow(void)
-{
-  /*
-  ** time() returns the value of time in seconds since the Epoch.
-  */
-  struct timeval now;
-  now.tv_sec = (long)time(NULL);
-  now.tv_usec = 0;
-  return now;
-}
-
-#endif
-
-/*
- * Make sure that the first argument is the more recent time, as otherwise
- * we'll get a weird negative time-diff back...
- *
- * Returns: the time difference in number of milliseconds.
- */
-long curlx_tvdiff(struct timeval newer, struct timeval older)
-{
-  return (newer.tv_sec-older.tv_sec)*1000+
-    (newer.tv_usec-older.tv_usec)/1000;
-}
-
-/*
- * Same as curlx_tvdiff but with full usec resolution.
- *
- * Returns: the time difference in seconds with subsecond resolution.
- */
-double curlx_tvdiff_secs(struct timeval newer, struct timeval older)
-{
-  if(newer.tv_sec != older.tv_sec)
-    return (double)(newer.tv_sec-older.tv_sec)+
-      (double)(newer.tv_usec-older.tv_usec)/1000000.0;
-  else
-    return (double)(newer.tv_usec-older.tv_usec)/1000000.0;
-}
-
-/* return the number of seconds in the given input timeval struct */
-long Curl_tvlong(struct timeval t1)
-{
-  return t1.tv_sec;
-}
diff --git a/lib/transfer.c b/lib/transfer.c
deleted file mode 100644 (file)
index a1dee1d..0000000
+++ /dev/null
@@ -1,2338 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#include "curl_strtoofft.h"
-#include "curl_strequal.h"
-#include "curl_rawstr.h"
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef HAVE_NET_IF_H
-#include <net/if.h>
-#endif
-#ifdef HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-#ifdef HAVE_SIGNAL_H
-#include <signal.h>
-#endif
-
-#ifdef HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-
-#ifdef HAVE_SYS_SELECT_H
-#include <sys/select.h>
-#endif
-
-#ifndef HAVE_SOCKET
-#error "We can't compile without socket() support!"
-#endif
-
-#include "curl_urldata.h"
-#include <curl/curl.h>
-#include "curl_netrc.h"
-
-#include "curl_content_encoding.h"
-#include "curl_hostip.h"
-#include "curl_transfer.h"
-#include "curl_sendf.h"
-#include "curl_speedcheck.h"
-#include "curl_progress.h"
-#include "curl_http.h"
-#include "curl_url.h"
-#include "curl_getinfo.h"
-#include "curl_sslgen.h"
-#include "curl_http_digest.h"
-#include "curl_ntlm.h"
-#include "curl_http_negotiate.h"
-#include "curl_share.h"
-#include "curl_memory.h"
-#include "curl_select.h"
-#include "curl_multiif.h"
-#include "curl_connect.h"
-#include "curl_non_ascii.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-#define CURL_TIMEOUT_EXPECT_100 1000 /* counting ms here */
-
-/*
- * This function will call the read callback to fill our buffer with data
- * to upload.
- */
-CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp)
-{
-  struct SessionHandle *data = conn->data;
-  size_t buffersize = (size_t)bytes;
-  int nread;
-#ifdef CURL_DOES_CONVERSIONS
-  bool sending_http_headers = FALSE;
-
-  if((conn->handler->protocol&(CURLPROTO_HTTP|CURLPROTO_RTSP)) &&
-     (data->state.proto.http->sending == HTTPSEND_REQUEST)) {
-    /* We're sending the HTTP request headers, not the data.
-       Remember that so we don't re-translate them into garbage. */
-    sending_http_headers = TRUE;
-  }
-#endif
-
-  if(data->req.upload_chunky) {
-    /* if chunked Transfer-Encoding */
-    buffersize -= (8 + 2 + 2);   /* 32bit hex + CRLF + CRLF */
-    data->req.upload_fromhere += (8 + 2); /* 32bit hex + CRLF */
-  }
-
-  /* this function returns a size_t, so we typecast to int to prevent warnings
-     with picky compilers */
-  nread = (int)conn->fread_func(data->req.upload_fromhere, 1,
-                                buffersize, conn->fread_in);
-
-  if(nread == CURL_READFUNC_ABORT) {
-    failf(data, "operation aborted by callback");
-    *nreadp = 0;
-    return CURLE_ABORTED_BY_CALLBACK;
-  }
-  else if(nread == CURL_READFUNC_PAUSE) {
-    struct SingleRequest *k = &data->req;
-    /* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */
-    k->keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */
-    if(data->req.upload_chunky) {
-      /* Back out the preallocation done above */
-      data->req.upload_fromhere -= (8 + 2);
-    }
-    *nreadp = 0;
-    return CURLE_OK; /* nothing was read */
-  }
-  else if((size_t)nread > buffersize) {
-    /* the read function returned a too large value */
-    *nreadp = 0;
-    failf(data, "read function returned funny value");
-    return CURLE_READ_ERROR;
-  }
-
-  if(!data->req.forbidchunk && data->req.upload_chunky) {
-    /* if chunked Transfer-Encoding
-     *    build chunk:
-     *
-     *        <HEX SIZE> CRLF
-     *        <DATA> CRLF
-     */
-    /* On non-ASCII platforms the <DATA> may or may not be
-       translated based on set.prefer_ascii while the protocol
-       portion must always be translated to the network encoding.
-       To further complicate matters, line end conversion might be
-       done later on, so we need to prevent CRLFs from becoming
-       CRCRLFs if that's the case.  To do this we use bare LFs
-       here, knowing they'll become CRLFs later on.
-     */
-
-    char hexbuffer[11];
-    const char *endofline_native;
-    const char *endofline_network;
-    int hexlen;
-
-    if(
-#ifdef CURL_DO_LINEEND_CONV
-       (data->set.prefer_ascii) ||
-#endif
-       (data->set.crlf)) {
-      /* \n will become \r\n later on */
-      endofline_native  = "\n";
-      endofline_network = "\x0a";
-    }
-    else {
-      endofline_native  = "\r\n";
-      endofline_network = "\x0d\x0a";
-    }
-    hexlen = snprintf(hexbuffer, sizeof(hexbuffer),
-                      "%x%s", nread, endofline_native);
-
-    /* move buffer pointer */
-    data->req.upload_fromhere -= hexlen;
-    nread += hexlen;
-
-    /* copy the prefix to the buffer, leaving out the NUL */
-    memcpy(data->req.upload_fromhere, hexbuffer, hexlen);
-
-    /* always append ASCII CRLF to the data */
-    memcpy(data->req.upload_fromhere + nread,
-           endofline_network,
-           strlen(endofline_network));
-
-#ifdef CURL_DOES_CONVERSIONS
-    CURLcode res;
-    int length;
-    if(data->set.prefer_ascii) {
-      /* translate the protocol and data */
-      length = nread;
-    }
-    else {
-      /* just translate the protocol portion */
-      length = strlen(hexbuffer);
-    }
-    res = Curl_convert_to_network(data, data->req.upload_fromhere, length);
-    /* Curl_convert_to_network calls failf if unsuccessful */
-    if(res)
-      return(res);
-#endif /* CURL_DOES_CONVERSIONS */
-
-    if((nread - hexlen) == 0)
-      /* mark this as done once this chunk is transferred */
-      data->req.upload_done = TRUE;
-
-    nread+=(int)strlen(endofline_native); /* for the added end of line */
-  }
-#ifdef CURL_DOES_CONVERSIONS
-  else if((data->set.prefer_ascii) && (!sending_http_headers)) {
-    CURLcode res;
-    res = Curl_convert_to_network(data, data->req.upload_fromhere, nread);
-    /* Curl_convert_to_network calls failf if unsuccessful */
-    if(res != CURLE_OK)
-      return(res);
-  }
-#endif /* CURL_DOES_CONVERSIONS */
-
-  *nreadp = nread;
-
-  return CURLE_OK;
-}
-
-
-/*
- * Curl_readrewind() rewinds the read stream. This is typically used for HTTP
- * POST/PUT with multi-pass authentication when a sending was denied and a
- * resend is necessary.
- */
-CURLcode Curl_readrewind(struct connectdata *conn)
-{
-  struct SessionHandle *data = conn->data;
-
-  conn->bits.rewindaftersend = FALSE; /* we rewind now */
-
-  /* explicitly switch off sending data on this connection now since we are
-     about to restart a new transfer and thus we want to avoid inadvertently
-     sending more data on the existing connection until the next transfer
-     starts */
-  data->req.keepon &= ~KEEP_SEND;
-
-  /* We have sent away data. If not using CURLOPT_POSTFIELDS or
-     CURLOPT_HTTPPOST, call app to rewind
-  */
-  if(data->set.postfields ||
-     (data->set.httpreq == HTTPREQ_POST_FORM))
-    ; /* do nothing */
-  else {
-    if(data->set.seek_func) {
-      int err;
-
-      err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET);
-      if(err) {
-        failf(data, "seek callback returned error %d", (int)err);
-        return CURLE_SEND_FAIL_REWIND;
-      }
-    }
-    else if(data->set.ioctl_func) {
-      curlioerr err;
-
-      err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD,
-                                   data->set.ioctl_client);
-      infof(data, "the ioctl callback returned %d\n", (int)err);
-
-      if(err) {
-        /* FIXME: convert to a human readable error message */
-        failf(data, "ioctl callback returned error %d", (int)err);
-        return CURLE_SEND_FAIL_REWIND;
-      }
-    }
-    else {
-      /* If no CURLOPT_READFUNCTION is used, we know that we operate on a
-         given FILE * stream and we can actually attempt to rewind that
-         ourselves with fseek() */
-      if(data->set.fread_func == (curl_read_callback)fread) {
-        if(-1 != fseek(data->set.in, 0, SEEK_SET))
-          /* successful rewind */
-          return CURLE_OK;
-      }
-
-      /* no callback set or failure above, makes us fail at once */
-      failf(data, "necessary data rewind wasn't possible");
-      return CURLE_SEND_FAIL_REWIND;
-    }
-  }
-  return CURLE_OK;
-}
-
-static int data_pending(const struct connectdata *conn)
-{
-  /* in the case of libssh2, we can never be really sure that we have emptied
-     its internal buffers so we MUST always try until we get EAGAIN back */
-  return conn->handler->protocol&(CURLPROTO_SCP|CURLPROTO_SFTP) ||
-    Curl_ssl_data_pending(conn, FIRSTSOCKET);
-}
-
-static void read_rewind(struct connectdata *conn,
-                        size_t thismuch)
-{
-  DEBUGASSERT(conn->read_pos >= thismuch);
-
-  conn->read_pos -= thismuch;
-  conn->bits.stream_was_rewound = TRUE;
-
-#ifdef DEBUGBUILD
-  {
-    char buf[512 + 1];
-    size_t show;
-
-    show = CURLMIN(conn->buf_len - conn->read_pos, sizeof(buf)-1);
-    if(conn->master_buffer) {
-      memcpy(buf, conn->master_buffer + conn->read_pos, show);
-      buf[show] = '\0';
-    }
-    else {
-      buf[0] = '\0';
-    }
-
-    DEBUGF(infof(conn->data,
-                 "Buffer after stream rewind (read_pos = %zu): [%s]\n",
-                 conn->read_pos, buf));
-  }
-#endif
-}
-
-/*
- * Check to see if CURLOPT_TIMECONDITION was met by comparing the time of the
- * remote document with the time provided by CURLOPT_TIMEVAL
- */
-bool Curl_meets_timecondition(struct SessionHandle *data, time_t timeofdoc)
-{
-  if((timeofdoc == 0) || (data->set.timevalue == 0))
-    return TRUE;
-
-  switch(data->set.timecondition) {
-  case CURL_TIMECOND_IFMODSINCE:
-  default:
-    if(timeofdoc <= data->set.timevalue) {
-      infof(data,
-            "The requested document is not new enough\n");
-      data->info.timecond = TRUE;
-      return FALSE;
-    }
-    break;
-  case CURL_TIMECOND_IFUNMODSINCE:
-    if(timeofdoc >= data->set.timevalue) {
-      infof(data,
-            "The requested document is not old enough\n");
-      data->info.timecond = TRUE;
-      return FALSE;
-    }
-    break;
-  }
-
-  return TRUE;
-}
-
-/*
- * Go ahead and do a read if we have a readable socket or if
- * the stream was rewound (in which case we have data in a
- * buffer)
- */
-static CURLcode readwrite_data(struct SessionHandle *data,
-                               struct connectdata *conn,
-                               struct SingleRequest *k,
-                               int *didwhat, bool *done)
-{
-  CURLcode result = CURLE_OK;
-  ssize_t nread; /* number of bytes read */
-  size_t excess = 0; /* excess bytes read */
-  bool is_empty_data = FALSE;
-  bool readmore = FALSE; /* used by RTP to signal for more data */
-
-  *done = FALSE;
-
-  /* This is where we loop until we have read everything there is to
-     read or we get a CURLE_AGAIN */
-  do {
-    size_t buffersize = data->set.buffer_size?
-      data->set.buffer_size : BUFSIZE;
-    size_t bytestoread = buffersize;
-
-    if(k->size != -1 && !k->header) {
-      /* make sure we don't read "too much" if we can help it since we
-         might be pipelining and then someone else might want to read what
-         follows! */
-      curl_off_t totalleft = k->size - k->bytecount;
-      if(totalleft < (curl_off_t)bytestoread)
-        bytestoread = (size_t)totalleft;
-    }
-
-    if(bytestoread) {
-      /* receive data from the network! */
-      result = Curl_read(conn, conn->sockfd, k->buf, bytestoread, &nread);
-
-      /* read would've blocked */
-      if(CURLE_AGAIN == result)
-        break; /* get out of loop */
-
-      if(result>0)
-        return result;
-    }
-    else {
-      /* read nothing but since we wanted nothing we consider this an OK
-         situation to proceed from */
-      nread = 0;
-    }
-
-    if((k->bytecount == 0) && (k->writebytecount == 0)) {
-      Curl_pgrsTime(data, TIMER_STARTTRANSFER);
-      if(k->exp100 > EXP100_SEND_DATA)
-        /* set time stamp to compare with when waiting for the 100 */
-        k->start100 = Curl_tvnow();
-    }
-
-    *didwhat |= KEEP_RECV;
-    /* indicates data of zero size, i.e. empty file */
-    is_empty_data = ((nread == 0) && (k->bodywrites == 0)) ? TRUE : FALSE;
-
-    /* NUL terminate, allowing string ops to be used */
-    if(0 < nread || is_empty_data) {
-      k->buf[nread] = 0;
-    }
-    else if(0 >= nread) {
-      /* if we receive 0 or less here, the server closed the connection
-         and we bail out from this! */
-      DEBUGF(infof(data, "nread <= 0, server closed connection, bailing\n"));
-      k->keepon &= ~KEEP_RECV;
-      break;
-    }
-
-    /* Default buffer to use when we write the buffer, it may be changed
-       in the flow below before the actual storing is done. */
-    k->str = k->buf;
-
-    if(conn->handler->readwrite) {
-      result = conn->handler->readwrite(data, conn, &nread, &readmore);
-      if(result)
-        return result;
-      if(readmore)
-        break;
-    }
-
-#ifndef CURL_DISABLE_HTTP
-    /* Since this is a two-state thing, we check if we are parsing
-       headers at the moment or not. */
-    if(k->header) {
-      /* we are in parse-the-header-mode */
-      bool stop_reading = FALSE;
-      result = Curl_http_readwrite_headers(data, conn, &nread, &stop_reading);
-      if(result)
-        return result;
-
-      if(conn->handler->readwrite &&
-         (k->maxdownload <= 0 && nread > 0)) {
-        result = conn->handler->readwrite(data, conn, &nread, &readmore);
-        if(result)
-          return result;
-        if(readmore)
-          break;
-      }
-
-      if(stop_reading) {
-        /* We've stopped dealing with input, get out of the do-while loop */
-
-        if(nread > 0) {
-          if(conn->data->multi && Curl_multi_canPipeline(conn->data->multi)) {
-            infof(data,
-                  "Rewinding stream by : %zd"
-                  " bytes on url %s (zero-length body)\n",
-                  nread, data->state.path);
-            read_rewind(conn, (size_t)nread);
-          }
-          else {
-            infof(data,
-                  "Excess found in a non pipelined read:"
-                  " excess = %zd"
-                  " url = %s (zero-length body)\n",
-                  nread, data->state.path);
-          }
-        }
-
-        break;
-      }
-    }
-#endif /* CURL_DISABLE_HTTP */
-
-
-    /* This is not an 'else if' since it may be a rest from the header
-       parsing, where the beginning of the buffer is headers and the end
-       is non-headers. */
-    if(k->str && !k->header && (nread > 0 || is_empty_data)) {
-
-#ifndef CURL_DISABLE_HTTP
-      if(0 == k->bodywrites && !is_empty_data) {
-        /* These checks are only made the first time we are about to
-           write a piece of the body */
-        if(conn->handler->protocol&(CURLPROTO_HTTP|CURLPROTO_RTSP)) {
-          /* HTTP-only checks */
-
-          if(data->req.newurl) {
-            if(conn->bits.close) {
-              /* Abort after the headers if "follow Location" is set
-                 and we're set to close anyway. */
-              k->keepon &= ~KEEP_RECV;
-              *done = TRUE;
-              return CURLE_OK;
-            }
-            /* We have a new url to load, but since we want to be able
-               to re-use this connection properly, we read the full
-               response in "ignore more" */
-            k->ignorebody = TRUE;
-            infof(data, "Ignoring the response-body\n");
-          }
-          if(data->state.resume_from && !k->content_range &&
-             (data->set.httpreq==HTTPREQ_GET) &&
-             !k->ignorebody) {
-            /* we wanted to resume a download, although the server doesn't
-             * seem to support this and we did this with a GET (if it
-             * wasn't a GET we did a POST or PUT resume) */
-            failf(data, "HTTP server doesn't seem to support "
-                  "byte ranges. Cannot resume.");
-            return CURLE_RANGE_ERROR;
-          }
-
-          if(data->set.timecondition && !data->state.range) {
-            /* A time condition has been set AND no ranges have been
-               requested. This seems to be what chapter 13.3.4 of
-               RFC 2616 defines to be the correct action for a
-               HTTP/1.1 client */
-
-            if(!Curl_meets_timecondition(data, k->timeofdoc)) {
-              *done = TRUE;
-              /* we abort the transfer before it is completed == we ruin the
-                 re-use ability. Close the connection */
-              conn->bits.close = TRUE;
-              return CURLE_OK;
-            }
-          } /* we have a time condition */
-
-        } /* this is HTTP or RTSP */
-      } /* this is the first time we write a body part */
-#endif /* CURL_DISABLE_HTTP */
-
-      k->bodywrites++;
-
-      /* pass data to the debug function before it gets "dechunked" */
-      if(data->set.verbose) {
-        if(k->badheader) {
-          Curl_debug(data, CURLINFO_DATA_IN, data->state.headerbuff,
-                     (size_t)k->hbuflen, conn);
-          if(k->badheader == HEADER_PARTHEADER)
-            Curl_debug(data, CURLINFO_DATA_IN,
-                       k->str, (size_t)nread, conn);
-        }
-        else
-          Curl_debug(data, CURLINFO_DATA_IN,
-                     k->str, (size_t)nread, conn);
-      }
-
-#ifndef CURL_DISABLE_HTTP
-      if(k->chunk) {
-        /*
-         * Here comes a chunked transfer flying and we need to decode this
-         * properly.  While the name says read, this function both reads
-         * and writes away the data. The returned 'nread' holds the number
-         * of actual data it wrote to the client.
-         */
-
-        CHUNKcode res =
-          Curl_httpchunk_read(conn, k->str, nread, &nread);
-
-        if(CHUNKE_OK < res) {
-          if(CHUNKE_WRITE_ERROR == res) {
-            failf(data, "Failed writing data");
-            return CURLE_WRITE_ERROR;
-          }
-          failf(data, "Problem (%d) in the Chunked-Encoded data", (int)res);
-          return CURLE_RECV_ERROR;
-        }
-        else if(CHUNKE_STOP == res) {
-          size_t dataleft;
-          /* we're done reading chunks! */
-          k->keepon &= ~KEEP_RECV; /* read no more */
-
-          /* There are now possibly N number of bytes at the end of the
-             str buffer that weren't written to the client.
-
-             We DO care about this data if we are pipelining.
-             Push it back to be read on the next pass. */
-
-          dataleft = conn->chunk.dataleft;
-          if(dataleft != 0) {
-            infof(conn->data, "Leftovers after chunking: %zu bytes\n",
-                  dataleft);
-            if(conn->data->multi &&
-               Curl_multi_canPipeline(conn->data->multi)) {
-              /* only attempt the rewind if we truly are pipelining */
-              infof(conn->data, "Rewinding %zu bytes\n",dataleft);
-              read_rewind(conn, dataleft);
-            }
-          }
-        }
-        /* If it returned OK, we just keep going */
-      }
-#endif   /* CURL_DISABLE_HTTP */
-
-      /* Account for body content stored in the header buffer */
-      if(k->badheader && !k->ignorebody) {
-        DEBUGF(infof(data, "Increasing bytecount by %zu from hbuflen\n",
-                     k->hbuflen));
-        k->bytecount += k->hbuflen;
-      }
-
-      if((-1 != k->maxdownload) &&
-         (k->bytecount + nread >= k->maxdownload)) {
-
-        excess = (size_t)(k->bytecount + nread - k->maxdownload);
-        if(excess > 0 && !k->ignorebody) {
-          if(conn->data->multi && Curl_multi_canPipeline(conn->data->multi)) {
-            /* The 'excess' amount below can't be more than BUFSIZE which
-               always will fit in a size_t */
-            infof(data,
-                  "Rewinding stream by : %zu"
-                  " bytes on url %s (size = %" FORMAT_OFF_T
-                  ", maxdownload = %" FORMAT_OFF_T
-                  ", bytecount = %" FORMAT_OFF_T ", nread = %zd)\n",
-                  excess, data->state.path,
-                  k->size, k->maxdownload, k->bytecount, nread);
-            read_rewind(conn, excess);
-          }
-          else {
-            infof(data,
-                  "Excess found in a non pipelined read:"
-                  " excess = %zu"
-                  ", size = %" FORMAT_OFF_T
-                  ", maxdownload = %" FORMAT_OFF_T
-                  ", bytecount = %" FORMAT_OFF_T "\n",
-                  excess, k->size, k->maxdownload, k->bytecount);
-          }
-        }
-
-        nread = (ssize_t) (k->maxdownload - k->bytecount);
-        if(nread < 0 ) /* this should be unusual */
-          nread = 0;
-
-        k->keepon &= ~KEEP_RECV; /* we're done reading */
-      }
-
-      k->bytecount += nread;
-
-      Curl_pgrsSetDownloadCounter(data, k->bytecount);
-
-      if(!k->chunk && (nread || k->badheader || is_empty_data)) {
-        /* If this is chunky transfer, it was already written */
-
-        if(k->badheader && !k->ignorebody) {
-          /* we parsed a piece of data wrongly assuming it was a header
-             and now we output it as body instead */
-
-          /* Don't let excess data pollute body writes */
-          if(k->maxdownload == -1 || (curl_off_t)k->hbuflen <= k->maxdownload)
-            result = Curl_client_write(conn, CLIENTWRITE_BODY,
-                                       data->state.headerbuff,
-                                       k->hbuflen);
-          else
-            result = Curl_client_write(conn, CLIENTWRITE_BODY,
-                                       data->state.headerbuff,
-                                       (size_t)k->maxdownload);
-
-          if(result)
-            return result;
-        }
-        if(k->badheader < HEADER_ALLBAD) {
-          /* This switch handles various content encodings. If there's an
-             error here, be sure to check over the almost identical code
-             in curl_http_chunks.c.
-             Make sure that ALL_CONTENT_ENCODINGS contains all the
-             encodings handled here. */
-#ifdef HAVE_LIBZ
-          switch (conn->data->set.http_ce_skip ?
-                  IDENTITY : k->auto_decoding) {
-          case IDENTITY:
-#endif
-            /* This is the default when the server sends no
-               Content-Encoding header. See Curl_readwrite_init; the
-               memset() call initializes k->auto_decoding to zero. */
-            if(!k->ignorebody) {
-
-#ifndef CURL_DISABLE_POP3
-              if(conn->handler->protocol&CURLPROTO_POP3)
-                result = Curl_pop3_write(conn, k->str, nread);
-              else
-#endif /* CURL_DISABLE_POP3 */
-
-                result = Curl_client_write(conn, CLIENTWRITE_BODY, k->str,
-                                           nread);
-            }
-#ifdef HAVE_LIBZ
-            break;
-
-          case DEFLATE:
-            /* Assume CLIENTWRITE_BODY; headers are not encoded. */
-            if(!k->ignorebody)
-              result = Curl_unencode_deflate_write(conn, k, nread);
-            break;
-
-          case GZIP:
-            /* Assume CLIENTWRITE_BODY; headers are not encoded. */
-            if(!k->ignorebody)
-              result = Curl_unencode_gzip_write(conn, k, nread);
-            break;
-
-          case COMPRESS:
-          default:
-            failf (data, "Unrecognized content encoding type. "
-                   "libcurl understands `identity', `deflate' and `gzip' "
-                   "content encodings.");
-            result = CURLE_BAD_CONTENT_ENCODING;
-            break;
-          }
-#endif
-        }
-        k->badheader = HEADER_NORMAL; /* taken care of now */
-
-        if(result)
-          return result;
-      }
-
-    } /* if(! header and data to read ) */
-
-    if(conn->handler->readwrite &&
-       (excess > 0 && !conn->bits.stream_was_rewound)) {
-      /* Parse the excess data */
-      k->str += nread;
-      nread = (ssize_t)excess;
-
-      result = conn->handler->readwrite(data, conn, &nread, &readmore);
-      if(result)
-        return result;
-
-      if(readmore)
-        k->keepon |= KEEP_RECV; /* we're not done reading */
-      break;
-    }
-
-    if(is_empty_data) {
-      /* if we received nothing, the server closed the connection and we
-         are done */
-      k->keepon &= ~KEEP_RECV;
-    }
-
-  } while(data_pending(conn));
-
-  if(((k->keepon & (KEEP_RECV|KEEP_SEND)) == KEEP_SEND) &&
-     conn->bits.close ) {
-    /* When we've read the entire thing and the close bit is set, the server
-       may now close the connection. If there's now any kind of sending going
-       on from our side, we need to stop that immediately. */
-    infof(data, "we are done reading and this is set to close, stop send\n");
-    k->keepon &= ~KEEP_SEND; /* no writing anymore either */
-  }
-
-  return CURLE_OK;
-}
-
-/*
- * Send data to upload to the server, when the socket is writable.
- */
-static CURLcode readwrite_upload(struct SessionHandle *data,
-                                 struct connectdata *conn,
-                                 struct SingleRequest *k,
-                                 int *didwhat)
-{
-  ssize_t i, si;
-  ssize_t bytes_written;
-  CURLcode result;
-  ssize_t nread; /* number of bytes read */
-  bool sending_http_headers = FALSE;
-
-  if((k->bytecount == 0) && (k->writebytecount == 0))
-    Curl_pgrsTime(data, TIMER_STARTTRANSFER);
-
-  *didwhat |= KEEP_SEND;
-
-  /*
-   * We loop here to do the READ and SEND loop until we run out of
-   * data to send or until we get EWOULDBLOCK back
-   *
-   * FIXME: above comment is misleading. Currently no looping is
-   * actually done in do-while loop below.
-   */
-  do {
-
-    /* only read more data if there's no upload data already
-       present in the upload buffer */
-    if(0 == data->req.upload_present) {
-      /* init the "upload from here" pointer */
-      data->req.upload_fromhere = k->uploadbuf;
-
-      if(!k->upload_done) {
-        /* HTTP pollution, this should be written nicer to become more
-           protocol agnostic. */
-        int fillcount;
-
-        if((k->exp100 == EXP100_SENDING_REQUEST) &&
-           (data->state.proto.http->sending == HTTPSEND_BODY)) {
-          /* If this call is to send body data, we must take some action:
-             We have sent off the full HTTP 1.1 request, and we shall now
-             go into the Expect: 100 state and await such a header */
-          k->exp100 = EXP100_AWAITING_CONTINUE; /* wait for the header */
-          k->keepon &= ~KEEP_SEND;         /* disable writing */
-          k->start100 = Curl_tvnow();       /* timeout count starts now */
-          *didwhat &= ~KEEP_SEND;  /* we didn't write anything actually */
-
-          /* set a timeout for the multi interface */
-          Curl_expire(data, CURL_TIMEOUT_EXPECT_100);
-          break;
-        }
-
-        if(conn->handler->protocol&(CURLPROTO_HTTP|CURLPROTO_RTSP)) {
-          if(data->state.proto.http->sending == HTTPSEND_REQUEST)
-            /* We're sending the HTTP request headers, not the data.
-               Remember that so we don't change the line endings. */
-            sending_http_headers = TRUE;
-          else
-            sending_http_headers = FALSE;
-        }
-
-        result = Curl_fillreadbuffer(conn, BUFSIZE, &fillcount);
-        if(result)
-          return result;
-
-        nread = (ssize_t)fillcount;
-      }
-      else
-        nread = 0; /* we're done uploading/reading */
-
-      if(!nread && (k->keepon & KEEP_SEND_PAUSE)) {
-        /* this is a paused transfer */
-        break;
-      }
-      else if(nread<=0) {
-        /* done */
-        k->keepon &= ~KEEP_SEND; /* we're done writing */
-
-        if(conn->bits.rewindaftersend) {
-          result = Curl_readrewind(conn);
-          if(result)
-            return result;
-        }
-        break;
-      }
-
-      /* store number of bytes available for upload */
-      data->req.upload_present = nread;
-
-#ifndef CURL_DISABLE_SMTP
-      if(conn->handler->protocol & CURLPROTO_SMTP) {
-        result = Curl_smtp_escape_eob(conn, nread);
-        if(result)
-          return result;
-      }
-      else
-#endif /* CURL_DISABLE_SMTP */
-
-      /* convert LF to CRLF if so asked */
-      if((!sending_http_headers) && (
-#ifdef CURL_DO_LINEEND_CONV
-         /* always convert if we're FTPing in ASCII mode */
-         (data->set.prefer_ascii) ||
-#endif
-         (data->set.crlf))) {
-        if(data->state.scratch == NULL)
-          data->state.scratch = malloc(2*BUFSIZE);
-        if(data->state.scratch == NULL) {
-          failf (data, "Failed to alloc scratch buffer!");
-          return CURLE_OUT_OF_MEMORY;
-        }
-        /*
-         * ASCII/EBCDIC Note: This is presumably a text (not binary)
-         * transfer so the data should already be in ASCII.
-         * That means the hex values for ASCII CR (0x0d) & LF (0x0a)
-         * must be used instead of the escape sequences \r & \n.
-         */
-        for(i = 0, si = 0; i < nread; i++, si++) {
-          if(data->req.upload_fromhere[i] == 0x0a) {
-            data->state.scratch[si++] = 0x0d;
-            data->state.scratch[si] = 0x0a;
-            if(!data->set.crlf) {
-              /* we're here only because FTP is in ASCII mode...
-                 bump infilesize for the LF we just added */
-              data->set.infilesize++;
-            }
-          }
-          else
-            data->state.scratch[si] = data->req.upload_fromhere[i];
-        }
-        if(si != nread) {
-          /* only perform the special operation if we really did replace
-             anything */
-          nread = si;
-
-          /* upload from the new (replaced) buffer instead */
-          data->req.upload_fromhere = data->state.scratch;
-
-          /* set the new amount too */
-          data->req.upload_present = nread;
-        }
-      }
-    } /* if 0 == data->req.upload_present */
-    else {
-      /* We have a partial buffer left from a previous "round". Use
-         that instead of reading more data */
-    }
-
-    /* write to socket (send away data) */
-    result = Curl_write(conn,
-                        conn->writesockfd,     /* socket to send to */
-                        data->req.upload_fromhere, /* buffer pointer */
-                        data->req.upload_present,  /* buffer size */
-                        &bytes_written);           /* actually sent */
-
-    if(result)
-      return result;
-
-    if(data->set.verbose)
-      /* show the data before we change the pointer upload_fromhere */
-      Curl_debug(data, CURLINFO_DATA_OUT, data->req.upload_fromhere,
-                 (size_t)bytes_written, conn);
-
-    k->writebytecount += bytes_written;
-
-    if(k->writebytecount == data->set.infilesize) {
-      /* we have sent all data we were supposed to */
-      k->upload_done = TRUE;
-      infof(data, "We are completely uploaded and fine\n");
-    }
-
-    if(data->req.upload_present != bytes_written) {
-      /* we only wrote a part of the buffer (if anything), deal with it! */
-
-      /* store the amount of bytes left in the buffer to write */
-      data->req.upload_present -= bytes_written;
-
-      /* advance the pointer where to find the buffer when the next send
-         is to happen */
-      data->req.upload_fromhere += bytes_written;
-    }
-    else {
-      /* we've uploaded that buffer now */
-      data->req.upload_fromhere = k->uploadbuf;
-      data->req.upload_present = 0; /* no more bytes left */
-
-      if(k->upload_done) {
-        /* switch off writing, we're done! */
-        k->keepon &= ~KEEP_SEND; /* we're done writing */
-      }
-    }
-
-    Curl_pgrsSetUploadCounter(data, k->writebytecount);
-
-  } WHILE_FALSE; /* just to break out from! */
-
-  return CURLE_OK;
-}
-
-/*
- * Curl_readwrite() is the low-level function to be called when data is to
- * be read and written to/from the connection.
- */
-CURLcode Curl_readwrite(struct connectdata *conn,
-                        bool *done)
-{
-  struct SessionHandle *data = conn->data;
-  struct SingleRequest *k = &data->req;
-  CURLcode result;
-  int didwhat=0;
-
-  curl_socket_t fd_read;
-  curl_socket_t fd_write;
-  int select_res = conn->cselect_bits;
-
-  conn->cselect_bits = 0;
-
-  /* only use the proper socket if the *_HOLD bit is not set simultaneously as
-     then we are in rate limiting state in that transfer direction */
-
-  if((k->keepon & KEEP_RECVBITS) == KEEP_RECV)
-    fd_read = conn->sockfd;
-  else
-    fd_read = CURL_SOCKET_BAD;
-
-  if((k->keepon & KEEP_SENDBITS) == KEEP_SEND)
-    fd_write = conn->writesockfd;
-  else
-    fd_write = CURL_SOCKET_BAD;
-
-  if(!select_res) /* Call for select()/poll() only, if read/write/error
-                     status is not known. */
-    select_res = Curl_socket_ready(fd_read, fd_write, 0);
-
-  if(select_res == CURL_CSELECT_ERR) {
-    failf(data, "select/poll returned error");
-    return CURLE_SEND_ERROR;
-  }
-
-  /* We go ahead and do a read if we have a readable socket or if
-     the stream was rewound (in which case we have data in a
-     buffer) */
-  if((k->keepon & KEEP_RECV) &&
-     ((select_res & CURL_CSELECT_IN) || conn->bits.stream_was_rewound)) {
-
-    result = readwrite_data(data, conn, k, &didwhat, done);
-    if(result || *done)
-      return result;
-  }
-
-  /* If we still have writing to do, we check if we have a writable socket. */
-  if((k->keepon & KEEP_SEND) && (select_res & CURL_CSELECT_OUT)) {
-    /* write */
-
-    result = readwrite_upload(data, conn, k, &didwhat);
-    if(result)
-      return result;
-  }
-
-  k->now = Curl_tvnow();
-  if(didwhat) {
-    /* Update read/write counters */
-    if(k->bytecountp)
-      *k->bytecountp = k->bytecount; /* read count */
-    if(k->writebytecountp)
-      *k->writebytecountp = k->writebytecount; /* write count */
-  }
-  else {
-    /* no read no write, this is a timeout? */
-    if(k->exp100 == EXP100_AWAITING_CONTINUE) {
-      /* This should allow some time for the header to arrive, but only a
-         very short time as otherwise it'll be too much wasted time too
-         often. */
-
-      /* Quoting RFC2616, section "8.2.3 Use of the 100 (Continue) Status":
-
-         Therefore, when a client sends this header field to an origin server
-         (possibly via a proxy) from which it has never seen a 100 (Continue)
-         status, the client SHOULD NOT wait for an indefinite period before
-         sending the request body.
-
-      */
-
-      long ms = Curl_tvdiff(k->now, k->start100);
-      if(ms > CURL_TIMEOUT_EXPECT_100) {
-        /* we've waited long enough, continue anyway */
-        k->exp100 = EXP100_SEND_DATA;
-        k->keepon |= KEEP_SEND;
-        infof(data, "Done waiting for 100-continue\n");
-      }
-    }
-  }
-
-  if(Curl_pgrsUpdate(conn))
-    result = CURLE_ABORTED_BY_CALLBACK;
-  else
-    result = Curl_speedcheck(data, k->now);
-  if(result)
-    return result;
-
-  if(k->keepon) {
-    if(0 > Curl_timeleft(data, &k->now, FALSE)) {
-      if(k->size != -1) {
-        failf(data, "Operation timed out after %ld milliseconds with %"
-              FORMAT_OFF_T " out of %" FORMAT_OFF_T " bytes received",
-              Curl_tvdiff(k->now, data->progress.t_startsingle), k->bytecount,
-              k->size);
-      }
-      else {
-        failf(data, "Operation timed out after %ld milliseconds with %"
-              FORMAT_OFF_T " bytes received",
-              Curl_tvdiff(k->now, data->progress.t_startsingle), k->bytecount);
-      }
-      return CURLE_OPERATION_TIMEDOUT;
-    }
-  }
-  else {
-    /*
-     * The transfer has been performed. Just make some general checks before
-     * returning.
-     */
-
-    if(!(data->set.opt_no_body) && (k->size != -1) &&
-       (k->bytecount != k->size) &&
-#ifdef CURL_DO_LINEEND_CONV
-       /* Most FTP servers don't adjust their file SIZE response for CRLFs,
-          so we'll check to see if the discrepancy can be explained
-          by the number of CRLFs we've changed to LFs.
-       */
-       (k->bytecount != (k->size + data->state.crlf_conversions)) &&
-#endif /* CURL_DO_LINEEND_CONV */
-       !data->req.newurl) {
-      failf(data, "transfer closed with %" FORMAT_OFF_T
-            " bytes remaining to read",
-            k->size - k->bytecount);
-      return CURLE_PARTIAL_FILE;
-    }
-    else if(!(data->set.opt_no_body) &&
-            k->chunk &&
-            (conn->chunk.state != CHUNK_STOP)) {
-      /*
-       * In chunked mode, return an error if the connection is closed prior to
-       * the empty (terminating) chunk is read.
-       *
-       * The condition above used to check for
-       * conn->proto.http->chunk.datasize != 0 which is true after reading
-       * *any* chunk, not just the empty chunk.
-       *
-       */
-      failf(data, "transfer closed with outstanding read data remaining");
-      return CURLE_PARTIAL_FILE;
-    }
-    if(Curl_pgrsUpdate(conn))
-      return CURLE_ABORTED_BY_CALLBACK;
-  }
-
-  /* Now update the "done" boolean we return */
-  *done = (0 == (k->keepon&(KEEP_RECV|KEEP_SEND|
-                            KEEP_RECV_PAUSE|KEEP_SEND_PAUSE))) ? TRUE : FALSE;
-
-  return CURLE_OK;
-}
-
-/*
- * Curl_single_getsock() gets called by the multi interface code when the app
- * has requested to get the sockets for the current connection. This function
- * will then be called once for every connection that the multi interface
- * keeps track of. This function will only be called for connections that are
- * in the proper state to have this information available.
- */
-int Curl_single_getsock(const struct connectdata *conn,
-                        curl_socket_t *sock, /* points to numsocks number
-                                                of sockets */
-                        int numsocks)
-{
-  const struct SessionHandle *data = conn->data;
-  int bitmap = GETSOCK_BLANK;
-  unsigned sockindex = 0;
-
-  if(conn->handler->perform_getsock)
-    return conn->handler->perform_getsock(conn, sock, numsocks);
-
-  if(numsocks < 2)
-    /* simple check but we might need two slots */
-    return GETSOCK_BLANK;
-
-  /* don't include HOLD and PAUSE connections */
-  if((data->req.keepon & KEEP_RECVBITS) == KEEP_RECV) {
-
-    DEBUGASSERT(conn->sockfd != CURL_SOCKET_BAD);
-
-    bitmap |= GETSOCK_READSOCK(sockindex);
-    sock[sockindex] = conn->sockfd;
-  }
-
-  /* don't include HOLD and PAUSE connections */
-  if((data->req.keepon & KEEP_SENDBITS) == KEEP_SEND) {
-
-    if((conn->sockfd != conn->writesockfd) ||
-       !(data->req.keepon & KEEP_RECV)) {
-      /* only if they are not the same socket or we didn't have a readable
-         one, we increase index */
-      if(data->req.keepon & KEEP_RECV)
-        sockindex++; /* increase index if we need two entries */
-
-      DEBUGASSERT(conn->writesockfd != CURL_SOCKET_BAD);
-
-      sock[sockindex] = conn->writesockfd;
-    }
-
-    bitmap |= GETSOCK_WRITESOCK(sockindex);
-  }
-
-  return bitmap;
-}
-
-/*
- * Determine optimum sleep time based on configured rate, current rate,
- * and packet size.
- * Returns value in milliseconds.
- *
- * The basic idea is to adjust the desired rate up/down in this method
- * based on whether we are running too slow or too fast.  Then, calculate
- * how many milliseconds to wait for the next packet to achieve this new
- * rate.
- */
-long Curl_sleep_time(curl_off_t rate_bps, curl_off_t cur_rate_bps,
-                             int pkt_size)
-{
-  curl_off_t min_sleep = 0;
-  curl_off_t rv = 0;
-
-  if(rate_bps == 0)
-    return 0;
-
-  /* If running faster than about .1% of the desired speed, slow
-   * us down a bit.  Use shift instead of division as the 0.1%
-   * cutoff is arbitrary anyway.
-   */
-  if(cur_rate_bps > (rate_bps + (rate_bps >> 10))) {
-    /* running too fast, decrease target rate by 1/64th of rate */
-    rate_bps -= rate_bps >> 6;
-    min_sleep = 1;
-  }
-  else if(cur_rate_bps < (rate_bps - (rate_bps >> 10))) {
-    /* running too slow, increase target rate by 1/64th of rate */
-    rate_bps += rate_bps >> 6;
-  }
-
-  /* Determine number of milliseconds to wait until we do
-   * the next packet at the adjusted rate.  We should wait
-   * longer when using larger packets, for instance.
-   */
-  rv = ((curl_off_t)((pkt_size * 8) * 1000) / rate_bps);
-
-  /* Catch rounding errors and always slow down at least 1ms if
-   * we are running too fast.
-   */
-  if(rv < min_sleep)
-    rv = min_sleep;
-
-  /* Bound value to fit in 'long' on 32-bit platform.  That's
-   * plenty long enough anyway!
-   */
-  if(rv > 0x7fffffff)
-    rv = 0x7fffffff;
-
-  return (long)rv;
-}
-
-
-/*
- * Transfer()
- *
- * This function is what performs the actual transfer. It is capable of doing
- * both ways simultaneously.  The transfer must already have been setup by a
- * call to Curl_setup_transfer().
- *
- * Note that headers are created in a preallocated buffer of a default size.
- * That buffer can be enlarged on demand, but it is never shrunken again.
- *
- */
-
-static CURLcode
-Transfer(struct connectdata *conn)
-{
-  CURLcode result;
-  struct SessionHandle *data = conn->data;
-  struct SingleRequest *k = &data->req;
-  bool done=FALSE;
-  bool first=TRUE;
-  long timeout_ms;
-  int buffersize;
-  long totmp;
-
-  if((conn->sockfd == CURL_SOCKET_BAD) &&
-     (conn->writesockfd == CURL_SOCKET_BAD))
-    /* nothing to read, nothing to write, we're already OK! */
-    return CURLE_OK;
-
-  /* we want header and/or body, if neither then don't do this! */
-  if(!k->getheader && data->set.opt_no_body)
-    return CURLE_OK;
-
-  while(!done) {
-    curl_socket_t fd_read = conn->sockfd;
-    curl_socket_t fd_write = conn->writesockfd;
-    int keepon = k->keepon;
-    timeout_ms = 1000;
-
-    if(conn->waitfor) {
-      /* if waitfor is set, get the RECV and SEND bits from that but keep the
-         other bits */
-      keepon &= ~ (KEEP_RECV|KEEP_SEND);
-      keepon |= conn->waitfor & (KEEP_RECV|KEEP_SEND);
-    }
-
-    /* limit-rate logic: if speed exceeds threshold, then do not include fd in
-       select set. The current speed is recalculated in each Curl_readwrite()
-       call */
-    if((keepon & KEEP_SEND) &&
-        (!data->set.max_send_speed ||
-         (data->progress.ulspeed < data->set.max_send_speed) )) {
-      k->keepon &= ~KEEP_SEND_HOLD;
-    }
-    else {
-      if(data->set.upload && data->set.max_send_speed &&
-         (data->progress.ulspeed > data->set.max_send_speed) ) {
-        /* calculate upload rate-limitation timeout. */
-        buffersize = (int)(data->set.buffer_size ?
-                           data->set.buffer_size : BUFSIZE);
-        totmp = Curl_sleep_time(data->set.max_send_speed,
-                                data->progress.ulspeed, buffersize);
-        if(totmp < timeout_ms)
-          timeout_ms = totmp;
-      }
-      fd_write = CURL_SOCKET_BAD;
-      if(keepon & KEEP_SEND)
-        k->keepon |= KEEP_SEND_HOLD; /* hold it */
-    }
-
-    if((keepon & KEEP_RECV) &&
-        (!data->set.max_recv_speed ||
-         (data->progress.dlspeed < data->set.max_recv_speed)) ) {
-      k->keepon &= ~KEEP_RECV_HOLD;
-    }
-    else {
-      if((!data->set.upload) && data->set.max_recv_speed &&
-         (data->progress.dlspeed > data->set.max_recv_speed)) {
-        /* Calculate download rate-limitation timeout. */
-        buffersize = (int)(data->set.buffer_size ?
-                           data->set.buffer_size : BUFSIZE);
-        totmp = Curl_sleep_time(data->set.max_recv_speed,
-                                data->progress.dlspeed, buffersize);
-        if(totmp < timeout_ms)
-          timeout_ms = totmp;
-      }
-      fd_read = CURL_SOCKET_BAD;
-      if(keepon & KEEP_RECV)
-        k->keepon |= KEEP_RECV_HOLD; /* hold it */
-    }
-
-    /* pause logic. Don't check descriptors for paused connections */
-    if(k->keepon & KEEP_RECV_PAUSE)
-      fd_read = CURL_SOCKET_BAD;
-    if(k->keepon & KEEP_SEND_PAUSE)
-      fd_write = CURL_SOCKET_BAD;
-
-    /* The *_HOLD and *_PAUSE logic is necessary since even though there might
-       be no traffic during the select interval, we still call
-       Curl_readwrite() for the timeout case and if we limit transfer speed we
-       must make sure that this function doesn't transfer anything while in
-       HOLD status.
-
-       The no timeout for the first round is for the protocols for which data
-       has already been slurped off the socket and thus waiting for action
-       won't work since it'll wait even though there is already data present
-       to work with. */
-    if(first &&
-       ((fd_read != CURL_SOCKET_BAD) || (fd_write != CURL_SOCKET_BAD)))
-      /* if this is the first lap and one of the file descriptors is fine
-         to work with, skip the timeout */
-      timeout_ms = 0;
-    else {
-      totmp = Curl_timeleft(data, &k->now, FALSE);
-      if(totmp < 0)
-        return CURLE_OPERATION_TIMEDOUT;
-      else if(!totmp)
-        totmp = 1000;
-
-      if(totmp < timeout_ms)
-        timeout_ms = totmp;
-    }
-
-    switch (Curl_socket_ready(fd_read, fd_write, timeout_ms)) {
-    case -1: /* select() error, stop reading */
-#ifdef EINTR
-      /* The EINTR is not serious, and it seems you might get this more
-         often when using the lib in a multi-threaded environment! */
-      if(SOCKERRNO == EINTR)
-        continue;
-#endif
-      return CURLE_RECV_ERROR;  /* indicate a network problem */
-    case 0:  /* timeout */
-    default: /* readable descriptors */
-
-      result = Curl_readwrite(conn, &done);
-      /* "done" signals to us if the transfer(s) are ready */
-      break;
-    }
-    if(result)
-      return result;
-
-    first = FALSE; /* not the first lap anymore */
-  }
-
-  return CURLE_OK;
-}
-
-
-/*
- * Curl_pretransfer() is called immediately before a transfer starts.
- */
-CURLcode Curl_pretransfer(struct SessionHandle *data)
-{
-  CURLcode res;
-  if(!data->change.url) {
-    /* we can't do anything without URL */
-    failf(data, "No URL set!");
-    return CURLE_URL_MALFORMAT;
-  }
-
-  /* Init the SSL session ID cache here. We do it here since we want to do it
-     after the *_setopt() calls (that could specify the size of the cache) but
-     before any transfer takes place. */
-  res = Curl_ssl_initsessions(data, data->set.ssl.max_ssl_sessions);
-  if(res)
-    return res;
-
-  data->set.followlocation=0; /* reset the location-follow counter */
-  data->state.this_is_a_follow = FALSE; /* reset this */
-  data->state.errorbuf = FALSE; /* no error has occurred */
-  data->state.httpversion = 0; /* don't assume any particular server version */
-
-  data->state.ssl_connect_retry = FALSE;
-
-  data->state.authproblem = FALSE;
-  data->state.authhost.want = data->set.httpauth;
-  data->state.authproxy.want = data->set.proxyauth;
-  Curl_safefree(data->info.wouldredirect);
-  data->info.wouldredirect = NULL;
-
-  /* If there is a list of cookie files to read, do it now! */
-  if(data->change.cookielist)
-    Curl_cookie_loadfiles(data);
-
-  /* If there is a list of host pairs to deal with */
-  if(data->change.resolve)
-    res = Curl_loadhostpairs(data);
-
-  if(!res) {
-    /* Allow data->set.use_port to set which port to use. This needs to be
-     * disabled for example when we follow Location: headers to URLs using
-     * different ports! */
-    data->state.allow_port = TRUE;
-
-#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(HAVE_MSG_NOSIGNAL)
-    /*************************************************************
-     * Tell signal handler to ignore SIGPIPE
-     *************************************************************/
-    if(!data->set.no_signal)
-      data->state.prev_signal = signal(SIGPIPE, SIG_IGN);
-#endif
-
-    Curl_initinfo(data); /* reset session-specific information "variables" */
-    Curl_pgrsStartNow(data);
-
-    if(data->set.timeout)
-      Curl_expire(data, data->set.timeout);
-
-    if(data->set.connecttimeout)
-      Curl_expire(data, data->set.connecttimeout);
-
-    /* In case the handle is re-used and an authentication method was picked
-       in the session we need to make sure we only use the one(s) we now
-       consider to be fine */
-    data->state.authhost.picked &= data->state.authhost.want;
-    data->state.authproxy.picked &= data->state.authproxy.want;
-  }
-
-  return res;
-}
-
-/*
- * Curl_posttransfer() is called immediately after a transfer ends
- */
-CURLcode Curl_posttransfer(struct SessionHandle *data)
-{
-#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(HAVE_MSG_NOSIGNAL)
-  /* restore the signal handler for SIGPIPE before we get back */
-  if(!data->set.no_signal)
-    signal(SIGPIPE, data->state.prev_signal);
-#else
-  (void)data; /* unused parameter */
-#endif
-
-  return CURLE_OK;
-}
-
-#ifndef CURL_DISABLE_HTTP
-/*
- * strlen_url() returns the length of the given URL if the spaces within the
- * URL were properly URL encoded.
- */
-static size_t strlen_url(const char *url)
-{
-  const char *ptr;
-  size_t newlen=0;
-  bool left=TRUE; /* left side of the ? */
-
-  for(ptr=url; *ptr; ptr++) {
-    switch(*ptr) {
-    case '?':
-      left=FALSE;
-      /* fall through */
-    default:
-      newlen++;
-      break;
-    case ' ':
-      if(left)
-        newlen+=3;
-      else
-        newlen++;
-      break;
-    }
-  }
-  return newlen;
-}
-
-/* strcpy_url() copies a url to a output buffer and URL-encodes the spaces in
- * the source URL accordingly.
- */
-static void strcpy_url(char *output, const char *url)
-{
-  /* we must add this with whitespace-replacing */
-  bool left=TRUE;
-  const char *iptr;
-  char *optr = output;
-  for(iptr = url;    /* read from here */
-      *iptr;         /* until zero byte */
-      iptr++) {
-    switch(*iptr) {
-    case '?':
-      left=FALSE;
-      /* fall through */
-    default:
-      *optr++=*iptr;
-      break;
-    case ' ':
-      if(left) {
-        *optr++='%'; /* add a '%' */
-        *optr++='2'; /* add a '2' */
-        *optr++='0'; /* add a '0' */
-      }
-      else
-        *optr++='+'; /* add a '+' here */
-      break;
-    }
-  }
-  *optr=0; /* zero terminate output buffer */
-
-}
-
-/*
- * Returns true if the given URL is absolute (as opposed to relative)
- */
-static bool is_absolute_url(const char *url)
-{
-  char prot[16]; /* URL protocol string storage */
-  char letter;   /* used for a silly sscanf */
-
-  return (2 == sscanf(url, "%15[^?&/:]://%c", prot, &letter)) ? TRUE : FALSE;
-}
-
-/*
- * Concatenate a relative URL to a base URL making it absolute.
- * URL-encodes any spaces.
- * The returned pointer must be freed by the caller unless NULL
- * (returns NULL on out of memory).
- */
-static char *concat_url(const char *base, const char *relurl)
-{
-  /***
-   TRY to append this new path to the old URL
-   to the right of the host part. Oh crap, this is doomed to cause
-   problems in the future...
-  */
-  char *newest;
-  char *protsep;
-  char *pathsep;
-  size_t newlen;
-
-  const char *useurl = relurl;
-  size_t urllen;
-
-  /* we must make our own copy of the URL to play with, as it may
-     point to read-only data */
-  char *url_clone=strdup(base);
-
-  if(!url_clone)
-    return NULL; /* skip out of this NOW */
-
-  /* protsep points to the start of the host name */
-  protsep=strstr(url_clone, "//");
-  if(!protsep)
-    protsep=url_clone;
-  else
-    protsep+=2; /* pass the slashes */
-
-  if('/' != relurl[0]) {
-    int level=0;
-
-    /* First we need to find out if there's a ?-letter in the URL,
-       and cut it and the right-side of that off */
-    pathsep = strchr(protsep, '?');
-    if(pathsep)
-      *pathsep=0;
-
-    /* we have a relative path to append to the last slash if there's one
-       available, or if the new URL is just a query string (starts with a
-       '?')  we append the new one at the end of the entire currently worked
-       out URL */
-    if(useurl[0] != '?') {
-      pathsep = strrchr(protsep, '/');
-      if(pathsep)
-        *pathsep=0;
-    }
-
-    /* Check if there's any slash after the host name, and if so, remember
-       that position instead */
-    pathsep = strchr(protsep, '/');
-    if(pathsep)
-      protsep = pathsep+1;
-    else
-      protsep = NULL;
-
-    /* now deal with one "./" or any amount of "../" in the newurl
-       and act accordingly */
-
-    if((useurl[0] == '.') && (useurl[1] == '/'))
-      useurl+=2; /* just skip the "./" */
-
-    while((useurl[0] == '.') &&
-          (useurl[1] == '.') &&
-          (useurl[2] == '/')) {
-      level++;
-      useurl+=3; /* pass the "../" */
-    }
-
-    if(protsep) {
-      while(level--) {
-        /* cut off one more level from the right of the original URL */
-        pathsep = strrchr(protsep, '/');
-        if(pathsep)
-          *pathsep=0;
-        else {
-          *protsep=0;
-          break;
-        }
-      }
-    }
-  }
-  else {
-    /* We got a new absolute path for this server */
-
-    if((relurl[0] == '/') && (relurl[1] == '/')) {
-      /* the new URL starts with //, just keep the protocol part from the
-         original one */
-      *protsep=0;
-      useurl = &relurl[2]; /* we keep the slashes from the original, so we
-                              skip the new ones */
-    }
-    else {
-      /* cut off the original URL from the first slash, or deal with URLs
-         without slash */
-      pathsep = strchr(protsep, '/');
-      if(pathsep) {
-        /* When people use badly formatted URLs, such as
-           "http://www.url.com?dir=/home/daniel" we must not use the first
-           slash, if there's a ?-letter before it! */
-        char *sep = strchr(protsep, '?');
-        if(sep && (sep < pathsep))
-          pathsep = sep;
-        *pathsep=0;
-      }
-      else {
-        /* There was no slash. Now, since we might be operating on a badly
-           formatted URL, such as "http://www.url.com?id=2380" which doesn't
-           use a slash separator as it is supposed to, we need to check for a
-           ?-letter as well! */
-        pathsep = strchr(protsep, '?');
-        if(pathsep)
-          *pathsep=0;
-      }
-    }
-  }
-
-  /* If the new part contains a space, this is a mighty stupid redirect
-     but we still make an effort to do "right". To the left of a '?'
-     letter we replace each space with %20 while it is replaced with '+'
-     on the right side of the '?' letter.
-  */
-  newlen = strlen_url(useurl);
-
-  urllen = strlen(url_clone);
-
-  newest = malloc(urllen + 1 + /* possible slash */
-                  newlen + 1 /* zero byte */);
-
-  if(!newest) {
-    free(url_clone); /* don't leak this */
-    return NULL;
-  }
-
-  /* copy over the root url part */
-  memcpy(newest, url_clone, urllen);
-
-  /* check if we need to append a slash */
-  if(('/' == useurl[0]) || (protsep && !*protsep) || ('?' == useurl[0]))
-    ;
-  else
-    newest[urllen++]='/';
-
-  /* then append the new piece on the right side */
-  strcpy_url(&newest[urllen], useurl);
-
-  free(url_clone);
-
-  return newest;
-}
-#endif /* CURL_DISABLE_HTTP */
-
-/*
- * Curl_follow() handles the URL redirect magic. Pass in the 'newurl' string
- * as given by the remote server and set up the new URL to request.
- */
-CURLcode Curl_follow(struct SessionHandle *data,
-                     char *newurl, /* this 'newurl' is the Location: string,
-                                      and it must be malloc()ed before passed
-                                      here */
-                     followtype type) /* see curl_transfer.h */
-{
-#ifdef CURL_DISABLE_HTTP
-  (void)data;
-  (void)newurl;
-  (void)type;
-  /* Location: following will not happen when HTTP is disabled */
-  return CURLE_TOO_MANY_REDIRECTS;
-#else
-
-  /* Location: redirect */
-  bool disallowport = FALSE;
-
-  if(type == FOLLOW_REDIR) {
-    if((data->set.maxredirs != -1) &&
-        (data->set.followlocation >= data->set.maxredirs)) {
-      failf(data,"Maximum (%ld) redirects followed", data->set.maxredirs);
-      return CURLE_TOO_MANY_REDIRECTS;
-    }
-
-    /* mark the next request as a followed location: */
-    data->state.this_is_a_follow = TRUE;
-
-    data->set.followlocation++; /* count location-followers */
-
-    if(data->set.http_auto_referer) {
-      /* We are asked to automatically set the previous URL as the referer
-         when we get the next URL. We pick the ->url field, which may or may
-         not be 100% correct */
-
-      if(data->change.referer_alloc) {
-        Curl_safefree(data->change.referer);
-        data->change.referer_alloc = FALSE;
-      }
-
-      data->change.referer = strdup(data->change.url);
-      if(!data->change.referer)
-        return CURLE_OUT_OF_MEMORY;
-      data->change.referer_alloc = TRUE; /* yes, free this later */
-    }
-  }
-
-  if(!is_absolute_url(newurl))  {
-    /***
-     *DANG* this is an RFC 2068 violation. The URL is supposed
-     to be absolute and this doesn't seem to be that!
-     */
-    char *absolute = concat_url(data->change.url, newurl);
-    if(!absolute)
-      return CURLE_OUT_OF_MEMORY;
-    free(newurl);
-    newurl = absolute;
-  }
-  else {
-    /* This is an absolute URL, don't allow the custom port number */
-    disallowport = TRUE;
-
-    if(strchr(newurl, ' ')) {
-      /* This new URL contains at least one space, this is a mighty stupid
-         redirect but we still make an effort to do "right". */
-      char *newest;
-      size_t newlen = strlen_url(newurl);
-
-      newest = malloc(newlen+1); /* get memory for this */
-      if(!newest)
-        return CURLE_OUT_OF_MEMORY;
-      strcpy_url(newest, newurl); /* create a space-free URL */
-
-      free(newurl); /* that was no good */
-      newurl = newest; /* use this instead now */
-    }
-
-  }
-
-  if(type == FOLLOW_FAKE) {
-    /* we're only figuring out the new url if we would've followed locations
-       but now we're done so we can get out! */
-    data->info.wouldredirect = newurl;
-    return CURLE_OK;
-  }
-
-  if(disallowport)
-    data->state.allow_port = FALSE;
-
-  if(data->change.url_alloc) {
-    Curl_safefree(data->change.url);
-    data->change.url_alloc = FALSE;
-  }
-
-  data->change.url = newurl;
-  data->change.url_alloc = TRUE;
-  newurl = NULL; /* don't free! */
-
-  infof(data, "Issue another request to this URL: '%s'\n", data->change.url);
-
-  /*
-   * We get here when the HTTP code is 300-399 (and 401). We need to perform
-   * differently based on exactly what return code there was.
-   *
-   * News from 7.10.6: we can also get here on a 401 or 407, in case we act on
-   * a HTTP (proxy-) authentication scheme other than Basic.
-   */
-  switch(data->info.httpcode) {
-    /* 401 - Act on a WWW-Authenticate, we keep on moving and do the
-       Authorization: XXXX header in the HTTP request code snippet */
-    /* 407 - Act on a Proxy-Authenticate, we keep on moving and do the
-       Proxy-Authorization: XXXX header in the HTTP request code snippet */
-    /* 300 - Multiple Choices */
-    /* 306 - Not used */
-    /* 307 - Temporary Redirect */
-  default:  /* for all above (and the unknown ones) */
-    /* Some codes are explicitly mentioned since I've checked RFC2616 and they
-     * seem to be OK to POST to.
-     */
-    break;
-  case 301: /* Moved Permanently */
-    /* (quote from RFC2616, section 10.3.2):
-     *
-     * When automatically redirecting a POST request after receiving a 301
-     * status code, some existing HTTP/1.0 user agents will erroneously change
-     * it into a GET request.
-     *
-     * ----
-     *
-     * As most of the important user agents do this obvious RFC2616 violation,
-     * many webservers expect this. So these servers often answers to a POST
-     * request with an error page.  To be sure that libcurl gets the page that
-     * most user agents would get, libcurl has to force GET.
-     *
-     * This behavior can be overridden with CURLOPT_POSTREDIR.
-     */
-    if((data->set.httpreq == HTTPREQ_POST
-        || data->set.httpreq == HTTPREQ_POST_FORM)
-       && !(data->set.keep_post & CURL_REDIR_POST_301)) {
-      infof(data,
-            "Violate RFC 2616/10.3.2 and switch from POST to GET\n");
-      data->set.httpreq = HTTPREQ_GET;
-    }
-    break;
-  case 302: /* Found */
-    /* (From 10.3.3)
-
-    Note: RFC 1945 and RFC 2068 specify that the client is not allowed
-    to change the method on the redirected request.  However, most
-    existing user agent implementations treat 302 as if it were a 303
-    response, performing a GET on the Location field-value regardless
-    of the original request method. The status codes 303 and 307 have
-    been added for servers that wish to make unambiguously clear which
-    kind of reaction is expected of the client.
-
-    (From 10.3.4)
-
-    Note: Many pre-HTTP/1.1 user agents do not understand the 303
-    status. When interoperability with such clients is a concern, the
-    302 status code may be used instead, since most user agents react
-    to a 302 response as described here for 303.
-
-    This behavior can be overridden with CURLOPT_POSTREDIR
-    */
-    if((data->set.httpreq == HTTPREQ_POST
-        || data->set.httpreq == HTTPREQ_POST_FORM)
-       && !(data->set.keep_post & CURL_REDIR_POST_302)) {
-      infof(data,
-            "Violate RFC 2616/10.3.3 and switch from POST to GET\n");
-      data->set.httpreq = HTTPREQ_GET;
-    }
-    break;
-
-  case 303: /* See Other */
-    /* Disable both types of POSTs, unless the user explicitely
-       asks for POST after POST */
-    if(data->set.httpreq != HTTPREQ_GET
-      && !(data->set.keep_post & CURL_REDIR_POST_303)) {
-      data->set.httpreq = HTTPREQ_GET; /* enforce GET request */
-      infof(data, "Disables POST, goes with %s\n",
-            data->set.opt_no_body?"HEAD":"GET");
-    }
-    break;
-  case 304: /* Not Modified */
-    /* 304 means we did a conditional request and it was "Not modified".
-     * We shouldn't get any Location: header in this response!
-     */
-    break;
-  case 305: /* Use Proxy */
-    /* (quote from RFC2616, section 10.3.6):
-     * "The requested resource MUST be accessed through the proxy given
-     * by the Location field. The Location field gives the URI of the
-     * proxy.  The recipient is expected to repeat this single request
-     * via the proxy. 305 responses MUST only be generated by origin
-     * servers."
-     */
-    break;
-  }
-  Curl_pgrsTime(data, TIMER_REDIRECT);
-  Curl_pgrsResetTimesSizes(data);
-
-  return CURLE_OK;
-#endif /* CURL_DISABLE_HTTP */
-}
-
-static CURLcode
-connect_host(struct SessionHandle *data,
-             struct connectdata **conn)
-{
-  CURLcode res = CURLE_OK;
-
-  bool async;
-  bool protocol_done=TRUE; /* will be TRUE always since this is only used
-                                within the easy interface */
-  Curl_pgrsTime(data, TIMER_STARTSINGLE);
-  res = Curl_connect(data, conn, &async, &protocol_done);
-
-  if((CURLE_OK == res) && async) {
-    /* Now, if async is TRUE here, we need to wait for the name
-       to resolve */
-    res = Curl_resolver_wait_resolv(*conn, NULL);
-    if(CURLE_OK == res) {
-      /* Resolved, continue with the connection */
-      res = Curl_async_resolved(*conn, &protocol_done);
-      if(res)
-        *conn = NULL;
-    }
-    else {
-      /* if we can't resolve, we kill this "connection" now */
-      (void)Curl_disconnect(*conn, /* dead_connection */ FALSE);
-      *conn = NULL;
-    }
-  }
-
-  return res;
-}
-
-CURLcode
-Curl_reconnect_request(struct connectdata **connp)
-{
-  CURLcode result = CURLE_OK;
-  struct connectdata *conn = *connp;
-  struct SessionHandle *data = conn->data;
-
-  /* This was a re-use of a connection and we got a write error in the
-   * DO-phase. Then we DISCONNECT this connection and have another attempt to
-   * CONNECT and then DO again! The retry cannot possibly find another
-   * connection to re-use, since we only keep one possible connection for
-   * each.  */
-
-  infof(data, "Re-used connection seems dead, get a new one\n");
-
-  conn->bits.close = TRUE; /* enforce close of this connection */
-  result = Curl_done(&conn, result, FALSE); /* we are so done with this */
-
-  /* conn may no longer be a good pointer, clear it to avoid mistakes by
-     parent functions */
-  *connp = NULL;
-
-  /*
-   * According to bug report #1330310. We need to check for CURLE_SEND_ERROR
-   * here as well. I figure this could happen when the request failed on a FTP
-   * connection and thus Curl_done() itself tried to use the connection
-   * (again). Slight Lack of feedback in the report, but I don't think this
-   * extra check can do much harm.
-   */
-  if((CURLE_OK == result) || (CURLE_SEND_ERROR == result)) {
-    bool async;
-    bool protocol_done = TRUE;
-
-    /* Now, redo the connect and get a new connection */
-    result = Curl_connect(data, connp, &async, &protocol_done);
-    if(CURLE_OK == result) {
-      /* We have connected or sent away a name resolve query fine */
-
-      conn = *connp; /* setup conn to again point to something nice */
-      if(async) {
-        /* Now, if async is TRUE here, we need to wait for the name
-           to resolve */
-        result = Curl_resolver_wait_resolv(conn, NULL);
-        if(result)
-          return result;
-
-        /* Resolved, continue with the connection */
-        result = Curl_async_resolved(conn, &protocol_done);
-        if(result)
-          return result;
-      }
-    }
-  }
-
-  return result;
-}
-
-/* Returns CURLE_OK *and* sets '*url' if a request retry is wanted.
-
-   NOTE: that the *url is malloc()ed. */
-CURLcode Curl_retry_request(struct connectdata *conn,
-                            char **url)
-{
-  struct SessionHandle *data = conn->data;
-
-  *url = NULL;
-
-  /* if we're talking upload, we can't do the checks below, unless the protocol
-     is HTTP as when uploading over HTTP we will still get a response */
-  if(data->set.upload &&
-     !(conn->handler->protocol&(CURLPROTO_HTTP|CURLPROTO_RTSP)))
-    return CURLE_OK;
-
-  if(/* workaround for broken TLS servers */ data->state.ssl_connect_retry ||
-      ((data->req.bytecount +
-        data->req.headerbytecount == 0) &&
-        conn->bits.reuse &&
-        !data->set.opt_no_body &&
-        data->set.rtspreq != RTSPREQ_RECEIVE)) {
-    /* We got no data, we attempted to re-use a connection and yet we want a
-       "body". This might happen if the connection was left alive when we were
-       done using it before, but that was closed when we wanted to read from
-       it again. Bad luck. Retry the same request on a fresh connect! */
-    infof(conn->data, "Connection died, retrying a fresh connect\n");
-    *url = strdup(conn->data->change.url);
-    if(!*url)
-      return CURLE_OUT_OF_MEMORY;
-
-    conn->bits.close = TRUE; /* close this connection */
-    conn->bits.retry = TRUE; /* mark this as a connection we're about
-                                to retry. Marking it this way should
-                                prevent i.e HTTP transfers to return
-                                error just because nothing has been
-                                transferred! */
-
-
-    if((conn->handler->protocol&CURLPROTO_HTTP) &&
-       data->state.proto.http->writebytecount)
-      return Curl_readrewind(conn);
-  }
-  return CURLE_OK;
-}
-
-static CURLcode Curl_do_perform(struct SessionHandle *data)
-{
-  CURLcode res;
-  CURLcode res2;
-  struct connectdata *conn=NULL;
-  char *newurl = NULL; /* possibly a new URL to follow to! */
-  followtype follow = FOLLOW_NONE;
-
-  data->state.used_interface = Curl_if_easy;
-
-  res = Curl_pretransfer(data);
-  if(res)
-    return res;
-
-  /*
-   * It is important that there is NO 'return' from this function at any other
-   * place than falling down to the end of the function! This is because we
-   * have cleanup stuff that must be done before we get back, and that is only
-   * performed after this do-while loop.
-   */
-
-  for(;;) {
-    res = connect_host(data, &conn);   /* primary connection */
-
-    if(res == CURLE_OK) {
-      bool do_done;
-      if(data->set.connect_only) {
-        /* keep connection open for application to use the socket */
-        conn->bits.close = FALSE;
-        res = Curl_done(&conn, CURLE_OK, FALSE);
-        break;
-      }
-      res = Curl_do(&conn, &do_done);
-
-      if(res == CURLE_OK) {
-        if(conn->data->set.wildcardmatch) {
-          if(conn->data->wildcard.state == CURLWC_DONE ||
-             conn->data->wildcard.state == CURLWC_SKIP) {
-            /* keep connection open for application to use the socket */
-            conn->bits.close = FALSE;
-            res = Curl_done(&conn, CURLE_OK, FALSE);
-            break;
-          }
-        }
-        res = Transfer(conn); /* now fetch that URL please */
-        if((res == CURLE_OK) || (res == CURLE_RECV_ERROR)) {
-          bool retry = FALSE;
-          CURLcode rc = Curl_retry_request(conn, &newurl);
-          if(rc)
-            res = rc;
-          else
-            retry = (newurl?TRUE:FALSE);
-
-          if(retry) {
-            /* we know (newurl != NULL) at this point */
-            res = CURLE_OK;
-            follow = FOLLOW_RETRY;
-          }
-          else if(res == CURLE_OK) {
-            /*
-             * We must duplicate the new URL here as the connection data may
-             * be free()ed in the Curl_done() function. We prefer the newurl
-             * one since that's used for redirects or just further requests
-             * for retries or multi-stage HTTP auth methods etc.
-             */
-            if(data->req.newurl) {
-              follow = FOLLOW_REDIR;
-              newurl = strdup(data->req.newurl);
-              if(!newurl)
-                res = CURLE_OUT_OF_MEMORY;
-            }
-            else if(data->req.location) {
-              follow = FOLLOW_FAKE;
-              newurl = strdup(data->req.location);
-              if(!newurl)
-                res = CURLE_OUT_OF_MEMORY;
-            }
-          }
-
-          /* in the above cases where 'newurl' gets assigned, we have a fresh
-           * allocated memory pointed to */
-        }
-        if(res != CURLE_OK) {
-          /* The transfer phase returned error, we mark the connection to get
-           * closed to prevent being re-used. This is because we can't
-           * possibly know if the connection is in a good shape or not now. */
-          conn->bits.close = TRUE;
-
-          if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) {
-            /* if we failed anywhere, we must clean up the secondary socket if
-               it was used */
-            Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
-            conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
-          }
-        }
-
-        /* Always run Curl_done(), even if some of the previous calls
-           failed, but return the previous (original) error code */
-        res2 = Curl_done(&conn, res, FALSE);
-
-        if(CURLE_OK == res)
-          res = res2;
-      }
-      else if(conn)
-        /* Curl_do() failed, clean up left-overs in the done-call, but note
-           that at some cases the conn pointer is NULL when Curl_do() failed
-           and the connection cache is very small so only call Curl_done() if
-           conn is still "alive". */
-        /* ignore return code since we already have an error to return */
-        (void)Curl_done(&conn, res, FALSE);
-
-      /*
-       * Important: 'conn' cannot be used here, since it may have been closed
-       * in 'Curl_done' or other functions.
-       */
-
-      if((res == CURLE_OK) && follow) {
-        res = Curl_follow(data, newurl, follow);
-        if(CURLE_OK == res) {
-          /* if things went fine, Curl_follow() freed or otherwise took
-             responsibility for the newurl pointer */
-          newurl = NULL;
-          if(follow >= FOLLOW_RETRY) {
-            follow = FOLLOW_NONE;
-            continue;
-          }
-          /* else we break out of the loop below */
-        }
-      }
-    }
-    break; /* it only reaches here when this shouldn't loop */
-
-  } /* loop if Location: */
-
-  if(newurl)
-    free(newurl);
-
-  if(res && !data->state.errorbuf) {
-    /*
-     * As an extra precaution: if no error string has been set and there was
-     * an error, use the strerror() string or if things are so bad that not
-     * even that is good, set a bad string that mentions the error code.
-     */
-    const char *str = curl_easy_strerror(res);
-    if(!str)
-      failf(data, "unspecified error %d", (int)res);
-    else
-      failf(data, "%s", str);
-  }
-
-  /* run post-transfer unconditionally, but don't clobber the return code if
-     we already have an error code recorder */
-  res2 = Curl_posttransfer(data);
-  if(!res && res2)
-    res = res2;
-
-  return res;
-}
-
-/*
- * Curl_perform() is the internal high-level function that gets called by the
- * external curl_easy_perform() function. It inits, performs and cleans up a
- * single file transfer.
- */
-CURLcode Curl_perform(struct SessionHandle *data)
-{
-  CURLcode res;
-  if(!data->set.wildcardmatch)
-    return Curl_do_perform(data);
-
-  /* init main wildcard structures */
-  res = Curl_wildcard_init(&data->wildcard);
-  if(res)
-    return res;
-
-  res = Curl_do_perform(data);
-  if(res) {
-    Curl_wildcard_dtor(&data->wildcard);
-    return res;
-  }
-
-  /* wildcard loop */
-  while(!res && data->wildcard.state != CURLWC_DONE)
-    res = Curl_do_perform(data);
-
-  Curl_wildcard_dtor(&data->wildcard);
-
-  /* wildcard download finished or failed */
-  data->wildcard.state = CURLWC_INIT;
-  return res;
-}
-
-/*
- * Curl_setup_transfer() is called to setup some basic properties for the
- * upcoming transfer.
- */
-void
-Curl_setup_transfer(
-  struct connectdata *conn, /* connection data */
-  int sockindex,            /* socket index to read from or -1 */
-  curl_off_t size,          /* -1 if unknown at this point */
-  bool getheader,           /* TRUE if header parsing is wanted */
-  curl_off_t *bytecountp,   /* return number of bytes read or NULL */
-  int writesockindex,       /* socket index to write to, it may very well be
-                               the same we read from. -1 disables */
-  curl_off_t *writecountp   /* return number of bytes written or NULL */
-  )
-{
-  struct SessionHandle *data;
-  struct SingleRequest *k;
-
-  DEBUGASSERT(conn != NULL);
-
-  data = conn->data;
-  k = &data->req;
-
-  DEBUGASSERT((sockindex <= 1) && (sockindex >= -1));
-
-  /* now copy all input parameters */
-  conn->sockfd = sockindex == -1 ?
-      CURL_SOCKET_BAD : conn->sock[sockindex];
-  conn->writesockfd = writesockindex == -1 ?
-      CURL_SOCKET_BAD:conn->sock[writesockindex];
-  k->getheader = getheader;
-
-  k->size = size;
-  k->bytecountp = bytecountp;
-  k->writebytecountp = writecountp;
-
-  /* The code sequence below is placed in this function just because all
-     necessary input is not always known in do_complete() as this function may
-     be called after that */
-
-  if(!k->getheader) {
-    k->header = FALSE;
-    if(size > 0)
-      Curl_pgrsSetDownloadSize(data, size);
-  }
-  /* we want header and/or body, if neither then don't do this! */
-  if(k->getheader || !data->set.opt_no_body) {
-
-    if(conn->sockfd != CURL_SOCKET_BAD)
-      k->keepon |= KEEP_RECV;
-
-    if(conn->writesockfd != CURL_SOCKET_BAD) {
-      /* HTTP 1.1 magic:
-
-         Even if we require a 100-return code before uploading data, we might
-         need to write data before that since the REQUEST may not have been
-         finished sent off just yet.
-
-         Thus, we must check if the request has been sent before we set the
-         state info where we wait for the 100-return code
-      */
-      if((data->state.expect100header) &&
-         (data->state.proto.http->sending == HTTPSEND_BODY)) {
-        /* wait with write until we either got 100-continue or a timeout */
-        k->exp100 = EXP100_AWAITING_CONTINUE;
-        k->start100 = Curl_tvnow();
-
-        /* set a timeout for the multi interface */
-        Curl_expire(data, CURL_TIMEOUT_EXPECT_100);
-      }
-      else {
-        if(data->state.expect100header)
-          /* when we've sent off the rest of the headers, we must await a
-             100-continue but first finish sending the request */
-          k->exp100 = EXP100_SENDING_REQUEST;
-
-        /* enable the write bit when we're not waiting for continue */
-        k->keepon |= KEEP_SEND;
-      }
-    } /* if(conn->writesockfd != CURL_SOCKET_BAD) */
-  } /* if(k->getheader || !data->set.opt_no_body) */
-
-}
diff --git a/lib/url.c b/lib/url.c
deleted file mode 100644 (file)
index 52badc5..0000000
--- a/lib/url.c
+++ /dev/null
@@ -1,5423 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef HAVE_NET_IF_H
-#include <net/if.h>
-#endif
-#ifdef HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-
-#ifdef HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-
-#ifdef __VMS
-#include <in.h>
-#include <inet.h>
-#endif
-
-#ifndef HAVE_SOCKET
-#error "We can't compile without socket() support!"
-#endif
-
-#ifdef HAVE_LIMITS_H
-#include <limits.h>
-#endif
-
-#ifdef USE_LIBIDN
-#include <idna.h>
-#include <tld.h>
-#include <stringprep.h>
-#ifdef HAVE_IDN_FREE_H
-#include <idn-free.h>
-#else
-/* prototype from idn-free.h, not provided by libidn 0.4.5's make install! */
-void idn_free (void *ptr);
-#endif
-#ifndef HAVE_IDN_FREE
-/* if idn_free() was not found in this version of libidn use free() instead */
-#define idn_free(x) (free)(x)
-#endif
-#elif defined(USE_WIN32_IDN)
-/* prototype for curl_win32_idn_to_ascii() */
-int curl_win32_idn_to_ascii(const char *in, char **out);
-#endif  /* USE_LIBIDN */
-
-#include "curl_urldata.h"
-#include "curl_netrc.h"
-
-#include "curl_formdata.h"
-#include "curl_sslgen.h"
-#include "curl_hostip.h"
-#include "curl_transfer.h"
-#include "curl_sendf.h"
-#include "curl_progress.h"
-#include "curl_cookie.h"
-#include "curl_strequal.h"
-#include "curl_strerror.h"
-#include "curl_escape.h"
-#include "curl_strtok.h"
-#include "curl_share.h"
-#include "curl_content_encoding.h"
-#include "curl_http_digest.h"
-#include "curl_http_negotiate.h"
-#include "curl_select.h"
-#include "curl_multiif.h"
-#include "curl_easyif.h"
-#include "curl_speedcheck.h"
-#include "curl_rawstr.h"
-#include "curl_warnless.h"
-#include "curl_non_ascii.h"
-#include "curl_inet_pton.h"
-
-/* And now for the protocols */
-#include "curl_ftp.h"
-#include "curl_dict.h"
-#include "curl_telnet.h"
-#include "curl_tftp.h"
-#include "curl_http.h"
-#include "curl_file.h"
-#include "curl_ldap.h"
-#include "curl_ssh.h"
-#include "curl_imap.h"
-#include "curl_url.h"
-#include "curl_connect.h"
-#include "curl_inet_ntop.h"
-#include "curl_ntlm.h"
-#include "curl_ntlm_wb.h"
-#include "curl_socks.h"
-#include "curl_rtmp.h"
-#include "curl_gopher.h"
-#include "curl_http_proxy.h"
-#include "curl_bundles.h"
-#include "curl_conncache.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-/* Local static prototypes */
-static bool ConnectionKillOne(struct SessionHandle *data);
-static void conn_free(struct connectdata *conn);
-static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke);
-static CURLcode do_init(struct connectdata *conn);
-static CURLcode parse_url_userpass(struct SessionHandle *data,
-                                   struct connectdata *conn,
-                                   char *user, char *passwd);
-/*
- * Protocol table.
- */
-
-static const struct Curl_handler * const protocols[] = {
-
-#ifndef CURL_DISABLE_HTTP
-  &Curl_handler_http,
-#endif
-
-#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP)
-  &Curl_handler_https,
-#endif
-
-#ifndef CURL_DISABLE_FTP
-  &Curl_handler_ftp,
-#endif
-
-#if defined(USE_SSL) && !defined(CURL_DISABLE_FTP)
-  &Curl_handler_ftps,
-#endif
-
-#ifndef CURL_DISABLE_TELNET
-  &Curl_handler_telnet,
-#endif
-
-#ifndef CURL_DISABLE_DICT
-  &Curl_handler_dict,
-#endif
-
-#ifndef CURL_DISABLE_LDAP
-  &Curl_handler_ldap,
-#if !defined(CURL_DISABLE_LDAPS) && \
-    ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \
-     (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL)))
-  &Curl_handler_ldaps,
-#endif
-#endif
-
-#ifndef CURL_DISABLE_FILE
-  &Curl_handler_file,
-#endif
-
-#ifndef CURL_DISABLE_TFTP
-  &Curl_handler_tftp,
-#endif
-
-#ifdef USE_LIBSSH2
-  &Curl_handler_scp,
-  &Curl_handler_sftp,
-#endif
-
-#ifndef CURL_DISABLE_IMAP
-  &Curl_handler_imap,
-#ifdef USE_SSL
-  &Curl_handler_imaps,
-#endif
-#endif
-
-#ifndef CURL_DISABLE_POP3
-  &Curl_handler_pop3,
-#ifdef USE_SSL
-  &Curl_handler_pop3s,
-#endif
-#endif
-
-#ifndef CURL_DISABLE_SMTP
-  &Curl_handler_smtp,
-#ifdef USE_SSL
-  &Curl_handler_smtps,
-#endif
-#endif
-
-#ifndef CURL_DISABLE_RTSP
-  &Curl_handler_rtsp,
-#endif
-
-#ifndef CURL_DISABLE_GOPHER
-  &Curl_handler_gopher,
-#endif
-
-#ifdef USE_LIBRTMP
-  &Curl_handler_rtmp,
-  &Curl_handler_rtmpt,
-  &Curl_handler_rtmpe,
-  &Curl_handler_rtmpte,
-  &Curl_handler_rtmps,
-  &Curl_handler_rtmpts,
-#endif
-
-  (struct Curl_handler *) NULL
-};
-
-/*
- * Dummy handler for undefined protocol schemes.
- */
-
-static const struct Curl_handler Curl_handler_dummy = {
-  "<no protocol>",                      /* scheme */
-  ZERO_NULL,                            /* setup_connection */
-  ZERO_NULL,                            /* do_it */
-  ZERO_NULL,                            /* done */
-  ZERO_NULL,                            /* do_more */
-  ZERO_NULL,                            /* connect_it */
-  ZERO_NULL,                            /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
-  ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* readwrite */
-  0,                                    /* defport */
-  0,                                    /* protocol */
-  PROTOPT_NONE                          /* flags */
-};
-
-static void close_connections(struct SessionHandle *data)
-{
-  /* Loop through all open connections and kill them one by one */
-  bool killed;
-  do {
-    killed = ConnectionKillOne(data);
-  } while(killed);
-}
-
-void Curl_freeset(struct SessionHandle * data)
-{
-  /* Free all dynamic strings stored in the data->set substructure. */
-  enum dupstring i;
-  for(i=(enum dupstring)0; i < STRING_LAST; i++)
-    Curl_safefree(data->set.str[i]);
-
-  if(data->change.referer_alloc) {
-    Curl_safefree(data->change.referer);
-    data->change.referer_alloc = FALSE;
-  }
-  data->change.referer = NULL;
-}
-
-static CURLcode setstropt(char **charp, char * s)
-{
-  /* Release the previous storage at `charp' and replace by a dynamic storage
-     copy of `s'. Return CURLE_OK or CURLE_OUT_OF_MEMORY. */
-
-  Curl_safefree(*charp);
-
-  if(s) {
-    s = strdup(s);
-
-    if(!s)
-      return CURLE_OUT_OF_MEMORY;
-
-    *charp = s;
-  }
-
-  return CURLE_OK;
-}
-
-static CURLcode setstropt_userpwd(char *option, char **user_storage,
-                                  char **pwd_storage)
-{
-  char* separator;
-  CURLcode result = CURLE_OK;
-
-  if(!option) {
-    /* we treat a NULL passed in as a hint to clear existing info */
-    Curl_safefree(*user_storage);
-    *user_storage = (char *) NULL;
-    Curl_safefree(*pwd_storage);
-    *pwd_storage = (char *) NULL;
-    return CURLE_OK;
-  }
-
-  separator = strchr(option, ':');
-  if(separator != NULL) {
-
-    /* store username part of option */
-    char * p;
-    size_t username_len = (size_t)(separator-option);
-    p = malloc(username_len+1);
-    if(!p)
-      result = CURLE_OUT_OF_MEMORY;
-    else {
-      memcpy(p, option, username_len);
-      p[username_len] = '\0';
-      Curl_safefree(*user_storage);
-      *user_storage = p;
-    }
-
-    /* store password part of option */
-    if(result == CURLE_OK)
-      result = setstropt(pwd_storage, separator+1);
-  }
-  else {
-    result = setstropt(user_storage, option);
-  }
-  return result;
-}
-
-CURLcode Curl_dupset(struct SessionHandle * dst, struct SessionHandle * src)
-{
-  CURLcode r = CURLE_OK;
-  enum dupstring i;
-
-  /* Copy src->set into dst->set first, then deal with the strings
-     afterwards */
-  dst->set = src->set;
-
-  /* clear all string pointers first */
-  memset(dst->set.str, 0, STRING_LAST * sizeof(char *));
-
-  /* duplicate all strings */
-  for(i=(enum dupstring)0; i< STRING_LAST; i++) {
-    r = setstropt(&dst->set.str[i], src->set.str[i]);
-    if(r != CURLE_OK)
-      break;
-  }
-
-  /* If a failure occurred, freeing has to be performed externally. */
-  return r;
-}
-
-/*
- * This is the internal function curl_easy_cleanup() calls. This should
- * cleanup and free all resources associated with this sessionhandle.
- *
- * NOTE: if we ever add something that attempts to write to a socket or
- * similar here, we must ignore SIGPIPE first. It is currently only done
- * when curl_easy_perform() is invoked.
- */
-
-CURLcode Curl_close(struct SessionHandle *data)
-{
-  struct Curl_multi *m;
-
-  if(!data)
-    return CURLE_OK;
-
-  Curl_expire(data, 0); /* shut off timers */
-
-  m = data->multi;
-
-  if(m)
-    /* This handle is still part of a multi handle, take care of this first
-       and detach this handle from there. */
-    curl_multi_remove_handle(data->multi, data);
-
-  /* Destroy the timeout list that is held in the easy handle. It is
-     /normally/ done by curl_multi_remove_handle() but this is "just in
-     case" */
-  if(data->state.timeoutlist) {
-    Curl_llist_destroy(data->state.timeoutlist, NULL);
-    data->state.timeoutlist = NULL;
-  }
-
-  data->magic = 0; /* force a clear AFTER the possibly enforced removal from
-                      the multi handle, since that function uses the magic
-                      field! */
-
-  if(data->state.conn_cache) {
-    if(data->state.conn_cache->type == CONNCACHE_PRIVATE) {
-      /* close all connections still alive that are in the private connection
-         cache, as we no longer have the pointer left to the shared one. */
-      close_connections(data);
-      Curl_conncache_destroy(data->state.conn_cache);
-      data->state.conn_cache = NULL;
-    }
-  }
-
-  if(data->dns.hostcachetype == HCACHE_PRIVATE)
-    Curl_hostcache_destroy(data);
-
-  if(data->state.rangestringalloc)
-    free(data->state.range);
-
-  /* Free the pathbuffer */
-  Curl_safefree(data->state.pathbuffer);
-  data->state.path = NULL;
-
-  Curl_safefree(data->state.proto.generic);
-
-  /* Close down all open SSL info and sessions */
-  Curl_ssl_close_all(data);
-  Curl_safefree(data->state.first_host);
-  Curl_safefree(data->state.scratch);
-  Curl_ssl_free_certinfo(data);
-
-  if(data->change.referer_alloc) {
-    Curl_safefree(data->change.referer);
-    data->change.referer_alloc = FALSE;
-  }
-  data->change.referer = NULL;
-
-  if(data->change.url_alloc) {
-    Curl_safefree(data->change.url);
-    data->change.url_alloc = FALSE;
-  }
-  data->change.url = NULL;
-
-  Curl_safefree(data->state.headerbuff);
-
-  Curl_flush_cookies(data, 1);
-
-  Curl_digest_cleanup(data);
-
-  Curl_safefree(data->info.contenttype);
-  Curl_safefree(data->info.wouldredirect);
-
-  /* this destroys the channel and we cannot use it anymore after this */
-  Curl_resolver_cleanup(data->state.resolver);
-
-  Curl_convert_close(data);
-
-  /* No longer a dirty share, if it exists */
-  if(data->share) {
-    Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
-    data->share->dirty--;
-    Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
-  }
-
-  Curl_freeset(data);
-  free(data);
-  return CURLE_OK;
-}
-
-/*
- * Initialize the UserDefined fields within a SessionHandle.
- * This may be safely called on a new or existing SessionHandle.
- */
-CURLcode Curl_init_userdefined(struct UserDefined *set)
-{
-  CURLcode res = CURLE_OK;
-
-  set->out = stdout; /* default output to stdout */
-  set->in  = stdin;  /* default input from stdin */
-  set->err  = stderr;  /* default stderr to stderr */
-
-  /* use fwrite as default function to store output */
-  set->fwrite_func = (curl_write_callback)fwrite;
-
-  /* use fread as default function to read input */
-  set->fread_func = (curl_read_callback)fread;
-  set->is_fread_set = 0;
-  set->is_fwrite_set = 0;
-
-  set->seek_func = ZERO_NULL;
-  set->seek_client = ZERO_NULL;
-
-  /* conversion callbacks for non-ASCII hosts */
-  set->convfromnetwork = ZERO_NULL;
-  set->convtonetwork   = ZERO_NULL;
-  set->convfromutf8    = ZERO_NULL;
-
-  set->infilesize = -1;      /* we don't know any size */
-  set->postfieldsize = -1;   /* unknown size */
-  set->maxredirs = -1;       /* allow any amount by default */
-
-  set->httpreq = HTTPREQ_GET; /* Default HTTP request */
-  set->rtspreq = RTSPREQ_OPTIONS; /* Default RTSP request */
-  set->ftp_use_epsv = TRUE;   /* FTP defaults to EPSV operations */
-  set->ftp_use_eprt = TRUE;   /* FTP defaults to EPRT operations */
-  set->ftp_use_pret = FALSE;  /* mainly useful for drftpd servers */
-  set->ftp_filemethod = FTPFILE_MULTICWD;
-
-  set->dns_cache_timeout = 60; /* Timeout every 60 seconds by default */
-
-  /* Set the default size of the SSL session ID cache */
-  set->ssl.max_ssl_sessions = 5;
-
-  set->proxyport = CURL_DEFAULT_PROXY_PORT; /* from curl_url.h */
-  set->proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */
-  set->httpauth = CURLAUTH_BASIC;  /* defaults to basic */
-  set->proxyauth = CURLAUTH_BASIC; /* defaults to basic */
-
-  /* make libcurl quiet by default: */
-  set->hide_progress = TRUE;  /* CURLOPT_NOPROGRESS changes these */
-
-  /*
-   * libcurl 7.10 introduced SSL verification *by default*! This needs to be
-   * switched off unless wanted.
-   */
-  set->ssl.verifypeer = TRUE;
-  set->ssl.verifyhost = TRUE;
-#ifdef USE_TLS_SRP
-  set->ssl.authtype = CURL_TLSAUTH_NONE;
-#endif
-  set->ssh_auth_types = CURLSSH_AUTH_DEFAULT; /* defaults to any auth
-                                                      type */
-  set->ssl.sessionid = TRUE; /* session ID caching enabled by default */
-
-  set->new_file_perms = 0644;    /* Default permissions */
-  set->new_directory_perms = 0755; /* Default permissions */
-
-  /* for the *protocols fields we don't use the CURLPROTO_ALL convenience
-     define since we internally only use the lower 16 bits for the passed
-     in bitmask to not conflict with the private bits */
-  set->allowed_protocols = CURLPROTO_ALL;
-  set->redir_protocols =
-    CURLPROTO_ALL & ~(CURLPROTO_FILE|CURLPROTO_SCP); /* not FILE or SCP */
-
-#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
-  /*
-   * disallow unprotected protection negotiation NEC reference implementation
-   * seem not to follow rfc1961 section 4.3/4.4
-   */
-  set->socks5_gssapi_nec = FALSE;
-  /* set default gssapi service name */
-  res = setstropt(&set->str[STRING_SOCKS5_GSSAPI_SERVICE],
-                  (char *) CURL_DEFAULT_SOCKS5_GSSAPI_SERVICE);
-  if(res != CURLE_OK)
-    return res;
-#endif
-
-  /* This is our preferred CA cert bundle/path since install time */
-#if defined(CURL_CA_BUNDLE)
-  res = setstropt(&set->str[STRING_SSL_CAFILE], (char *) CURL_CA_BUNDLE);
-#elif defined(CURL_CA_PATH)
-  res = setstropt(&set->str[STRING_SSL_CAPATH], (char *) CURL_CA_PATH);
-#endif
-
-  set->wildcardmatch  = FALSE;
-  set->chunk_bgn      = ZERO_NULL;
-  set->chunk_end      = ZERO_NULL;
-
-  /* tcp keepalives are disabled by default, but provide reasonable values for
-   * the interval and idle times.
-   */
-  set->tcp_keepalive = FALSE;
-  set->tcp_keepintvl = 60;
-  set->tcp_keepidle = 60;
-
-  return res;
-}
-
-/**
- * Curl_open()
- *
- * @param curl is a pointer to a sessionhandle pointer that gets set by this
- * function.
- * @return CURLcode
- */
-
-CURLcode Curl_open(struct SessionHandle **curl)
-{
-  CURLcode res = CURLE_OK;
-  struct SessionHandle *data;
-  CURLcode status;
-
-  /* Very simple start-up: alloc the struct, init it with zeroes and return */
-  data = calloc(1, sizeof(struct SessionHandle));
-  if(!data) {
-    /* this is a very serious error */
-    DEBUGF(fprintf(stderr, "Error: calloc of SessionHandle failed\n"));
-    return CURLE_OUT_OF_MEMORY;
-  }
-
-  data->magic = CURLEASY_MAGIC_NUMBER;
-
-  status = Curl_resolver_init(&data->state.resolver);
-  if(status) {
-    DEBUGF(fprintf(stderr, "Error: resolver_init failed\n"));
-    free(data);
-    return status;
-  }
-
-  /* We do some initial setup here, all those fields that can't be just 0 */
-
-  data->state.headerbuff = malloc(HEADERSIZE);
-  if(!data->state.headerbuff) {
-    DEBUGF(fprintf(stderr, "Error: malloc of headerbuff failed\n"));
-    res = CURLE_OUT_OF_MEMORY;
-  }
-  else {
-    Curl_easy_initHandleData(data);
-    res = Curl_init_userdefined(&data->set);
-
-    data->state.headersize=HEADERSIZE;
-
-    Curl_convert_init(data);
-
-    /* most recent connection is not yet defined */
-    data->state.lastconnect = NULL;
-
-    data->progress.flags |= PGRS_HIDE;
-    data->state.current_speed = -1; /* init to negative == impossible */
-
-    data->wildcard.state = CURLWC_INIT;
-    data->wildcard.filelist = NULL;
-    data->set.fnmatch = ZERO_NULL;
-    /* This no longer creates a connection cache here. It is instead made on
-       the first call to curl_easy_perform() or when the handle is added to a
-       multi stack. */
-  }
-
-
-  if(res) {
-    Curl_resolver_cleanup(data->state.resolver);
-    if(data->state.headerbuff)
-      free(data->state.headerbuff);
-    Curl_freeset(data);
-    free(data);
-    data = NULL;
-  }
-  else
-    *curl = data;
-
-  return res;
-}
-
-CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
-                     va_list param)
-{
-  char *argptr;
-  CURLcode result = CURLE_OK;
-  long arg;
-#ifndef CURL_DISABLE_HTTP
-  curl_off_t bigsize;
-#endif
-
-  switch(option) {
-  case CURLOPT_DNS_CACHE_TIMEOUT:
-    data->set.dns_cache_timeout = va_arg(param, long);
-    break;
-  case CURLOPT_DNS_USE_GLOBAL_CACHE:
-    /* remember we want this enabled */
-    arg = va_arg(param, long);
-    data->set.global_dns_cache = (0 != arg)?TRUE:FALSE;
-    break;
-  case CURLOPT_SSL_CIPHER_LIST:
-    /* set a list of cipher we want to use in the SSL connection */
-    result = setstropt(&data->set.str[STRING_SSL_CIPHER_LIST],
-                       va_arg(param, char *));
-    break;
-
-  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.
-     */
-    result = setstropt(&data->set.str[STRING_SSL_RANDOM_FILE],
-                       va_arg(param, char *));
-    break;
-  case CURLOPT_EGDSOCKET:
-    /*
-     * The Entropy Gathering Daemon socket pathname
-     */
-    result = setstropt(&data->set.str[STRING_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.
-     */
-    data->set.maxconnects = va_arg(param, long);
-    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->set.reuse_forbid = (0 != 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->set.reuse_fresh = (0 != 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.
-     */
-    data->set.verbose = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-  case CURLOPT_HEADER:
-    /*
-     * Set to include the header in the general data output stream.
-     */
-    data->set.include_header = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-  case CURLOPT_NOPROGRESS:
-    /*
-     * Shut off the internal supported progress meter
-     */
-    data->set.hide_progress = (0 != va_arg(param, long))?TRUE:FALSE;
-    if(data->set.hide_progress)
-      data->progress.flags |= PGRS_HIDE;
-    else
-      data->progress.flags &= ~PGRS_HIDE;
-    break;
-  case CURLOPT_NOBODY:
-    /*
-     * Do not include the body part in the output data stream.
-     */
-    data->set.opt_no_body = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-  case CURLOPT_FAILONERROR:
-    /*
-     * Don't output the >=300 error code HTML-page, but instead only
-     * return error.
-     */
-    data->set.http_fail_on_error = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-  case CURLOPT_UPLOAD:
-  case CURLOPT_PUT:
-    /*
-     * We want to sent data to the remote host. If this is HTTP, that equals
-     * using the PUT request.
-     */
-    data->set.upload = (0 != va_arg(param, long))?TRUE:FALSE;
-    if(data->set.upload) {
-      /* If this is HTTP, PUT is what's needed to "upload" */
-      data->set.httpreq = HTTPREQ_PUT;
-      data->set.opt_no_body = FALSE; /* this is implied */
-    }
-    else
-      /* In HTTP, the opposite of upload is GET (unless NOBODY is true as
-         then this can be changed to HEAD later on) */
-      data->set.httpreq = HTTPREQ_GET;
-    break;
-  case CURLOPT_FILETIME:
-    /*
-     * Try to get the file time of the remote document. The time will
-     * later (possibly) become available using curl_easy_getinfo().
-     */
-    data->set.get_filetime = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-  case CURLOPT_FTP_CREATE_MISSING_DIRS:
-    /*
-     * An FTP option that modifies an upload to create missing directories on
-     * the server.
-     */
-    switch(va_arg(param, long)) {
-    case 0:
-      data->set.ftp_create_missing_dirs = 0;
-      break;
-    case 1:
-      data->set.ftp_create_missing_dirs = 1;
-      break;
-    case 2:
-      data->set.ftp_create_missing_dirs = 2;
-      break;
-    default:
-      /* reserve other values for future use */
-      result = CURLE_UNKNOWN_OPTION;
-      break;
-    }
-    break;
-  case CURLOPT_SERVER_RESPONSE_TIMEOUT:
-    /*
-     * Option that specifies how quickly an server response must be obtained
-     * before it is considered failure. For pingpong protocols.
-     */
-    data->set.server_response_timeout = va_arg( param , long ) * 1000;
-    break;
-  case CURLOPT_TFTP_BLKSIZE:
-    /*
-     * TFTP option that specifies the block size to use for data transmission
-     */
-    data->set.tftp_blksize = va_arg(param, long);
-    break;
-  case CURLOPT_DIRLISTONLY:
-    /*
-     * An option that changes the command to one that asks for a list
-     * only, no file info details.
-     */
-    data->set.ftp_list_only = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-  case CURLOPT_APPEND:
-    /*
-     * We want to upload and append to an existing file.
-     */
-    data->set.ftp_append = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-  case CURLOPT_FTP_FILEMETHOD:
-    /*
-     * How do access files over FTP.
-     */
-    data->set.ftp_filemethod = (curl_ftpfile)va_arg(param, long);
-    break;
-  case CURLOPT_NETRC:
-    /*
-     * Parse the $HOME/.netrc file
-     */
-    data->set.use_netrc = (enum CURL_NETRC_OPTION)va_arg(param, long);
-    break;
-  case CURLOPT_NETRC_FILE:
-    /*
-     * Use this file instead of the $HOME/.netrc file
-     */
-    result = setstropt(&data->set.str[STRING_NETRC_FILE],
-                       va_arg(param, char *));
-    break;
-  case CURLOPT_TRANSFERTEXT:
-    /*
-     * This option was previously named 'FTPASCII'. Renamed to work with
-     * more protocols than merely FTP.
-     *
-     * Transfer using ASCII (instead of BINARY).
-     */
-    data->set.prefer_ascii = (0 != 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->set.timecondition = (curl_TimeCond)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->set.timevalue = (time_t)va_arg(param, long);
-    break;
-  case CURLOPT_SSLVERSION:
-    /*
-     * Set explicit SSL version to try to connect with, as some SSL
-     * implementations are lame.
-     */
-    data->set.ssl.version = va_arg(param, long);
-    break;
-
-#ifndef CURL_DISABLE_HTTP
-  case CURLOPT_AUTOREFERER:
-    /*
-     * Switch on automatic referer that gets set if curl follows locations.
-     */
-    data->set.http_auto_referer = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-
-  case CURLOPT_ACCEPT_ENCODING:
-    /*
-     * String to use at the value of Accept-Encoding header.
-     *
-     * If the encoding is set to "" we use an Accept-Encoding header that
-     * encompasses all the encodings we support.
-     * If the encoding is set to NULL we don't send an Accept-Encoding header
-     * and ignore an received Content-Encoding header.
-     *
-     */
-    argptr = va_arg(param, char *);
-    result = setstropt(&data->set.str[STRING_ENCODING],
-                       (argptr && !*argptr)?
-                       (char *) ALL_CONTENT_ENCODINGS: argptr);
-    break;
-
-  case CURLOPT_TRANSFER_ENCODING:
-    data->set.http_transfer_encoding = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-
-  case CURLOPT_FOLLOWLOCATION:
-    /*
-     * Follow Location: header hints on a HTTP-server.
-     */
-    data->set.http_follow_location = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-
-  case CURLOPT_UNRESTRICTED_AUTH:
-    /*
-     * Send authentication (user+password) when following locations, even when
-     * hostname changed.
-     */
-    data->set.http_disable_hostname_check_before_authentication =
-      (0 != va_arg(param, long))?TRUE:FALSE;
-    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->set.maxredirs = va_arg(param, long);
-    break;
-
-  case CURLOPT_POSTREDIR:
-  {
-    /*
-     * Set the behaviour of POST when redirecting
-     * CURL_REDIR_GET_ALL - POST is changed to GET after 301 and 302
-     * CURL_REDIR_POST_301 - POST is kept as POST after 301
-     * CURL_REDIR_POST_302 - POST is kept as POST after 302
-     * CURL_REDIR_POST_303 - POST is kept as POST after 303
-     * CURL_REDIR_POST_ALL - POST is kept as POST after 301, 302 and 303
-     * other - POST is kept as POST after 301 and 302
-     */
-    int postRedir = curlx_sltosi(va_arg(param, long));
-    data->set.keep_post = postRedir & CURL_REDIR_POST_ALL;
-  }
-  break;
-
-  case CURLOPT_POST:
-    /* Does this option serve a purpose anymore? Yes it does, when
-       CURLOPT_POSTFIELDS isn't used and the POST data is read off the
-       callback! */
-    if(va_arg(param, long)) {
-      data->set.httpreq = HTTPREQ_POST;
-      data->set.opt_no_body = FALSE; /* this is implied */
-    }
-    else
-      data->set.httpreq = HTTPREQ_GET;
-    break;
-
-  case CURLOPT_COPYPOSTFIELDS:
-    /*
-     * A string with POST data. Makes curl HTTP POST. Even if it is NULL.
-     * If needed, CURLOPT_POSTFIELDSIZE must have been set prior to
-     *  CURLOPT_COPYPOSTFIELDS and not altered later.
-     */
-    argptr = va_arg(param, char *);
-
-    if(!argptr || data->set.postfieldsize == -1)
-      result = setstropt(&data->set.str[STRING_COPYPOSTFIELDS], argptr);
-    else {
-      /*
-       *  Check that requested length does not overflow the size_t type.
-       */
-
-      if((data->set.postfieldsize < 0) ||
-         ((sizeof(curl_off_t) != sizeof(size_t)) &&
-          (data->set.postfieldsize > (curl_off_t)((size_t)-1))))
-        result = CURLE_OUT_OF_MEMORY;
-      else {
-        char * p;
-
-        (void) setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL);
-
-        /* Allocate even when size == 0. This satisfies the need of possible
-           later address compare to detect the COPYPOSTFIELDS mode, and
-           to mark that postfields is used rather than read function or
-           form data.
-        */
-        p = malloc((size_t)(data->set.postfieldsize?
-                            data->set.postfieldsize:1));
-
-        if(!p)
-          result = CURLE_OUT_OF_MEMORY;
-        else {
-          if(data->set.postfieldsize)
-            memcpy(p, argptr, (size_t)data->set.postfieldsize);
-
-          data->set.str[STRING_COPYPOSTFIELDS] = p;
-        }
-      }
-    }
-
-    data->set.postfields = data->set.str[STRING_COPYPOSTFIELDS];
-    data->set.httpreq = HTTPREQ_POST;
-    break;
-
-  case CURLOPT_POSTFIELDS:
-    /*
-     * Like above, but use static data instead of copying it.
-     */
-    data->set.postfields = va_arg(param, void *);
-    /* Release old copied data. */
-    (void) setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL);
-    data->set.httpreq = HTTPREQ_POST;
-    break;
-
-  case CURLOPT_POSTFIELDSIZE:
-    /*
-     * The size of the POSTFIELD data to prevent libcurl to do strlen() to
-     * figure it out. Enables binary posts.
-     */
-    bigsize = va_arg(param, long);
-
-    if(data->set.postfieldsize < bigsize &&
-       data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) {
-      /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */
-      (void) setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL);
-      data->set.postfields = NULL;
-    }
-
-    data->set.postfieldsize = bigsize;
-    break;
-
-  case CURLOPT_POSTFIELDSIZE_LARGE:
-    /*
-     * The size of the POSTFIELD data to prevent libcurl to do strlen() to
-     * figure it out. Enables binary posts.
-     */
-    bigsize = va_arg(param, curl_off_t);
-
-    if(data->set.postfieldsize < bigsize &&
-       data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) {
-      /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */
-      (void) setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL);
-      data->set.postfields = NULL;
-    }
-
-    data->set.postfieldsize = bigsize;
-    break;
-
-  case CURLOPT_HTTPPOST:
-    /*
-     * Set to make us do HTTP POST
-     */
-    data->set.httppost = va_arg(param, struct curl_httppost *);
-    data->set.httpreq = HTTPREQ_POST_FORM;
-    data->set.opt_no_body = FALSE; /* this is implied */
-    break;
-
-  case CURLOPT_REFERER:
-    /*
-     * String to set in the HTTP Referer: field.
-     */
-    if(data->change.referer_alloc) {
-      Curl_safefree(data->change.referer);
-      data->change.referer_alloc = FALSE;
-    }
-    result = setstropt(&data->set.str[STRING_SET_REFERER],
-                       va_arg(param, char *));
-    data->change.referer = data->set.str[STRING_SET_REFERER];
-    break;
-
-  case CURLOPT_USERAGENT:
-    /*
-     * String to use in the HTTP User-Agent field
-     */
-    result = setstropt(&data->set.str[STRING_USERAGENT],
-                       va_arg(param, char *));
-    break;
-
-  case CURLOPT_HTTPHEADER:
-    /*
-     * Set a list with HTTP headers to use (or replace internals with)
-     */
-    data->set.headers = va_arg(param, struct curl_slist *);
-    break;
-
-  case CURLOPT_HTTP200ALIASES:
-    /*
-     * Set a list of aliases for HTTP 200 in response header
-     */
-    data->set.http200aliases = va_arg(param, struct curl_slist *);
-    break;
-
-#if !defined(CURL_DISABLE_COOKIES)
-  case CURLOPT_COOKIE:
-    /*
-     * Cookie string to send to the remote server in the request.
-     */
-    result = setstropt(&data->set.str[STRING_COOKIE],
-                       va_arg(param, char *));
-    break;
-
-  case CURLOPT_COOKIEFILE:
-    /*
-     * Set cookie file to read and parse. Can be used multiple times.
-     */
-    argptr = (char *)va_arg(param, void *);
-    if(argptr) {
-      struct curl_slist *cl;
-      /* append the cookie file name to the list of file names, and deal with
-         them later */
-      cl = curl_slist_append(data->change.cookielist, argptr);
-      if(!cl) {
-        curl_slist_free_all(data->change.cookielist);
-        data->change.cookielist = NULL;
-        return CURLE_OUT_OF_MEMORY;
-      }
-      data->change.cookielist = cl; /* store the list for later use */
-    }
-    break;
-
-  case CURLOPT_COOKIEJAR:
-    /*
-     * Set cookie file name to dump all cookies to when we're done.
-     */
-    result = setstropt(&data->set.str[STRING_COOKIEJAR],
-                       va_arg(param, char *));
-
-    /*
-     * Activate the cookie parser. This may or may not already
-     * have been made.
-     */
-    data->cookies = Curl_cookie_init(data, NULL, data->cookies,
-                                     data->set.cookiesession);
-    break;
-
-  case CURLOPT_COOKIESESSION:
-    /*
-     * Set this option to TRUE to start a new "cookie session". It will
-     * prevent the forthcoming read-cookies-from-file actions to accept
-     * cookies that are marked as being session cookies, as they belong to a
-     * previous session.
-     *
-     * In the original Netscape cookie spec, "session cookies" are cookies
-     * with no expire date set. RFC2109 describes the same action if no
-     * 'Max-Age' is set and RFC2965 includes the RFC2109 description and adds
-     * a 'Discard' action that can enforce the discard even for cookies that
-     * have a Max-Age.
-     *
-     * We run mostly with the original cookie spec, as hardly anyone implements
-     * anything else.
-     */
-    data->set.cookiesession = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-
-  case CURLOPT_COOKIELIST:
-    argptr = va_arg(param, char *);
-
-    if(argptr == NULL)
-      break;
-
-    if(Curl_raw_equal(argptr, "ALL")) {
-      /* clear all cookies */
-      Curl_cookie_clearall(data->cookies);
-      break;
-    }
-    else if(Curl_raw_equal(argptr, "SESS")) {
-      /* clear session cookies */
-      Curl_cookie_clearsess(data->cookies);
-      break;
-    }
-    else if(Curl_raw_equal(argptr, "FLUSH")) {
-      /* flush cookies to file */
-      Curl_flush_cookies(data, 0);
-      break;
-    }
-
-    if(!data->cookies)
-      /* if cookie engine was not running, activate it */
-      data->cookies = Curl_cookie_init(data, NULL, NULL, TRUE);
-
-    argptr = strdup(argptr);
-    if(!argptr) {
-      result = CURLE_OUT_OF_MEMORY;
-      break;
-    }
-
-    if(checkprefix("Set-Cookie:", argptr))
-      /* HTTP Header format line */
-      Curl_cookie_add(data, data->cookies, TRUE, argptr + 11, NULL, NULL);
-
-    else
-      /* Netscape format line */
-      Curl_cookie_add(data, data->cookies, FALSE, argptr, NULL, NULL);
-
-    free(argptr);
-    break;
-#endif /* CURL_DISABLE_COOKIES */
-
-  case CURLOPT_HTTPGET:
-    /*
-     * Set to force us do HTTP GET
-     */
-    if(va_arg(param, long)) {
-      data->set.httpreq = HTTPREQ_GET;
-      data->set.upload = FALSE; /* switch off upload */
-      data->set.opt_no_body = FALSE; /* this is implied */
-    }
-    break;
-
-  case CURLOPT_HTTP_VERSION:
-    /*
-     * This sets a requested HTTP version to be used. The value is one of
-     * the listed enums in curl/curl.h.
-     */
-    data->set.httpversion = va_arg(param, long);
-    break;
-
-  case CURLOPT_HTTPAUTH:
-    /*
-     * Set HTTP Authentication type BITMASK.
-     */
-  {
-    int bitcheck;
-    bool authbits;
-    unsigned long auth = va_arg(param, unsigned long);
-
-    if(auth == CURLAUTH_NONE) {
-      data->set.httpauth = auth;
-      break;
-    }
-
-    /* the DIGEST_IE bit is only used to set a special marker, for all the
-       rest we need to handle it as normal DIGEST */
-    data->state.authhost.iestyle = (auth & CURLAUTH_DIGEST_IE)?TRUE:FALSE;
-
-    if(auth & CURLAUTH_DIGEST_IE) {
-      auth |= CURLAUTH_DIGEST; /* set standard digest bit */
-      auth &= ~CURLAUTH_DIGEST_IE; /* unset ie digest bit */
-    }
-
-    /* switch off bits we can't support */
-#ifndef USE_NTLM
-    auth &= ~CURLAUTH_NTLM;    /* no NTLM support */
-    auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */
-#elif !defined(NTLM_WB_ENABLED)
-    auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */
-#endif
-#ifndef USE_HTTP_NEGOTIATE
-    auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI or
-                                       WINDOWS_SSPI */
-#endif
-
-    /* check if any auth bit lower than CURLAUTH_ONLY is still set */
-    bitcheck = 0;
-    authbits = FALSE;
-    while(bitcheck < 31) {
-      if(auth & (1UL << bitcheck++)) {
-        authbits = TRUE;
-        break;
-      }
-    }
-    if(!authbits)
-      return CURLE_NOT_BUILT_IN; /* no supported types left! */
-
-    data->set.httpauth = auth;
-  }
-  break;
-
-#endif   /* CURL_DISABLE_HTTP */
-
-  case CURLOPT_CUSTOMREQUEST:
-    /*
-     * Set a custom string to use as request
-     */
-    result = setstropt(&data->set.str[STRING_CUSTOMREQUEST],
-                       va_arg(param, char *));
-
-    /* we don't set
-       data->set.httpreq = HTTPREQ_CUSTOM;
-       here, we continue as if we were using the already set type
-       and this just changes the actual request keyword */
-    break;
-
-#ifndef CURL_DISABLE_PROXY
-  case CURLOPT_HTTPPROXYTUNNEL:
-    /*
-     * Tunnel operations through the proxy instead of normal proxy use
-     */
-    data->set.tunnel_thru_httpproxy = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-
-  case CURLOPT_PROXYPORT:
-    /*
-     * Explicitly set HTTP proxy port number.
-     */
-    data->set.proxyport = va_arg(param, long);
-    break;
-
-  case CURLOPT_PROXYAUTH:
-    /*
-     * Set HTTP Authentication type BITMASK.
-     */
-  {
-    int bitcheck;
-    bool authbits;
-    unsigned long auth = va_arg(param, unsigned long);
-
-    if(auth == CURLAUTH_NONE) {
-      data->set.proxyauth = auth;
-      break;
-    }
-
-    /* the DIGEST_IE bit is only used to set a special marker, for all the
-       rest we need to handle it as normal DIGEST */
-    data->state.authproxy.iestyle = (auth & CURLAUTH_DIGEST_IE)?TRUE:FALSE;
-
-    if(auth & CURLAUTH_DIGEST_IE) {
-      auth |= CURLAUTH_DIGEST; /* set standard digest bit */
-      auth &= ~CURLAUTH_DIGEST_IE; /* unset ie digest bit */
-    }
-    /* switch off bits we can't support */
-#ifndef USE_NTLM
-    auth &= ~CURLAUTH_NTLM;    /* no NTLM support */
-    auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */
-#elif !defined(NTLM_WB_ENABLED)
-    auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */
-#endif
-#ifndef USE_HTTP_NEGOTIATE
-    auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI or
-                                       WINDOWS_SSPI */
-#endif
-
-    /* check if any auth bit lower than CURLAUTH_ONLY is still set */
-    bitcheck = 0;
-    authbits = FALSE;
-    while(bitcheck < 31) {
-      if(auth & (1UL << bitcheck++)) {
-        authbits = TRUE;
-        break;
-      }
-    }
-    if(!authbits)
-      return CURLE_NOT_BUILT_IN; /* no supported types left! */
-
-    data->set.proxyauth = auth;
-  }
-  break;
-
-  case CURLOPT_PROXY:
-    /*
-     * Set proxy server:port to use as HTTP proxy.
-     *
-     * If the proxy is set to "" we explicitly say that we don't want to use a
-     * proxy (even though there might be environment variables saying so).
-     *
-     * Setting it to NULL, means no proxy but allows the environment variables
-     * to decide for us.
-     */
-    result = setstropt(&data->set.str[STRING_PROXY],
-                       va_arg(param, char *));
-    break;
-
-  case CURLOPT_PROXYTYPE:
-    /*
-     * Set proxy type. HTTP/HTTP_1_0/SOCKS4/SOCKS4a/SOCKS5/SOCKS5_HOSTNAME
-     */
-    data->set.proxytype = (curl_proxytype)va_arg(param, long);
-    break;
-
-  case CURLOPT_PROXY_TRANSFER_MODE:
-    /*
-     * set transfer mode (;type=<a|i>) when doing FTP via an HTTP proxy
-     */
-    switch (va_arg(param, long)) {
-    case 0:
-      data->set.proxy_transfer_mode = FALSE;
-      break;
-    case 1:
-      data->set.proxy_transfer_mode = TRUE;
-      break;
-    default:
-      /* reserve other values for future use */
-      result = CURLE_UNKNOWN_OPTION;
-      break;
-    }
-    break;
-#endif   /* CURL_DISABLE_PROXY */
-
-#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
-  case CURLOPT_SOCKS5_GSSAPI_SERVICE:
-    /*
-     * Set gssapi service name
-     */
-    result = setstropt(&data->set.str[STRING_SOCKS5_GSSAPI_SERVICE],
-                       va_arg(param, char *));
-    break;
-
-  case CURLOPT_SOCKS5_GSSAPI_NEC:
-    /*
-     * set flag for nec socks5 support
-     */
-    data->set.socks5_gssapi_nec = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-#endif
-
-  case CURLOPT_WRITEHEADER:
-    /*
-     * Custom pointer to pass the header write callback function
-     */
-    data->set.writeheader = (void *)va_arg(param, void *);
-    break;
-  case CURLOPT_ERRORBUFFER:
-    /*
-     * Error buffer provided by the caller to get the human readable
-     * error string in.
-     */
-    data->set.errorbuffer = va_arg(param, char *);
-    break;
-  case CURLOPT_FILE:
-    /*
-     * FILE pointer to write to or include in the data write callback
-     */
-    data->set.out = va_arg(param, FILE *);
-    break;
-  case CURLOPT_FTPPORT:
-    /*
-     * Use FTP PORT, this also specifies which IP address to use
-     */
-    result = setstropt(&data->set.str[STRING_FTPPORT],
-                       va_arg(param, char *));
-    data->set.ftp_use_port = (NULL != data->set.str[STRING_FTPPORT]) ?
-                             TRUE:FALSE;
-    break;
-
-  case CURLOPT_FTP_USE_EPRT:
-    data->set.ftp_use_eprt = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-
-  case CURLOPT_FTP_USE_EPSV:
-    data->set.ftp_use_epsv = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-
-  case CURLOPT_FTP_USE_PRET:
-    data->set.ftp_use_pret = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-
-  case CURLOPT_FTP_SSL_CCC:
-    data->set.ftp_ccc = (curl_ftpccc)va_arg(param, long);
-    break;
-
-  case CURLOPT_FTP_SKIP_PASV_IP:
-    /*
-     * Enable or disable FTP_SKIP_PASV_IP, which will disable/enable the
-     * bypass of the IP address in PASV responses.
-     */
-    data->set.ftp_skip_ip = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-
-  case CURLOPT_INFILE:
-    /*
-     * FILE pointer to read the file to be uploaded from. Or possibly
-     * used as argument to the read callback.
-     */
-    data->set.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->set.infilesize = va_arg(param, long);
-    break;
-  case CURLOPT_INFILESIZE_LARGE:
-    /*
-     * If known, this should inform curl about the file size of the
-     * to-be-uploaded file.
-     */
-    data->set.infilesize = va_arg(param, curl_off_t);
-    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->set.low_speed_limit=va_arg(param, long);
-    break;
-  case CURLOPT_MAX_SEND_SPEED_LARGE:
-    /*
-     * When transfer uploads are faster then CURLOPT_MAX_SEND_SPEED_LARGE
-     * bytes per second the transfer is throttled..
-     */
-    data->set.max_send_speed=va_arg(param, curl_off_t);
-    break;
-  case CURLOPT_MAX_RECV_SPEED_LARGE:
-    /*
-     * When receiving data faster than CURLOPT_MAX_RECV_SPEED_LARGE bytes per
-     * second the transfer is throttled..
-     */
-    data->set.max_recv_speed=va_arg(param, curl_off_t);
-    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->set.low_speed_time=va_arg(param, long);
-    break;
-  case CURLOPT_URL:
-    /*
-     * The URL to fetch.
-     */
-    if(data->change.url_alloc) {
-      /* the already set URL is allocated, free it first! */
-      Curl_safefree(data->change.url);
-      data->change.url_alloc = FALSE;
-    }
-    result = setstropt(&data->set.str[STRING_SET_URL],
-                       va_arg(param, char *));
-    data->change.url = data->set.str[STRING_SET_URL];
-    break;
-  case CURLOPT_PORT:
-    /*
-     * The port number to use when getting the URL
-     */
-    data->set.use_port = va_arg(param, long);
-    break;
-  case CURLOPT_TIMEOUT:
-    /*
-     * The maximum time you allow curl to use for a single transfer
-     * operation.
-     */
-    data->set.timeout = va_arg(param, long) * 1000L;
-    break;
-
-  case CURLOPT_TIMEOUT_MS:
-    data->set.timeout = va_arg(param, long);
-    break;
-
-  case CURLOPT_CONNECTTIMEOUT:
-    /*
-     * The maximum time you allow curl to use to connect.
-     */
-    data->set.connecttimeout = va_arg(param, long) * 1000L;
-    break;
-
-  case CURLOPT_CONNECTTIMEOUT_MS:
-    data->set.connecttimeout = va_arg(param, long);
-    break;
-
-  case CURLOPT_ACCEPTTIMEOUT_MS:
-    /*
-     * The maximum time you allow curl to wait for server connect
-     */
-    data->set.accepttimeout = va_arg(param, long);
-    break;
-
-  case CURLOPT_USERPWD:
-    /*
-     * user:password to use in the operation
-     */
-    result = setstropt_userpwd(va_arg(param, char *),
-                               &data->set.str[STRING_USERNAME],
-                               &data->set.str[STRING_PASSWORD]);
-    break;
-  case CURLOPT_USERNAME:
-    /*
-     * authentication user name to use in the operation
-     */
-    result = setstropt(&data->set.str[STRING_USERNAME],
-                       va_arg(param, char *));
-    break;
-  case CURLOPT_PASSWORD:
-    /*
-     * authentication password to use in the operation
-     */
-    result = setstropt(&data->set.str[STRING_PASSWORD],
-                       va_arg(param, char *));
-    break;
-  case CURLOPT_POSTQUOTE:
-    /*
-     * List of RAW FTP commands to use after a transfer
-     */
-    data->set.postquote = va_arg(param, struct curl_slist *);
-    break;
-  case CURLOPT_PREQUOTE:
-    /*
-     * List of RAW FTP commands to use prior to RETR (Wesley Laxton)
-     */
-    data->set.prequote = va_arg(param, struct curl_slist *);
-    break;
-  case CURLOPT_QUOTE:
-    /*
-     * List of RAW FTP commands to use before a transfer
-     */
-    data->set.quote = va_arg(param, struct curl_slist *);
-    break;
-  case CURLOPT_RESOLVE:
-    /*
-     * List of NAME:[address] names to populate the DNS cache with
-     * Prefix the NAME with dash (-) to _remove_ the name from the cache.
-     *
-     * Names added with this API will remain in the cache until explicitly
-     * removed or the handle is cleaned up.
-     *
-     * This API can remove any name from the DNS cache, but only entries
-     * that aren't actually in use right now will be pruned immediately.
-     */
-    data->set.resolve = va_arg(param, struct curl_slist *);
-    data->change.resolve = data->set.resolve;
-    break;
-  case CURLOPT_PROGRESSFUNCTION:
-    /*
-     * Progress callback function
-     */
-    data->set.fprogress = va_arg(param, curl_progress_callback);
-    if(data->set.fprogress)
-      data->progress.callback = TRUE; /* no longer internal */
-    else
-      data->progress.callback = FALSE; /* NULL enforces internal */
-
-    break;
-  case CURLOPT_PROGRESSDATA:
-    /*
-     * Custom client data to pass to the progress callback
-     */
-    data->set.progress_client = va_arg(param, void *);
-    break;
-
-#ifndef CURL_DISABLE_PROXY
-  case CURLOPT_PROXYUSERPWD:
-    /*
-     * user:password needed to use the proxy
-     */
-    result = setstropt_userpwd(va_arg(param, char *),
-                               &data->set.str[STRING_PROXYUSERNAME],
-                               &data->set.str[STRING_PROXYPASSWORD]);
-    break;
-  case CURLOPT_PROXYUSERNAME:
-    /*
-     * authentication user name to use in the operation
-     */
-    result = setstropt(&data->set.str[STRING_PROXYUSERNAME],
-                       va_arg(param, char *));
-    break;
-  case CURLOPT_PROXYPASSWORD:
-    /*
-     * authentication password to use in the operation
-     */
-    result = setstropt(&data->set.str[STRING_PROXYPASSWORD],
-                       va_arg(param, char *));
-    break;
-  case CURLOPT_NOPROXY:
-    /*
-     * proxy exception list
-     */
-    result = setstropt(&data->set.str[STRING_NOPROXY],
-                       va_arg(param, char *));
-    break;
-#endif
-
-  case CURLOPT_RANGE:
-    /*
-     * What range of the file you want to transfer
-     */
-    result = setstropt(&data->set.str[STRING_SET_RANGE],
-                       va_arg(param, char *));
-    break;
-  case CURLOPT_RESUME_FROM:
-    /*
-     * Resume transfer at the give file position
-     */
-    data->set.set_resume_from = va_arg(param, long);
-    break;
-  case CURLOPT_RESUME_FROM_LARGE:
-    /*
-     * Resume transfer at the give file position
-     */
-    data->set.set_resume_from = va_arg(param, curl_off_t);
-    break;
-  case CURLOPT_DEBUGFUNCTION:
-    /*
-     * stderr write callback.
-     */
-    data->set.fdebug = va_arg(param, curl_debug_callback);
-    /*
-     * if the callback provided is NULL, it'll use the default callback
-     */
-    break;
-  case CURLOPT_DEBUGDATA:
-    /*
-     * Set to a void * that should receive all error writes. This
-     * defaults to CURLOPT_STDERR for normal operations.
-     */
-    data->set.debugdata = va_arg(param, void *);
-    break;
-  case CURLOPT_STDERR:
-    /*
-     * Set to a FILE * that should receive all error writes. This
-     * defaults to stderr for normal operations.
-     */
-    data->set.err = va_arg(param, FILE *);
-    if(!data->set.err)
-      data->set.err = stderr;
-    break;
-  case CURLOPT_HEADERFUNCTION:
-    /*
-     * Set header write callback
-     */
-    data->set.fwrite_header = va_arg(param, curl_write_callback);
-    break;
-  case CURLOPT_WRITEFUNCTION:
-    /*
-     * Set data write callback
-     */
-    data->set.fwrite_func = va_arg(param, curl_write_callback);
-    if(!data->set.fwrite_func) {
-      data->set.is_fwrite_set = 0;
-      /* When set to NULL, reset to our internal default function */
-      data->set.fwrite_func = (curl_write_callback)fwrite;
-    }
-    else
-      data->set.is_fwrite_set = 1;
-    break;
-  case CURLOPT_READFUNCTION:
-    /*
-     * Read data callback
-     */
-    data->set.fread_func = va_arg(param, curl_read_callback);
-    if(!data->set.fread_func) {
-      data->set.is_fread_set = 0;
-      /* When set to NULL, reset to our internal default function */
-      data->set.fread_func = (curl_read_callback)fread;
-    }
-    else
-      data->set.is_fread_set = 1;
-    break;
-  case CURLOPT_SEEKFUNCTION:
-    /*
-     * Seek callback. Might be NULL.
-     */
-    data->set.seek_func = va_arg(param, curl_seek_callback);
-    break;
-  case CURLOPT_SEEKDATA:
-    /*
-     * Seek control callback. Might be NULL.
-     */
-    data->set.seek_client = va_arg(param, void *);
-    break;
-  case CURLOPT_CONV_FROM_NETWORK_FUNCTION:
-    /*
-     * "Convert from network encoding" callback
-     */
-    data->set.convfromnetwork = va_arg(param, curl_conv_callback);
-    break;
-  case CURLOPT_CONV_TO_NETWORK_FUNCTION:
-    /*
-     * "Convert to network encoding" callback
-     */
-    data->set.convtonetwork = va_arg(param, curl_conv_callback);
-    break;
-  case CURLOPT_CONV_FROM_UTF8_FUNCTION:
-    /*
-     * "Convert from UTF-8 encoding" callback
-     */
-    data->set.convfromutf8 = va_arg(param, curl_conv_callback);
-    break;
-  case CURLOPT_IOCTLFUNCTION:
-    /*
-     * I/O control callback. Might be NULL.
-     */
-    data->set.ioctl_func = va_arg(param, curl_ioctl_callback);
-    break;
-  case CURLOPT_IOCTLDATA:
-    /*
-     * I/O control data pointer. Might be NULL.
-     */
-    data->set.ioctl_client = va_arg(param, void *);
-    break;
-  case CURLOPT_SSLCERT:
-    /*
-     * String that holds file name of the SSL certificate to use
-     */
-    result = setstropt(&data->set.str[STRING_CERT],
-                       va_arg(param, char *));
-    break;
-  case CURLOPT_SSLCERTTYPE:
-    /*
-     * String that holds file type of the SSL certificate to use
-     */
-    result = setstropt(&data->set.str[STRING_CERT_TYPE],
-                       va_arg(param, char *));
-    break;
-  case CURLOPT_SSLKEY:
-    /*
-     * String that holds file name of the SSL key to use
-     */
-    result = setstropt(&data->set.str[STRING_KEY],
-                       va_arg(param, char *));
-    break;
-  case CURLOPT_SSLKEYTYPE:
-    /*
-     * String that holds file type of the SSL key to use
-     */
-    result = setstropt(&data->set.str[STRING_KEY_TYPE],
-                       va_arg(param, char *));
-    break;
-  case CURLOPT_KEYPASSWD:
-    /*
-     * String that holds the SSL or SSH private key password.
-     */
-    result = setstropt(&data->set.str[STRING_KEY_PASSWD],
-                       va_arg(param, char *));
-    break;
-  case CURLOPT_SSLENGINE:
-    /*
-     * String that holds the SSL crypto engine.
-     */
-    argptr = va_arg(param, char *);
-    if(argptr && argptr[0])
-      result = Curl_ssl_set_engine(data, argptr);
-    break;
-
-  case CURLOPT_SSLENGINE_DEFAULT:
-    /*
-     * flag to set engine as default.
-     */
-    result = Curl_ssl_set_engine_default(data);
-    break;
-  case CURLOPT_CRLF:
-    /*
-     * Kludgy option to enable CRLF conversions. Subject for removal.
-     */
-    data->set.crlf = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-
-  case CURLOPT_INTERFACE:
-    /*
-     * Set what interface or address/hostname to bind the socket to when
-     * performing an operation and thus what from-IP your connection will use.
-     */
-    result = setstropt(&data->set.str[STRING_DEVICE],
-                       va_arg(param, char *));
-    break;
-  case CURLOPT_LOCALPORT:
-    /*
-     * Set what local port to bind the socket to when performing an operation.
-     */
-    data->set.localport = curlx_sltous(va_arg(param, long));
-    break;
-  case CURLOPT_LOCALPORTRANGE:
-    /*
-     * Set number of local ports to try, starting with CURLOPT_LOCALPORT.
-     */
-    data->set.localportrange = curlx_sltosi(va_arg(param, long));
-    break;
-  case CURLOPT_KRBLEVEL:
-    /*
-     * A string that defines the kerberos security level.
-     */
-    result = setstropt(&data->set.str[STRING_KRB_LEVEL],
-                       va_arg(param, char *));
-    data->set.krb = (NULL != data->set.str[STRING_KRB_LEVEL])?TRUE:FALSE;
-    break;
-  case CURLOPT_GSSAPI_DELEGATION:
-    /*
-     * GSSAPI credential delegation
-     */
-    data->set.gssapi_delegation = va_arg(param, long);
-    break;
-  case CURLOPT_SSL_VERIFYPEER:
-    /*
-     * Enable peer SSL verifying.
-     */
-    data->set.ssl.verifypeer = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-  case CURLOPT_SSL_VERIFYHOST:
-    /*
-     * Enable verification of the host name in the peer certificate
-     */
-    arg = va_arg(param, long);
-
-    /* Obviously people are not reading documentation and too many thought
-       this argument took a boolean when it wasn't and misused it. We thus ban
-       1 as a sensible input and we warn about its use. Then we only have the
-       2 action internally stored as TRUE. */
-
-    if(1 == arg) {
-      failf(data, "CURLOPT_SSL_VERIFYHOST no longer supports 1 as value!");
-      return CURLE_BAD_FUNCTION_ARGUMENT;
-    }
-
-    data->set.ssl.verifyhost = (0 != arg)?TRUE:FALSE;
-    break;
-#ifdef USE_SSLEAY
-    /* since these two options are only possible to use on an OpenSSL-
-       powered libcurl we #ifdef them on this condition so that libcurls
-       built against other SSL libs will return a proper error when trying
-       to set this option! */
-  case CURLOPT_SSL_CTX_FUNCTION:
-    /*
-     * Set a SSL_CTX callback
-     */
-    data->set.ssl.fsslctx = va_arg(param, curl_ssl_ctx_callback);
-    break;
-  case CURLOPT_SSL_CTX_DATA:
-    /*
-     * Set a SSL_CTX callback parameter pointer
-     */
-    data->set.ssl.fsslctxp = va_arg(param, void *);
-    break;
-  case CURLOPT_CERTINFO:
-    data->set.ssl.certinfo = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-#endif
-  case CURLOPT_CAINFO:
-    /*
-     * Set CA info for SSL connection. Specify file name of the CA certificate
-     */
-    result = setstropt(&data->set.str[STRING_SSL_CAFILE],
-                       va_arg(param, char *));
-    break;
-  case CURLOPT_CAPATH:
-    /*
-     * Set CA path info for SSL connection. Specify directory name of the CA
-     * certificates which have been prepared using openssl c_rehash utility.
-     */
-    /* This does not work on windows. */
-    result = setstropt(&data->set.str[STRING_SSL_CAPATH],
-                       va_arg(param, char *));
-    break;
-  case CURLOPT_CRLFILE:
-    /*
-     * Set CRL file info for SSL connection. Specify file name of the CRL
-     * to check certificates revocation
-     */
-    result = setstropt(&data->set.str[STRING_SSL_CRLFILE],
-                       va_arg(param, char *));
-    break;
-  case CURLOPT_ISSUERCERT:
-    /*
-     * Set Issuer certificate file
-     * to check certificates issuer
-     */
-    result = setstropt(&data->set.str[STRING_SSL_ISSUERCERT],
-                       va_arg(param, char *));
-    break;
-  case CURLOPT_TELNETOPTIONS:
-    /*
-     * Set a linked list of telnet options
-     */
-    data->set.telnet_options = va_arg(param, struct curl_slist *);
-    break;
-
-  case CURLOPT_BUFFERSIZE:
-    /*
-     * The application kindly asks for a differently sized receive buffer.
-     * If it seems reasonable, we'll use it.
-     */
-    data->set.buffer_size = va_arg(param, long);
-
-    if((data->set.buffer_size> (BUFSIZE -1 )) ||
-       (data->set.buffer_size < 1))
-      data->set.buffer_size = 0; /* huge internal default */
-
-    break;
-
-  case CURLOPT_NOSIGNAL:
-    /*
-     * The application asks not to set any signal() or alarm() handlers,
-     * even when using a timeout.
-     */
-    data->set.no_signal = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-
-  case CURLOPT_SHARE:
-  {
-    struct Curl_share *set;
-    set = va_arg(param, struct Curl_share *);
-
-    /* disconnect from old share, if any */
-    if(data->share) {
-      Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
-
-      if(data->dns.hostcachetype == HCACHE_SHARED) {
-        data->dns.hostcache = NULL;
-        data->dns.hostcachetype = HCACHE_NONE;
-      }
-
-#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
-      if(data->share->cookies == data->cookies)
-        data->cookies = NULL;
-#endif
-
-      if(data->share->sslsession == data->state.session)
-        data->state.session = NULL;
-
-      data->share->dirty--;
-
-      Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
-      data->share = NULL;
-    }
-
-    /* use new share if it set */
-    data->share = set;
-    if(data->share) {
-
-      Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
-
-      data->share->dirty++;
-
-      if(data->share->hostcache) {
-        /* use shared host cache, first free the private one if any */
-        if(data->dns.hostcachetype == HCACHE_PRIVATE)
-          Curl_hostcache_destroy(data);
-
-        data->dns.hostcache = data->share->hostcache;
-        data->dns.hostcachetype = HCACHE_SHARED;
-      }
-#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
-      if(data->share->cookies) {
-        /* use shared cookie list, first free own one if any */
-        if(data->cookies)
-          Curl_cookie_cleanup(data->cookies);
-        /* enable cookies since we now use a share that uses cookies! */
-        data->cookies = data->share->cookies;
-      }
-#endif   /* CURL_DISABLE_HTTP */
-      if(data->share->sslsession) {
-        data->set.ssl.max_ssl_sessions = data->share->max_ssl_sessions;
-        data->state.session = data->share->sslsession;
-      }
-      Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
-
-    }
-    /* check for host cache not needed,
-     * it will be done by curl_easy_perform */
-  }
-  break;
-
-  case CURLOPT_PRIVATE:
-    /*
-     * Set private data pointer.
-     */
-    data->set.private_data = va_arg(param, void *);
-    break;
-
-  case CURLOPT_MAXFILESIZE:
-    /*
-     * Set the maximum size of a file to download.
-     */
-    data->set.max_filesize = va_arg(param, long);
-    break;
-
-#ifdef USE_SSL
-  case CURLOPT_USE_SSL:
-    /*
-     * Make transfers attempt to use SSL/TLS.
-     */
-    data->set.use_ssl = (curl_usessl)va_arg(param, long);
-    break;
-
-  case CURLOPT_SSL_OPTIONS:
-    arg = va_arg(param, long);
-    data->set.ssl_enable_beast = arg&CURLSSLOPT_ALLOW_BEAST?TRUE:FALSE;
-    break;
-
-#endif
-  case CURLOPT_FTPSSLAUTH:
-    /*
-     * Set a specific auth for FTP-SSL transfers.
-     */
-    data->set.ftpsslauth = (curl_ftpauth)va_arg(param, long);
-    break;
-
-  case CURLOPT_IPRESOLVE:
-    data->set.ipver = va_arg(param, long);
-    break;
-
-  case CURLOPT_MAXFILESIZE_LARGE:
-    /*
-     * Set the maximum size of a file to download.
-     */
-    data->set.max_filesize = va_arg(param, curl_off_t);
-    break;
-
-  case CURLOPT_TCP_NODELAY:
-    /*
-     * Enable or disable TCP_NODELAY, which will disable/enable the Nagle
-     * algorithm
-     */
-    data->set.tcp_nodelay = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-
-  case CURLOPT_FTP_ACCOUNT:
-    result = setstropt(&data->set.str[STRING_FTP_ACCOUNT],
-                       va_arg(param, char *));
-    break;
-
-  case CURLOPT_IGNORE_CONTENT_LENGTH:
-    data->set.ignorecl = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-
-  case CURLOPT_CONNECT_ONLY:
-    /*
-     * No data transfer, set up connection and let application use the socket
-     */
-    data->set.connect_only = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-
-  case CURLOPT_FTP_ALTERNATIVE_TO_USER:
-    result = setstropt(&data->set.str[STRING_FTP_ALTERNATIVE_TO_USER],
-                       va_arg(param, char *));
-    break;
-
-  case CURLOPT_SOCKOPTFUNCTION:
-    /*
-     * socket callback function: called after socket() but before connect()
-     */
-    data->set.fsockopt = va_arg(param, curl_sockopt_callback);
-    break;
-
-  case CURLOPT_SOCKOPTDATA:
-    /*
-     * socket callback data pointer. Might be NULL.
-     */
-    data->set.sockopt_client = va_arg(param, void *);
-    break;
-
-  case CURLOPT_OPENSOCKETFUNCTION:
-    /*
-     * open/create socket callback function: called instead of socket(),
-     * before connect()
-     */
-    data->set.fopensocket = va_arg(param, curl_opensocket_callback);
-    break;
-
-  case CURLOPT_OPENSOCKETDATA:
-    /*
-     * socket callback data pointer. Might be NULL.
-     */
-    data->set.opensocket_client = va_arg(param, void *);
-    break;
-
-  case CURLOPT_CLOSESOCKETFUNCTION:
-    /*
-     * close socket callback function: called instead of close()
-     * when shutting down a connection
-     */
-    data->set.fclosesocket = va_arg(param, curl_closesocket_callback);
-    break;
-
-  case CURLOPT_CLOSESOCKETDATA:
-    /*
-     * socket callback data pointer. Might be NULL.
-     */
-    data->set.closesocket_client = va_arg(param, void *);
-    break;
-
-  case CURLOPT_SSL_SESSIONID_CACHE:
-    data->set.ssl.sessionid = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-
-#ifdef USE_LIBSSH2
-    /* we only include SSH options if explicitly built to support SSH */
-  case CURLOPT_SSH_AUTH_TYPES:
-    data->set.ssh_auth_types = va_arg(param, long);
-    break;
-
-  case CURLOPT_SSH_PUBLIC_KEYFILE:
-    /*
-     * Use this file instead of the $HOME/.ssh/id_dsa.pub file
-     */
-    result = setstropt(&data->set.str[STRING_SSH_PUBLIC_KEY],
-                       va_arg(param, char *));
-    break;
-
-  case CURLOPT_SSH_PRIVATE_KEYFILE:
-    /*
-     * Use this file instead of the $HOME/.ssh/id_dsa file
-     */
-    result = setstropt(&data->set.str[STRING_SSH_PRIVATE_KEY],
-                       va_arg(param, char *));
-    break;
-  case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5:
-    /*
-     * Option to allow for the MD5 of the host public key to be checked
-     * for validation purposes.
-     */
-    result = setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5],
-                       va_arg(param, char *));
-    break;
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
-  case CURLOPT_SSH_KNOWNHOSTS:
-    /*
-     * Store the file name to read known hosts from.
-     */
-    result = setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS],
-                       va_arg(param, char *));
-    break;
-
-  case CURLOPT_SSH_KEYFUNCTION:
-    /* setting to NULL is fine since the curl_ssh.c functions themselves will
-       then rever to use the internal default */
-    data->set.ssh_keyfunc = va_arg(param, curl_sshkeycallback);
-    break;
-
-  case CURLOPT_SSH_KEYDATA:
-    /*
-     * Custom client data to pass to the SSH keyfunc callback
-     */
-    data->set.ssh_keyfunc_userp = va_arg(param, void *);
-    break;
-#endif /* HAVE_LIBSSH2_KNOWNHOST_API */
-
-#endif /* USE_LIBSSH2 */
-
-  case CURLOPT_HTTP_TRANSFER_DECODING:
-    /*
-     * disable libcurl transfer encoding is used
-     */
-    data->set.http_te_skip = (0 == va_arg(param, long))?TRUE:FALSE;
-    break;
-
-  case CURLOPT_HTTP_CONTENT_DECODING:
-    /*
-     * raw data passed to the application when content encoding is used
-     */
-    data->set.http_ce_skip = (0 == va_arg(param, long))?TRUE:FALSE;
-    break;
-
-  case CURLOPT_NEW_FILE_PERMS:
-    /*
-     * Uses these permissions instead of 0644
-     */
-    data->set.new_file_perms = va_arg(param, long);
-    break;
-
-  case CURLOPT_NEW_DIRECTORY_PERMS:
-    /*
-     * Uses these permissions instead of 0755
-     */
-    data->set.new_directory_perms = va_arg(param, long);
-    break;
-
-  case CURLOPT_ADDRESS_SCOPE:
-    /*
-     * We always get longs when passed plain numericals, but for this value we
-     * know that an unsigned int will always hold the value so we blindly
-     * typecast to this type
-     */
-    data->set.scope = curlx_sltoui(va_arg(param, long));
-    break;
-
-  case CURLOPT_PROTOCOLS:
-    /* set the bitmask for the protocols that are allowed to be used for the
-       transfer, which thus helps the app which takes URLs from users or other
-       external inputs and want to restrict what protocol(s) to deal
-       with. Defaults to CURLPROTO_ALL. */
-    data->set.allowed_protocols = va_arg(param, long);
-    break;
-
-  case CURLOPT_REDIR_PROTOCOLS:
-    /* set the bitmask for the protocols that libcurl is allowed to follow to,
-       as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs
-       to be set in both bitmasks to be allowed to get redirected to. Defaults
-       to all protocols except FILE and SCP. */
-    data->set.redir_protocols = va_arg(param, long);
-    break;
-
-  case CURLOPT_MAIL_FROM:
-    result = setstropt(&data->set.str[STRING_MAIL_FROM],
-                       va_arg(param, char *));
-    break;
-
-  case CURLOPT_MAIL_AUTH:
-    result = setstropt(&data->set.str[STRING_MAIL_AUTH],
-                       va_arg(param, char *));
-    break;
-
-  case CURLOPT_MAIL_RCPT:
-    /* get a list of mail recipients */
-    data->set.mail_rcpt = va_arg(param, struct curl_slist *);
-    break;
-
-  case CURLOPT_RTSP_REQUEST:
-    {
-      /*
-       * Set the RTSP request method (OPTIONS, SETUP, PLAY, etc...)
-       * Would this be better if the RTSPREQ_* were just moved into here?
-       */
-      long curl_rtspreq = va_arg(param, long);
-      Curl_RtspReq rtspreq = RTSPREQ_NONE;
-      switch(curl_rtspreq) {
-        case CURL_RTSPREQ_OPTIONS:
-          rtspreq = RTSPREQ_OPTIONS;
-          break;
-
-        case CURL_RTSPREQ_DESCRIBE:
-          rtspreq = RTSPREQ_DESCRIBE;
-          break;
-
-        case CURL_RTSPREQ_ANNOUNCE:
-          rtspreq = RTSPREQ_ANNOUNCE;
-          break;
-
-        case CURL_RTSPREQ_SETUP:
-          rtspreq = RTSPREQ_SETUP;
-          break;
-
-        case CURL_RTSPREQ_PLAY:
-          rtspreq = RTSPREQ_PLAY;
-          break;
-
-        case CURL_RTSPREQ_PAUSE:
-          rtspreq = RTSPREQ_PAUSE;
-          break;
-
-        case CURL_RTSPREQ_TEARDOWN:
-          rtspreq = RTSPREQ_TEARDOWN;
-          break;
-
-        case CURL_RTSPREQ_GET_PARAMETER:
-          rtspreq = RTSPREQ_GET_PARAMETER;
-          break;
-
-        case CURL_RTSPREQ_SET_PARAMETER:
-          rtspreq = RTSPREQ_SET_PARAMETER;
-          break;
-
-        case CURL_RTSPREQ_RECORD:
-          rtspreq = RTSPREQ_RECORD;
-          break;
-
-        case CURL_RTSPREQ_RECEIVE:
-          rtspreq = RTSPREQ_RECEIVE;
-          break;
-        default:
-          rtspreq = RTSPREQ_NONE;
-      }
-
-      data->set.rtspreq = rtspreq;
-    break;
-    }
-
-
-  case CURLOPT_RTSP_SESSION_ID:
-    /*
-     * Set the RTSP Session ID manually. Useful if the application is
-     * resuming a previously established RTSP session
-     */
-    result = setstropt(&data->set.str[STRING_RTSP_SESSION_ID],
-                       va_arg(param, char *));
-    break;
-
-  case CURLOPT_RTSP_STREAM_URI:
-    /*
-     * Set the Stream URI for the RTSP request. Unless the request is
-     * for generic server options, the application will need to set this.
-     */
-    result = setstropt(&data->set.str[STRING_RTSP_STREAM_URI],
-                       va_arg(param, char *));
-    break;
-
-  case CURLOPT_RTSP_TRANSPORT:
-    /*
-     * The content of the Transport: header for the RTSP request
-     */
-    result = setstropt(&data->set.str[STRING_RTSP_TRANSPORT],
-                       va_arg(param, char *));
-    break;
-
-  case CURLOPT_RTSP_CLIENT_CSEQ:
-    /*
-     * Set the CSEQ number to issue for the next RTSP request. Useful if the
-     * application is resuming a previously broken connection. The CSEQ
-     * will increment from this new number henceforth.
-     */
-    data->state.rtsp_next_client_CSeq = va_arg(param, long);
-    break;
-
-  case CURLOPT_RTSP_SERVER_CSEQ:
-    /* Same as the above, but for server-initiated requests */
-    data->state.rtsp_next_client_CSeq = va_arg(param, long);
-    break;
-
-  case CURLOPT_INTERLEAVEDATA:
-    data->set.rtp_out = va_arg(param, void *);
-    break;
-  case CURLOPT_INTERLEAVEFUNCTION:
-    /* Set the user defined RTP write function */
-    data->set.fwrite_rtp = va_arg(param, curl_write_callback);
-    break;
-
-  case CURLOPT_WILDCARDMATCH:
-    data->set.wildcardmatch = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-  case CURLOPT_CHUNK_BGN_FUNCTION:
-    data->set.chunk_bgn = va_arg(param, curl_chunk_bgn_callback);
-    break;
-  case CURLOPT_CHUNK_END_FUNCTION:
-    data->set.chunk_end = va_arg(param, curl_chunk_end_callback);
-    break;
-  case CURLOPT_FNMATCH_FUNCTION:
-    data->set.fnmatch = va_arg(param, curl_fnmatch_callback);
-    break;
-  case CURLOPT_CHUNK_DATA:
-    data->wildcard.customptr = va_arg(param, void *);
-    break;
-  case CURLOPT_FNMATCH_DATA:
-    data->set.fnmatch_data = va_arg(param, void *);
-    break;
-#ifdef USE_TLS_SRP
-  case CURLOPT_TLSAUTH_USERNAME:
-    result = setstropt(&data->set.str[STRING_TLSAUTH_USERNAME],
-                       va_arg(param, char *));
-    if(data->set.str[STRING_TLSAUTH_USERNAME] && !data->set.ssl.authtype)
-      data->set.ssl.authtype = CURL_TLSAUTH_SRP; /* default to SRP */
-    break;
-  case CURLOPT_TLSAUTH_PASSWORD:
-    result = setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD],
-                       va_arg(param, char *));
-    if(data->set.str[STRING_TLSAUTH_USERNAME] && !data->set.ssl.authtype)
-      data->set.ssl.authtype = CURL_TLSAUTH_SRP; /* default to SRP */
-    break;
-  case CURLOPT_TLSAUTH_TYPE:
-    if(strnequal((char *)va_arg(param, char *), "SRP", strlen("SRP")))
-      data->set.ssl.authtype = CURL_TLSAUTH_SRP;
-    else
-      data->set.ssl.authtype = CURL_TLSAUTH_NONE;
-    break;
-#endif
-  case CURLOPT_DNS_SERVERS:
-    result = Curl_set_dns_servers(data, va_arg(param, char *));
-    break;
-
-  case CURLOPT_TCP_KEEPALIVE:
-    data->set.tcp_keepalive = (0 != va_arg(param, long))?TRUE:FALSE;
-    break;
-  case CURLOPT_TCP_KEEPIDLE:
-    data->set.tcp_keepidle = va_arg(param, long);
-    break;
-  case CURLOPT_TCP_KEEPINTVL:
-    data->set.tcp_keepintvl = va_arg(param, long);
-    break;
-
-  default:
-    /* unknown tag and its companion, just ignore: */
-    result = CURLE_UNKNOWN_OPTION;
-    break;
-  }
-
-  return result;
-}
-
-static void conn_free(struct connectdata *conn)
-{
-  if(!conn)
-    return;
-
-  /* possible left-overs from the async name resolvers */
-  Curl_resolver_cancel(conn);
-
-  /* close the SSL stuff before we close any sockets since they will/may
-     write to the sockets */
-  Curl_ssl_close(conn, FIRSTSOCKET);
-  Curl_ssl_close(conn, SECONDARYSOCKET);
-
-  /* close possibly still open sockets */
-  if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
-    Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
-  if(CURL_SOCKET_BAD != conn->sock[FIRSTSOCKET])
-    Curl_closesocket(conn, conn->sock[FIRSTSOCKET]);
-
-#if defined(USE_NTLM) && defined(NTLM_WB_ENABLED)
-  Curl_ntlm_wb_cleanup(conn);
-#endif
-
-  Curl_safefree(conn->user);
-  Curl_safefree(conn->passwd);
-  Curl_safefree(conn->proxyuser);
-  Curl_safefree(conn->proxypasswd);
-  Curl_safefree(conn->allocptr.proxyuserpwd);
-  Curl_safefree(conn->allocptr.uagent);
-  Curl_safefree(conn->allocptr.userpwd);
-  Curl_safefree(conn->allocptr.accept_encoding);
-  Curl_safefree(conn->allocptr.te);
-  Curl_safefree(conn->allocptr.rangeline);
-  Curl_safefree(conn->allocptr.ref);
-  Curl_safefree(conn->allocptr.host);
-  Curl_safefree(conn->allocptr.cookiehost);
-  Curl_safefree(conn->allocptr.rtsp_transport);
-  Curl_safefree(conn->trailer);
-  Curl_safefree(conn->host.rawalloc); /* host name buffer */
-  Curl_safefree(conn->proxy.rawalloc); /* proxy name buffer */
-  Curl_safefree(conn->master_buffer);
-
-  Curl_llist_destroy(conn->send_pipe, NULL);
-  Curl_llist_destroy(conn->recv_pipe, NULL);
-  Curl_llist_destroy(conn->pend_pipe, NULL);
-  Curl_llist_destroy(conn->done_pipe, NULL);
-
-  conn->send_pipe = NULL;
-  conn->recv_pipe = NULL;
-  conn->pend_pipe = NULL;
-  conn->done_pipe = NULL;
-
-  Curl_safefree(conn->localdev);
-  Curl_free_ssl_config(&conn->ssl_config);
-
-  free(conn); /* free all the connection oriented data */
-}
-
-CURLcode Curl_disconnect(struct connectdata *conn, bool dead_connection)
-{
-  struct SessionHandle *data;
-  if(!conn)
-    return CURLE_OK; /* this is closed and fine already */
-  data = conn->data;
-
-  if(!data) {
-    DEBUGF(fprintf(stderr, "DISCONNECT without easy handle, ignoring\n"));
-    return CURLE_OK;
-  }
-
-  if(conn->dns_entry != NULL) {
-    Curl_resolv_unlock(data, conn->dns_entry);
-    conn->dns_entry = NULL;
-  }
-
-  Curl_hostcache_prune(data); /* kill old DNS cache entries */
-
-  {
-    int has_host_ntlm = (conn->ntlm.state != NTLMSTATE_NONE);
-    int has_proxy_ntlm = (conn->proxyntlm.state != NTLMSTATE_NONE);
-
-    /* Authentication data is a mix of connection-related and sessionhandle-
-       related stuff. NTLM is connection-related so when we close the shop
-       we shall forget. */
-
-    if(has_host_ntlm) {
-      data->state.authhost.done = FALSE;
-      data->state.authhost.picked =
-        data->state.authhost.want;
-    }
-
-    if(has_proxy_ntlm) {
-      data->state.authproxy.done = FALSE;
-      data->state.authproxy.picked =
-        data->state.authproxy.want;
-    }
-
-    if(has_host_ntlm || has_proxy_ntlm) {
-      data->state.authproblem = FALSE;
-
-      Curl_http_ntlm_cleanup(conn);
-    }
-  }
-
-  /* Cleanup possible redirect junk */
-  if(data->req.newurl) {
-    free(data->req.newurl);
-    data->req.newurl = NULL;
-  }
-
-  if(conn->handler->disconnect)
-    /* This is set if protocol-specific cleanups should be made */
-    conn->handler->disconnect(conn, dead_connection);
-
-    /* unlink ourselves! */
-  infof(data, "Closing connection %d\n", conn->connection_id);
-  Curl_conncache_remove_conn(data->state.conn_cache, conn);
-
-#if defined(USE_LIBIDN)
-  if(conn->host.encalloc)
-    idn_free(conn->host.encalloc); /* encoded host name buffer, must be freed
-                                      with idn_free() since this was allocated
-                                      by libidn */
-  if(conn->proxy.encalloc)
-    idn_free(conn->proxy.encalloc); /* encoded proxy name buffer, must be
-                                       freed with idn_free() since this was
-                                       allocated by libidn */
-#elif defined(USE_WIN32_IDN)
-  free(conn->host.encalloc); /* encoded host name buffer, must be freed with
-                                idn_free() since this was allocated by
-                                curl_win32_idn_to_ascii */
-  if(conn->proxy.encalloc)
-    free(conn->proxy.encalloc); /* encoded proxy name buffer, must be freed
-                                   with idn_free() since this was allocated by
-                                   curl_win32_idn_to_ascii */
-#endif
-
-  Curl_ssl_close(conn, FIRSTSOCKET);
-
-  /* Indicate to all handles on the pipe that we're dead */
-  if(Curl_isPipeliningEnabled(data)) {
-    signalPipeClose(conn->send_pipe, TRUE);
-    signalPipeClose(conn->recv_pipe, TRUE);
-    signalPipeClose(conn->pend_pipe, TRUE);
-    signalPipeClose(conn->done_pipe, FALSE);
-  }
-
-  conn_free(conn);
-  data->state.current_conn = NULL;
-  Curl_speedinit(data);
-
-  return CURLE_OK;
-}
-
-/*
- * 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(curl_socket_t sock)
-{
-  int sval;
-  bool ret_val = TRUE;
-
-  sval = Curl_socket_ready(sock, CURL_SOCKET_BAD, 0);
-  if(sval == 0)
-    /* timeout */
-    ret_val = FALSE;
-
-  return ret_val;
-}
-
-static bool IsPipeliningPossible(const struct SessionHandle *handle,
-                                 const struct connectdata *conn)
-{
-  if((conn->handler->protocol & CURLPROTO_HTTP) &&
-     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;
-}
-
-bool Curl_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 *pipeline)
-{
-  if(!Curl_llist_insert_next(pipeline, pipeline->tail, data))
-    return CURLE_OUT_OF_MEMORY;
-  return CURLE_OK;
-}
-
-int Curl_removeHandleFromPipeline(struct SessionHandle *handle,
-                                  struct curl_llist *pipeline)
-{
-  struct curl_llist_element *curr;
-
-  curr = pipeline->head;
-  while(curr) {
-    if(curr->ptr == handle) {
-      Curl_llist_remove(pipeline, curr, NULL);
-      return 1; /* we removed a handle */
-    }
-    curr = curr->next;
-  }
-
-  return 0;
-}
-
-#if 0 /* this code is saved here as it is useful for debugging purposes */
-static void Curl_printPipeline(struct curl_llist *pipeline)
-{
-  struct curl_llist_element *curr;
-
-  curr = pipeline->head;
-  while(curr) {
-    struct SessionHandle *data = (struct SessionHandle *) curr->ptr;
-    infof(data, "Handle in pipeline: %s\n", data->state.path);
-    curr = curr->next;
-  }
-}
-#endif
-
-static struct SessionHandle* gethandleathead(struct curl_llist *pipeline)
-{
-  struct curl_llist_element *curr = pipeline->head;
-  if(curr) {
-    return (struct SessionHandle *) curr->ptr;
-  }
-
-  return NULL;
-}
-
-/* remove the specified connection from all (possible) pipelines and related
-   queues */
-void Curl_getoff_all_pipelines(struct SessionHandle *data,
-                               struct connectdata *conn)
-{
-  bool recv_head = (conn->readchannel_inuse &&
-    (gethandleathead(conn->recv_pipe) == data)) ? TRUE : FALSE;
-
-  bool send_head = (conn->writechannel_inuse &&
-    (gethandleathead(conn->send_pipe) == data)) ? TRUE : FALSE;
-
-  if(Curl_removeHandleFromPipeline(data, conn->recv_pipe) && recv_head)
-    conn->readchannel_inuse = FALSE;
-  if(Curl_removeHandleFromPipeline(data, conn->send_pipe) && send_head)
-    conn->writechannel_inuse = FALSE;
-  Curl_removeHandleFromPipeline(data, conn->pend_pipe);
-  Curl_removeHandleFromPipeline(data, conn->done_pipe);
-}
-
-static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke)
-{
-  struct curl_llist_element *curr;
-
-  if(!pipeline)
-    return;
-
-  curr = pipeline->head;
-  while(curr) {
-    struct curl_llist_element *next = curr->next;
-    struct SessionHandle *data = (struct SessionHandle *) curr->ptr;
-
-#ifdef DEBUGBUILD /* debug-only code */
-    if(data->magic != CURLEASY_MAGIC_NUMBER) {
-      /* MAJOR BADNESS */
-      infof(data, "signalPipeClose() found BAAD easy handle\n");
-    }
-#endif
-
-    if(pipe_broke)
-      data->state.pipe_broke = TRUE;
-    Curl_multi_handlePipeBreak(data);
-    Curl_llist_remove(pipeline, curr, NULL);
-    curr = next;
-  }
-}
-
-
-/*
- * 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.
- */
-static bool
-ConnectionExists(struct SessionHandle *data,
-                 struct connectdata *needle,
-                 struct connectdata **usethis)
-{
-  struct connectdata *check;
-  struct connectdata *chosen = 0;
-  bool canPipeline = IsPipeliningPossible(data, needle);
-  bool wantNTLM = (data->state.authhost.want==CURLAUTH_NTLM) ||
-                  (data->state.authhost.want==CURLAUTH_NTLM_WB) ? TRUE : FALSE;
-  struct connectbundle *bundle;
-
-  /* Look up the bundle with all the connections to this
-     particular host */
-  bundle = Curl_conncache_find_bundle(data->state.conn_cache,
-                                      needle->host.name);
-  if(bundle) {
-    struct curl_llist_element *curr;
-
-    infof(data, "Found bundle for host %s: %p\n", needle->host.name, bundle);
-
-    curr = bundle->conn_list->head;
-    while(curr) {
-      bool match = FALSE;
-      bool credentialsMatch = FALSE;
-      size_t pipeLen;
-
-      /*
-       * Note that if we use a HTTP proxy, we check connections to that
-       * proxy and not to the actual remote server.
-       */
-      check = curr->ptr;
-      curr = curr->next;
-
-      pipeLen = check->send_pipe->size + check->recv_pipe->size;
-
-      if(!pipeLen && !check->inuse) {
-        /* The check for a dead socket makes sense only if there are no
-           handles in pipeline and the connection isn't already marked in
-           use */
-        bool dead;
-        if(check->handler->protocol & CURLPROTO_RTSP)
-          /* RTSP is a special case due to RTP interleaving */
-          dead = Curl_rtsp_connisdead(check);
-        else
-          dead = SocketIsDead(check->sock[FIRSTSOCKET]);
-
-        if(dead) {
-          check->data = data;
-          infof(data, "Connection %d seems to be dead!\n",
-                check->connection_id);
-
-          /* disconnect resources */
-          Curl_disconnect(check, /* dead_connection */ TRUE);
-          continue;
-        }
-      }
-
-      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, check))
-            continue;
-        }
-        else if(rh) {
-          if(!IsPipeliningPossible(rh, check))
-            continue;
-        }
-#ifdef DEBUGBUILD
-      if(pipeLen > MAX_PIPELINE_LENGTH) {
-        infof(data, "BAD! Connection #%ld has too big pipeline!\n",
-              check->connection_id);
-      }
-#endif
-      }
-      else {
-        if(pipeLen > 0) {
-          /* can only happen within multi handles, and means that another easy
-             handle is using this connection */
-          continue;
-        }
-
-        if(Curl_resolver_asynch()) {
-          /* ip_addr_str[0] is NUL only if the resolving of the name hasn't
-             completed yet and until then we don't re-use this connection */
-          if(!check->ip_addr_str[0]) {
-            infof(data,
-                  "Connection #%ld is still name resolving, can't reuse\n",
-                  check->connection_id);
-            continue;
-          }
-        }
-
-        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->connection_id);
-#ifdef DEBUGBUILD
-          if(check->recv_pipe->size > 0) {
-            infof(data,
-                  "BAD! Unconnected #%ld has a non-empty recv pipeline!\n",
-                  check->connection_id);
-          }
-#endif
-          continue;
-        }
-      }
-
-      if((needle->handler->flags&PROTOPT_SSL) !=
-         (check->handler->flags&PROTOPT_SSL))
-        /* don't do mixed SSL and non-SSL connections */
-        if(!(needle->handler->protocol & check->handler->protocol))
-          /* except protocols that have been upgraded via TLS */
-          continue;
-
-      if(needle->handler->flags&PROTOPT_SSL) {
-        if((data->set.ssl.verifypeer != check->verifypeer) ||
-           (data->set.ssl.verifyhost != check->verifyhost))
-          continue;
-      }
-
-      if(needle->bits.proxy != check->bits.proxy)
-        /* don't do mixed proxy and non-proxy connections */
-        continue;
-
-      if(!canPipeline && check->inuse)
-        /* this request can't be pipelined but the checked connection is
-           already in use so we skip it */
-        continue;
-
-      if(needle->localdev || needle->localport) {
-        /* If we are bound to a specific local end (IP+port), we must not
-           re-use a random other one, although if we didn't ask for a
-           particular one we can reuse one that was bound.
-
-           This comparison is a bit rough and too strict. Since the input
-           parameters can be specified in numerous ways and still end up the
-           same it would take a lot of processing to make it really accurate.
-           Instead, this matching will assume that re-uses of bound connections
-           will most likely also re-use the exact same binding parameters and
-           missing out a few edge cases shouldn't hurt anyone very much.
-        */
-        if((check->localport != needle->localport) ||
-           (check->localportrange != needle->localportrange) ||
-           !check->localdev ||
-           !needle->localdev ||
-           strcmp(check->localdev, needle->localdev))
-          continue;
-      }
-
-      if(!needle->bits.httpproxy || needle->handler->flags&PROTOPT_SSL ||
-         (needle->bits.httpproxy && check->bits.httpproxy &&
-          needle->bits.tunnel_proxy && check->bits.tunnel_proxy &&
-          Curl_raw_equal(needle->proxy.name, check->proxy.name) &&
-          (needle->port == check->port))) {
-        /* The requested connection does not use a HTTP proxy or it uses SSL or
-           it is a non-SSL protocol tunneled over the same http proxy name and
-           port number or it is a non-SSL protocol which is allowed to be
-           upgraded via TLS */
-
-        if((Curl_raw_equal(needle->handler->scheme, check->handler->scheme) ||
-            needle->handler->protocol & check->handler->protocol) &&
-           Curl_raw_equal(needle->host.name, check->host.name) &&
-           needle->remote_port == check->remote_port) {
-          if(needle->handler->flags & PROTOPT_SSL) {
-            /* This is a SSL connection so verify that we're using the same
-               SSL options as well */
-            if(!Curl_ssl_config_matches(&needle->ssl_config,
-                                        &check->ssl_config)) {
-              DEBUGF(infof(data,
-                           "Connection #%ld has different SSL parameters, "
-                           "can't reuse\n",
-                           check->connection_id));
-              continue;
-            }
-            else if(check->ssl[FIRSTSOCKET].state != ssl_connection_complete) {
-              DEBUGF(infof(data,
-                           "Connection #%ld has not started SSL connect, "
-                           "can't reuse\n",
-                           check->connection_id));
-              continue;
-            }
-          }
-          if((needle->handler->protocol & CURLPROTO_FTP) ||
-             ((needle->handler->protocol & CURLPROTO_HTTP) && wantNTLM)) {
-            /* 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;
-            }
-            credentialsMatch = TRUE;
-          }
-          match = TRUE;
-        }
-      }
-      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) &&
-           (needle->bits.tunnel_proxy == check->bits.tunnel_proxy) &&
-           Curl_raw_equal(needle->proxy.name, check->proxy.name) &&
-           needle->port == check->port) {
-          /* This is the same proxy connection, use it! */
-          match = TRUE;
-        }
-      }
-
-      if(match) {
-        chosen = check;
-
-        /* If we are not looking for an NTLM connection, we can choose this one
-           immediately. */
-        if(!wantNTLM)
-          break;
-
-        /* Otherwise, check if this is already authenticating with the right
-           credentials. If not, keep looking so that we can reuse NTLM
-           connections if possible. (Especially we must reuse the same
-           connection if partway through a handshake!) */
-        if(credentialsMatch && chosen->ntlm.state != NTLMSTATE_NONE)
-          break;
-      }
-    }
-  }
-
-  if(chosen) {
-    chosen->inuse = TRUE; /* mark this as being in use so that no other
-                            handle in a multi stack may nick it */
-    *usethis = chosen;
-    return TRUE; /* yes, we found one to use! */
-  }
-
-  return FALSE; /* no matching connecting exists */
-}
-
-/*
- * This function kills and removes an existing connection in the connection
- * cache. The connection that has been unused for the longest time.
- *
- * Returns FALSE if it can't find any unused connection to kill.
- */
-static bool
-ConnectionKillOne(struct SessionHandle *data)
-{
-  struct conncache *bc = data->state.conn_cache;
-  struct curl_hash_iterator iter;
-  struct curl_llist_element *curr;
-  struct curl_hash_element *he;
-  long highscore=-1;
-  long score;
-  struct timeval now;
-  struct connectdata *conn_candidate = NULL;
-  struct connectbundle *bundle;
-
-  now = Curl_tvnow();
-
-  Curl_hash_start_iterate(bc->hash, &iter);
-
-  he = Curl_hash_next_element(&iter);
-  while(he) {
-    struct connectdata *conn;
-
-    bundle = he->ptr;
-
-    curr = bundle->conn_list->head;
-    while(curr) {
-      conn = curr->ptr;
-
-      if(!conn->inuse) {
-        /* Set higher score for the age passed since the connection was used */
-        score = Curl_tvdiff(now, conn->now);
-
-        if(score > highscore) {
-          highscore = score;
-          conn_candidate = conn;
-        }
-      }
-      curr = curr->next;
-    }
-
-    he = Curl_hash_next_element(&iter);
-  }
-
-  if(conn_candidate) {
-    /* Set the connection's owner correctly */
-    conn_candidate->data = data;
-
-    bundle = conn_candidate->bundle;
-
-    /* the winner gets the honour of being disconnected */
-    (void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE);
-
-    return TRUE;
-  }
-
-  return FALSE;
-}
-
-/* this connection can now be marked 'idle' */
-static void
-ConnectionDone(struct connectdata *conn)
-{
-  conn->inuse = FALSE;
-}
-
-/*
- * The given input connection struct pointer is to be stored in the connection
- * cache. If the cache is already full, least interesting existing connection
- * (if any) gets closed.
- *
- * The given connection should be unique. That must've been checked prior to
- * this call.
- */
-static CURLcode ConnectionStore(struct SessionHandle *data,
-                                struct connectdata *conn)
-{
-  static int connection_id_counter = 0;
-
-  CURLcode result;
-
-  /* Assign a number to the connection for easier tracking in the log
-     output */
-  conn->connection_id = connection_id_counter++;
-
-  result = Curl_conncache_add_conn(data->state.conn_cache, conn);
-  if(result != CURLE_OK)
-    conn->connection_id = -1;
-
-  return result;
-}
-
-/* after a TCP connection to the proxy has been verified, this function does
-   the next magic step.
-
-   Note: this function's sub-functions call failf()
-
-*/
-CURLcode Curl_connected_proxy(struct connectdata *conn)
-{
-  switch(conn->proxytype) {
-#ifndef CURL_DISABLE_PROXY
-  case CURLPROXY_SOCKS5:
-  case CURLPROXY_SOCKS5_HOSTNAME:
-    return Curl_SOCKS5(conn->proxyuser, conn->proxypasswd,
-                       conn->host.name, conn->remote_port,
-                       FIRSTSOCKET, conn);
-
-  case CURLPROXY_SOCKS4:
-    return Curl_SOCKS4(conn->proxyuser, conn->host.name,
-                       conn->remote_port, FIRSTSOCKET, conn, FALSE);
-
-  case CURLPROXY_SOCKS4A:
-    return Curl_SOCKS4(conn->proxyuser, conn->host.name,
-                       conn->remote_port, FIRSTSOCKET, conn, TRUE);
-
-#endif /* CURL_DISABLE_PROXY */
-  case CURLPROXY_HTTP:
-  case CURLPROXY_HTTP_1_0:
-    /* do nothing here. handled later. */
-    break;
-  default:
-    break;
-  } /* switch proxytype */
-
-  return CURLE_OK;
-}
-
-static CURLcode ConnectPlease(struct SessionHandle *data,
-                              struct connectdata *conn,
-                              bool *connected)
-{
-  CURLcode result;
-  Curl_addrinfo *addr;
-#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 %ld (#%ld)\n",
-        conn->bits.proxy?"proxy ":"",
-        hostname, conn->port, conn->connection_id);
-#else
-  (void)data;
-#endif
-
-  /*************************************************************
-   * Connect to server/proxy
-   *************************************************************/
-  result= Curl_connecthost(conn,
-                           conn->dns_entry,
-                           &conn->sock[FIRSTSOCKET],
-                           &addr,
-                           connected);
-  if(CURLE_OK == result) {
-    /* All is cool, we store the current information */
-    conn->ip_addr = addr;
-
-    if(*connected) {
-      result = Curl_connected_proxy(conn);
-      if(!result) {
-        conn->bits.tcpconnect[FIRSTSOCKET] = TRUE;
-        Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */
-      }
-    }
-  }
-
-  if(result)
-    *connected = FALSE; /* mark it as not connected */
-
-  return result;
-}
-
-/*
- * verboseconnect() displays verbose information after a connect
- */
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-void Curl_verboseconnect(struct connectdata *conn)
-{
-  if(conn->data->set.verbose)
-    infof(conn->data, "Connected to %s (%s) port %ld (#%ld)\n",
-          conn->bits.proxy ? conn->proxy.dispname : conn->host.dispname,
-          conn->ip_addr_str, conn->port, conn->connection_id);
-}
-#endif
-
-int Curl_protocol_getsock(struct connectdata *conn,
-                          curl_socket_t *socks,
-                          int numsocks)
-{
-  if(conn->handler->proto_getsock)
-    return conn->handler->proto_getsock(conn, socks, numsocks);
-  return GETSOCK_BLANK;
-}
-
-int Curl_doing_getsock(struct connectdata *conn,
-                       curl_socket_t *socks,
-                       int numsocks)
-{
-  if(conn && conn->handler->doing_getsock)
-    return conn->handler->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->handler->connecting) {
-    *done = FALSE;
-    result = conn->handler->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->handler->doing) {
-    *done = FALSE;
-    result = conn->handler->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)
-{
-  CURLcode result=CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  *protocol_done = FALSE;
-
-  if(conn->bits.tcpconnect[FIRSTSOCKET] && 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->handler->connecting)
-      *protocol_done = TRUE;
-
-    return CURLE_OK;
-  }
-
-  Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */
-  Curl_verboseconnect(conn);
-
-  if(!conn->bits.protoconnstart) {
-
-    /* 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();
-
-    result = Curl_proxy_connect(conn);
-    if(result)
-      return result;
-
-    if(conn->handler->connect_it) {
-      /* is there a protocol-specific connect() procedure? */
-
-      /* Call the protocol-specific connect function */
-      result = conn->handler->connect_it(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;
-  }
-
-  return result; /* pass back status */
-}
-
-/*
- * Helpers for IDNA convertions.
- */
-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;
-}
-
-#ifdef USE_LIBIDN
-/*
- * Check if characters in hostname is allowed in Top Level Domain.
- */
-static bool tld_check_name(struct SessionHandle *data,
-                           const char *ace_hostname)
-{
-  size_t err_pos;
-  char *uc_name = NULL;
-  int rc;
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-  const char *tld_errmsg = "<no msg>";
-#else
-  (void)data;
-#endif
-
-  /* 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)
-    return FALSE;
-
-  rc = tld_check_lz(uc_name, &err_pos, NULL);
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-#ifdef HAVE_TLD_STRERROR
-  if(rc != TLD_SUCCESS)
-    tld_errmsg = tld_strerror((Tld_rc)rc);
-#endif
-  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);
-  else if(rc != TLD_SUCCESS)
-    infof(data, "WARNING: TLD check for %s failed; %s\n",
-          uc_name, tld_errmsg);
-#endif /* CURL_DISABLE_VERBOSE_STRINGS */
-  if(uc_name)
-     idn_free(uc_name);
-  if(rc != TLD_SUCCESS)
-    return FALSE;
-
-  return TRUE;
-}
-#endif
-
-/*
- * Perform any necessary IDN conversion of hostname
- */
-static void fix_hostname(struct SessionHandle *data,
-                         struct connectdata *conn, struct hostname *host)
-{
-#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 */
-  host->dispname = host->name;
-  if(!is_ASCII_name(host->name)) {
-#ifdef USE_LIBIDN
-  /*************************************************************
-   * Check name for non-ASCII and convert hostname to ACE form.
-   *************************************************************/
-  if(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));
-    else {
-      /* tld_check_name() displays a warning if the host name contains
-         "illegal" characters for this TLD */
-      (void)tld_check_name(data, ace_hostname);
-
-      host->encalloc = ace_hostname;
-      /* change the name pointer to point to the encoded hostname */
-      host->name = host->encalloc;
-    }
-  }
-#elif defined(USE_WIN32_IDN)
-  /*************************************************************
-   * Check name for non-ASCII and convert hostname to ACE form.
-   *************************************************************/
-    char *ace_hostname = NULL;
-    int rc = curl_win32_idn_to_ascii(host->name, &ace_hostname);
-    if(rc == 0)
-      infof(data, "Failed to convert %s to ACE;\n",
-            host->name);
-    else {
-      host->encalloc = ace_hostname;
-      /* change the name pointer to point to the encoded hostname */
-      host->name = host->encalloc;
-    }
-#else
-    infof(data, "IDN support not present, can't parse Unicode domains\n");
-#endif
-  }
-}
-
-static void llist_dtor(void *user, void *element)
-{
-  (void)user;
-  (void)element;
-  /* Do nothing */
-}
-
-/*
- * Allocate and initialize a new connectdata object.
- */
-static struct connectdata *allocate_conn(struct SessionHandle *data)
-{
-  struct connectdata *conn = calloc(1, sizeof(struct connectdata));
-  if(!conn)
-    return NULL;
-
-  conn->handler = &Curl_handler_dummy;  /* Be sure we have a handler defined
-                                           already from start to avoid NULL
-                                           situations and checks */
-
-  /* and we setup a few fields in case we end up actually using this struct */
-
-  conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD;     /* no file descriptor */
-  conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
-  conn->connection_id = -1;    /* no ID */
-  conn->port = -1; /* unknown at this point */
-
-  /* Default protocol-independent behavior doesn't support persistent
-     connections, so we set this to force-close. Protocols that support
-     this need to set this to FALSE in their "curl_do" functions. */
-  conn->bits.close = TRUE;
-
-  /* Store creation time to help future close decision making */
-  conn->created = Curl_tvnow();
-
-  conn->data = data; /* Setup the association between this connection
-                        and the SessionHandle */
-
-  conn->proxytype = data->set.proxytype; /* type */
-
-#ifdef CURL_DISABLE_PROXY
-
-  conn->bits.proxy = FALSE;
-  conn->bits.httpproxy = FALSE;
-  conn->bits.proxy_user_passwd = FALSE;
-  conn->bits.tunnel_proxy = FALSE;
-
-#else /* CURL_DISABLE_PROXY */
-
-  /* note that these two proxy bits are now just on what looks to be
-     requested, they may be altered down the road */
-  conn->bits.proxy = (data->set.str[STRING_PROXY] &&
-                      *data->set.str[STRING_PROXY])?TRUE:FALSE;
-  conn->bits.httpproxy = (conn->bits.proxy &&
-                          (conn->proxytype == CURLPROXY_HTTP ||
-                           conn->proxytype == CURLPROXY_HTTP_1_0))?TRUE:FALSE;
-  conn->bits.proxy_user_passwd =
-    (NULL != data->set.str[STRING_PROXYUSERNAME])?TRUE:FALSE;
-  conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy;
-
-#endif /* CURL_DISABLE_PROXY */
-
-  conn->bits.user_passwd = (NULL != data->set.str[STRING_USERNAME])?TRUE:FALSE;
-  conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
-  conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;
-
-  conn->verifypeer = data->set.ssl.verifypeer;
-  conn->verifyhost = data->set.ssl.verifyhost;
-
-  conn->ip_version = data->set.ipver;
-
-#if defined(USE_NTLM) && defined(NTLM_WB_ENABLED)
-  conn->ntlm_auth_hlpr_socket = CURL_SOCKET_BAD;
-  conn->ntlm_auth_hlpr_pid = 0;
-  conn->challenge_header = NULL;
-  conn->response_header = NULL;
-#endif
-
-  if(data->multi && Curl_multi_canPipeline(data->multi) &&
-      !conn->master_buffer) {
-    /* Allocate master_buffer to be used for pipelining */
-    conn->master_buffer = calloc(BUFSIZE, sizeof (char));
-    if(!conn->master_buffer)
-      goto error;
-  }
-
-  /* Initialize the pipeline lists */
-  conn->send_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
-  conn->recv_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
-  conn->pend_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
-  conn->done_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
-  if(!conn->send_pipe || !conn->recv_pipe || !conn->pend_pipe ||
-     !conn->done_pipe)
-    goto error;
-
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
-  conn->data_prot = PROT_CLEAR;
-#endif
-
-  /* Store the local bind parameters that will be used for this connection */
-  if(data->set.str[STRING_DEVICE]) {
-    conn->localdev = strdup(data->set.str[STRING_DEVICE]);
-    if(!conn->localdev)
-      goto error;
-  }
-  conn->localportrange = data->set.localportrange;
-  conn->localport = data->set.localport;
-
-  /* the close socket stuff needs to be copied to the connection struct as
-     it may live on without (this specific) SessionHandle */
-  conn->fclosesocket = data->set.fclosesocket;
-  conn->closesocket_client = data->set.closesocket_client;
-
-  return conn;
-  error:
-
-  Curl_llist_destroy(conn->send_pipe, NULL);
-  Curl_llist_destroy(conn->recv_pipe, NULL);
-  Curl_llist_destroy(conn->pend_pipe, NULL);
-  Curl_llist_destroy(conn->done_pipe, NULL);
-
-  conn->send_pipe = NULL;
-  conn->recv_pipe = NULL;
-  conn->pend_pipe = NULL;
-  conn->done_pipe = NULL;
-
-  Curl_safefree(conn->master_buffer);
-  Curl_safefree(conn->localdev);
-  Curl_safefree(conn);
-  return NULL;
-}
-
-static CURLcode findprotocol(struct SessionHandle *data,
-                             struct connectdata *conn,
-                             const char *protostr)
-{
-  const struct Curl_handler * const *pp;
-  const struct Curl_handler *p;
-
-  /* Scan protocol handler table and match against 'protostr' to set a few
-     variables based on the URL. Now that the handler may be changed later
-     when the protocol specific setup function is called. */
-  for(pp = protocols; (p = *pp) != NULL; pp++) {
-    if(Curl_raw_equal(p->scheme, protostr)) {
-      /* Protocol found in table. Check if allowed */
-      if(!(data->set.allowed_protocols & p->protocol))
-        /* nope, get out */
-        break;
-
-      /* it is allowed for "normal" request, now do an extra check if this is
-         the result of a redirect */
-      if(data->state.this_is_a_follow &&
-         !(data->set.redir_protocols & p->protocol))
-        /* nope, get out */
-        break;
-
-      /* Perform setup complement if some. */
-      conn->handler = conn->given = p;
-
-      /* 'port' and 'remote_port' are set in setup_connection_internals() */
-      return CURLE_OK;
-    }
-  }
-
-
-  /* The protocol was not found in the table, but we don't have to assign it
-     to anything since it is already assigned to a dummy-struct in the
-     create_conn() function when the connectdata struct is allocated. */
-  failf(data, "Protocol %s not supported or disabled in " LIBCURL_NAME,
-        protostr);
-
-  return CURLE_UNSUPPORTED_PROTOCOL;
-}
-
-/*
- * Parse URL and fill in the relevant members of the connection struct.
- */
-static CURLcode parseurlandfillconn(struct SessionHandle *data,
-                                    struct connectdata *conn,
-                                    bool *prot_missing,
-                                    char *user,
-                                    char *passwd)
-{
-  char *at;
-  char *fragment;
-  char *path = data->state.path;
-  char *query;
-  int rc;
-  char protobuf[16];
-  const char *protop;
-  CURLcode result;
-
-  *prot_missing = FALSE;
-
-  /*************************************************************
-   * 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]",
-                  protobuf, path)) &&
-     Curl_raw_equal(protobuf, "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);
-    }
-    /*
-     * 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
-     */
-    if(path[0] != '/') {
-      /* the URL included a host name, we ignore host names in file:// URLs
-         as the standards don't define what to do with them */
-      char *ptr=strchr(path, '/');
-      if(ptr) {
-        /* there was a slash present
-
-           RFC1738 (section 3.1, page 5) says:
-
-           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.
-
-           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,
-           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 */
-          ptr++;
-
-        /* This cannot be made with strcpy, as the memory chunks overlap! */
-        memmove(path, ptr, strlen(ptr)+1);
-      }
-    }
-
-    protop = "file"; /* protocol string */
-  }
-  else {
-    /* clear path */
-    path[0]=0;
-
-    if(2 > sscanf(data->change.url,
-                   "%15[^\n:]://%[^\n/?]%[^\n]",
-                   protobuf,
-                   conn->host.name, path)) {
-
-      /*
-       * The URL was badly formatted, let's try the browser-style _without_
-       * protocol specified like 'http://'.
-       */
-      rc = sscanf(data->change.url, "%[^\n/?]%[^\n]", conn->host.name, path);
-      if(1 > rc) {
-        /*
-         * We couldn't even get this format.
-         * djgpp 2.04 has a sscanf() bug where 'conn->host.name' is
-         * assigned, but the return value is EOF!
-         */
-#if defined(__DJGPP__) && (DJGPP_MINOR == 4)
-        if(!(rc == -1 && *conn->host.name))
-#endif
-        {
-          failf(data, "<url> malformed");
-          return CURLE_URL_MALFORMAT;
-        }
-      }
-
-      /*
-       * 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/curl_version.c too! */
-
-      if(checkprefix("FTP.", conn->host.name))
-        protop = "ftp";
-      else if(checkprefix("DICT.", conn->host.name))
-        protop = "DICT";
-      else if(checkprefix("LDAP.", conn->host.name))
-        protop = "LDAP";
-      else if(checkprefix("IMAP.", conn->host.name))
-        protop = "IMAP";
-      else {
-        protop = "http";
-      }
-
-      *prot_missing = TRUE; /* not given in URL */
-    }
-    else
-      protop = protobuf;
-  }
-
-  /* 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)
-    query = strchr(at+1, '?');
-  else
-    query = strchr(conn->host.name, '?');
-
-  if(query) {
-    /* 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(query);
-    size_t pathlen = strlen(path);
-
-    /* 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 */
-    memcpy(path+1, query, hostlen);
-
-    path[0]='/'; /* prepend the missing slash */
-
-    *query=0; /* now cut off the hostname at the ? */
-  }
-  else if(!path[0]) {
-    /* if there's no path set, use a single slash */
-    strcpy(path, "/");
-  }
-
-  /* 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 '?'.
-   */
-  if(path[0] == '?') {
-    /* 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] = '/';
-  }
-
-  /*************************************************************
-   * Parse a user name and password in the URL and strip it out
-   * of the host name
-   *************************************************************/
-  result = parse_url_userpass(data, conn, user, passwd);
-  if(result != CURLE_OK)
-    return result;
-
-  if(conn->host.name[0] == '[') {
-    /* This looks like an IPv6 address literal.  See if there is an address
-       scope.  */
-    char *percent = strstr (conn->host.name, "%25");
-    if(percent) {
-      char *endp;
-      unsigned long scope = strtoul (percent + 3, &endp, 10);
-      if(*endp == ']') {
-        /* The address scope was well formed.  Knock it out of the
-           hostname. */
-        memmove(percent, endp, strlen(endp)+1);
-        if(!data->state.this_is_a_follow)
-          /* Don't honour a scope given in a Location: header */
-          conn->scope = (unsigned int)scope;
-      }
-      else
-        infof(data, "Invalid IPv6 address format\n");
-    }
-  }
-
-  if(data->set.scope)
-    /* Override any scope that was set above.  */
-    conn->scope = data->set.scope;
-
-  /* Remove the fragment part of the path. Per RFC 2396, this is always the
-     last part of the URI. We are looking for the first '#' so that we deal
-     gracefully with non conformant URI such as http://example.com#foo#bar. */
-  fragment = strchr(path, '#');
-  if(fragment) {
-    *fragment = 0;
-
-    /* we know the path part ended with a fragment, so we know the full URL
-       string does too and we need to cut it off from there so it isn't used
-       over proxy */
-    fragment = strchr(data->change.url, '#');
-    if(fragment)
-      *fragment = 0;
-  }
-
-  /*
-   * So if the URL was A://B/C#D,
-   *   protop is A
-   *   conn->host.name is B
-   *   data->state.path is /C
-   */
-
-  return findprotocol(data, conn, protop);
-}
-
-/*
- * If we're doing a resumed transfer, we need to setup our stuff
- * properly.
- */
-static CURLcode setup_range(struct SessionHandle *data)
-{
-  struct UrlState *s = &data->state;
-  s->resume_from = data->set.set_resume_from;
-  if(s->resume_from || data->set.str[STRING_SET_RANGE]) {
-    if(s->rangestringalloc)
-      free(s->range);
-
-    if(s->resume_from)
-      s->range = aprintf("%" FORMAT_OFF_TU "-", s->resume_from);
-    else
-      s->range = strdup(data->set.str[STRING_SET_RANGE]);
-
-    s->rangestringalloc = (s->range)?TRUE:FALSE;
-
-    if(!s->range)
-      return CURLE_OUT_OF_MEMORY;
-
-    /* tell ourselves to fetch this range */
-    s->use_range = TRUE;        /* enable range download */
-  }
-  else
-    s->use_range = FALSE; /* disable range download */
-
-  return CURLE_OK;
-}
-
-
-/***************************************************************
-* Setup connection internals specific to the requested protocol.
-* This MUST get called after proxy magic has been figured out.
-***************************************************************/
-static CURLcode setup_connection_internals(struct connectdata *conn)
-{
-  const struct Curl_handler * p;
-  CURLcode result;
-
-  conn->socktype = SOCK_STREAM; /* most of them are TCP streams */
-
-  /* Scan protocol handler table. */
-
-  /* Perform setup complement if some. */
-  p = conn->handler;
-
-  if(p->setup_connection) {
-    result = (*p->setup_connection)(conn);
-
-    if(result != CURLE_OK)
-      return result;
-
-    p = conn->handler;              /* May have changed. */
-  }
-
-  if(conn->port < 0)
-    /* we check for -1 here since if proxy was detected already, this
-       was very likely already set to the proxy port */
-    conn->port = p->defport;
-  conn->remote_port = (unsigned short)conn->given->defport;
-
-  return CURLE_OK;
-}
-
-#ifndef CURL_DISABLE_PROXY
-/****************************************************************
-* Checks if the host is in the noproxy list. returns true if it matches
-* and therefore the proxy should NOT be used.
-****************************************************************/
-static bool check_noproxy(const char* name, const char* no_proxy)
-{
-  /* no_proxy=domain1.dom,host.domain2.dom
-   *   (a comma-separated list of hosts which should
-   *   not be proxied, or an asterisk to override
-   *   all proxy variables)
-   */
-  size_t tok_start;
-  size_t tok_end;
-  const char* separator = ", ";
-  size_t no_proxy_len;
-  size_t namelen;
-  char *endptr;
-
-  if(no_proxy && no_proxy[0]) {
-    if(Curl_raw_equal("*", no_proxy)) {
-      return TRUE;
-    }
-
-    /* NO_PROXY was specified and it wasn't just an asterisk */
-
-    no_proxy_len = strlen(no_proxy);
-    endptr = strchr(name, ':');
-    if(endptr)
-      namelen = endptr - name;
-    else
-      namelen = strlen(name);
-
-    for(tok_start = 0; tok_start < no_proxy_len; tok_start = tok_end + 1) {
-      while(tok_start < no_proxy_len &&
-            strchr(separator, no_proxy[tok_start]) != NULL) {
-        /* Look for the beginning of the token. */
-        ++tok_start;
-      }
-
-      if(tok_start == no_proxy_len)
-        break; /* It was all trailing separator chars, no more tokens. */
-
-      for(tok_end = tok_start; tok_end < no_proxy_len &&
-            strchr(separator, no_proxy[tok_end]) == NULL; ++tok_end)
-        /* Look for the end of the token. */
-        ;
-
-      /* To match previous behaviour, where it was necessary to specify
-       * ".local.com" to prevent matching "notlocal.com", we will leave
-       * the '.' off.
-       */
-      if(no_proxy[tok_start] == '.')
-        ++tok_start;
-
-      if((tok_end - tok_start) <= namelen) {
-        /* Match the last part of the name to the domain we are checking. */
-        const char *checkn = name + namelen - (tok_end - tok_start);
-        if(Curl_raw_nequal(no_proxy + tok_start, checkn,
-                           tok_end - tok_start)) {
-          if((tok_end - tok_start) == namelen || *(checkn - 1) == '.') {
-            /* We either have an exact match, or the previous character is a .
-             * so it is within the same domain, so no proxy for this host.
-             */
-            return TRUE;
-          }
-        }
-      } /* if((tok_end - tok_start) <= namelen) */
-    } /* for(tok_start = 0; tok_start < no_proxy_len;
-         tok_start = tok_end + 1) */
-  } /* NO_PROXY was specified and it wasn't just an asterisk */
-
-  return FALSE;
-}
-
-/****************************************************************
-* Detect what (if any) proxy to use. Remember that this selects a host
-* name and is not limited to HTTP proxies only.
-* The returned pointer must be freed by the caller (unless NULL)
-****************************************************************/
-static char *detect_proxy(struct connectdata *conn)
-{
-  char *proxy = NULL;
-
-#ifndef CURL_DISABLE_HTTP
-  /* If proxy was not specified, we check for default proxy environment
-   * variables, to enable i.e Lynx compliance:
-   *
-   * http_proxy=http://some.server.dom:port/
-   * https_proxy=http://some.server.dom:port/
-   * ftp_proxy=http://some.server.dom:port/
-   * no_proxy=domain1.dom,host.domain2.dom
-   *   (a comma-separated list of hosts which should
-   *   not be proxied, or an asterisk to override
-   *   all proxy variables)
-   * all_proxy=http://some.server.dom:port/
-   *   (seems to exist for the CERN www lib. Probably
-   *   the first to check for.)
-   *
-   * For compatibility, the all-uppercase versions of these variables are
-   * checked if the lowercase versions don't exist.
-   */
-  char *no_proxy=NULL;
-  char proxy_env[128];
-
-  no_proxy=curl_getenv("no_proxy");
-  if(!no_proxy)
-    no_proxy=curl_getenv("NO_PROXY");
-
-  if(!check_noproxy(conn->host.name, no_proxy)) {
-    /* It was not listed as without proxy */
-    const char *protop = conn->handler->scheme;
-    char *envp = proxy_env;
-    char *prox;
-
-    /* Now, build <protocol>_proxy and check for such a one to use */
-    while(*protop)
-      *envp++ = (char)tolower((int)*protop++);
-
-    /* append _proxy */
-    strcpy(envp, "_proxy");
-
-    /* read the protocol proxy: */
-    prox=curl_getenv(proxy_env);
-
-    /*
-     * We don't try the uppercase version of HTTP_PROXY because of
-     * security reasons:
-     *
-     * When curl is used in a webserver application
-     * environment (cgi or php), this environment variable can
-     * be controlled by the web server user by setting the
-     * http header 'Proxy:' to some value.
-     *
-     * This can cause 'internal' http/ftp requests to be
-     * arbitrarily redirected by any external attacker.
-     */
-    if(!prox && !Curl_raw_equal("http_proxy", proxy_env)) {
-      /* There was no lowercase variable, try the uppercase version: */
-      Curl_strntoupper(proxy_env, proxy_env, sizeof(proxy_env));
-      prox=curl_getenv(proxy_env);
-    }
-
-    if(prox && *prox) { /* don't count "" strings */
-      proxy = prox; /* use this */
-    }
-    else {
-      proxy = curl_getenv("all_proxy"); /* default proxy to use */
-      if(!proxy)
-        proxy=curl_getenv("ALL_PROXY");
-    }
-  } /* if(!check_noproxy(conn->host.name, no_proxy)) - it wasn't specified
-       non-proxy */
-  if(no_proxy)
-    free(no_proxy);
-
-#else /* !CURL_DISABLE_HTTP */
-
-  (void)conn;
-#endif /* CURL_DISABLE_HTTP */
-
-  return proxy;
-}
-
-/*
- * If this is supposed to use a proxy, we need to figure out the proxy
- * host name, so that we can re-use an existing connection
- * that may exist registered to the same proxy host.
- * proxy will be freed before this function returns.
- */
-static CURLcode parse_proxy(struct SessionHandle *data,
-                            struct connectdata *conn, char *proxy)
-{
-  char *prox_portno;
-  char *endofprot;
-
-  /* We use 'proxyptr' to point to the proxy name from now on... */
-  char *proxyptr;
-  char *portptr;
-  char *atsign;
-
-  /* We do the proxy host string parsing here. We want the host name and the
-   * port name. Accept a protocol:// prefix
-   */
-
-  /* Parse the protocol part if present */
-  endofprot = strstr(proxy, "://");
-  if(endofprot) {
-    proxyptr = endofprot+3;
-    if(checkprefix("socks5h", proxy))
-      conn->proxytype = CURLPROXY_SOCKS5_HOSTNAME;
-    else if(checkprefix("socks5", proxy))
-      conn->proxytype = CURLPROXY_SOCKS5;
-    else if(checkprefix("socks4a", proxy))
-      conn->proxytype = CURLPROXY_SOCKS4A;
-    else if(checkprefix("socks4", proxy) || checkprefix("socks", proxy))
-      conn->proxytype = CURLPROXY_SOCKS4;
-    /* Any other xxx:// : change to http proxy */
-  }
-  else
-    proxyptr = proxy; /* No xxx:// head: It's a HTTP proxy */
-
-  /* Is there a username and password given in this proxy url? */
-  atsign = strchr(proxyptr, '@');
-  if(atsign) {
-    char proxyuser[MAX_CURL_USER_LENGTH];
-    char proxypasswd[MAX_CURL_PASSWORD_LENGTH];
-    proxypasswd[0] = 0;
-
-    if(1 <= sscanf(proxyptr,
-                   "%" MAX_CURL_USER_LENGTH_TXT"[^:@]:"
-                   "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]",
-                   proxyuser, proxypasswd)) {
-      CURLcode res = CURLE_OK;
-
-      /* found user and password, rip them out.  note that we are
-         unescaping them, as there is otherwise no way to have a
-         username or password with reserved characters like ':' in
-         them. */
-      Curl_safefree(conn->proxyuser);
-      conn->proxyuser = curl_easy_unescape(data, proxyuser, 0, NULL);
-
-      if(!conn->proxyuser)
-        res = CURLE_OUT_OF_MEMORY;
-      else {
-        Curl_safefree(conn->proxypasswd);
-        conn->proxypasswd = curl_easy_unescape(data, proxypasswd, 0, NULL);
-
-        if(!conn->proxypasswd)
-          res = CURLE_OUT_OF_MEMORY;
-      }
-
-      if(CURLE_OK == res) {
-        conn->bits.proxy_user_passwd = TRUE; /* enable it */
-        atsign++; /* the right side of the @-letter */
-
-        if(atsign)
-          proxyptr = atsign; /* now use this instead */
-        else
-          res = CURLE_OUT_OF_MEMORY;
-      }
-
-      if(res)
-        return res;
-    }
-  }
-
-  /* start scanning for port number at this point */
-  portptr = proxyptr;
-
-  /* detect and extract RFC2732-style IPv6-addresses */
-  if(*proxyptr == '[') {
-    char *ptr = ++proxyptr; /* advance beyond the initial bracket */
-    while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '%') ||
-                   (*ptr == '.')))
-      ptr++;
-    if(*ptr == ']')
-      /* yeps, it ended nicely with a bracket as well */
-      *ptr++ = 0;
-    else
-      infof(data, "Invalid IPv6 address format\n");
-    portptr = ptr;
-    /* Note that if this didn't end with a bracket, we still advanced the
-     * proxyptr first, but I can't see anything wrong with that as no host
-     * name nor a numeric can legally start with a bracket.
-     */
-  }
-
-  /* Get port number off proxy.server.com:1080 */
-  prox_portno = strchr(portptr, ':');
-  if(prox_portno) {
-    *prox_portno = 0x0; /* cut off number from host name */
-    prox_portno ++;
-    /* now set the local port number */
-    conn->port = strtol(prox_portno, NULL, 10);
-  }
-  else {
-    if(proxyptr[0]=='/')
-      /* If the first character in the proxy string is a slash, fail
-         immediately. The following code will otherwise clear the string which
-         will lead to code running as if no proxy was set! */
-      return CURLE_COULDNT_RESOLVE_PROXY;
-
-    /* without a port number after the host name, some people seem to use
-       a slash so we strip everything from the first slash */
-    atsign = strchr(proxyptr, '/');
-    if(atsign)
-      *atsign = 0x0; /* cut off path part from host name */
-
-    if(data->set.proxyport)
-      /* None given in the proxy string, then get the default one if it is
-         given */
-      conn->port = data->set.proxyport;
-  }
-
-  /* now, clone the cleaned proxy host name */
-  conn->proxy.rawalloc = strdup(proxyptr);
-  conn->proxy.name = conn->proxy.rawalloc;
-
-  if(!conn->proxy.rawalloc)
-    return CURLE_OUT_OF_MEMORY;
-
-  return CURLE_OK;
-}
-
-/*
- * Extract the user and password from the authentication string
- */
-static CURLcode parse_proxy_auth(struct SessionHandle *data,
-                                 struct connectdata *conn)
-{
-  char proxyuser[MAX_CURL_USER_LENGTH]="";
-  char proxypasswd[MAX_CURL_PASSWORD_LENGTH]="";
-
-  if(data->set.str[STRING_PROXYUSERNAME] != NULL) {
-    strncpy(proxyuser, data->set.str[STRING_PROXYUSERNAME],
-            MAX_CURL_USER_LENGTH);
-    proxyuser[MAX_CURL_USER_LENGTH-1] = '\0';   /*To be on safe side*/
-  }
-  if(data->set.str[STRING_PROXYPASSWORD] != NULL) {
-    strncpy(proxypasswd, data->set.str[STRING_PROXYPASSWORD],
-            MAX_CURL_PASSWORD_LENGTH);
-    proxypasswd[MAX_CURL_PASSWORD_LENGTH-1] = '\0'; /*To be on safe side*/
-  }
-
-  conn->proxyuser = curl_easy_unescape(data, proxyuser, 0, NULL);
-  if(!conn->proxyuser)
-    return CURLE_OUT_OF_MEMORY;
-
-  conn->proxypasswd = curl_easy_unescape(data, proxypasswd, 0, NULL);
-  if(!conn->proxypasswd)
-    return CURLE_OUT_OF_MEMORY;
-
-  return CURLE_OK;
-}
-#endif /* CURL_DISABLE_PROXY */
-
-/*
- *
- * Parse a user name and password in the URL and strip it out of the host name
- *
- * Inputs: data->set.use_netrc (CURLOPT_NETRC)
- *         conn->host.name
- *
- * Outputs: (almost :- all currently undefined)
- *          conn->bits.user_passwd  - non-zero if non-default passwords exist
- *          user                    - non-zero length if defined
- *          passwd                  -   ditto
- *          conn->host.name         - remove user name and password
- */
-static CURLcode parse_url_userpass(struct SessionHandle *data,
-                                   struct connectdata *conn,
-                                   char *user, char *passwd)
-{
-  /* At this point, we're hoping all the other special cases have
-   * been taken care of, so conn->host.name is at most
-   *    [user[:password]]@]hostname
-   *
-   * We need somewhere to put the embedded details, so do that first.
-   */
-
-  char *ptr=strchr(conn->host.name, '@');
-  char *userpass = conn->host.name;
-
-  user[0] =0;   /* to make everything well-defined */
-  passwd[0]=0;
-
-  /* We will now try to extract the
-   * possible user+password pair in a string like:
-   * ftp://user:password@ftp.my.site:8021/README */
-  if(ptr != NULL) {
-    /* there's a user+password given here, to the left of the @ */
-
-    conn->host.name = ++ptr;
-
-    /* So the hostname is sane.  Only bother interpreting the
-     * results if we could care.  It could still be wasted
-     * work because it might be overtaken by the programmatically
-     * set user/passwd, but doing that first adds more cases here :-(
-     */
-
-    conn->bits.userpwd_in_url = TRUE;
-    if(data->set.use_netrc != CURL_NETRC_REQUIRED) {
-      /* We could use the one in the URL */
-
-      conn->bits.user_passwd = TRUE; /* enable user+password */
-
-      if(*userpass != ':') {
-        /* the name is given, get user+password */
-        sscanf(userpass, "%" MAX_CURL_USER_LENGTH_TXT "[^:@]:"
-               "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]",
-               user, passwd);
-      }
-      else
-        /* no name given, get the password only */
-        sscanf(userpass, ":%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]", passwd);
-
-      if(user[0]) {
-        char *newname=curl_easy_unescape(data, user, 0, NULL);
-        if(!newname)
-          return CURLE_OUT_OF_MEMORY;
-        if(strlen(newname) < MAX_CURL_USER_LENGTH)
-          strcpy(user, newname);
-
-        /* if the new name is longer than accepted, then just use
-           the unconverted name, it'll be wrong but what the heck */
-        free(newname);
-      }
-      if(passwd[0]) {
-        /* we have a password found in the URL, decode it! */
-        char *newpasswd=curl_easy_unescape(data, passwd, 0, NULL);
-        if(!newpasswd)
-          return CURLE_OUT_OF_MEMORY;
-        if(strlen(newpasswd) < MAX_CURL_PASSWORD_LENGTH)
-          strcpy(passwd, newpasswd);
-
-        free(newpasswd);
-      }
-    }
-  }
-  return CURLE_OK;
-}
-
-/*************************************************************
- * Figure out the remote port number and fix it in the URL
- *
- * No matter if we use a proxy or not, we have to figure out the remote
- * port number of various reasons.
- *
- * To be able to detect port number flawlessly, we must not confuse them
- * IPv6-specified addresses in the [0::1] style. (RFC2732)
- *
- * The conn->host.name is currently [user:passwd@]host[:port] where host
- * could be a hostname, IPv4 address or IPv6 address.
- *
- * The port number embedded in the URL is replaced, if necessary.
- *************************************************************/
-static CURLcode parse_remote_port(struct SessionHandle *data,
-                                  struct connectdata *conn)
-{
-  char *portptr;
-  char endbracket;
-
-  /* Note that at this point, the IPv6 address cannot contain any scope
-     suffix as that has already been removed in the parseurlandfillconn()
-     function */
-  if((1 == sscanf(conn->host.name, "[%*45[0123456789abcdefABCDEF:.]%c",
-                  &endbracket)) &&
-     (']' == endbracket)) {
-    /* this is a RFC2732-style specified IP-address */
-    conn->bits.ipv6_ip = TRUE;
-
-    conn->host.name++; /* skip over the starting bracket */
-    portptr = strchr(conn->host.name, ']');
-    if(portptr) {
-      *portptr++ = '\0'; /* zero terminate, killing the bracket */
-      if(':' != *portptr)
-        portptr = NULL; /* no port number available */
-    }
-  }
-  else {
-#ifdef ENABLE_IPV6
-    struct in6_addr in6;
-    if(Curl_inet_pton(AF_INET6, conn->host.name, &in6) > 0) {
-      /* This is a numerical IPv6 address, meaning this is a wrongly formatted
-         URL */
-      failf(data, "IPv6 numerical address used in URL without brackets");
-      return CURLE_URL_MALFORMAT;
-    }
-#endif
-
-    portptr = strrchr(conn->host.name, ':');
-  }
-
-  if(data->set.use_port && data->state.allow_port) {
-    /* if set, we use this and ignore the port possibly given in the URL */
-    conn->remote_port = (unsigned short)data->set.use_port;
-    if(portptr)
-      *portptr = '\0'; /* cut off the name there anyway - if there was a port
-                      number - since the port number is to be ignored! */
-    if(conn->bits.httpproxy) {
-      /* we need to create new URL with the new port number */
-      char *url;
-      char type[12]="";
-
-      if(conn->bits.type_set)
-        snprintf(type, sizeof(type), ";type=%c",
-                 data->set.prefer_ascii?'A':
-                 (data->set.ftp_list_only?'D':'I'));
-
-      /*
-       * This synthesized URL isn't always right--suffixes like ;type=A are
-       * stripped off. It would be better to work directly from the original
-       * URL and simply replace the port part of it.
-       */
-      url = aprintf("%s://%s%s%s:%hu%s%s%s", conn->given->scheme,
-                    conn->bits.ipv6_ip?"[":"", conn->host.name,
-                    conn->bits.ipv6_ip?"]":"", conn->remote_port,
-                    data->state.slash_removed?"/":"", data->state.path,
-                    type);
-      if(!url)
-        return CURLE_OUT_OF_MEMORY;
-
-      if(data->change.url_alloc) {
-        Curl_safefree(data->change.url);
-        data->change.url_alloc = FALSE;
-      }
-
-      data->change.url = url;
-      data->change.url_alloc = TRUE;
-    }
-  }
-  else if(portptr) {
-    /* no CURLOPT_PORT given, extract the one from the URL */
-
-    char *rest;
-    unsigned long port;
-
-    port=strtoul(portptr+1, &rest, 10);  /* Port number must be decimal */
-
-    if(rest != (portptr+1) && *rest == '\0') {
-      /* The colon really did have only digits after it,
-       * so it is either a port number or a mistake */
-
-      if(port > 0xffff) {   /* Single unix standard says port numbers are
-                              * 16 bits long */
-        failf(data, "Port number too large: %lu", port);
-        return CURLE_URL_MALFORMAT;
-      }
-
-      *portptr = '\0'; /* cut off the name there */
-      conn->remote_port = curlx_ultous(port);
-    }
-    else if(!port)
-      /* Browser behavior adaptation. If there's a colon with no digits after,
-         just cut off the name there which makes us ignore the colon and just
-         use the default port. Firefox and Chrome both do that. */
-      *portptr = '\0';
-  }
-  return CURLE_OK;
-}
-
-/*
- * Override a user name and password from the URL with that in the
- * CURLOPT_USERPWD option or a .netrc file, if applicable.
- */
-static void override_userpass(struct SessionHandle *data,
-                              struct connectdata *conn,
-                              char *user, char *passwd)
-{
-  if(data->set.str[STRING_USERNAME] != NULL) {
-    strncpy(user, data->set.str[STRING_USERNAME], MAX_CURL_USER_LENGTH);
-    user[MAX_CURL_USER_LENGTH-1] = '\0';   /*To be on safe side*/
-  }
-  if(data->set.str[STRING_PASSWORD] != NULL) {
-    strncpy(passwd, data->set.str[STRING_PASSWORD], MAX_CURL_PASSWORD_LENGTH);
-    passwd[MAX_CURL_PASSWORD_LENGTH-1] = '\0'; /*To be on safe side*/
-  }
-
-  conn->bits.netrc = FALSE;
-  if(data->set.use_netrc != CURL_NETRC_IGNORED) {
-    if(Curl_parsenetrc(conn->host.name,
-                       user, passwd,
-                       data->set.str[STRING_NETRC_FILE])) {
-      infof(data, "Couldn't find host %s in the "
-            DOT_CHAR "netrc file; using defaults\n",
-            conn->host.name);
-    }
-    else {
-      /* set bits.netrc TRUE to remember that we got the name from a .netrc
-         file, so that it is safe to use even if we followed a Location: to a
-         different host or similar. */
-      conn->bits.netrc = TRUE;
-
-      conn->bits.user_passwd = TRUE; /* enable user+password */
-    }
-  }
-}
-
-/*
- * Set password so it's available in the connection.
- */
-static CURLcode set_userpass(struct connectdata *conn,
-                             const char *user, const char *passwd)
-{
-  /* If our protocol needs a password and we have none, use the defaults */
-  if((conn->handler->flags & PROTOPT_NEEDSPWD) &&
-     !conn->bits.user_passwd) {
-
-    conn->user = strdup(CURL_DEFAULT_USER);
-    if(conn->user)
-      conn->passwd = strdup(CURL_DEFAULT_PASSWORD);
-    else
-      conn->passwd = NULL;
-    /* This is the default password, so DON'T set conn->bits.user_passwd */
-  }
-  else {
-    /* store user + password, zero-length if not set */
-    conn->user = strdup(user);
-    if(conn->user)
-      conn->passwd = strdup(passwd);
-    else
-      conn->passwd = NULL;
-  }
-  if(!conn->user || !conn->passwd)
-    return CURLE_OUT_OF_MEMORY;
-
-  return CURLE_OK;
-}
-
-/*************************************************************
- * Resolve the address of the server or proxy
- *************************************************************/
-static CURLcode resolve_server(struct SessionHandle *data,
-                               struct connectdata *conn,
-                               bool *async)
-{
-  CURLcode result=CURLE_OK;
-  long timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
-  /*************************************************************
-   * Resolve the name of the server or proxy
-   *************************************************************/
-  if(conn->bits.reuse)
-    /* We're reusing the connection - no need to resolve anything, and
-       fix_hostname() was called already in create_conn() for the re-use
-       case. */
-    *async = FALSE;
-
-  else {
-    /* this is a fresh connect */
-    int rc;
-    struct Curl_dns_entry *hostaddr;
-
-    /* set a pointer to the hostname we display */
-    fix_hostname(data, conn, &conn->host);
-
-    if(!conn->proxy.name || !*conn->proxy.name) {
-      /* If not connecting via a proxy, extract the port from the URL, if it is
-       * there, thus overriding any defaults that might have been set above. */
-      conn->port =  conn->remote_port; /* it is the same port */
-
-      /* Resolve target host right on */
-      rc = Curl_resolv_timeout(conn, conn->host.name, (int)conn->port,
-                               &hostaddr, timeout_ms);
-      if(rc == CURLRESOLV_PENDING)
-        *async = TRUE;
-
-      else if(rc == CURLRESOLV_TIMEDOUT)
-        result = CURLE_OPERATION_TIMEDOUT;
-
-      else if(!hostaddr) {
-        failf(data, "Couldn't resolve host '%s'", conn->host.dispname);
-        result =  CURLE_COULDNT_RESOLVE_HOST;
-        /* don't return yet, we need to clean up the timeout first */
-      }
-    }
-    else {
-      /* This is a proxy that hasn't been resolved yet. */
-
-      /* IDN-fix the proxy name */
-      fix_hostname(data, conn, &conn->proxy);
-
-      /* resolve proxy */
-      rc = Curl_resolv_timeout(conn, conn->proxy.name, (int)conn->port,
-                               &hostaddr, timeout_ms);
-
-      if(rc == CURLRESOLV_PENDING)
-        *async = TRUE;
-
-      else if(rc == CURLRESOLV_TIMEDOUT)
-        result = CURLE_OPERATION_TIMEDOUT;
-
-      else if(!hostaddr) {
-        failf(data, "Couldn't resolve proxy '%s'", conn->proxy.dispname);
-        result = CURLE_COULDNT_RESOLVE_PROXY;
-        /* don't return yet, we need to clean up the timeout first */
-      }
-    }
-    DEBUGASSERT(conn->dns_entry == NULL);
-    conn->dns_entry = hostaddr;
-  }
-
-  return result;
-}
-
-/*
- * Cleanup the connection just allocated before we can move along and use the
- * previously existing one.  All relevant data is copied over and old_conn is
- * ready for freeing once this function returns.
- */
-static void reuse_conn(struct connectdata *old_conn,
-                       struct connectdata *conn)
-{
-  if(old_conn->proxy.rawalloc)
-    free(old_conn->proxy.rawalloc);
-
-  /* free the SSL config struct from this connection struct as this was
-     allocated in vain and is targeted for destruction */
-  Curl_free_ssl_config(&old_conn->ssl_config);
-
-  conn->data = old_conn->data;
-
-  /* get the user+password information from the old_conn struct since it may
-   * be new for this request even when we re-use an existing connection */
-  conn->bits.user_passwd = old_conn->bits.user_passwd;
-  if(conn->bits.user_passwd) {
-    /* use the new user name and password though */
-    Curl_safefree(conn->user);
-    Curl_safefree(conn->passwd);
-    conn->user = old_conn->user;
-    conn->passwd = old_conn->passwd;
-    old_conn->user = NULL;
-    old_conn->passwd = NULL;
-  }
-
-  conn->bits.proxy_user_passwd = old_conn->bits.proxy_user_passwd;
-  if(conn->bits.proxy_user_passwd) {
-    /* use the new proxy user name and proxy password though */
-    Curl_safefree(conn->proxyuser);
-    Curl_safefree(conn->proxypasswd);
-    conn->proxyuser = old_conn->proxyuser;
-    conn->proxypasswd = old_conn->proxypasswd;
-    old_conn->proxyuser = NULL;
-    old_conn->proxypasswd = NULL;
-  }
-
-  /* host can change, when doing keepalive with a proxy or if the case is
-     different this time etc */
-  Curl_safefree(conn->host.rawalloc);
-  conn->host=old_conn->host;
-
-  /* persist connection info in session handle */
-  Curl_persistconninfo(conn);
-
-  /* re-use init */
-  conn->bits.reuse = TRUE; /* yes, we're re-using here */
-
-  Curl_safefree(old_conn->user);
-  Curl_safefree(old_conn->passwd);
-  Curl_safefree(old_conn->proxyuser);
-  Curl_safefree(old_conn->proxypasswd);
-  Curl_safefree(old_conn->localdev);
-
-  Curl_llist_destroy(old_conn->send_pipe, NULL);
-  Curl_llist_destroy(old_conn->recv_pipe, NULL);
-  Curl_llist_destroy(old_conn->pend_pipe, NULL);
-  Curl_llist_destroy(old_conn->done_pipe, NULL);
-
-  old_conn->send_pipe = NULL;
-  old_conn->recv_pipe = NULL;
-  old_conn->pend_pipe = NULL;
-  old_conn->done_pipe = NULL;
-
-  Curl_safefree(old_conn->master_buffer);
-}
-
-/**
- * create_conn() sets up a new connectdata struct, or re-uses an already
- * existing one, and resolves host name.
- *
- * if this function returns CURLE_OK and *async is set to TRUE, the resolve
- * response will be coming asynchronously. If *async is FALSE, the name is
- * already resolved.
- *
- * @param data The sessionhandle pointer
- * @param in_connect is set to the next connection data pointer
- * @param async is set TRUE when an async DNS resolution is pending
- * @see Curl_setup_conn()
- *
- * *NOTE* this function assigns the conn->data pointer!
- */
-
-static CURLcode create_conn(struct SessionHandle *data,
-                            struct connectdata **in_connect,
-                            bool *async)
-{
-  CURLcode result=CURLE_OK;
-  struct connectdata *conn;
-  struct connectdata *conn_temp = NULL;
-  size_t urllen;
-  char user[MAX_CURL_USER_LENGTH];
-  char passwd[MAX_CURL_PASSWORD_LENGTH];
-  bool reuse;
-  char *proxy = NULL;
-  bool prot_missing = FALSE;
-
-  *async = FALSE;
-
-  /*************************************************************
-   * Check input data
-   *************************************************************/
-
-  if(!data->change.url)
-    return CURLE_URL_MALFORMAT;
-
-  /* First, split up the current URL in parts so that we can use the
-     parts for checking against the already present connections. In order
-     to not have to modify everything at once, we allocate a temporary
-     connection data struct and fill in for comparison purposes. */
-  conn = allocate_conn(data);
-
-  if(!conn)
-    return CURLE_OUT_OF_MEMORY;
-
-  /* We must set the return variable as soon as possible, so that our
-     parent can cleanup any possible allocs we may have done before
-     any failure */
-  *in_connect = conn;
-
-  /* This initing continues below, see the comment "Continue connectdata
-   * initialization here" */
-
-  /***********************************************************
-   * We need to allocate memory to store the path in. We get the size of the
-   * full URL to be sure, and we need to make it at least 256 bytes since
-   * other parts of the code will rely on this fact
-   ***********************************************************/
-#define LEAST_PATH_ALLOC 256
-  urllen=strlen(data->change.url);
-  if(urllen < LEAST_PATH_ALLOC)
-    urllen=LEAST_PATH_ALLOC;
-
-  /*
-   * We malloc() the buffers below urllen+2 to make room for 2 possibilities:
-   * 1 - an extra terminating zero
-   * 2 - an extra slash (in case a syntax like "www.host.com?moo" is used)
-   */
-
-  Curl_safefree(data->state.pathbuffer);
-  data->state.path = NULL;
-
-  data->state.pathbuffer = malloc(urllen+2);
-  if(NULL == data->state.pathbuffer)
-    return CURLE_OUT_OF_MEMORY; /* really bad error */
-  data->state.path = data->state.pathbuffer;
-
-  conn->host.rawalloc = malloc(urllen+2);
-  if(NULL == conn->host.rawalloc) {
-    Curl_safefree(data->state.pathbuffer);
-    data->state.path = NULL;
-    return CURLE_OUT_OF_MEMORY;
-  }
-
-  conn->host.name = conn->host.rawalloc;
-  conn->host.name[0] = 0;
-
-  result = parseurlandfillconn(data, conn, &prot_missing, user, passwd);
-  if(result != CURLE_OK)
-    return result;
-
-  /*************************************************************
-   * No protocol part in URL was used, add it!
-   *************************************************************/
-  if(prot_missing) {
-    /* We're guessing prefixes here and if we're told to use a proxy or if
-       we're gonna follow a Location: later or... then we need the protocol
-       part added so that we have a valid URL. */
-    char *reurl;
-
-    reurl = aprintf("%s://%s", conn->handler->scheme, data->change.url);
-
-    if(!reurl) {
-      Curl_safefree(proxy);
-      return CURLE_OUT_OF_MEMORY;
-    }
-
-    if(data->change.url_alloc) {
-      Curl_safefree(data->change.url);
-      data->change.url_alloc = FALSE;
-    }
-
-    data->change.url = reurl;
-    data->change.url_alloc = TRUE; /* free this later */
-  }
-
-  /*************************************************************
-   * If the protocol can't handle url query strings, then cut
-   * of the unhandable part
-   *************************************************************/
-  if((conn->given->flags&PROTOPT_NOURLQUERY)) {
-    char *path_q_sep = strchr(conn->data->state.path, '?');
-    if(path_q_sep) {
-      /* according to rfc3986, allow the query (?foo=bar)
-         also on protocols that can't handle it.
-
-         cut the string-part after '?'
-      */
-
-      /* terminate the string */
-      path_q_sep[0] = 0;
-    }
-  }
-
-#ifndef CURL_DISABLE_PROXY
-  /*************************************************************
-   * Extract the user and password from the authentication string
-   *************************************************************/
-  if(conn->bits.proxy_user_passwd) {
-    result = parse_proxy_auth(data, conn);
-    if(result != CURLE_OK)
-      return result;
-  }
-
-  /*************************************************************
-   * Detect what (if any) proxy to use
-   *************************************************************/
-  if(data->set.str[STRING_PROXY]) {
-    proxy = strdup(data->set.str[STRING_PROXY]);
-    /* if global proxy is set, this is it */
-    if(NULL == proxy) {
-      failf(data, "memory shortage");
-      return CURLE_OUT_OF_MEMORY;
-    }
-  }
-
-  if(data->set.str[STRING_NOPROXY] &&
-     check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY])) {
-    if(proxy) {
-      free(proxy);  /* proxy is in exception list */
-      proxy = NULL;
-    }
-  }
-  else if(!proxy)
-    proxy = detect_proxy(conn);
-
-  if(proxy && (!*proxy || (conn->handler->flags & PROTOPT_NONETWORK))) {
-    free(proxy);  /* Don't bother with an empty proxy string or if the
-                     protocol doesn't work with network */
-    proxy = NULL;
-  }
-
-  /***********************************************************************
-   * If this is supposed to use a proxy, we need to figure out the proxy host
-   * name, proxy type and port number, so that we can re-use an existing
-   * connection that may exist registered to the same proxy host.
-   ***********************************************************************/
-  if(proxy) {
-    result = parse_proxy(data, conn, proxy);
-
-    free(proxy); /* parse_proxy copies the proxy string */
-
-    if(result)
-      return result;
-
-    if((conn->proxytype == CURLPROXY_HTTP) ||
-       (conn->proxytype == CURLPROXY_HTTP_1_0)) {
-#ifdef CURL_DISABLE_HTTP
-      /* asking for a HTTP proxy is a bit funny when HTTP is disabled... */
-      return CURLE_UNSUPPORTED_PROTOCOL;
-#else
-      /* force this connection's protocol to become HTTP if not already
-         compatible - if it isn't tunneling through */
-      if(!(conn->handler->protocol & CURLPROTO_HTTP) &&
-         !conn->bits.tunnel_proxy)
-        conn->handler = &Curl_handler_http;
-
-      conn->bits.httpproxy = TRUE;
-#endif
-    }
-    else
-      conn->bits.httpproxy = FALSE; /* not a HTTP proxy */
-    conn->bits.proxy = TRUE;
-  }
-  else {
-    /* we aren't using the proxy after all... */
-    conn->bits.proxy = FALSE;
-    conn->bits.httpproxy = FALSE;
-    conn->bits.proxy_user_passwd = FALSE;
-    conn->bits.tunnel_proxy = FALSE;
-  }
-
-#endif /* CURL_DISABLE_PROXY */
-
-  /*************************************************************
-   * Setup internals depending on protocol. Needs to be done after
-   * we figured out what/if proxy to use.
-   *************************************************************/
-  result = setup_connection_internals(conn);
-  if(result != CURLE_OK) {
-    Curl_safefree(proxy);
-    return result;
-  }
-
-  conn->recv[FIRSTSOCKET] = Curl_recv_plain;
-  conn->send[FIRSTSOCKET] = Curl_send_plain;
-  conn->recv[SECONDARYSOCKET] = Curl_recv_plain;
-  conn->send[SECONDARYSOCKET] = Curl_send_plain;
-
-  /***********************************************************************
-   * file: is a special case in that it doesn't need a network connection
-   ***********************************************************************/
-#ifndef CURL_DISABLE_FILE
-  if(conn->handler->flags & PROTOPT_NONETWORK) {
-    bool done;
-    /* this is supposed to be the connect function so we better at least check
-       that the file is present here! */
-    DEBUGASSERT(conn->handler->connect_it);
-    result = conn->handler->connect_it(conn, &done);
-
-    /* Setup a "faked" transfer that'll do nothing */
-    if(CURLE_OK == result) {
-      conn->data = data;
-      conn->bits.tcpconnect[FIRSTSOCKET] = TRUE; /* we are "connected */
-
-      ConnectionStore(data, conn);
-
-      /*
-       * Setup whatever necessary for a resumed transfer
-       */
-      result = setup_range(data);
-      if(result) {
-        DEBUGASSERT(conn->handler->done);
-        /* we ignore the return code for the protocol-specific DONE */
-        (void)conn->handler->done(conn, result, FALSE);
-        return result;
-      }
-
-      Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
-                          -1, NULL); /* no upload */
-    }
-
-    return result;
-  }
-#endif
-
-  /*************************************************************
-   * If the protocol is using SSL and HTTP proxy is used, we set
-   * the tunnel_proxy bit.
-   *************************************************************/
-  if((conn->given->flags&PROTOPT_SSL) && conn->bits.httpproxy)
-    conn->bits.tunnel_proxy = TRUE;
-
-  /*************************************************************
-   * Figure out the remote port number and fix it in the URL
-   *************************************************************/
-  result = parse_remote_port(data, conn);
-  if(result != CURLE_OK)
-    return result;
-
-  /*************************************************************
-   * Check for an overridden user name and password, then set it
-   * for use
-   *************************************************************/
-  override_userpass(data, conn, user, passwd);
-  result = set_userpass(conn, user, passwd);
-  if(result != CURLE_OK)
-    return result;
-
-  /* Get a cloned copy of the SSL config situation stored in the
-     connection struct. But to get this going nicely, we must first make
-     sure that the strings in the master copy are pointing to the correct
-     strings in the session handle strings array!
-
-     Keep in mind that the pointers in the master copy are pointing to strings
-     that will be freed as part of the SessionHandle struct, but all cloned
-     copies will be separately allocated.
-  */
-  data->set.ssl.CApath = data->set.str[STRING_SSL_CAPATH];
-  data->set.ssl.CAfile = data->set.str[STRING_SSL_CAFILE];
-  data->set.ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE];
-  data->set.ssl.issuercert = data->set.str[STRING_SSL_ISSUERCERT];
-  data->set.ssl.random_file = data->set.str[STRING_SSL_RANDOM_FILE];
-  data->set.ssl.egdsocket = data->set.str[STRING_SSL_EGDSOCKET];
-  data->set.ssl.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST];
-#ifdef USE_TLS_SRP
-  data->set.ssl.username = data->set.str[STRING_TLSAUTH_USERNAME];
-  data->set.ssl.password = data->set.str[STRING_TLSAUTH_PASSWORD];
-#endif
-
-  if(!Curl_clone_ssl_config(&data->set.ssl, &conn->ssl_config))
-    return CURLE_OUT_OF_MEMORY;
-
-  /*************************************************************
-   * Check the current list of connections to see if we can
-   * re-use an already existing one or if we have to create a
-   * new one.
-   *************************************************************/
-
-  /* reuse_fresh is TRUE if we are told to use a new connection by force, but
-     we only acknowledge this option if this is not a re-used connection
-     already (which happens due to follow-location or during a HTTP
-     authentication phase). */
-  if(data->set.reuse_fresh && !data->state.this_is_a_follow)
-    reuse = FALSE;
-  else
-    reuse = ConnectionExists(data, conn, &conn_temp);
-
-  if(reuse) {
-    /*
-     * We already have a connection for this, we got the former connection
-     * in the conn_temp variable and thus we need to cleanup the one we
-     * just allocated before we can move along and use the previously
-     * existing one.
-     */
-    reuse_conn(conn, conn_temp);
-    free(conn);          /* we don't need this anymore */
-    conn = conn_temp;
-    *in_connect = conn;
-
-    /* set a pointer to the hostname we display */
-    fix_hostname(data, conn, &conn->host);
-
-    infof(data, "Re-using existing connection! (#%ld) with host %s\n",
-          conn->connection_id,
-          conn->proxy.name?conn->proxy.dispname:conn->host.dispname);
-  }
-  else {
-    /*
-     * This is a brand new connection, so let's store it in the connection
-     * cache of ours!
-     */
-    ConnectionStore(data, conn);
-  }
-
-  /* Setup and init stuff before DO starts, in preparing for the transfer. */
-  do_init(conn);
-
-  /*
-   * Setup whatever necessary for a resumed transfer
-   */
-  result = setup_range(data);
-  if(result)
-    return result;
-
-  /* Continue connectdata initialization here. */
-
-  /*
-   * Inherit the proper values from the urldata struct AFTER we have arranged
-   * the persistent connection stuff
-   */
-  conn->fread_func = data->set.fread_func;
-  conn->fread_in = data->set.in;
-  conn->seek_func = data->set.seek_func;
-  conn->seek_client = data->set.seek_client;
-
-  /*************************************************************
-   * Resolve the address of the server or proxy
-   *************************************************************/
-  result = resolve_server(data, conn, async);
-
-  return result;
-}
-
-/* Curl_setup_conn() is called after the name resolve initiated in
- * create_conn() is all done.
- *
- * Curl_setup_conn() also handles reused connections
- *
- * conn->data MUST already have been setup fine (in create_conn)
- */
-
-CURLcode Curl_setup_conn(struct connectdata *conn,
-                         bool *protocol_done)
-{
-  CURLcode result = CURLE_OK;
-  struct SessionHandle *data = conn->data;
-
-  Curl_pgrsTime(data, TIMER_NAMELOOKUP);
-
-  if(conn->handler->flags & PROTOPT_NONETWORK) {
-    /* nothing to setup when not using a network */
-    *protocol_done = TRUE;
-    return result;
-  }
-  *protocol_done = FALSE; /* default to not done */
-
-  /* set proxy_connect_closed to false unconditionally already here since it
-     is used strictly to provide extra information to a parent function in the
-     case of proxy CONNECT failures and we must make sure we don't have it
-     lingering set from a previous invoke */
-  conn->bits.proxy_connect_closed = FALSE;
-
-  /*
-   * Set user-agent. Used for HTTP, but since we can attempt to tunnel
-   * basically anything through a http proxy we can't limit this based on
-   * protocol.
-   */
-  if(data->set.str[STRING_USERAGENT]) {
-    Curl_safefree(conn->allocptr.uagent);
-    conn->allocptr.uagent =
-      aprintf("User-Agent: %s\r\n", data->set.str[STRING_USERAGENT]);
-    if(!conn->allocptr.uagent)
-      return CURLE_OUT_OF_MEMORY;
-  }
-
-  data->req.headerbytecount = 0;
-
-#ifdef CURL_DO_LINEEND_CONV
-  data->state.crlf_conversions = 0; /* reset CRLF conversion counter */
-#endif /* CURL_DO_LINEEND_CONV */
-
-  for(;;) {
-    /* loop for CURL_SERVER_CLOSED_CONNECTION */
-
-    if(CURL_SOCKET_BAD == conn->sock[FIRSTSOCKET]) {
-      /* Try to connect only if not already connected */
-      bool connected = FALSE;
-
-      result = ConnectPlease(data, conn, &connected);
-
-      if(result && !conn->ip_addr) {
-        /* transport connection failure not related with authentication */
-        conn->bits.tcpconnect[FIRSTSOCKET] = FALSE;
-        return result;
-      }
-
-      if(connected) {
-        result = Curl_protocol_connect(conn, protocol_done);
-        if(CURLE_OK == result)
-          conn->bits.tcpconnect[FIRSTSOCKET] = TRUE;
-      }
-      else
-        conn->bits.tcpconnect[FIRSTSOCKET] = FALSE;
-
-      /* if the connection was closed by the server while exchanging
-         authentication informations, retry with the new set
-         authentication information */
-      if(conn->bits.proxy_connect_closed) {
-        /* reset the error buffer */
-        if(data->set.errorbuffer)
-          data->set.errorbuffer[0] = '\0';
-        data->state.errorbuf = FALSE;
-        continue;
-      }
-
-      if(CURLE_OK != result)
-        return result;
-    }
-    else {
-      Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */
-      Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */
-      conn->bits.tcpconnect[FIRSTSOCKET] = TRUE;
-      *protocol_done = TRUE;
-      Curl_verboseconnect(conn);
-      Curl_updateconninfo(conn, conn->sock[FIRSTSOCKET]);
-    }
-    /* Stop the loop now */
-    break;
-  }
-
-  conn->now = Curl_tvnow(); /* time this *after* the connect is done, we
-                               set this here perhaps a second time */
-
-#ifdef __EMX__
-  /*
-   * This check is quite a hack. We're calling _fsetmode to fix the problem
-   * with fwrite converting newline characters (you get mangled text files,
-   * and corrupted binary files when you download to stdout and redirect it to
-   * a file).
-   */
-
-  if((data->set.out)->_handle == NULL) {
-    _fsetmode(stdout, "b");
-  }
-#endif
-
-  return result;
-}
-
-CURLcode Curl_connect(struct SessionHandle *data,
-                      struct connectdata **in_connect,
-                      bool *asyncp,
-                      bool *protocol_done)
-{
-  CURLcode code;
-
-  *asyncp = FALSE; /* assume synchronous resolves by default */
-
-  /* call the stuff that needs to be called */
-  code = create_conn(data, in_connect, asyncp);
-
-  if(CURLE_OK == code) {
-    /* no error */
-    if((*in_connect)->send_pipe->size || (*in_connect)->recv_pipe->size)
-      /* pipelining */
-      *protocol_done = TRUE;
-    else if(!*asyncp) {
-      /* DNS resolution is done: that's either because this is a reused
-         connection, in which case DNS was unnecessary, or because DNS
-         really did finish already (synch resolver/fast async resolve) */
-      code = Curl_setup_conn(*in_connect, protocol_done);
-    }
-  }
-
-  if(code && *in_connect) {
-    /* We're not allowed to return failure with memory left allocated
-       in the connectdata struct, free those here */
-    Curl_disconnect(*in_connect, FALSE); /* close the connection */
-    *in_connect = NULL;           /* return a NULL */
-  }
-
-  return code;
-}
-
-CURLcode Curl_done(struct connectdata **connp,
-                   CURLcode status,  /* an error if this is called after an
-                                        error was detected */
-                   bool premature)
-{
-  CURLcode result;
-  struct connectdata *conn;
-  struct SessionHandle *data;
-
-  DEBUGASSERT(*connp);
-
-  conn = *connp;
-  data = conn->data;
-
-  if(conn->bits.done)
-    /* Stop if Curl_done() has already been called */
-    return CURLE_OK;
-
-  Curl_getoff_all_pipelines(data, conn);
-
-  if((conn->send_pipe->size + conn->recv_pipe->size != 0 &&
-      !data->set.reuse_forbid &&
-      !conn->bits.close))
-    /* Stop if pipeline is not empty and we do not have to close
-       connection. */
-    return CURLE_OK;
-
-  conn->bits.done = TRUE; /* called just now! */
-
-  /* Cleanup possible redirect junk */
-  if(data->req.newurl) {
-    free(data->req.newurl);
-    data->req.newurl = NULL;
-  }
-  if(data->req.location) {
-    free(data->req.location);
-    data->req.location = NULL;
-  }
-
-  Curl_resolver_cancel(conn);
-
-  if(conn->dns_entry) {
-    Curl_resolv_unlock(data, conn->dns_entry); /* done with this */
-    conn->dns_entry = NULL;
-  }
-
-  /* this calls the protocol-specific function pointer previously set */
-  if(conn->handler->done)
-    result = conn->handler->done(conn, status, premature);
-  else
-    result = CURLE_OK;
-
-  if(Curl_pgrsDone(conn) && !result)
-    result = CURLE_ABORTED_BY_CALLBACK;
-
-  /* if the transfer was completed in a paused state there can be buffered
-     data left to write and then kill */
-  if(data->state.tempwrite) {
-    free(data->state.tempwrite);
-    data->state.tempwrite = NULL;
-  }
-
-  /* if data->set.reuse_forbid is TRUE, it means the libcurl client has
-     forced us to close this no matter what we think.
-
-     if conn->bits.close is TRUE, it means that the connection should be
-     closed in spite of all our efforts to be nice, due to protocol
-     restrictions in our or the server's end
-
-     if premature is TRUE, it means this connection was said to be DONE before
-     the entire request operation is complete and thus we can't know in what
-     state it is for re-using, so we're forced to close it. In a perfect world
-     we can add code that keep track of if we really must close it here or not,
-     but currently we have no such detail knowledge.
-
-     connection_id == -1 here means that the connection has not been added
-     to the connection cache (OOM) and thus we must disconnect it here.
-  */
-  if(data->set.reuse_forbid || conn->bits.close || premature ||
-     (-1 == conn->connection_id)) {
-    CURLcode res2 = Curl_disconnect(conn, premature); /* close connection */
-
-    /* If we had an error already, make sure we return that one. But
-       if we got a new error, return that. */
-    if(!result && res2)
-      result = res2;
-  }
-  else {
-    ConnectionDone(conn); /* the connection is no longer in use */
-
-    /* remember the most recently used connection */
-    data->state.lastconnect = conn;
-
-    infof(data, "Connection #%ld to host %s left intact\n",
-          conn->connection_id,
-          conn->bits.httpproxy?conn->proxy.dispname:conn->host.dispname);
-  }
-
-  *connp = NULL; /* to make the caller of this function better detect that
-                    this was either closed or handed over to the connection
-                    cache here, and therefore cannot be used from this point on
-                 */
-
-  return result;
-}
-
-/*
- * do_init() inits the readwrite session. This is inited each time (in the DO
- * function before the protocol-specific DO functions are invoked) for a
- * transfer, sometimes multiple times on the same SessionHandle. Make sure
- * nothing in here depends on stuff that are setup dynamically for the
- * transfer.
- */
-
-static CURLcode do_init(struct connectdata *conn)
-{
-  struct SessionHandle *data = conn->data;
-  struct SingleRequest *k = &data->req;
-
-  conn->bits.done = FALSE; /* Curl_done() is not called yet */
-  conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to use */
-  data->state.expect100header = FALSE;
-
-  if(data->set.opt_no_body)
-    /* in HTTP lingo, no body means using the HEAD request... */
-    data->set.httpreq = HTTPREQ_HEAD;
-  else if(HTTPREQ_HEAD == data->set.httpreq)
-    /* ... but if unset there really is no perfect method that is the
-       "opposite" of HEAD but in reality most people probably think GET
-       then. The important thing is that we can't let it remain HEAD if the
-       opt_no_body is set FALSE since then we'll behave wrong when getting
-       HTTP. */
-    data->set.httpreq = HTTPREQ_GET;
-
-  /* NB: the content encoding software depends on this initialization */
-  Curl_easy_initHandleData(data);
-
-  k->start = Curl_tvnow(); /* start time */
-  k->now = k->start;   /* current time is now */
-  k->header = TRUE; /* assume header */
-
-  k->bytecount = 0;
-
-  k->buf = data->state.buffer;
-  k->uploadbuf = data->state.uploadbuffer;
-  k->hbufp = data->state.headerbuff;
-  k->ignorebody=FALSE;
-
-  Curl_speedinit(data);
-
-  Curl_pgrsSetUploadCounter(data, 0);
-  Curl_pgrsSetDownloadCounter(data, 0);
-
-  return CURLE_OK;
-}
-
-/*
- * do_complete is called when the DO actions are complete.
- *
- * We init chunking and trailer bits to their default values here immediately
- * before receiving any header data for the current request in the pipeline.
- */
-static void do_complete(struct connectdata *conn)
-{
-  conn->data->req.chunk=FALSE;
-  conn->data->req.maxfd = (conn->sockfd>conn->writesockfd?
-                           conn->sockfd:conn->writesockfd)+1;
-  Curl_pgrsTime(conn->data, TIMER_PRETRANSFER);
-}
-
-CURLcode Curl_do(struct connectdata **connp, bool *done)
-{
-  CURLcode result=CURLE_OK;
-  struct connectdata *conn = *connp;
-  struct SessionHandle *data = conn->data;
-
-  if(conn->handler->do_it) {
-    /* generic protocol-specific function pointer set in curl_connect() */
-    result = conn->handler->do_it(conn, done);
-
-    /* This was formerly done in curl_transfer.c, but we better do it here */
-    if((CURLE_SEND_ERROR == result) && conn->bits.reuse) {
-      /*
-       * If the connection is using an easy handle, call reconnect
-       * to re-establish the connection.  Otherwise, let the multi logic
-       * figure out how to re-establish the connection.
-       */
-      if(!data->multi) {
-        result = Curl_reconnect_request(connp);
-
-        if(result == CURLE_OK) {
-          /* ... finally back to actually retry the DO phase */
-          conn = *connp; /* re-assign conn since Curl_reconnect_request
-                            creates a new connection */
-          result = conn->handler->do_it(conn, done);
-        }
-      }
-      else
-        return result;
-    }
-
-    if((result == CURLE_OK) && *done)
-      /* do_complete must be called after the protocol-specific DO function */
-      do_complete(conn);
-  }
-  return result;
-}
-
-/*
- * Curl_do_more() is called during the DO_MORE multi state. It is basically a
- * second stage DO state which (wrongly) was introduced to support FTP's
- * second connection.
- *
- * TODO: A future libcurl should be able to work away this state.
- *
- */
-
-CURLcode Curl_do_more(struct connectdata *conn, bool *completed)
-{
-  CURLcode result=CURLE_OK;
-
-  *completed = FALSE;
-
-  if(conn->handler->do_more)
-    result = conn->handler->do_more(conn, completed);
-
-  if(!result && *completed)
-    /* do_complete must be called after the protocol-specific DO function */
-    do_complete(conn);
-
-  return result;
-}
-
-/* Called on connect, and if there's already a protocol-specific struct
-   allocated for a different connection, this frees it that it can be setup
-   properly later on. */
-void Curl_reset_reqproto(struct connectdata *conn)
-{
-  struct SessionHandle *data = conn->data;
-  if(data->state.proto.generic && data->state.current_conn != conn) {
-    free(data->state.proto.generic);
-    data->state.proto.generic = NULL;
-  }
-  data->state.current_conn = conn;
-}
diff --git a/lib/version.c b/lib/version.c
deleted file mode 100644 (file)
index fe1f736..0000000
+++ /dev/null
@@ -1,343 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#include <curl/curl.h>
-#include "curl_urldata.h"
-#include "curl_sslgen.h"
-
-#define _MPRINTF_REPLACE /* use the internal *printf() functions */
-#include <curl/mprintf.h>
-
-#ifdef USE_ARES
-#  if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
-     (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__))
-#    define CARES_STATICLIB
-#  endif
-#  include <ares.h>
-#endif
-
-#ifdef USE_LIBIDN
-#include <stringprep.h>
-#endif
-
-#if defined(HAVE_ICONV) && defined(CURL_DOES_CONVERSIONS)
-#include <iconv.h>
-#endif
-
-#ifdef USE_LIBRTMP
-#include <librtmp/rtmp.h>
-#endif
-
-#ifdef USE_LIBSSH2
-#include <libssh2.h>
-#endif
-
-#ifdef HAVE_LIBSSH2_VERSION
-/* get it run-time if possible */
-#define CURL_LIBSSH2_VERSION libssh2_version(0)
-#else
-/* use build-time if run-time not possible */
-#define CURL_LIBSSH2_VERSION LIBSSH2_VERSION
-#endif
-
-char *curl_version(void)
-{
-  static char version[200];
-  char *ptr = version;
-  size_t len;
-  size_t left = sizeof(version);
-
-  strcpy(ptr, LIBCURL_NAME "/" LIBCURL_VERSION);
-  len = strlen(ptr);
-  left -= len;
-  ptr += len;
-
-  if(left > 1) {
-    len = Curl_ssl_version(ptr + 1, left - 1);
-
-    if(len > 0) {
-      *ptr = ' ';
-      left -= ++len;
-      ptr += len;
-    }
-  }
-
-#ifdef HAVE_LIBZ
-  len = snprintf(ptr, left, " zlib/%s", zlibVersion());
-  left -= len;
-  ptr += len;
-#endif
-#ifdef USE_ARES
-  /* this function is only present in c-ares, not in the original ares */
-  len = snprintf(ptr, left, " c-ares/%s", ares_version(NULL));
-  left -= len;
-  ptr += len;
-#endif
-#ifdef USE_LIBIDN
-  if(stringprep_check_version(LIBIDN_REQUIRED_VERSION)) {
-    len = snprintf(ptr, left, " libidn/%s", stringprep_check_version(NULL));
-    left -= len;
-    ptr += len;
-  }
-#endif
-#ifdef USE_WIN32_IDN
-  len = snprintf(ptr, left, " WinIDN");
-  left -= len;
-  ptr += len;
-#endif
-#if defined(HAVE_ICONV) && defined(CURL_DOES_CONVERSIONS)
-#ifdef _LIBICONV_VERSION
-  len = snprintf(ptr, left, " iconv/%d.%d",
-                 _LIBICONV_VERSION >> 8, _LIBICONV_VERSION & 255);
-#else
-  /* version unknown */
-  len = snprintf(ptr, left, " iconv");
-#endif /* _LIBICONV_VERSION */
-  left -= len;
-  ptr += len;
-#endif
-#ifdef USE_LIBSSH2
-  len = snprintf(ptr, left, " libssh2/%s", CURL_LIBSSH2_VERSION);
-  left -= len;
-  ptr += len;
-#endif
-#ifdef USE_LIBRTMP
-  {
-    char suff[2];
-    if(RTMP_LIB_VERSION & 0xff) {
-      suff[0] = (RTMP_LIB_VERSION & 0xff) + 'a' - 1;
-      suff[1] = '\0';
-    }
-    else
-      suff[0] = '\0';
-
-    snprintf(ptr, left, " librtmp/%d.%d%s",
-             RTMP_LIB_VERSION >> 16, (RTMP_LIB_VERSION >> 8) & 0xff,
-             suff);
-/*
-  If another lib version is added below this one, this code would
-  also have to do:
-
-    len = what snprintf() returned
-
-    left -= len;
-    ptr += len;
-*/
-  }
-#endif
-
-  return version;
-}
-
-/* data for curl_version_info
-
-   Keep the list sorted alphabetically. It is also written so that each
-   protocol line has its own #if line to make things easier on the eye.
- */
-
-static const char * const protocols[] = {
-#ifndef CURL_DISABLE_DICT
-  "dict",
-#endif
-#ifndef CURL_DISABLE_FILE
-  "file",
-#endif
-#ifndef CURL_DISABLE_FTP
-  "ftp",
-#endif
-#if defined(USE_SSL) && !defined(CURL_DISABLE_FTP)
-  "ftps",
-#endif
-#ifndef CURL_DISABLE_GOPHER
-  "gopher",
-#endif
-#ifndef CURL_DISABLE_HTTP
-  "http",
-#endif
-#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP)
-  "https",
-#endif
-#ifndef CURL_DISABLE_IMAP
-  "imap",
-#endif
-#if defined(USE_SSL) && !defined(CURL_DISABLE_IMAP)
-  "imaps",
-#endif
-#ifndef CURL_DISABLE_LDAP
-  "ldap",
-#if !defined(CURL_DISABLE_LDAPS) && \
-    ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \
-     (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL)))
-  "ldaps",
-#endif
-#endif
-#ifndef CURL_DISABLE_POP3
-  "pop3",
-#endif
-#if defined(USE_SSL) && !defined(CURL_DISABLE_POP3)
-  "pop3s",
-#endif
-#ifdef USE_LIBRTMP
-  "rtmp",
-#endif
-#ifndef CURL_DISABLE_RTSP
-  "rtsp",
-#endif
-#ifdef USE_LIBSSH2
-  "scp",
-#endif
-#ifdef USE_LIBSSH2
-  "sftp",
-#endif
-#ifndef CURL_DISABLE_SMTP
-  "smtp",
-#endif
-#if defined(USE_SSL) && !defined(CURL_DISABLE_SMTP)
-  "smtps",
-#endif
-#ifndef CURL_DISABLE_TELNET
-  "telnet",
-#endif
-#ifndef CURL_DISABLE_TFTP
-  "tftp",
-#endif
-
-  NULL
-};
-
-static curl_version_info_data version_info = {
-  CURLVERSION_NOW,
-  LIBCURL_VERSION,
-  LIBCURL_VERSION_NUM,
-  OS, /* as found by configure or set by hand at build-time */
-  0 /* features is 0 by default */
-#ifdef ENABLE_IPV6
-  | CURL_VERSION_IPV6
-#endif
-#ifdef HAVE_KRB4
-  | CURL_VERSION_KERBEROS4
-#endif
-#ifdef USE_SSL
-  | CURL_VERSION_SSL
-#endif
-#ifdef USE_NTLM
-  | CURL_VERSION_NTLM
-#endif
-#if defined(USE_NTLM) && defined(NTLM_WB_ENABLED)
-  | CURL_VERSION_NTLM_WB
-#endif
-#ifdef USE_WINDOWS_SSPI
-  | CURL_VERSION_SSPI
-#endif
-#ifdef HAVE_LIBZ
-  | CURL_VERSION_LIBZ
-#endif
-#ifdef USE_HTTP_NEGOTIATE
-  | CURL_VERSION_GSSNEGOTIATE
-#endif
-#ifdef DEBUGBUILD
-  | CURL_VERSION_DEBUG
-#endif
-#ifdef CURLDEBUG
-  | CURL_VERSION_CURLDEBUG
-#endif
-#ifdef CURLRES_ASYNCH
-  | CURL_VERSION_ASYNCHDNS
-#endif
-#ifdef HAVE_SPNEGO
-  | CURL_VERSION_SPNEGO
-#endif
-#if (CURL_SIZEOF_CURL_OFF_T > 4) && \
-    ( (SIZEOF_OFF_T > 4) || defined(USE_WIN32_LARGE_FILES) )
-  | CURL_VERSION_LARGEFILE
-#endif
-#if defined(CURL_DOES_CONVERSIONS)
-  | CURL_VERSION_CONV
-#endif
-#if defined(USE_TLS_SRP)
-  | CURL_VERSION_TLSAUTH_SRP
-#endif
-  ,
-  NULL, /* ssl_version */
-  0,    /* ssl_version_num, this is kept at zero */
-  NULL, /* zlib_version */
-  protocols,
-  NULL, /* c-ares version */
-  0,    /* c-ares version numerical */
-  NULL, /* libidn version */
-  0,    /* iconv version */
-  NULL, /* ssh lib version */
-};
-
-curl_version_info_data *curl_version_info(CURLversion stamp)
-{
-#ifdef USE_LIBSSH2
-  static char ssh_buffer[80];
-#endif
-
-#ifdef USE_SSL
-  static char ssl_buffer[80];
-  Curl_ssl_version(ssl_buffer, sizeof(ssl_buffer));
-  version_info.ssl_version = ssl_buffer;
-#endif
-
-#ifdef HAVE_LIBZ
-  version_info.libz_version = zlibVersion();
-  /* libz left NULL if non-existing */
-#endif
-#ifdef USE_ARES
-  {
-    int aresnum;
-    version_info.ares = ares_version(&aresnum);
-    version_info.ares_num = aresnum;
-  }
-#endif
-#ifdef USE_LIBIDN
-  /* This returns a version string if we use the given version or later,
-     otherwise it returns NULL */
-  version_info.libidn = stringprep_check_version(LIBIDN_REQUIRED_VERSION);
-  if(version_info.libidn)
-    version_info.features |= CURL_VERSION_IDN;
-#elif defined(USE_WIN32_IDN)
-  version_info.features |= CURL_VERSION_IDN;
-#endif
-
-#if defined(HAVE_ICONV) && defined(CURL_DOES_CONVERSIONS)
-#ifdef _LIBICONV_VERSION
-  version_info.iconv_ver_num = _LIBICONV_VERSION;
-#else
-  /* version unknown */
-  version_info.iconv_ver_num = -1;
-#endif /* _LIBICONV_VERSION */
-#endif
-
-#ifdef USE_LIBSSH2
-  snprintf(ssh_buffer, sizeof(ssh_buffer), "libssh2/%s", LIBSSH2_VERSION);
-  version_info.libssh_version = ssh_buffer;
-#endif
-
-  (void)stamp; /* avoid compiler warnings, we don't use this */
-
-  return &version_info;
-}
diff --git a/lib/warnless.c b/lib/warnless.c
deleted file mode 100644 (file)
index 30cdbe6..0000000
+++ /dev/null
@@ -1,431 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#if defined(__INTEL_COMPILER) && defined(__unix__)
-
-#ifdef HAVE_NETINET_IN_H
-#  include <netinet/in.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#  include <arpa/inet.h>
-#endif
-
-#endif /* __INTEL_COMPILER && __unix__ */
-
-#define BUILDING_WARNLESS_C 1
-
-#include "curl_warnless.h"
-
-#define CURL_MASK_SCHAR  0x7F
-#define CURL_MASK_UCHAR  0xFF
-
-#if (SIZEOF_SHORT == 2)
-#  define CURL_MASK_SSHORT  0x7FFF
-#  define CURL_MASK_USHORT  0xFFFF
-#elif (SIZEOF_SHORT == 4)
-#  define CURL_MASK_SSHORT  0x7FFFFFFF
-#  define CURL_MASK_USHORT  0xFFFFFFFF
-#elif (SIZEOF_SHORT == 8)
-#  define CURL_MASK_SSHORT  0x7FFFFFFFFFFFFFFF
-#  define CURL_MASK_USHORT  0xFFFFFFFFFFFFFFFF
-#else
-#  error "SIZEOF_SHORT not defined"
-#endif
-
-#if (SIZEOF_INT == 2)
-#  define CURL_MASK_SINT  0x7FFF
-#  define CURL_MASK_UINT  0xFFFF
-#elif (SIZEOF_INT == 4)
-#  define CURL_MASK_SINT  0x7FFFFFFF
-#  define CURL_MASK_UINT  0xFFFFFFFF
-#elif (SIZEOF_INT == 8)
-#  define CURL_MASK_SINT  0x7FFFFFFFFFFFFFFF
-#  define CURL_MASK_UINT  0xFFFFFFFFFFFFFFFF
-#elif (SIZEOF_INT == 16)
-#  define CURL_MASK_SINT  0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
-#  define CURL_MASK_UINT  0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
-#else
-#  error "SIZEOF_INT not defined"
-#endif
-
-#if (CURL_SIZEOF_LONG == 2)
-#  define CURL_MASK_SLONG  0x7FFFL
-#  define CURL_MASK_ULONG  0xFFFFUL
-#elif (CURL_SIZEOF_LONG == 4)
-#  define CURL_MASK_SLONG  0x7FFFFFFFL
-#  define CURL_MASK_ULONG  0xFFFFFFFFUL
-#elif (CURL_SIZEOF_LONG == 8)
-#  define CURL_MASK_SLONG  0x7FFFFFFFFFFFFFFFL
-#  define CURL_MASK_ULONG  0xFFFFFFFFFFFFFFFFUL
-#elif (CURL_SIZEOF_LONG == 16)
-#  define CURL_MASK_SLONG  0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFL
-#  define CURL_MASK_ULONG  0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFUL
-#else
-#  error "CURL_SIZEOF_LONG not defined"
-#endif
-
-#if (CURL_SIZEOF_CURL_OFF_T == 2)
-#  define CURL_MASK_SCOFFT  CURL_OFF_T_C(0x7FFF)
-#  define CURL_MASK_UCOFFT  CURL_OFF_TU_C(0xFFFF)
-#elif (CURL_SIZEOF_CURL_OFF_T == 4)
-#  define CURL_MASK_SCOFFT  CURL_OFF_T_C(0x7FFFFFFF)
-#  define CURL_MASK_UCOFFT  CURL_OFF_TU_C(0xFFFFFFFF)
-#elif (CURL_SIZEOF_CURL_OFF_T == 8)
-#  define CURL_MASK_SCOFFT  CURL_OFF_T_C(0x7FFFFFFFFFFFFFFF)
-#  define CURL_MASK_UCOFFT  CURL_OFF_TU_C(0xFFFFFFFFFFFFFFFF)
-#elif (CURL_SIZEOF_CURL_OFF_T == 16)
-#  define CURL_MASK_SCOFFT  CURL_OFF_T_C(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
-#  define CURL_MASK_UCOFFT  CURL_OFF_TU_C(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
-#else
-#  error "CURL_SIZEOF_CURL_OFF_T not defined"
-#endif
-
-#if (SIZEOF_SIZE_T == SIZEOF_SHORT)
-#  define CURL_MASK_SSIZE_T  CURL_MASK_SSHORT
-#  define CURL_MASK_USIZE_T  CURL_MASK_USHORT
-#elif (SIZEOF_SIZE_T == SIZEOF_INT)
-#  define CURL_MASK_SSIZE_T  CURL_MASK_SINT
-#  define CURL_MASK_USIZE_T  CURL_MASK_UINT
-#elif (SIZEOF_SIZE_T == CURL_SIZEOF_LONG)
-#  define CURL_MASK_SSIZE_T  CURL_MASK_SLONG
-#  define CURL_MASK_USIZE_T  CURL_MASK_ULONG
-#elif (SIZEOF_SIZE_T == CURL_SIZEOF_CURL_OFF_T)
-#  define CURL_MASK_SSIZE_T  CURL_MASK_SCOFFT
-#  define CURL_MASK_USIZE_T  CURL_MASK_UCOFFT
-#else
-#  error "SIZEOF_SIZE_T not defined"
-#endif
-
-/*
-** unsigned long to unsigned short
-*/
-
-unsigned short curlx_ultous(unsigned long ulnum)
-{
-#ifdef __INTEL_COMPILER
-#  pragma warning(push)
-#  pragma warning(disable:810) /* conversion may lose significant bits */
-#endif
-
-  DEBUGASSERT(ulnum <= (unsigned long) CURL_MASK_USHORT);
-  return (unsigned short)(ulnum & (unsigned long) CURL_MASK_USHORT);
-
-#ifdef __INTEL_COMPILER
-#  pragma warning(pop)
-#endif
-}
-
-/*
-** unsigned long to unsigned char
-*/
-
-unsigned char curlx_ultouc(unsigned long ulnum)
-{
-#ifdef __INTEL_COMPILER
-#  pragma warning(push)
-#  pragma warning(disable:810) /* conversion may lose significant bits */
-#endif
-
-  DEBUGASSERT(ulnum <= (unsigned long) CURL_MASK_UCHAR);
-  return (unsigned char)(ulnum & (unsigned long) CURL_MASK_UCHAR);
-
-#ifdef __INTEL_COMPILER
-#  pragma warning(pop)
-#endif
-}
-
-/*
-** unsigned long to signed int
-*/
-
-int curlx_ultosi(unsigned long ulnum)
-{
-#ifdef __INTEL_COMPILER
-#  pragma warning(push)
-#  pragma warning(disable:810) /* conversion may lose significant bits */
-#endif
-
-  DEBUGASSERT(ulnum <= (unsigned long) CURL_MASK_SINT);
-  return (int)(ulnum & (unsigned long) CURL_MASK_SINT);
-
-#ifdef __INTEL_COMPILER
-#  pragma warning(pop)
-#endif
-}
-
-/*
-** unsigned size_t to signed int
-*/
-
-int curlx_uztosi(size_t uznum)
-{
-#ifdef __INTEL_COMPILER
-#  pragma warning(push)
-#  pragma warning(disable:810) /* conversion may lose significant bits */
-#endif
-
-  DEBUGASSERT(uznum <= (size_t) CURL_MASK_SINT);
-  return (int)(uznum & (size_t) CURL_MASK_SINT);
-
-#ifdef __INTEL_COMPILER
-#  pragma warning(pop)
-#endif
-}
-
-/*
-** unsigned size_t to unsigned long
-*/
-
-unsigned long curlx_uztoul(size_t uznum)
-{
-#ifdef __INTEL_COMPILER
-# pragma warning(push)
-# pragma warning(disable:810) /* conversion may lose significant bits */
-#endif
-
-#if (CURL_SIZEOF_LONG < SIZEOF_SIZE_T)
-  DEBUGASSERT(uznum <= (size_t) CURL_MASK_ULONG);
-#endif
-  return (unsigned long)(uznum & (size_t) CURL_MASK_ULONG);
-
-#ifdef __INTEL_COMPILER
-# pragma warning(pop)
-#endif
-}
-
-/*
-** unsigned size_t to unsigned int
-*/
-
-unsigned int curlx_uztoui(size_t uznum)
-{
-#ifdef __INTEL_COMPILER
-# pragma warning(push)
-# pragma warning(disable:810) /* conversion may lose significant bits */
-#endif
-
-#if (SIZEOF_INT < SIZEOF_SIZE_T)
-  DEBUGASSERT(uznum <= (size_t) CURL_MASK_UINT);
-#endif
-  return (unsigned int)(uznum & (size_t) CURL_MASK_UINT);
-
-#ifdef __INTEL_COMPILER
-# pragma warning(pop)
-#endif
-}
-
-/*
-** signed long to signed int
-*/
-
-int curlx_sltosi(long slnum)
-{
-#ifdef __INTEL_COMPILER
-#  pragma warning(push)
-#  pragma warning(disable:810) /* conversion may lose significant bits */
-#endif
-
-  DEBUGASSERT(slnum >= 0);
-#if (SIZEOF_INT < CURL_SIZEOF_LONG)
-  DEBUGASSERT((unsigned long) slnum <= (unsigned long) CURL_MASK_SINT);
-#endif
-  return (int)(slnum & (long) CURL_MASK_SINT);
-
-#ifdef __INTEL_COMPILER
-#  pragma warning(pop)
-#endif
-}
-
-/*
-** signed long to unsigned int
-*/
-
-unsigned int curlx_sltoui(long slnum)
-{
-#ifdef __INTEL_COMPILER
-#  pragma warning(push)
-#  pragma warning(disable:810) /* conversion may lose significant bits */
-#endif
-
-  DEBUGASSERT(slnum >= 0);
-#if (SIZEOF_INT < CURL_SIZEOF_LONG)
-  DEBUGASSERT((unsigned long) slnum <= (unsigned long) CURL_MASK_UINT);
-#endif
-  return (unsigned int)(slnum & (long) CURL_MASK_UINT);
-
-#ifdef __INTEL_COMPILER
-#  pragma warning(pop)
-#endif
-}
-
-/*
-** signed long to unsigned short
-*/
-
-unsigned short curlx_sltous(long slnum)
-{
-#ifdef __INTEL_COMPILER
-#  pragma warning(push)
-#  pragma warning(disable:810) /* conversion may lose significant bits */
-#endif
-
-  DEBUGASSERT(slnum >= 0);
-  DEBUGASSERT((unsigned long) slnum <= (unsigned long) CURL_MASK_USHORT);
-  return (unsigned short)(slnum & (long) CURL_MASK_USHORT);
-
-#ifdef __INTEL_COMPILER
-#  pragma warning(pop)
-#endif
-}
-
-/*
-** unsigned size_t to signed ssize_t
-*/
-
-ssize_t curlx_uztosz(size_t uznum)
-{
-#ifdef __INTEL_COMPILER
-#  pragma warning(push)
-#  pragma warning(disable:810) /* conversion may lose significant bits */
-#endif
-
-  DEBUGASSERT(uznum <= (size_t) CURL_MASK_SSIZE_T);
-  return (ssize_t)(uznum & (size_t) CURL_MASK_SSIZE_T);
-
-#ifdef __INTEL_COMPILER
-#  pragma warning(pop)
-#endif
-}
-
-/*
-** signed curl_off_t to unsigned size_t
-*/
-
-size_t curlx_sotouz(curl_off_t sonum)
-{
-#ifdef __INTEL_COMPILER
-#  pragma warning(push)
-#  pragma warning(disable:810) /* conversion may lose significant bits */
-#endif
-
-  DEBUGASSERT(sonum >= 0);
-  return (size_t)(sonum & (curl_off_t) CURL_MASK_USIZE_T);
-
-#ifdef __INTEL_COMPILER
-#  pragma warning(pop)
-#endif
-}
-
-/*
-** signed ssize_t to signed int
-*/
-
-int curlx_sztosi(ssize_t sznum)
-{
-#ifdef __INTEL_COMPILER
-#  pragma warning(push)
-#  pragma warning(disable:810) /* conversion may lose significant bits */
-#endif
-
-  DEBUGASSERT(sznum >= 0);
-#if (SIZEOF_INT < SIZEOF_SIZE_T)
-  DEBUGASSERT((size_t) sznum <= (size_t) CURL_MASK_SINT);
-#endif
-  return (int)(sznum & (ssize_t) CURL_MASK_SINT);
-
-#ifdef __INTEL_COMPILER
-#  pragma warning(pop)
-#endif
-}
-
-/*
-** signed int to unsigned size_t
-*/
-
-size_t curlx_sitouz(int sinum)
-{
-#ifdef __INTEL_COMPILER
-#  pragma warning(push)
-#  pragma warning(disable:810) /* conversion may lose significant bits */
-#endif
-
-  DEBUGASSERT(sinum >= 0);
-  return (size_t) sinum;
-
-#ifdef __INTEL_COMPILER
-#  pragma warning(pop)
-#endif
-}
-
-#if defined(__INTEL_COMPILER) && defined(__unix__)
-
-int curlx_FD_ISSET(int fd, fd_set *fdset)
-{
-  #pragma warning(push)
-  #pragma warning(disable:1469) /* clobber ignored */
-  return FD_ISSET(fd, fdset);
-  #pragma warning(pop)
-}
-
-void curlx_FD_SET(int fd, fd_set *fdset)
-{
-  #pragma warning(push)
-  #pragma warning(disable:1469) /* clobber ignored */
-  FD_SET(fd, fdset);
-  #pragma warning(pop)
-}
-
-void curlx_FD_ZERO(fd_set *fdset)
-{
-  #pragma warning(push)
-  #pragma warning(disable:593) /* variable was set but never used */
-  FD_ZERO(fdset);
-  #pragma warning(pop)
-}
-
-unsigned short curlx_htons(unsigned short usnum)
-{
-#if (__INTEL_COMPILER == 910) && defined(__i386__)
-  return (unsigned short)(((usnum << 8) & 0xFF00) | ((usnum >> 8) & 0x00FF));
-#else
-  #pragma warning(push)
-  #pragma warning(disable:810) /* conversion may lose significant bits */
-  return htons(usnum);
-  #pragma warning(pop)
-#endif
-}
-
-unsigned short curlx_ntohs(unsigned short usnum)
-{
-#if (__INTEL_COMPILER == 910) && defined(__i386__)
-  return (unsigned short)(((usnum << 8) & 0xFF00) | ((usnum >> 8) & 0x00FF));
-#else
-  #pragma warning(push)
-  #pragma warning(disable:810) /* conversion may lose significant bits */
-  return ntohs(usnum);
-  #pragma warning(pop)
-#endif
-}
-
-#endif /* __INTEL_COMPILER && __unix__ */
diff --git a/lib/wildcard.c b/lib/wildcard.c
deleted file mode 100644 (file)
index d6ba2b2..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#include "curl_wildcard.h"
-#include "curl_llist.h"
-#include "curl_fileinfo.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "curl_memdebug.h"
-
-CURLcode Curl_wildcard_init(struct WildcardData *wc)
-{
-  DEBUGASSERT(wc->filelist == NULL);
-  /* now allocate only wc->filelist, everything else
-     will be allocated if it is needed. */
-  wc->filelist = Curl_llist_alloc(Curl_fileinfo_dtor);
-  if(!wc->filelist) {;
-    return CURLE_OUT_OF_MEMORY;
-  }
-  return CURLE_OK;
-}
-
-void Curl_wildcard_dtor(struct WildcardData *wc)
-{
-  if(!wc)
-    return;
-
-  if(wc->tmp_dtor) {
-    wc->tmp_dtor(wc->tmp);
-    wc->tmp_dtor = ZERO_NULL;
-    wc->tmp = NULL;
-  }
-  DEBUGASSERT(wc->tmp == NULL);
-
-  if(wc->filelist) {
-    Curl_llist_destroy(wc->filelist, NULL);
-    wc->filelist = NULL;
-  }
-
-  if(wc->path) {
-    free(wc->path);
-    wc->path = NULL;
-  }
-
-  if(wc->pattern) {
-    free(wc->pattern);
-    wc->pattern = NULL;
-  }
-
-  wc->customptr = NULL;
-  wc->state = CURLWC_INIT;
-}