Andy Cedilnik 23 лет назад
Родитель
Сommit
baeba76200
78 измененных файлов с 24960 добавлено и 0 удалено
  1. 309 0
      Source/CTest/Curl/CMakeLists.txt
  2. 106 0
      Source/CTest/Curl/WindowsCache.cmake
  3. 101 0
      Source/CTest/Curl/arpa_telnet.h
  4. 270 0
      Source/CTest/Curl/base64.c
  5. 27 0
      Source/CTest/Curl/base64.h
  6. 409 0
      Source/CTest/Curl/config.h.in
  7. 581 0
      Source/CTest/Curl/connect.c
  8. 35 0
      Source/CTest/Curl/connect.h
  9. 738 0
      Source/CTest/Curl/cookie.c
  10. 84 0
      Source/CTest/Curl/cookie.h
  11. 241 0
      Source/CTest/Curl/dict.c
  12. 29 0
      Source/CTest/Curl/dict.h
  13. 96 0
      Source/CTest/Curl/dllinit.c
  14. 340 0
      Source/CTest/Curl/easy.c
  15. 120 0
      Source/CTest/Curl/escape.c
  16. 32 0
      Source/CTest/Curl/escape.h
  17. 206 0
      Source/CTest/Curl/file.c
  18. 28 0
      Source/CTest/Curl/file.h
  19. 1501 0
      Source/CTest/Curl/formdata.c
  20. 73 0
      Source/CTest/Curl/formdata.h
  21. 2138 0
      Source/CTest/Curl/ftp.c
  22. 37 0
      Source/CTest/Curl/ftp.h
  23. 2149 0
      Source/CTest/Curl/getdate.c
  24. 37 0
      Source/CTest/Curl/getdate.h
  25. 77 0
      Source/CTest/Curl/getenv.c
  26. 161 0
      Source/CTest/Curl/getinfo.c
  27. 28 0
      Source/CTest/Curl/getinfo.h
  28. 252 0
      Source/CTest/Curl/getpass.c
  29. 35 0
      Source/CTest/Curl/getpass.h
  30. 285 0
      Source/CTest/Curl/hash.c
  31. 85 0
      Source/CTest/Curl/hash.h
  32. 529 0
      Source/CTest/Curl/hostip.c
  33. 60 0
      Source/CTest/Curl/hostip.h
  34. 985 0
      Source/CTest/Curl/http.c
  35. 42 0
      Source/CTest/Curl/http.h
  36. 230 0
      Source/CTest/Curl/http_chunks.c
  37. 87 0
      Source/CTest/Curl/http_chunks.h
  38. 134 0
      Source/CTest/Curl/if2ip.c
  39. 33 0
      Source/CTest/Curl/if2ip.h
  40. 9 0
      Source/CTest/Curl/inet_ntoa_r.h
  41. 401 0
      Source/CTest/Curl/krb4.c
  42. 27 0
      Source/CTest/Curl/krb4.h
  43. 231 0
      Source/CTest/Curl/ldap.c
  44. 29 0
      Source/CTest/Curl/ldap.h
  45. 168 0
      Source/CTest/Curl/llist.c
  46. 64 0
      Source/CTest/Curl/llist.h
  47. 221 0
      Source/CTest/Curl/memdebug.c
  48. 61 0
      Source/CTest/Curl/memdebug.h
  49. 1181 0
      Source/CTest/Curl/mprintf.c
  50. 361 0
      Source/CTest/Curl/multi.c
  51. 189 0
      Source/CTest/Curl/multi.h
  52. 211 0
      Source/CTest/Curl/netrc.c
  53. 28 0
      Source/CTest/Curl/netrc.h
  54. 384 0
      Source/CTest/Curl/progress.c
  55. 68 0
      Source/CTest/Curl/progress.h
  56. 562 0
      Source/CTest/Curl/security.c
  57. 72 0
      Source/CTest/Curl/security.h
  58. 365 0
      Source/CTest/Curl/sendf.c
  59. 56 0
      Source/CTest/Curl/sendf.h
  60. 169 0
      Source/CTest/Curl/setup.h
  61. 78 0
      Source/CTest/Curl/speedcheck.c
  62. 34 0
      Source/CTest/Curl/speedcheck.h
  63. 956 0
      Source/CTest/Curl/ssluse.c
  64. 38 0
      Source/CTest/Curl/ssluse.h
  65. 121 0
      Source/CTest/Curl/strequal.c
  66. 40 0
      Source/CTest/Curl/strequal.h
  67. 74 0
      Source/CTest/Curl/strtok.c
  68. 38 0
      Source/CTest/Curl/strtok.h
  69. 1213 0
      Source/CTest/Curl/telnet.c
  70. 29 0
      Source/CTest/Curl/telnet.h
  71. 89 0
      Source/CTest/Curl/timeval.c
  72. 52 0
      Source/CTest/Curl/timeval.h
  73. 1357 0
      Source/CTest/Curl/transfer.c
  74. 49 0
      Source/CTest/Curl/transfer.h
  75. 2379 0
      Source/CTest/Curl/url.c
  76. 38 0
      Source/CTest/Curl/url.h
  77. 687 0
      Source/CTest/Curl/urldata.h
  78. 121 0
      Source/CTest/Curl/version.c

+ 309 - 0
Source/CTest/Curl/CMakeLists.txt

@@ -0,0 +1,309 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 1.5)
+PROJECT(LIBCURL C)
+
+SET(PACKAGE "curl")
+SET(VERSION "7.9.5")
+SET(OPERATING_SYSTEM ${CMAKE_SYSTEM_NAME})
+
+SET(CMAKE_C_FLAGS "${CMAKE_ANSI_CFLAGS} ${CMAKE_C_FLAGS}")
+SET(CMAKE_REQUIRED_FLAGS ${CMAKE_ANSI_CFLAGS})
+
+INCLUDE (${CMAKE_ROOT}/Modules/CheckLibraryExists.cmake)
+INCLUDE (${CMAKE_ROOT}/Modules/CheckFunctionExists.cmake)
+INCLUDE (${CMAKE_ROOT}/Modules/CheckTypeSize.cmake)
+INCLUDE (${CMAKE_ROOT}/Modules/CheckIncludeFile.cmake)
+INCLUDE (${CMAKE_ROOT}/Modules/CheckIncludeFiles.cmake)
+
+SET(libCurl_SRCS
+    file.c
+    timeval.c
+    base64.c
+    hostip.c
+    progress.c
+    formdata.c
+    cookie.c
+    http.c
+    sendf.c
+    ftp.c
+    url.c
+    dict.c
+    if2ip.c
+    speedcheck.c
+    getdate.c
+    ldap.c
+    ssluse.c
+    version.c
+    getenv.c
+    escape.c
+    mprintf.c
+    telnet.c
+    getpass.c
+    netrc.c
+    getinfo.c
+    transfer.c
+    strequal.c
+    easy.c
+    security.c
+    krb4.c
+    memdebug.c
+    http_chunks.c
+    strtok.c
+    connect.c
+    llist.c
+    hash.c
+    multi.c
+    )
+
+IF(WIN32)
+  INCLUDE(${LIBCURL_SOURCE_DIR}/WindowsCache.cmake)
+ENDIF(WIN32)
+
+SET(CURL_LIBS "")
+
+CHECK_LIBRARY_EXISTS("nsl;${CURL_LIBS}" gethostname "" HAVE_LIBNSL)
+IF(HAVE_LIBNSL)
+  SET(CURL_LIBS ${CURL_LIBS} nsl)
+ENDIF(HAVE_LIBNSL)
+
+CHECK_LIBRARY_EXISTS("dl;${CURL_LIBS}" dlopen "" HAVE_LIBDL)
+IF(HAVE_LIBDL)
+  SET(HAVE_DLOPEN 1)
+  SET(CURL_LIBS ${CURL_LIBS} dl)
+ENDIF(HAVE_LIBDL)
+
+CHECK_LIBRARY_EXISTS("ucb;${CURL_LIBS}" gethostname "" HAVE_LIBUCB)
+IF(HAVE_LIBUCB)
+  SET(CURL_LIBS ${CURL_LIBS} ucb)
+ENDIF(HAVE_LIBUCB)
+
+CHECK_LIBRARY_EXISTS("socket;${CURL_LIBS}" connect "" HAVE_LIBSOCKET)
+IF(HAVE_LIBSOCKET)
+  SET(CURL_LIBS ${CURL_LIBS} socket)
+ENDIF(HAVE_LIBSOCKET)
+
+CHECK_LIBRARY_EXISTS("ws2_32;${CURL_LIBS}" printf "" HAVE_LIBWS2_32)
+IF(HAVE_LIBWS2_32)
+  SET(CURL_LIBS ${CURL_LIBS} ws2_32)
+ENDIF(HAVE_LIBWS2_32)
+
+SET(CMAKE_REQUIRED_LIBRARIES ${CURL_LIBS})
+
+CHECK_INCLUDE_FILE("sys/types.h"    HAVE_SYS_TYPES_H)
+CHECK_INCLUDE_FILE("inttypes.h"    HAVE_INTTYPES_H)
+CHECK_INCLUDE_FILE("alloca.h"    HAVE_ALLOCA_H)
+CHECK_INCLUDE_FILE("arpa/inet.h"    HAVE_ARPA_INET_H)
+CHECK_INCLUDE_FILE("dlfcn.h"    HAVE_DLFCN_H)
+CHECK_INCLUDE_FILE("fcntl.h"    HAVE_FCNTL_H)
+CHECK_INCLUDE_FILE("malloc.h"    HAVE_MALLOC_H)
+CHECK_INCLUDE_FILE("memory.h"    HAVE_MEMORY_H)
+CHECK_INCLUDE_FILE("netdb.h"    HAVE_NETDB_H)
+CHECK_INCLUDE_FILE("netinet/in.h"    HAVE_NETINET_IN_H)
+CHECK_INCLUDE_FILE("sys/socket.h"    HAVE_SYS_SOCKET_H)
+
+IF(HAVE_SYS_TYPES_H)
+  SET(TEST_NETWORK_INCLUDES ${TEST_NETWORK_INCLUDES} "sys/types.h")
+ENDIF(HAVE_SYS_TYPES_H)
+IF(HAVE_SYS_SOCKET_H)
+  SET(TEST_NETWORK_INCLUDES ${TEST_NETWORK_INCLUDES} "sys/socket.h")
+ENDIF(HAVE_SYS_SOCKET_H)
+
+CHECK_INCLUDE_FILES("${TEST_NETWORK_INCLUDES};net/if.h"    HAVE_NET_IF_H)
+
+IF(HAVE_NET_IF_H)
+  SET(TEST_NETWORK_INCLUDES ${TEST_NETWORK_INCLUDES} "net/if.h")
+ENDIF(HAVE_NET_IF_H)
+IF(HAVE_NETINET_IN_H)
+  SET(TEST_NETWORK_INCLUDES ${TEST_NETWORK_INCLUDES} "netinet/in.h")
+ENDIF(HAVE_NETINET_IN_H)
+
+CHECK_INCLUDE_FILES("${TEST_NETWORK_INCLUDES};netinet/if_ether.h"    HAVE_NETINET_IF_ETHER_H)
+
+CHECK_INCLUDE_FILE("netinet/in.h"    HAVE_NETINET_IN_H)
+CHECK_INCLUDE_FILE("pwd.h"    HAVE_PWD_H)
+CHECK_INCLUDE_FILE("sgtty.h"    HAVE_SGTTY_H)
+CHECK_INCLUDE_FILE("stdint.h"    HAVE_STDINT_H)
+CHECK_INCLUDE_FILE("stdlib.h"    HAVE_STDLIB_H)
+CHECK_INCLUDE_FILE("string.h"    HAVE_STRING_H)
+CHECK_INCLUDE_FILE("strings.h"    HAVE_STRINGS_H)
+CHECK_INCLUDE_FILE("sys/param.h"    HAVE_SYS_PARAM_H)
+CHECK_INCLUDE_FILE("sys/select.h"    HAVE_SYS_SELECT_H)
+CHECK_INCLUDE_FILE("sys/stat.h"    HAVE_SYS_STAT_H)
+CHECK_INCLUDE_FILE("sys/time.h"    HAVE_SYS_TIME_H)
+CHECK_INCLUDE_FILE("termios.h"    HAVE_TERMIOS_H)
+CHECK_INCLUDE_FILE("termio.h"    HAVE_TERMIO_H)
+CHECK_INCLUDE_FILE("io.h"    HAVE_IO_H)
+CHECK_INCLUDE_FILE("time.h"    HAVE_TIME_H)
+CHECK_INCLUDE_FILE("unistd.h"    HAVE_UNISTD_H)
+CHECK_INCLUDE_FILE("utime.h"    HAVE_UTIME_H)
+CHECK_INCLUDE_FILE("sys/utime.h"    HAVE_SYS_UTIME_H)
+CHECK_INCLUDE_FILE("winsock.h"    HAVE_WINSOCK_H)
+CHECK_INCLUDE_FILE("sockio.h"    HAVE_SOCKIO_H)
+CHECK_INCLUDE_FILE("sys/sockio.h"    HAVE_SYS_SOCKIO_H)
+CHECK_INCLUDE_FILE("x509.h"    HAVE_X509_H)
+
+CHECK_TYPE_SIZE(ssize_t  SIZEOF_SSIZE_T)
+CHECK_TYPE_SIZE("long double"  SIZEOF_LONG_DOUBLE)
+IF(NOT HAVE_SIZEOF_SSIZE_T)
+  SET(ssize_t int)
+ENDIF(NOT HAVE_SIZEOF_SSIZE_T)
+
+FIND_FILE(RANDOM_FILE urandom /dev)
+
+CHECK_FUNCTION_EXISTS(_doprnt        HAVE_DOPRNT)
+CHECK_FUNCTION_EXISTS(vprintf        HAVE_VPRINTF)
+
+CHECK_FUNCTION_EXISTS(socket            HAVE_SOCKET)
+CHECK_FUNCTION_EXISTS(select            HAVE_SELECT)
+CHECK_FUNCTION_EXISTS(strdup            HAVE_STRDUP)
+CHECK_FUNCTION_EXISTS(strstr            HAVE_STRSTR)
+CHECK_FUNCTION_EXISTS(strtok_r          HAVE_STRTOK_R)
+CHECK_FUNCTION_EXISTS(strftime          HAVE_STRFTIME)
+CHECK_FUNCTION_EXISTS(uname             HAVE_UNAME)
+CHECK_FUNCTION_EXISTS(strcasecmp        HAVE_STRCASECMP)
+CHECK_FUNCTION_EXISTS(stricmp           HAVE_STRICMP)
+CHECK_FUNCTION_EXISTS(strcmpi           HAVE_STRCMPI)
+CHECK_FUNCTION_EXISTS(gethostname       HAVE_GETHOSTNAME)
+CHECK_FUNCTION_EXISTS(gethostbyaddr     HAVE_GETHOSTBYADDR)
+CHECK_FUNCTION_EXISTS(gettimeofday      HAVE_GETTIMEOFDAY)
+CHECK_FUNCTION_EXISTS(inet_addr         HAVE_INET_ADDR)
+CHECK_FUNCTION_EXISTS(inet_ntoa         HAVE_INET_NTOA)
+CHECK_FUNCTION_EXISTS(inet_ntoa_r       HAVE_INET_NTOA_R)
+CHECK_FUNCTION_EXISTS(tcsetattr         HAVE_TCSETATTR)
+CHECK_FUNCTION_EXISTS(tcgetattr         HAVE_TCGETATTR)
+CHECK_FUNCTION_EXISTS(perror            HAVE_PERROR)
+CHECK_FUNCTION_EXISTS(closesocket       HAVE_CLOSESOCKET)
+CHECK_FUNCTION_EXISTS(setvbuf           HAVE_SETVBUF)
+CHECK_FUNCTION_EXISTS(sigaction         HAVE_SIGACTION)
+CHECK_FUNCTION_EXISTS(signal            HAVE_SIGNAL)
+CHECK_FUNCTION_EXISTS(getpass_r         HAVE_GETPASS_R)
+CHECK_FUNCTION_EXISTS(strlcat           HAVE_STRLCAT)
+CHECK_FUNCTION_EXISTS(getpwuid          HAVE_GETPWUID)
+CHECK_FUNCTION_EXISTS(geteuid           HAVE_GETEUID)
+CHECK_FUNCTION_EXISTS(utime             HAVE_UTIME)
+CHECK_FUNCTION_EXISTS(RAND_status       HAVE_RAND_STATUS)
+CHECK_FUNCTION_EXISTS(RAND_screen       HAVE_RAND_SCREEN)
+CHECK_FUNCTION_EXISTS(RAND_egd          HAVE_RAND_EGD)
+
+CHECK_FUNCTION_EXISTS(gethostbyname       HAVE_GETHOSTBYNAME_R)
+CHECK_FUNCTION_EXISTS(gethostbyaddr       HAVE_GETHOSTBYADDR_R)
+CHECK_FUNCTION_EXISTS(gmtime              HAVE_GMTIME_R)
+CHECK_FUNCTION_EXISTS(localtime           HAVE_LOCALTIME_R)
+
+MACRO(CURL_INTERNAL_TEST CURL_TEST)
+  IF("${CURL_TEST}" MATCHES "^${CURL_TEST}$")
+    SET(MACRO_CHECK_FUNCTION_DEFINITIONS 
+	"-D${CURL_TEST} ${CMAKE_REQUIRED_FLAGS}")
+    IF(CMAKE_REQUIRED_LIBRARIES)
+      SET(CURL_TEST_ADD_LIBRARIES
+          "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}")
+    ENDIF(CMAKE_REQUIRED_LIBRARIES)
+
+    MESSAGE(STATUS "Performing Curl Test ${CURL_TEST}")
+    TRY_COMPILE(${CURL_TEST}
+                ${CMAKE_BINARY_DIR}
+                ${LIBCURL_SOURCE_DIR}/CMake/CurlTests.c
+                CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS}
+                            "${CURL_TEST_ADD_LIBRARIES}"
+                OUTPUT_VARIABLE OUTPUT)
+    IF(${CURL_TEST})
+      SET(${CURL_TEST} 1 CACHE INTERNAL "Curl test ${FUNCTION}")
+      MESSAGE(STATUS "Performing Curl Test ${CURL_TEST} - Success")
+    ELSE(${CURL_TEST})
+      MESSAGE(STATUS "Performing Curl Test ${CURL_TEST} - Failed")
+      SET(${CURL_TEST} "" CACHE INTERNAL "Curl test ${FUNCTION}")
+      WRITE_FILE(${CMAKE_BINARY_DIR}/CMakeError.log 
+        "Performing Curl Test ${CURL_TEST} failed with the following output:\n"
+        "${OUTPUT}\n" APPEND)
+    ENDIF(${CURL_TEST})
+  ENDIF("${CURL_TEST}" MATCHES "^${CURL_TEST}$")
+ENDMACRO(CURL_INTERNAL_TEST) 
+
+FOREACH(CURL_TEST 
+        TIME_WITH_SYS_TIME
+        HAVE_O_NONBLOCK
+        HAVE_GETHOSTBYADDR_R_5
+        HAVE_GETHOSTBYADDR_R_7
+        HAVE_GETHOSTBYADDR_R_8
+        HAVE_GETHOSTBYADDR_R_5_REENTRANT
+        HAVE_GETHOSTBYADDR_R_7_REENTRANT
+        HAVE_GETHOSTBYADDR_R_8_REENTRANT
+        HAVE_GETHOSTBYNAME_R_3
+        HAVE_GETHOSTBYNAME_R_5
+        HAVE_GETHOSTBYNAME_R_6
+        HAVE_GETHOSTBYNAME_R_3_REENTRANT
+        HAVE_GETHOSTBYNAME_R_5_REENTRANT
+        HAVE_GETHOSTBYNAME_R_6_REENTRANT
+        HAVE_SOCKLEN_T
+        HAVE_IN_ADDR_T
+        STDC_HEADERS
+        RETSIGTYPE_TEST
+	HAVE_INET_NTOA_R_DECL
+	HAVE_INET_NTOA_R_DECL_REENTRANT
+        )
+  CURL_INTERNAL_TEST(${CURL_TEST})
+ENDFOREACH(CURL_TEST)
+
+FOREACH(CURL_TEST
+        HAVE_GETHOSTBYADDR_R_5
+        HAVE_GETHOSTBYADDR_R_7
+        HAVE_GETHOSTBYADDR_R_8
+        HAVE_GETHOSTBYNAME_R_3
+        HAVE_GETHOSTBYNAME_R_5
+        HAVE_GETHOSTBYNAME_R_6
+	HAVE_INET_NTOA_R_DECL_REENTRANT)
+  IF(NOT ${CURL_TEST})
+    IF(${CURL_TEST}_REENTRANT)
+      SET(NEED_REENTRANT 1)
+    ENDIF(${CURL_TEST}_REENTRANT)
+  ENDIF(NOT ${CURL_TEST})
+ENDFOREACH(CURL_TEST)
+
+IF(NEED_REENTRANT)
+  FOREACH(CURL_TEST
+          HAVE_GETHOSTBYADDR_R_5
+          HAVE_GETHOSTBYADDR_R_7
+          HAVE_GETHOSTBYADDR_R_8
+          HAVE_GETHOSTBYNAME_R_3
+          HAVE_GETHOSTBYNAME_R_5
+          HAVE_GETHOSTBYNAME_R_6)
+  SET(${CURL_TEST} 0)
+  IF(${CURL_TEST}_REENTRANT)
+    SET(${CURL_TEST} 1)
+  ENDIF(${CURL_TEST}_REENTRANT)
+  ENDFOREACH(CURL_TEST)
+ENDIF(NEED_REENTRANT)
+
+IF(HAVE_INET_NTOA_R_DECL_REENTRANT)
+  SET(HAVE_INET_NTOA_R_DECL 1)
+  SET(NEED_REENTRANT 1)
+ENDIF(HAVE_INET_NTOA_R_DECL_REENTRANT)
+
+IF(NOT HAVE_SOCKLEN_T)
+  SET(socklen_t "int")
+ENDIF(NOT HAVE_SOCKLEN_T)
+
+IF(NOT HAVE_IN_ADDR_T)
+  SET(in_addr_t "unsigned long")
+ENDIF(NOT HAVE_IN_ADDR_T)
+
+IF(RETSIGTYPE_TEST)
+  SET(RETSIGTYPE void)
+ELSE(RETSIGTYPE_TEST)
+  SET(RETSIGTYPE int)
+ENDIF(RETSIGTYPE_TEST)
+
+INCLUDE_DIRECTORIES(${LIBCURL_SOURCE_DIR})
+INCLUDE_DIRECTORIES(${LIBCURL_BINARY_DIR})
+ADD_DEFINITIONS(-DHAVE_CONFIG_H)
+CONFIGURE_FILE(${LIBCURL_SOURCE_DIR}/config.h.in
+               ${LIBCURL_BINARY_DIR}/config.h)
+
+ADD_LIBRARY(Curl STATIC ${libCurl_SRCS})
+
+TARGET_LINK_LIBRARIES(Curl ${CURL_LIBS})
+
+OPTION(CURL_TESTING "Do libCurl testing" ON)
+IF(CURL_TESTING)
+  SUBDIRS(Testing)
+ENDIF(CURL_TESTING)
+

+ 106 - 0
Source/CTest/Curl/WindowsCache.cmake

@@ -0,0 +1,106 @@
+IF(WIN32)
+  SET(HAVE_GETHOSTNAME 1)
+  SET(HAVE_GETHOSTBYADDR 1)
+  SET(HAVE_GETSERVBYNAME 1)
+  SET(HAVE_INET_ADDR 1)
+  SET(HAVE_INET_NTOA 1)
+  SET(HAVE_SELECT 1)
+  SET(HAVE_SOCKET 1)
+  SET(HAVE_ARPA_INET 1)
+  SET(HAVE_NETDB_H 1)
+  SET(HAVE_SYS_SOCKET_H 0)
+  SET(HAVE_TERMIO_H 1)
+  SET(HAVE_TERMIOS_H 1)
+  SET(HAVE_ARPA_INET_H 1)
+  SET(HAVE_UTIME_H 0)
+  SET(HAVE_CLOSESOCKET 1)
+  SET(HAVE_RAND_SCREEN 1)
+  SET(HAVE_SIGNAL 0)
+  SET(OPERATING_SYSTEM "win32")
+  SET(HAVE_SYS_TYPES_H 1)
+  SET(HAVE_INTTYPES_H 0)
+  SET(HAVE_ALLOCA_H 0)
+  SET(HAVE_DLFCN_H 0)
+  SET(HAVE_FCNTL_H 1)
+  SET(HAVE_MALLOC_H 1)
+  SET(HAVE_MEMORY_H 1)
+  SET(HAVE_NETINET_IN_H 0)
+  SET(HAVE_NETINET_IF_ETHER_H 0)
+  SET(HAVE_NET_IF_H 0)
+  SET(HAVE_PWD_H 0)
+  SET(HAVE_SGTTY_H 0)
+  SET(HAVE_STDINT_H 0)
+  SET(HAVE_STDLIB_H 1)
+  SET(HAVE_STRING_H 1)
+  SET(HAVE_STRINGS_H 0)
+  SET(HAVE_SYS_PARAM_H 0)
+  SET(HAVE_SYS_SELECT_H 0)
+  SET(HAVE_SYS_SOCKIO_H 1)
+  SET(HAVE_SYS_STAT_H 1)
+  SET(HAVE_SYS_TIME_H 0)
+  SET(HAVE_IO_H 1)
+  SET(HAVE_TIME_H 1)
+  SET(HAVE_WINSOCK_H 1)
+  SET(HAVE_SOCK_H 0)
+  SET(HAVE_X509_H 0)
+  SET(SIZEOF_SSIZE_T 0)
+  SET(HAVE_DOPRNT 0)
+  SET(HAVE_VPRINTF 1)
+  SET(HAVE_STRDUP 1)
+  SET(HAVE_STRSTR 1)
+  SET(HAVE_STRTOK_R 0)
+  SET(HAVE_STRFTIME 1)
+  SET(HAVE_UNAME 0)
+  SET(HAVE_STRCASECMP 0)
+  SET(HAVE_STRICMP 1)
+  SET(HAVE_STRCMPI 0)
+  SET(HAVE_GETTIMEOFDAY 0)
+  SET(HAVE_TCGETATTR 0)
+  SET(HAVE_PERROR 1)
+  SET(HAVE_SETVBUF 1)
+  SET(HAVE_SIGACTION 0)
+  SET(HAVE_GETPASS_R 0)
+  SET(HAVE_STRLCAT 0)
+  SET(HAVE_GETPWUID 0)
+  SET(HAVE_GETEUID 0)
+  SET(HAVE_UTIME 0)
+  SET(HAVE_RAND_STATUS 0)
+  SET(HAVE_RAND_EGD 0)
+  SET(HAVE_DLOPEN 0)
+  SET(HAVE_CONNECT 0)
+  SET(HAVE_GETHOSTBYNAME 0)
+  SET(HAVE_LOCALTIME_R 0)
+  SET(HAVE_GMTIME_R 0)
+  SET(HAVE_LIBNSL 0)
+  SET(HAVE_LIBUCB 0)
+  SET(HAVE_LIBDL 0)
+  SET(HAVE_LIBSOCKET 0)
+  SET(HAVE_UNISTD_H 0)
+  SET(HAVE_SYS_UTIME_H 1)
+  SET(HAVE_SOCKIO_H 0)
+  SET(HAVE_INET_NTOA_R 0)
+  SET(HAVE_TCSETATTR 0)
+  SET(HAVE_GETHOSTBYNAME_R 0)
+  SET(HAVE_GETHOSTBYADDR_R 0)
+  SET(TIME_WITH_SYS_TIME 0)
+  SET(HAVE_O_NONBLOCK 0)
+  SET(HAVE_GETHOSTBYADDR_R_5 0)
+  SET(HAVE_GETHOSTBYADDR_R_7 0)
+  SET(HAVE_GETHOSTBYADDR_R_8 0)
+  SET(HAVE_GETHOSTBYADDR_R_5_REENTRANT 0)
+  SET(HAVE_GETHOSTBYADDR_R_7_REENTRANT 0)
+  SET(HAVE_GETHOSTBYADDR_R_8_REENTRANT 0)
+  SET(HAVE_GETHOSTBYNAME_R_3 0)
+  SET(HAVE_GETHOSTBYNAME_R_5 0)
+  SET(HAVE_GETHOSTBYNAME_R_6 0)
+  SET(HAVE_GETHOSTBYNAME_R_3_REENTRANT 0)
+  SET(HAVE_GETHOSTBYNAME_R_5_REENTRANT 0)
+  SET(HAVE_GETHOSTBYNAME_R_6_REENTRANT 0)
+  SET(HAVE_SOCKLEN_T 0)
+  SET(HAVE_IN_ADDR_T 0)
+  SET(STDC_HEADERS 1)
+  SET(RETSIGTYPE_TEST 1)
+
+ELSE(WIN32)
+  MESSAGE("This file should be included on Windows platform only")
+ENDIF(WIN32)

+ 101 - 0
Source/CTest/Curl/arpa_telnet.h

@@ -0,0 +1,101 @@
+#ifndef __ARPA_TELNET_H
+#define __ARPA_TELNET_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2001, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+/*
+ * Telnet option defines. Add more here if in need.
+ */
+#define TELOPT_BINARY   0  /* binary 8bit data */
+#define TELOPT_SGA      3  /* Supress Go Ahead */
+#define TELOPT_EXOPL  255  /* EXtended OPtions List */
+#define TELOPT_TTYPE   24  /* Terminal TYPE */
+#define TELOPT_XDISPLOC 35 /* X DISPlay LOCation */
+
+#define TELOPT_NEW_ENVIRON 39  /* NEW ENVIRONment variables */
+#define NEW_ENV_VAR   0
+#define NEW_ENV_VALUE 1
+
+/*
+ * The telnet options represented as strings
+ */
+static const char *telnetoptions[]=
+{
+  "BINARY",      "ECHO",           "RCP",           "SUPPRESS GO AHEAD",
+  "NAME",        "STATUS",         "TIMING MARK",   "RCTE",
+  "NAOL",        "NAOP",           "NAOCRD",        "NAOHTS",
+  "NAOHTD",      "NAOFFD",         "NAOVTS",        "NAOVTD",
+  "NAOLFD",      "EXTEND ASCII",   "LOGOUT",        "BYTE MACRO",
+  "DE TERMINAL", "SUPDUP",         "SUPDUP OUTPUT", "SEND LOCATION",
+  "TERM TYPE",   "END OF RECORD",  "TACACS UID",    "OUTPUT MARKING",
+  "TTYLOC",      "3270 REGIME",    "X3 PAD",        "NAWS",
+  "TERM SPEED",  "LFLOW",          "LINEMODE",      "XDISPLOC",
+  "OLD-ENVIRON", "AUTHENTICATION", "ENCRYPT",       "NEW-ENVIRON"
+};
+
+#define TELOPT_MAXIMUM TELOPT_NEW_ENVIRON
+
+#define TELOPT_OK(x) ((x) <= TELOPT_MAXIMUM)
+#define TELOPT(x)    telnetoptions[x]
+
+#define NTELOPTS 40 
+
+/*
+ * First some defines
+ */
+#define xEOF 236 /* End Of File */ 
+#define SE   240 /* Sub negotiation End */
+#define NOP  241 /* No OPeration */
+#define DM   242 /* Data Mark */
+#define GA   249 /* Go Ahead, reverse the line */
+#define SB   250 /* SuBnegotiation */
+#define WILL 251 /* Our side WILL use this option */
+#define WONT 252 /* Our side WON'T use this option */
+#define DO   253 /* DO use this option! */
+#define DONT 254 /* DON'T use this option! */
+#define IAC  255 /* Interpret As Command */
+
+/*
+ * Then those numbers represented as strings:
+ */
+static const char *telnetcmds[]=
+{
+  "EOF",  "SUSP",  "ABORT", "EOR",  "SE",
+  "NOP",  "DMARK", "BRK",   "IP",   "AO",
+  "AYT",  "EC",    "EL",    "GA",   "SB",
+  "WILL", "WONT",  "DO",    "DONT", "IAC"
+};
+
+#define TELCMD_MINIMUM xEOF /* the first one */
+#define TELCMD_MAXIMUM  IAC  /* surprise, 255 is the last one! ;-) */
+
+#define TELQUAL_IS   0
+#define TELQUAL_SEND 1
+#define TELQUAL_INFO 2
+#define TELQUAL_NAME 3
+
+#define TELCMD_OK(x) ( ((unsigned int)(x) >= TELCMD_MINIMUM) && \
+                       ((unsigned int)(x) <= TELCMD_MAXIMUM) )
+#define TELCMD(x)    telnetcmds[(x)-TELCMD_MINIMUM]
+
+#endif

+ 270 - 0
Source/CTest/Curl/base64.c

@@ -0,0 +1,270 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2001, Andrew Francis and Daniel Stenberg
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+/* Base64 encoding/decoding
+ *
+ * Test harnesses down the bottom - compile with -DTEST_ENCODE for
+ * a program that will read in raw data from stdin and write out
+ * a base64-encoded version to stdout, and the length returned by the
+ * encoding function to stderr. Compile with -DTEST_DECODE for a program that
+ * will go the other way.
+ * 
+ * This code will break if int is smaller than 32 bits
+ */
+
+#include "setup.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#include "base64.h"
+
+#ifdef MALLOCDEBUG
+#include "memdebug.h"
+#endif
+
+static void decodeQuantum(unsigned char *dest, char *src)
+{
+  unsigned int x = 0;
+  int i;
+  for(i = 0; i < 4; i++) {
+    if(src[i] >= 'A' && src[i] <= 'Z')
+      x = (x << 6) + (unsigned int)(src[i] - 'A' + 0);
+    else if(src[i] >= 'a' && src[i] <= 'z')
+      x = (x << 6) + (unsigned int)(src[i] - 'a' + 26);
+    else if(src[i] >= '0' && src[i] <= '9') 
+      x = (x << 6) + (unsigned int)(src[i] - '0' + 52);
+    else if(src[i] == '+')
+      x = (x << 6) + 62;
+    else if(src[i] == '/')
+      x = (x << 6) + 63;
+  }
+
+  dest[2] = (unsigned char)(x & 255); x >>= 8;
+  dest[1] = (unsigned char)(x & 255); x >>= 8;
+  dest[0] = (unsigned char)(x & 255); x >>= 8;
+}
+
+/* base64Decode
+ * Given a base64 string at src, decode it into the memory pointed
+ * to by dest. If rawLength points to a valid address (ie not NULL),
+ * store the length of the decoded data to it.
+ */
+static void base64Decode(unsigned char *dest, char *src, int *rawLength)
+{
+  int length = 0;
+  int equalsTerm = 0;
+  int i;
+  unsigned char lastQuantum[3];
+        
+  while((src[length] != '=') && src[length])
+    length++;
+  while(src[length+equalsTerm] == '=')
+    equalsTerm++;
+  
+  if(rawLength)
+    *rawLength = (length * 3 / 4) - equalsTerm;
+  
+  for(i = 0; i < length/4 - 1; i++) {
+    decodeQuantum(dest, src);
+    dest += 3; src += 4;
+  }
+
+  decodeQuantum(lastQuantum, src);
+  for(i = 0; i < 3 - equalsTerm; i++) dest[i] = lastQuantum[i];
+        
+}
+
+/* ---- Base64 Encoding --- */
+static char table64[]=
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+  
+/*
+ * Curl_base64_encode()
+ *
+ * Returns the length of the newly created base64 string. The third argument
+ * is a pointer to an allocated area holding the base64 data. If something
+ * went wrong, -1 is returned.
+ *
+ */
+int Curl_base64_encode(const void *inp, int insize, char **outptr)
+{
+  unsigned char ibuf[3];
+  unsigned char obuf[4];
+  int i;
+  int inputparts;
+  char *output;
+  char *base64data;
+
+  char *indata = (char *)inp;
+
+  if(0 == insize)
+    insize = strlen(indata);
+
+  base64data = output = (char*)malloc(insize*4/3+4);
+  if(NULL == output)
+    return -1;
+
+  while(insize > 0) {
+    for (i = inputparts = 0; i < 3; i++) { 
+      if(*indata) {
+        inputparts++;
+        ibuf[i] = *indata;
+        indata++;
+        insize--;
+      }
+      else
+        ibuf[i] = 0;
+    }
+                       
+    obuf [0] = (ibuf [0] & 0xFC) >> 2;
+    obuf [1] = ((ibuf [0] & 0x03) << 4) | ((ibuf [1] & 0xF0) >> 4);
+    obuf [2] = ((ibuf [1] & 0x0F) << 2) | ((ibuf [2] & 0xC0) >> 6);
+    obuf [3] = ibuf [2] & 0x3F;
+
+    switch(inputparts) {
+    case 1: /* only one byte read */
+      sprintf(output, "%c%c==", 
+              table64[obuf[0]],
+              table64[obuf[1]]);
+      break;
+    case 2: /* two bytes read */
+      sprintf(output, "%c%c%c=", 
+              table64[obuf[0]],
+              table64[obuf[1]],
+              table64[obuf[2]]);
+      break;
+    default:
+      sprintf(output, "%c%c%c%c", 
+              table64[obuf[0]],
+              table64[obuf[1]],
+              table64[obuf[2]],
+              table64[obuf[3]] );
+      break;
+    }
+    output += 4;
+  }
+  *output=0;
+  *outptr = base64data; /* make it return the actual data memory */
+
+  return strlen(base64data); /* return the length of the new data */
+}
+/* ---- End of Base64 Encoding ---- */
+
+int Curl_base64_decode(const char *str, void *data)
+{
+  int ret;
+
+  base64Decode((unsigned char *)data, (char *)str, &ret);
+  return ret;
+}
+
+/************* TEST HARNESS STUFF ****************/
+
+
+#ifdef TEST_ENCODE
+/* encoding test harness. Read in standard input and write out the length
+ * returned by Curl_base64_encode, followed by the base64'd data itself
+ */
+#include <stdio.h>
+
+#define TEST_NEED_SUCK
+void *suck(int *);
+
+int main(int argc, char **argv, char **envp) {
+        char *base64;
+        int base64Len;
+        unsigned char *data;
+        int dataLen;
+        
+        data = (unsigned char *)suck(&dataLen);
+        base64Len = Curl_base64_encode(data, dataLen, &base64);
+
+        fprintf(stderr, "%d\n", base64Len);
+        fprintf(stdout, "%s",   base64);
+
+        free(base64); free(data);
+        return 0;
+}
+#endif
+
+#ifdef TEST_DECODE
+/* decoding test harness. Read in a base64 string from stdin and write out the 
+ * length returned by Curl_base64_decode, followed by the decoded data itself
+ */
+#include <stdio.h>
+
+#define TEST_NEED_SUCK
+void *suck(int *);
+
+int main(int argc, char **argv, char **envp) {
+        char *base64;
+        int base64Len;
+        unsigned char *data;
+        int dataLen;
+        
+        base64 = (char *)suck(&base64Len);
+        data = (unsigned char *)malloc(base64Len * 3/4 + 8);
+        dataLen = Curl_base64_decode(base64, data);
+
+        fprintf(stderr, "%d\n", dataLen);
+        fwrite(data,1,dataLen,stdout);
+        
+
+        free(base64); free(data);
+        return 0;
+}
+#endif
+
+#ifdef TEST_NEED_SUCK
+/* this function 'sucks' in as much as possible from stdin */
+void *suck(int *lenptr) {
+        int cursize = 8192;
+        unsigned char *buf = NULL;
+        int lastread;
+        int len = 0;
+        
+        do {
+                cursize *= 2;
+                buf = (unsigned char *)realloc(buf, cursize);
+                memset(buf + len, 0, cursize - len);
+                lastread = fread(buf + len, 1, cursize - len, stdin);
+                len += lastread;
+        } while(!feof(stdin));
+        
+        lenptr[0] = len;
+        return (void *)buf;
+}
+#endif
+
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 27 - 0
Source/CTest/Curl/base64.h

@@ -0,0 +1,27 @@
+#ifndef __BASE64_H
+#define __BASE64_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+int Curl_base64_encode(const void *data, int size, char **str);
+int Curl_base64_decode(const char *str, void *data);
+#endif

+ 409 - 0
Source/CTest/Curl/config.h.in

@@ -0,0 +1,409 @@
+/* lib/config.h.in.  Generated automatically from configure.in by autoheader.  */
+/* Name of this package! */
+#define PACKAGE "${PACKAGE}"
+
+/* Version number of this archive. */
+#define VERSION "${VERSION}"
+
+/* Define if you have the getpass function.  */
+#cmakedefine HAVE_GETPASS	${HAVE_GETPASS}
+
+/* Define cpu-machine-OS */
+#define OS "${OPERATING_SYSTEM}"
+
+/* Define if you have the gethostbyaddr_r() function with 5 arguments */
+#cmakedefine HAVE_GETHOSTBYADDR_R_5	${HAVE_GETHOSTBYADDR_R_5}
+
+/* Define if you have the gethostbyaddr_r() function with 7 arguments */
+#cmakedefine HAVE_GETHOSTBYADDR_R_7	${HAVE_GETHOSTBYADDR_R_7}
+
+/* Define if you have the gethostbyaddr_r() function with 8 arguments */
+#cmakedefine HAVE_GETHOSTBYADDR_R_8	${HAVE_GETHOSTBYADDR_R_8}
+
+/* Define if you have the gethostbyname_r() function with 3 arguments */
+#cmakedefine HAVE_GETHOSTBYNAME_R_3	${HAVE_GETHOSTBYNAME_R_3}
+
+/* Define if you have the gethostbyname_r() function with 5 arguments */
+#cmakedefine HAVE_GETHOSTBYNAME_R_5	${HAVE_GETHOSTBYNAME_R_5}
+
+/* Define if you have the gethostbyname_r() function with 6 arguments */
+#cmakedefine HAVE_GETHOSTBYNAME_R_6	${HAVE_GETHOSTBYNAME_R_6}
+
+/* Define if you have the inet_ntoa_r function declared. */
+#cmakedefine HAVE_INET_NTOA_R_DECL	${HAVE_INET_NTOA_R_DECL}
+
+/* Define if you have the getservbyname function.  */
+#cmakedefine HAVE_GETSERVBYNAME         ${HAVE_GETSERVBYNAME}
+
+/* Define if you need the _REENTRANT define for some functions */
+#cmakedefine NEED_REENTRANT	${NEED_REENTRANT}
+
+/* Define if you have the Kerberos4 libraries (including -ldes) */
+#cmakedefine KRB4	${KRB4}
+
+/* Define if you want to enable IPv6 support */
+#cmakedefine ENABLE_IPV6	${ENABLE_IPV6}
+
+/* Define this to 'int' if ssize_t is not an available typedefed type */
+#cmakedefine ssize_t	${ssize_t}
+
+/* Define this to 'int' if socklen_t is not an available typedefed type */
+#cmakedefine socklen_t	${socklen_t}
+
+/* Define this as a suitable file to read random data from */
+#cmakedefine RANDOM_FILE	"${RANDOM_FILE}"
+
+/* Define this to your Entropy Gathering Daemon socket pathname */
+#cmakedefine EGD_SOCKET	${EGD_SOCKET}
+
+/* Define if you have a working OpenSSL installation */
+#cmakedefine OPENSSL_ENABLED	${OPENSSL_ENABLED}
+
+/* Define the one correct non-blocking socket method below */
+#cmakedefine HAVE_FIONBIO	${HAVE_FIONBIO}
+#cmakedefine HAVE_IOCTLSOCKET	${HAVE_IOCTLSOCKET}
+#cmakedefine HAVE_IOCTLSOCKET_CASE	${HAVE_IOCTLSOCKET_CASE}
+#cmakedefine HAVE_O_NONBLOCK	${HAVE_O_NONBLOCK}
+#cmakedefine HAVE_DISABLED_NONBLOCKING	${HAVE_DISABLED_NONBLOCKING}
+
+/* Define this to 'int' if in_addr_t is not an available typedefed type */
+#cmakedefine in_addr_t	${in_addr_t}
+
+/* Set to explicitly specify we don't want to use thread-safe functions */
+#cmakedefine DISABLED_THREADSAFE	${DISABLED_THREADSAFE}
+
+/* Define if you want to enable IPv6 support */
+#cmakedefine ENABLE_IPV6	${ENABLE_IPV6}
+
+/* Define if you have the <alloca.h> header file. */
+#cmakedefine HAVE_ALLOCA_H	${HAVE_ALLOCA_H}
+
+/* Define if you have the <arpa/inet.h> header file. */
+#cmakedefine HAVE_ARPA_INET_H	${HAVE_ARPA_INET_H}
+
+/* Define if you have the `closesocket' function. */
+#cmakedefine HAVE_CLOSESOCKET	${HAVE_CLOSESOCKET}
+
+/* Define if you have the <crypto.h> header file. */
+#cmakedefine HAVE_CRYPTO_H	${HAVE_CRYPTO_H}
+
+/* Define if you have the <des.h> header file. */
+#cmakedefine HAVE_DES_H	${HAVE_DES_H}
+
+/* Define if you have the <dlfcn.h> header file. */
+#cmakedefine HAVE_DLFCN_H	${HAVE_DLFCN_H}
+
+/* Define if you have the `dlopen' function. */
+#cmakedefine HAVE_DLOPEN	${HAVE_DLOPEN}
+
+/* Define if you have the <err.h> header file. */
+#cmakedefine HAVE_ERR_H	${HAVE_ERR_H}
+
+/* Define if you have the <fcntl.h> header file. */
+#cmakedefine HAVE_FCNTL_H	${HAVE_FCNTL_H}
+
+/* Define if getaddrinfo exists and works */
+#cmakedefine HAVE_GETADDRINFO	${HAVE_GETADDRINFO}
+
+/* Define if you have the `geteuid' function. */
+#cmakedefine HAVE_GETEUID	${HAVE_GETEUID}
+
+/* Define if you have the `gethostbyaddr' function. */
+#cmakedefine HAVE_GETHOSTBYADDR	${HAVE_GETHOSTBYADDR}
+
+/* Define if you have the `gethostbyaddr_r' function. */
+#cmakedefine HAVE_GETHOSTBYADDR_R	${HAVE_GETHOSTBYADDR_R}
+
+/* Define if you have the `gethostbyname_r' function. */
+#cmakedefine HAVE_GETHOSTBYNAME_R	${HAVE_GETHOSTBYNAME_R}
+
+/* Define if you have the `gethostname' function. */
+#cmakedefine HAVE_GETHOSTNAME	${HAVE_GETHOSTNAME}
+
+/* Define if you have the `getpass_r' function. */
+#cmakedefine HAVE_GETPASS_R	${HAVE_GETPASS_R}
+
+/* Define if you have the `getpwuid' function. */
+#cmakedefine HAVE_GETPWUID	${HAVE_GETPWUID}
+
+/* Define if you have the `gettimeofday' function. */
+#cmakedefine HAVE_GETTIMEOFDAY	${HAVE_GETTIMEOFDAY}
+
+/* Define if you have the `gmtime_r' function. */
+#cmakedefine HAVE_GMTIME_R	${HAVE_GMTIME_R}
+
+/* Define if you have the `inet_addr' function. */
+#cmakedefine HAVE_INET_ADDR	${HAVE_INET_ADDR}
+
+/* Define if you have the `inet_ntoa' function. */
+#cmakedefine HAVE_INET_NTOA	${HAVE_INET_NTOA}
+
+/* Define if you have the `inet_ntoa_r' function. */
+#cmakedefine HAVE_INET_NTOA_R	${HAVE_INET_NTOA_R}
+
+/* Define if you have the <inttypes.h> header file. */
+#cmakedefine HAVE_INTTYPES_H	${HAVE_INTTYPES_H}
+
+/* Define if you have the <io.h> header file. */
+#cmakedefine HAVE_IO_H	${HAVE_IO_H}
+
+/* Define if you have the `krb_get_our_ip_for_realm' function. */
+#cmakedefine HAVE_KRB_GET_OUR_IP_FOR_REALM	${HAVE_KRB_GET_OUR_IP_FOR_REALM}
+/* Define if you have the <krb.h> header file. */
+#cmakedefine HAVE_KRB_H	${HAVE_KRB_H}
+
+/* Define if you have the `crypto' library (-lcrypto). */
+#cmakedefine HAVE_LIBCRYPTO	${HAVE_LIBCRYPTO}
+
+/* Define if you have the `dl' library (-ldl). */
+#cmakedefine HAVE_LIBDL	${HAVE_LIBDL}
+
+/* Define if you have the `nsl' library (-lnsl). */
+#cmakedefine HAVE_LIBNSL	${HAVE_LIBNSL}
+
+/* Define if you have the `resolv' library (-lresolv). */
+#cmakedefine HAVE_LIBRESOLV	${HAVE_LIBRESOLV}
+
+/* Define if you have the `resolve' library (-lresolve). */
+#cmakedefine HAVE_LIBRESOLVE	${HAVE_LIBRESOLVE}
+
+/* Define if you have the `socket' library (-lsocket). */
+#cmakedefine HAVE_LIBSOCKET	${HAVE_LIBSOCKET}
+
+/* Define if you have the `ssl' library (-lssl). */
+#cmakedefine HAVE_LIBSSL	${HAVE_LIBSSL}
+
+/* Define if you have the `ucb' library (-lucb). */
+#cmakedefine HAVE_LIBUCB	${HAVE_LIBUCB}
+
+/* Define if you have the `localtime_r' function. */
+#cmakedefine HAVE_LOCALTIME_R	${HAVE_LOCALTIME_R}
+
+/* Define if you have the <malloc.h> header file. */
+#cmakedefine HAVE_MALLOC_H	${HAVE_MALLOC_H}
+
+/* Define if you have the <memory.h> header file. */
+#cmakedefine HAVE_MEMORY_H	${HAVE_MEMORY_H}
+
+/* Define if you have the <netdb.h> header file. */
+#cmakedefine HAVE_NETDB_H	${HAVE_NETDB_H}
+
+/* Define if you have the <netinet/if_ether.h> header file. */
+#cmakedefine HAVE_NETINET_IF_ETHER_H	${HAVE_NETINET_IF_ETHER_H}
+
+/* Define if you have the <netinet/in.h> header file. */
+#cmakedefine HAVE_NETINET_IN_H	${HAVE_NETINET_IN_H}
+
+/* Define if you have the <net/if.h> header file. */
+#cmakedefine HAVE_NET_IF_H	${HAVE_NET_IF_H}
+
+/* Define if you have the <openssl/crypto.h> header file. */
+#cmakedefine HAVE_OPENSSL_CRYPTO_H	${HAVE_OPENSSL_CRYPTO_H}
+
+/* Define if you have the <openssl/engine.h> header file. */
+#cmakedefine HAVE_OPENSSL_ENGINE_H	${HAVE_OPENSSL_ENGINE_H}
+
+/* Define if you have the <openssl/err.h> header file. */
+#cmakedefine HAVE_OPENSSL_ERR_H	${HAVE_OPENSSL_ERR_H}
+
+/* Define if you have the <openssl/pem.h> header file. */
+#cmakedefine HAVE_OPENSSL_PEM_H	${HAVE_OPENSSL_PEM_H}
+
+/* Define if you have the <openssl/rsa.h> header file. */
+#cmakedefine HAVE_OPENSSL_RSA_H	${HAVE_OPENSSL_RSA_H}
+
+/* Define if you have the <openssl/ssl.h> header file. */
+#cmakedefine HAVE_OPENSSL_SSL_H	${HAVE_OPENSSL_SSL_H}
+
+/* Define if you have the <openssl/x509.h> header file. */
+#cmakedefine HAVE_OPENSSL_X509_H	${HAVE_OPENSSL_X509_H}
+
+/* Define if you have the <pem.h> header file. */
+#cmakedefine HAVE_PEM_H	${HAVE_PEM_H}
+
+/* Define if you have the `perror' function. */
+#cmakedefine HAVE_PERROR	${HAVE_PERROR}
+
+/* Define if you have the <pwd.h> header file. */
+#cmakedefine HAVE_PWD_H	${HAVE_PWD_H}
+
+/* Define if you have the `RAND_egd' function. */
+#cmakedefine HAVE_RAND_EGD	${HAVE_RAND_EGD}
+
+/* Define if you have the `RAND_screen' function. */
+#cmakedefine HAVE_RAND_SCREEN	${HAVE_RAND_SCREEN}
+
+/* Define if you have the `RAND_status' function. */
+#cmakedefine HAVE_RAND_STATUS	${HAVE_RAND_STATUS}
+
+/* Define if you have the <rsa.h> header file. */
+#cmakedefine HAVE_RSA_H	${HAVE_RSA_H}
+
+/* Define if you have the `select' function. */
+#cmakedefine HAVE_SELECT	${HAVE_SELECT}
+
+/* Define if you have the `setvbuf' function. */
+#cmakedefine HAVE_SETVBUF	${HAVE_SETVBUF}
+
+/* Define if you have the <sgtty.h> header file. */
+#cmakedefine HAVE_SGTTY_H	${HAVE_SGTTY_H}
+
+/* Define if you have the `sigaction' function. */
+#cmakedefine HAVE_SIGACTION	${HAVE_SIGACTION}
+
+/* Define if you have the `signal' function. */
+#cmakedefine HAVE_SIGNAL	${HAVE_SIGNAL}
+
+/* Define if you have the `socket' function. */
+#cmakedefine HAVE_SOCKET	${HAVE_SOCKET}
+
+/* Define if you have the <ssl.h> header file. */
+#cmakedefine HAVE_SSL_H	${HAVE_SSL_H}
+
+/* Define if you have the <stdint.h> header file. */
+#cmakedefine HAVE_STDINT_H	${HAVE_STDINT_H}
+
+/* Define if you have the <stdlib.h> header file. */
+#cmakedefine HAVE_STDLIB_H	${HAVE_STDLIB_H}
+
+/* Define if you have the `strcasecmp' function. */
+#cmakedefine HAVE_STRCASECMP	${HAVE_STRCASECMP}
+
+/* Define if you have the `strcmpi' function. */
+#cmakedefine HAVE_STRCMPI	${HAVE_STRCMPI}
+
+/* Define if you have the `strdup' function. */
+#cmakedefine HAVE_STRDUP	${HAVE_STRDUP}
+
+/* Define if you have the `strftime' function. */
+#cmakedefine HAVE_STRFTIME	${HAVE_STRFTIME}
+
+/* Define if you have the `stricmp' function. */
+#cmakedefine HAVE_STRICMP	${HAVE_STRICMP}
+
+/* Define if you have the <strings.h> header file. */
+#cmakedefine HAVE_STRINGS_H	${HAVE_STRINGS_H}
+
+/* Define if you have the <string.h> header file. */
+#cmakedefine HAVE_STRING_H	${HAVE_STRING_H}
+
+/* Define if you have the `strlcat' function. */
+#cmakedefine HAVE_STRLCAT	${HAVE_STRLCAT}
+
+/* Define if you have the `strlcpy' function. */
+#cmakedefine HAVE_STRLCPY	${HAVE_STRLCPY}
+
+/* Define if you have the `strstr' function. */
+#cmakedefine HAVE_STRSTR	${HAVE_STRSTR}
+
+/* Define if you have the `strtok_r' function. */
+#cmakedefine HAVE_STRTOK_R	${HAVE_STRTOK_R}
+
+/* Define if you have the <sys/param.h> header file. */
+#cmakedefine HAVE_SYS_PARAM_H	${HAVE_SYS_PARAM_H}
+
+/* Define if you have the <sys/select.h> header file. */
+#cmakedefine HAVE_SYS_SELECT_H	${HAVE_SYS_SELECT_H}
+
+/* Define if you have the <sys/socket.h> header file. */
+#cmakedefine HAVE_SYS_SOCKET_H	${HAVE_SYS_SOCKET_H}
+
+/* Define if you have the <sys/sockio.h> header file. */
+#cmakedefine HAVE_SYS_SOCKIO_H	${HAVE_SYS_SOCKIO_H}
+
+/* Define if you have the <sys/stat.h> header file. */
+#cmakedefine HAVE_SYS_STAT_H	${HAVE_SYS_STAT_H}
+
+/* Define if you have the <sys/time.h> header file. */
+#cmakedefine HAVE_SYS_TIME_H	${HAVE_SYS_TIME_H}
+
+/* Define if you have the <sys/types.h> header file. */
+#cmakedefine HAVE_SYS_TYPES_H	${HAVE_SYS_TYPES_H}
+
+/* Define if you have the <sys/utime.h> header file. */
+#cmakedefine HAVE_SYS_UTIME_H	${HAVE_SYS_UTIME_H}
+
+/* Define if you have the `tcgetattr' function. */
+#cmakedefine HAVE_TCGETATTR	${HAVE_TCGETATTR}
+
+/* Define if you have the `tcsetattr' function. */
+#cmakedefine HAVE_TCSETATTR	${HAVE_TCSETATTR}
+
+/* Define if you have the <termios.h> header file. */
+#cmakedefine HAVE_TERMIOS_H	${HAVE_TERMIOS_H}
+
+/* Define if you have the <termio.h> header file. */
+#cmakedefine HAVE_TERMIO_H	${HAVE_TERMIO_H}
+
+/* Define if you have the <time.h> header file. */
+#cmakedefine HAVE_TIME_H	${HAVE_TIME_H}
+
+/* Define if you have the `uname' function. */
+#cmakedefine HAVE_UNAME	${HAVE_UNAME}
+
+/* Define if you have the <unistd.h> header file. */
+#cmakedefine HAVE_UNISTD_H	${HAVE_UNISTD_H}
+
+/* Define if you have the `utime' function. */
+#cmakedefine HAVE_UTIME	${HAVE_UTIME}
+
+/* Define if you have the <utime.h> header file. */
+#cmakedefine HAVE_UTIME_H	${HAVE_UTIME_H}
+
+/* Define if you have the <winsock.h> header file. */
+#cmakedefine HAVE_WINSOCK_H	${HAVE_WINSOCK_H}
+
+/* Define if you have the <x509.h> header file. */
+#cmakedefine HAVE_X509_H	${HAVE_X509_H}
+
+/* Name of package */
+#cmakedefine PACKAGE	"${PACKAGE}"
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#cmakedefine RETSIGTYPE	${RETSIGTYPE}
+
+/* Define if you have the ANSI C header files. */
+#cmakedefine STDC_HEADERS	${STDC_HEADERS}
+
+/* Define if you can safely include both <sys/time.h> and <time.h>. */
+#cmakedefine TIME_WITH_SYS_TIME	${TIME_WITH_SYS_TIME}
+
+/* Version number of package */
+#cmakedefine VERSION	"${VERSION}"
+
+/* Define if on AIX 3.
+   System headers sometimes define this.
+   We just want to avoid a redefinition error message.  */
+#ifndef _ALL_SOURCE
+# undef _ALL_SOURCE
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#cmakedefine _FILE_OFFSET_BITS	${_FILE_OFFSET_BITS}
+
+/* Define for large files, on AIX-style hosts. */
+#cmakedefine _LARGE_FILES	${_LARGE_FILES}
+
+/* Define to empty if `const' does not conform to ANSI C. */
+#cmakedefine const	${const}
+
+/* type to use in place of in_addr_t if not defined */
+#cmakedefine in_addr_t	${in_addr_t}
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+#cmakedefine size_t	${size_t}
+
+/* type to use in place of socklen_t if not defined */
+#cmakedefine socklen_t	${socklen_t}
+
+/* Define to `int' if <sys/types.h> does not define. */
+#cmakedefine ssize_t	${ssize_t}
+
+/* Define if you don't have vprintf but do have _doprnt.  */
+#cmakedefine HAVE_DOPRNT ${HAVE_DOPRNT}
+
+/* Define if you have the vprintf function.  */
+#cmakedefine HAVE_VPRINTF ${HAVE_VPRINTF}
+
+/* The number of bytes in a long double.  */
+#cmakedefine SIZEOF_LONG_DOUBLE ${SIZEOF_LONG_DOUBLE}

+ 581 - 0
Source/CTest/Curl/connect.c

@@ -0,0 +1,581 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2001, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+#ifndef WIN32
+/* headers for non-win32 */
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#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_STDLIB_H
+#include <stdlib.h> /* required for free() prototype, without it, this crashes
+                       on macos 68K */
+#endif
+#ifdef  VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#endif
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+#ifdef WIN32
+#define HAVE_IOCTLSOCKET
+#include <windows.h>
+#include <winsock.h>
+#define EINPROGRESS WSAEINPROGRESS
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#define EISCONN     WSAEISCONN
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "if2ip.h"
+
+/* The last #include file should be: */
+#ifdef MALLOCDEBUG
+#include "memdebug.h"
+#endif
+
+static
+int geterrno(void)
+{
+#ifdef WIN32
+  return (int)GetLastError();
+#else
+  return errno;
+#endif
+}
+
+/*************************************************************************
+ * Curl_nonblock
+ *
+ * Description:
+ *  Set the socket to either blocking or non-blocking mode.
+ */
+
+int Curl_nonblock(int socket,    /* operate on this */
+                  int nonblock   /* TRUE or FALSE */)
+{
+#undef SETBLOCK
+#ifdef HAVE_O_NONBLOCK
+  int flags;
+
+  flags = fcntl(socket, F_GETFL, 0);
+  if (TRUE == nonblock)
+    return fcntl(socket, F_SETFL, flags | O_NONBLOCK);
+  else
+    return fcntl(socket, F_SETFL, flags & (~O_NONBLOCK));
+#define SETBLOCK 1
+#endif
+
+#ifdef HAVE_FIONBIO
+  int flags;
+
+  flags = nonblock;
+  return ioctl(socket, FIONBIO, &flags);
+#define SETBLOCK 2
+#endif
+
+#ifdef HAVE_IOCTLSOCKET
+  int flags;
+  flags = nonblock;
+  return ioctlsocket(socket, FIONBIO, &flags);
+#define SETBLOCK 3
+#endif
+
+#ifdef HAVE_IOCTLSOCKET_CASE
+  return IoctlSocket(socket, FIONBIO, (long)nonblock);
+#define SETBLOCK 4
+#endif
+
+#ifdef HAVE_DISABLED_NONBLOCKING
+  return 0; /* returns success */
+#define SETBLOCK 5
+#endif
+
+#ifndef SETBLOCK
+#error "no non-blocking method was found/used/set"
+#endif
+}
+
+/*
+ * Return 0 on fine connect, -1 on error and 1 on timeout.
+ */
+static
+int waitconnect(int sockfd, /* socket */
+                int timeout_msec)
+{
+  fd_set fd;
+  fd_set errfd;
+  struct timeval interval;
+  int rc;
+
+  /* now select() until we get connect or timeout */
+  FD_ZERO(&fd);
+  FD_SET(sockfd, &fd);
+
+  FD_ZERO(&errfd);
+  FD_SET(sockfd, &errfd);
+
+  interval.tv_sec = timeout_msec/1000;
+  timeout_msec -= interval.tv_sec*1000;
+
+  interval.tv_usec = timeout_msec*1000;
+
+  rc = select(sockfd+1, NULL, &fd, &errfd, &interval);
+  if(-1 == rc)
+    /* error, no connect here, try next */
+    return -1;
+  
+  else if(0 == rc)
+    /* timeout, no connect today */
+    return 1;
+
+  if(FD_ISSET(sockfd, &errfd)) {
+    /* error condition caught */
+    return 2;
+  }
+
+  /* we have a connect! */
+  return 0;
+}
+
+#ifndef ENABLE_IPV6
+static CURLcode bindlocal(struct connectdata *conn,
+                          int sockfd)
+{
+#if !defined(WIN32)||defined(__CYGWIN32__)
+  /* We don't generally like checking for OS-versions, we should make this
+     HAVE_XXXX based, although at the moment I don't have a decent test for
+     this! */
+
+#ifdef HAVE_INET_NTOA
+
+#ifndef INADDR_NONE
+#define INADDR_NONE (in_addr_t) ~0
+#endif
+
+  struct SessionHandle *data = conn->data;
+
+  /*************************************************************
+   * Select device to bind socket to
+   *************************************************************/
+  if (strlen(data->set.device)<255) {
+    struct sockaddr_in sa;
+    struct hostent *h=NULL;
+    char *hostdataptr=NULL;
+    size_t size;
+    char myhost[256] = "";
+    in_addr_t in;
+
+    if(Curl_if2ip(data->set.device, myhost, sizeof(myhost))) {
+      h = Curl_resolv(data, myhost, 0, &hostdataptr);
+    }
+    else {
+      if(strlen(data->set.device)>1) {
+        h = Curl_resolv(data, data->set.device, 0, &hostdataptr);
+      }
+      if(h) {
+        /* we know data->set.device is shorter than the myhost array */
+        strcpy(myhost, data->set.device);
+      }
+    }
+
+    if(! *myhost) {
+      /* need to fix this
+         h=Curl_gethost(data,
+         getmyhost(*myhost,sizeof(myhost)),
+         hostent_buf,
+         sizeof(hostent_buf));
+      */
+      return CURLE_HTTP_PORT_FAILED;
+    }
+
+    infof(data, "We bind local end to %s\n", myhost);
+
+    in=inet_addr(myhost);
+    if (INADDR_NONE != in) {
+
+      if ( h ) {
+        memset((char *)&sa, 0, sizeof(sa));
+        memcpy((char *)&sa.sin_addr,
+               h->h_addr,
+               h->h_length);
+        sa.sin_family = AF_INET;
+        sa.sin_addr.s_addr = in;
+        sa.sin_port = 0; /* get any port */
+        
+        if( bind(sockfd, (struct sockaddr *)&sa, sizeof(sa)) >= 0) {
+          /* we succeeded to bind */
+          struct sockaddr_in add;
+        
+          size = sizeof(add);
+          if(getsockname(sockfd, (struct sockaddr *) &add,
+                         (socklen_t *)&size)<0) {
+            failf(data, "getsockname() failed");
+            return CURLE_HTTP_PORT_FAILED;
+          }
+        }
+        else {
+          switch(errno) {
+          case EBADF:
+            failf(data, "Invalid descriptor: %d", errno);
+            break;
+          case EINVAL:
+            failf(data, "Invalid request: %d", errno);
+            break;
+          case EACCES:
+            failf(data, "Address is protected, user not superuser: %d", errno);
+            break;
+          case ENOTSOCK:
+            failf(data,
+                  "Argument is a descriptor for a file, not a socket: %d",
+                  errno);
+            break;
+          case EFAULT:
+            failf(data, "Inaccessable memory error: %d", errno);
+            break;
+          case ENAMETOOLONG:
+            failf(data, "Address too long: %d", errno);
+            break;
+          case ENOMEM:
+            failf(data, "Insufficient kernel memory was available: %d", errno);
+            break;
+          default:
+            failf(data, "errno %d", errno);
+            break;
+          } /* end of switch(errno) */
+        
+          return CURLE_HTTP_PORT_FAILED;
+        } /* end of else */
+        
+      } /* end of if  h */
+      else {
+        failf(data,"could't find my own IP address (%s)", myhost);
+        return CURLE_HTTP_PORT_FAILED;
+      }
+    } /* end of inet_addr */
+
+    else {
+      failf(data, "could't find my own IP address (%s)", myhost);
+      return CURLE_HTTP_PORT_FAILED;
+    }
+
+    return CURLE_OK;
+
+  } /* end of device selection support */
+#endif /* end of HAVE_INET_NTOA */
+#endif /* end of not WIN32 */
+
+  return CURLE_HTTP_PORT_FAILED;
+}
+#endif /* end of ipv4-specific section */
+
+static
+int socketerror(int sockfd)
+{
+  int err = 0;
+  socklen_t errSize = sizeof(err);
+
+  if( -1 == getsockopt(sockfd, SOL_SOCKET, SO_ERROR,
+                       (void *)&err, &errSize))
+    err = geterrno();
+  
+  return err;
+}
+
+/*
+ * 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 */
+                          Curl_addrinfo *remotehost, /* use one in here */
+                          int port,                  /* connect to this */
+                          int *sockconn,             /* the connected socket */
+                          Curl_ipconnect **addr)     /* the one we used */
+{
+  struct SessionHandle *data = conn->data;
+  int rc;
+  int sockfd=-1;
+  int aliasindex=0;
+
+  struct timeval after;
+  struct timeval before = Curl_tvnow();
+
+  /*************************************************************
+   * Figure out what maximum time we have left
+   *************************************************************/
+  long timeout_ms=300000; /* milliseconds, default to five minutes */
+  if(data->set.timeout || data->set.connecttimeout) {
+    double has_passed;
+
+    /* Evaluate in milliseconds how much time that has passed */
+    has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start);
+
+#ifndef min
+#define min(a, b)   ((a) < (b) ? (a) : (b))
+#endif
+
+    /* get the most strict timeout of the ones converted to milliseconds */
+    if(data->set.timeout && data->set.connecttimeout) {
+      if (data->set.timeout < data->set.connecttimeout)
+        timeout_ms = data->set.timeout*1000;
+      else 
+        timeout_ms = data->set.connecttimeout*1000;
+    }
+    else if(data->set.timeout)
+      timeout_ms = data->set.timeout*1000;
+    else
+      timeout_ms = data->set.connecttimeout*1000;
+
+    /* subtract the passed time */
+    timeout_ms -= (long)has_passed;
+
+    if(timeout_ms < 0) {
+      /* a precaution, no need to continue if time already is up */
+      failf(data, "Connection time-out");
+      return CURLE_OPERATION_TIMEOUTED;
+    }
+  }
+
+#ifdef ENABLE_IPV6
+  /*
+   * Connecting with IPv6 support is so much easier and cleanly done
+   */
+  {
+    struct addrinfo *ai;
+    port =0; /* prevent compiler warning */
+
+    for (ai = remotehost; ai; ai = ai->ai_next, aliasindex++) {
+      sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+      if (sockfd < 0)
+        continue;
+
+      /* set socket non-blocking */
+      Curl_nonblock(sockfd, TRUE);
+
+      rc = connect(sockfd, ai->ai_addr, ai->ai_addrlen);
+
+      if(-1 == rc) {
+        int error=geterrno();
+
+        switch (error) {
+        case EINPROGRESS:
+        case EWOULDBLOCK:
+#if defined(EAGAIN) && 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
+        case EINTR:
+
+          /* asynchronous connect, wait for connect or timeout */
+          rc = waitconnect(sockfd, timeout_ms);
+          break;
+        case ECONNREFUSED: /* no one listening */
+        default:
+          /* unknown error, fallthrough and try another address! */
+          failf(data, "Failed to connect");
+          break;
+        }
+      }
+      if(0 == rc) {
+        /* we might be connected, if the socket says it is OK! Ask it! */
+        int err;
+
+        err = socketerror(sockfd);
+        if ((0 == err) || (EISCONN == err)) {
+          /* we are connected, awesome! */
+          break;
+        }
+        /* we are _not_ connected, it was a false alert, continue please */
+      }
+
+      /* connect failed or timed out */
+      sclose(sockfd);
+      sockfd = -1;
+
+      /* 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_TIMEOUTED;
+      }
+      before = after;
+      continue;
+    }
+    if (sockfd < 0) {
+      failf(data, "connect() failed");
+      return CURLE_COULDNT_CONNECT;
+    }
+
+    /* leave the socket in non-blocking mode */
+
+    if(addr)
+      *addr = ai; /* the address we ended up connected to */
+  }
+#else
+  /*
+   * Connecting with IPv4-only support
+   */
+  if(!remotehost->h_addr_list[0]) {
+    /* If there is no addresses in the address list, then we return
+       error right away */
+    failf(data, "no address available");
+    return CURLE_COULDNT_CONNECT;
+  }
+  /* create an IPv4 TCP socket */
+  sockfd = socket(AF_INET, SOCK_STREAM, 0);
+  if(-1 == sockfd) {
+    failf(data, "couldn't create socket");
+    return CURLE_COULDNT_CONNECT; /* big time error */
+  }
+  
+  if(conn->data->set.device) {
+    /* user selected to bind the outgoing socket to a specified "device"
+       before doing connect */
+    CURLcode res = bindlocal(conn, sockfd);
+    if(res)
+      return res;
+  }
+
+  /* Convert socket to non-blocking type */
+  Curl_nonblock(sockfd, TRUE);
+
+  /* This is the loop that attempts to connect to all IP-addresses we
+     know for the given host. One by one. */
+  for(rc=-1, aliasindex=0;
+      rc && (struct in_addr *)remotehost->h_addr_list[aliasindex];
+      aliasindex++) {
+    struct sockaddr_in serv_addr;
+
+    /* do this nasty work to do the connect */
+    memset((char *) &serv_addr, '\0', sizeof(serv_addr));
+    memcpy((char *)&(serv_addr.sin_addr),
+           (struct in_addr *)remotehost->h_addr_list[aliasindex],
+           sizeof(struct in_addr));
+    serv_addr.sin_family = remotehost->h_addrtype;
+    serv_addr.sin_port = htons(port);
+  
+    rc = connect(sockfd, (struct sockaddr *)&serv_addr,
+                 sizeof(serv_addr));
+
+    if(-1 == rc) {
+      int error=geterrno();
+
+      switch (error) {
+      case EINPROGRESS:
+      case EWOULDBLOCK:
+#if defined(EAGAIN) && 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
+
+        /* asynchronous connect, wait for connect or timeout */
+        rc = waitconnect(sockfd, timeout_ms);
+        break;
+      default:
+        /* unknown error, fallthrough and try another address! */
+        failf(data, "Failed to connect to IP number %d", aliasindex+1);
+        break;
+      }
+    }
+
+    if(0 == rc) {
+      int err = socketerror(sockfd);
+      if ((0 == err) || (EISCONN == err)) {
+        /* we are connected, awesome! */
+        break;
+      }
+      /* nope, not connected for real */
+      rc = -1;
+    }
+
+    if(0 != rc) {
+      /* get a new timeout for next attempt */
+      after = Curl_tvnow();
+      timeout_ms -= Curl_tvdiff(after, before);
+      if(timeout_ms < 0) {
+        failf(data, "Connect timeout on IP number %d", aliasindex+1);
+        break;
+      }
+      before = after;
+      continue; /* try next address */
+    }
+    break;
+  }
+  if(0 != rc) {
+    /* no good connect was made */
+    sclose(sockfd);
+    *sockconn = -1;
+    failf(data, "Couldn't connect to host");
+    return CURLE_COULDNT_CONNECT;
+  }
+
+  /* leave the socket in non-blocking mode */
+
+  if(addr)
+    /* this is the address we've connected to */
+    *addr = (struct in_addr *)remotehost->h_addr_list[aliasindex];
+#endif
+
+  /* allow NULL-pointers to get passed in */
+  if(sockconn)
+    *sockconn = sockfd;    /* the socket descriptor we've connected */
+
+  return CURLE_OK;
+}
+

+ 35 - 0
Source/CTest/Curl/connect.h

@@ -0,0 +1,35 @@
+#ifndef __CONNECT_H
+#define __CONNECT_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2001, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+int Curl_nonblock(int socket,    /* operate on this */
+                  int nonblock   /* TRUE or FALSE */);
+
+CURLcode Curl_connecthost(struct connectdata *conn,
+                          Curl_addrinfo *host, /* connect to this */
+                          int port,       /* connect to this port number */
+                          int *sockconn,  /* not set if error is returned */
+                          Curl_ipconnect **addr /* the one we used */
+                          ); /*  index we used */
+#endif

+ 738 - 0
Source/CTest/Curl/cookie.c

@@ -0,0 +1,738 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2002, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+/***
+
+
+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 "setup.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "cookie.h"
+#include "getdate.h"
+#include "strequal.h"
+#include "strtok.h"
+
+/* The last #include file should be: */
+#ifdef MALLOCDEBUG
+#include "memdebug.h"
+#endif
+
+/****************************************************************************
+ *
+ * Curl_cookie_add()
+ *
+ * Add a single cookie line to the cookie keeping object.
+ *
+ ***************************************************************************/
+
+struct Cookie *
+Curl_cookie_add(struct CookieInfo *c,
+                bool httpheader, /* TRUE if HTTP header-style line */
+                char *lineptr,   /* first non-space of the line */
+                char *domain)    /* default domain */
+{
+  struct Cookie *clist;
+  char what[MAX_COOKIE_LINE];
+  char name[MAX_NAME];
+  char *ptr;
+  char *semiptr;
+  struct Cookie *co;
+  struct Cookie *lastc=NULL;
+  time_t now = time(NULL);
+  bool replace_old = FALSE;
+
+  /* First, alloc and init a new struct for it */
+  co = (struct Cookie *)malloc(sizeof(struct Cookie));
+  if(!co)
+    return NULL; /* bail out if we're this low on memory */
+
+  /* clear the whole struct first */
+  memset(co, 0, sizeof(struct Cookie));
+            
+  if(httpheader) {
+    /* This line was read off a HTTP-header */
+    char *sep;
+    semiptr=strchr(lineptr, ';'); /* first, find a semicolon */
+    ptr = lineptr;
+    do {
+      /* we have a <what>=<this> pair or a 'secure' word here */
+      sep = strchr(ptr, '=');
+      if(sep && (!semiptr || (semiptr>sep)) ) {
+        /*
+         * There is a = sign and if there was a semicolon too, which make sure
+         * that the semicolon comes _after_ the equal sign.
+         */
+
+        name[0]=what[0]=0; /* init the buffers */
+        if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;=]=%"
+                       MAX_COOKIE_LINE_TXT "[^;\r\n]",
+                       name, what)) {
+          /* this is a <name>=<what> pair */
+
+          /* Strip off trailing whitespace from the 'what' */
+          int len=strlen(what);
+          while(len && isspace((int)what[len-1])) {
+            what[len-1]=0;
+            len--;
+          }
+
+          if(strequal("path", name)) {
+            co->path=strdup(what);
+          }
+          else if(strequal("domain", name)) {
+            co->domain=strdup(what);
+            co->field1= (what[0]=='.')?2:1;
+          }
+          else if(strequal("version", name)) {
+            co->version=strdup(what);
+          }
+          else if(strequal("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.
+
+             */
+            co->maxage = strdup(what);
+            co->expires =
+              atoi((*co->maxage=='\"')?&co->maxage[1]:&co->maxage[0]) + now;
+          }
+          else if(strequal("expires", name)) {
+            co->expirestr=strdup(what);
+            co->expires = curl_getdate(what, &now);
+          }
+          else if(!co->name) {
+            co->name = strdup(name);
+            co->value = strdup(what);
+          }
+          /*
+            else this is the second (or more) name we don't know
+            about! */
+        }
+        else {
+          /* this is an "illegal" <what>=<this> pair */
+        }
+      }
+      else {
+        if(sscanf(ptr, "%" MAX_COOKIE_LINE_TXT "[^;\r\n]",
+                  what)) {
+          if(strequal("secure", what))
+            co->secure = TRUE;
+          /* else,
+             unsupported keyword without assign! */
+
+        }
+      }
+      if(!semiptr || !*semiptr) {
+        /* we already know there are no more cookies */
+        semiptr = NULL;
+        continue;
+      }
+
+      ptr=semiptr+1;
+      while(ptr && *ptr && isspace((int)*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(NULL == co->name) {
+      /* we didn't get a cookie name, this is an illegal line, bail out */
+      if(co->domain)
+        free(co->domain);
+      if(co->path)
+        free(co->path);
+      if(co->name)
+        free(co->name);
+      if(co->value)
+        free(co->value);
+      free(co);
+      return NULL;
+    }
+
+    if(NULL == co->domain)
+      /* no domain given in the header line, set the default now */
+      co->domain=domain?strdup(domain):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 *firstptr;
+    char *tok_buf;
+    int fields;
+
+    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); /* first 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; ptr=strtok_r(NULL, "\t", &tok_buf), fields++) {
+      switch(fields) {
+      case 0:
+        co->domain = strdup(ptr);
+        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
+
+           We don't currently take advantage of this knowledge.
+        */
+        co->field1=strequal(ptr, "TRUE")+1; /* store information */
+        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);
+          break;
+        }
+        /* this doesn't look like a path, make one up! */
+        co->path = strdup("/");
+        fields++; /* add a field and fall down to secure */
+        /* FALLTHROUGH */
+      case 3:
+        co->secure = strequal(ptr, "TRUE");
+        break;
+      case 4:
+        co->expires = atoi(ptr);
+        break;
+      case 5:
+        co->name = strdup(ptr);
+        break;
+      case 6:
+        co->value = strdup(ptr);
+        break;
+      }
+    }
+
+    if(7 != fields) {
+      /* we did not find the sufficient number of fields to recognize this
+         as a valid line, abort and go home */
+
+      if(co->domain)
+        free(co->domain);
+      if(co->path)
+        free(co->path);
+      if(co->name)
+        free(co->name);
+      if(co->value)
+        free(co->value);
+
+      free(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(strequal(clist->name, co->name)) {
+      /* the names are identical */
+
+      if(clist->domain && co->domain) {
+        if(strequal(clist->domain, co->domain))
+          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(strequal(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! */
+        if(co->domain)
+          free(co->domain);
+        if(co->path)
+          free(co->path);
+        if(co->name)
+          free(co->name);
+        if(co->value)
+          free(co->value);
+
+        free(co);
+        return NULL;
+      }
+
+      if(replace_old) {
+        co->next = clist->next; /* get the next-pointer first */
+
+        /* then free all the old pointers */
+        if(clist->name)
+          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(!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.
+ *
+ ****************************************************************************/
+struct CookieInfo *Curl_cookie_init(char *file, struct CookieInfo *inc)
+{
+  char line[MAX_COOKIE_LINE];
+  struct CookieInfo *c;
+  FILE *fp;
+  bool fromfile=TRUE;
+  
+  if(NULL == inc) {
+    /* we didn't get a struct, create one */
+    c = (struct CookieInfo *)malloc(sizeof(struct CookieInfo));
+    if(!c)
+      return NULL; /* failed to get memory */
+    memset(c, 0, sizeof(struct CookieInfo));
+    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
+    fp = file?fopen(file, "r"):NULL;
+
+  if(fp) {
+    char *lineptr;
+    bool headerline;
+    while(fgets(line, MAX_COOKIE_LINE, fp)) {
+      if(strnequal("Set-Cookie:", line, 11)) {
+        /* This is a cookie line, get it! */
+        lineptr=&line[11];
+        headerline=TRUE;
+      }
+      else {
+        lineptr=line;
+        headerline=FALSE;
+      }
+      while(*lineptr && isspace((int)*lineptr))
+        lineptr++;
+
+      Curl_cookie_add(c, headerline, lineptr, NULL);
+    }
+    if(fromfile)
+      fclose(fp);
+  }
+
+  c->running = TRUE; /* now, we're running */
+
+  return c;
+}
+
+/*****************************************************************************
+ *
+ * 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,
+                                   char *host, char *path, bool secure)
+{
+   struct Cookie *newco;
+   struct Cookie *co;
+   time_t now = time(NULL);
+   int hostlen=strlen(host);
+   int domlen;
+
+   struct Cookie *mainco=NULL;
+
+   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<=0 || (co->expires> now)) &&
+         (co->secure?secure:TRUE) ) {
+
+         /* now check if the domain is correct */
+         domlen=co->domain?strlen(co->domain):0;
+         if(!co->domain ||
+            ((domlen<=hostlen) &&
+             strequal(host+(hostlen-domlen), 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 ||
+               strnequal(path, co->path, strlen(co->path))) {
+
+               /* and now, we know this is a match and we should create an
+                  entry for the return-linked-list */
+
+               newco = (struct Cookie *)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;
+               }
+            }
+         }
+      }
+      co = co->next;
+   }
+
+   return mainco; /* return the new list */
+}
+
+
+/*****************************************************************************
+ *
+ * Curl_cookie_freelist()
+ *
+ * Free a list of cookies previously returned by Curl_cookie_getlist();
+ *
+ ****************************************************************************/
+
+void Curl_cookie_freelist(struct Cookie *co)
+{
+   struct Cookie *next;
+   if(co) {
+      while(co) {
+         next = co->next;
+         free(co); /* we only free the struct since the "members" are all
+                      just copied! */
+         co = next;
+      }
+   }
+}
+
+/*****************************************************************************
+ *
+ * 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) {
+         if(co->name)
+            free(co->name);
+         if(co->value)
+            free(co->value);
+         if(co->domain)
+            free(co->domain);
+         if(co->path)
+            free(co->path);
+         if(co->expirestr)
+            free(co->expirestr);
+
+         if(co->version)
+            free(co->version);
+         if(co->maxage)
+            free(co->maxage);
+
+         next = co->next;
+         free(co);
+         co = next;
+      }
+      free(c); /* free the base struct as well */
+   }
+}
+
+/*
+ * Curl_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.
+ */
+int Curl_cookie_output(struct CookieInfo *c, 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) {
+    fputs("# Netscape HTTP Cookie File\n"
+          "# http://www.netscape.com/newsref/std/cookie_spec.html\n"
+          "# This file was generated by libcurl! Edit at your own risk.\n\n",
+          out);
+    co = c->cookies;
+     
+    while(co) {
+      fprintf(out,
+              "%s\t" /* domain */
+              "%s\t" /* field1 */
+              "%s\t" /* path */
+              "%s\t" /* secure */
+              "%u\t" /* expires */
+              "%s\t" /* name */
+              "%s\n", /* value */
+              co->domain?co->domain:"unknown",
+              co->field1==2?"TRUE":"FALSE",
+              co->path?co->path:"/",
+              co->secure?"TRUE":"FALSE",
+              (unsigned int)co->expires,
+              co->name,
+              co->value?co->value:"");
+
+      co=co->next;
+    }
+  }
+
+  if(!use_stdout)
+    fclose(out);
+
+  return 0;
+}
+
+#ifdef CURL_COOKIE_DEBUG
+
+/*
+ * On my Solaris box, this command line builds this test program:
+ *
+ * gcc -g -o cooktest -DCURL_COOKIE_DEBUG -DHAVE_CONFIG_H -I.. -I../include cookie.c strequal.o getdate.o memdebug.o mprintf.o strtok.o -lnsl -lsocket
+ *
+ */
+
+int main(int argc, char **argv)
+{
+  struct CookieInfo *c=NULL;
+  if(argc>1) {
+    c = Curl_cookie_init(argv[1], c);
+    Curl_cookie_add(c, TRUE, "PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/ftgw; secure");
+    Curl_cookie_add(c, TRUE, "foobar=yes; domain=.haxx.se; path=/looser;");
+    c = Curl_cookie_init(argv[1], c);
+
+    Curl_cookie_output(c);
+    Curl_cookie_cleanup(c);
+    return 0;
+  }
+  return 1;
+}
+
+#endif
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 84 - 0
Source/CTest/Curl/cookie.h

@@ -0,0 +1,84 @@
+#ifndef __COOKIE_H
+#define __COOKIE_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include <stdio.h>
+#ifdef WIN32
+#include <time.h>
+#else
+#include <sys/time.h>
+#endif
+
+#include <curl/curl.h>
+
+struct Cookie {
+  struct Cookie *next; /* next in the chain */
+  char *name;        /* <this> = value */
+  char *value;       /* name = <this> */
+  char *path;         /* path = <this> */
+  char *domain;      /* domain = <this> */
+  long expires;    /* expires = <this> */
+  char *expirestr;   /* the plain text version */
+
+  char field1;       /* read from a cookie file, 1 => FALSE, 2=> TRUE */
+  
+  /* RFC 2109 keywords. Version=1 means 2109-compliant cookie sending */
+  char *version;     /* Version = <value> */
+  char *maxage;      /* Max-Age = <value> */
+  
+  bool secure;       /* whether the 'secure' keyword was used */
+  bool livecookie;   /* updated from a server, not a stored file */
+};
+
+struct CookieInfo {
+  /* linked list of cookies we know of */
+  struct Cookie *cookies;
+
+  char *filename; /* file we read from/write to */
+  bool running;   /* state info, for cookie adding information */
+  long numcookies; /* number of cookies in the "jar" */
+};
+
+/* This is the maximum line length we accept for a cookie line */
+#define MAX_COOKIE_LINE 2048
+#define MAX_COOKIE_LINE_TXT "2047"
+
+/* This is the maximum length of a cookie name we deal with: */
+#define MAX_NAME 256
+#define MAX_NAME_TXT "255"
+
+/*
+ * Add a cookie to the internal list of cookies. The domain argument is only
+ * used if the header boolean is TRUE.
+ */
+struct Cookie *Curl_cookie_add(struct CookieInfo *, bool header, char *line,
+                               char *domain);
+
+struct CookieInfo *Curl_cookie_init(char *, struct CookieInfo *);
+struct Cookie *Curl_cookie_getlist(struct CookieInfo *, char *, char *, bool);
+void Curl_cookie_freelist(struct Cookie *);
+void Curl_cookie_cleanup(struct CookieInfo *);
+int Curl_cookie_output(struct CookieInfo *, char *);
+
+#endif

+ 241 - 0
Source/CTest/Curl/dict.c

@@ -0,0 +1,241 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+/* -- WIN32 approved -- */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+
+#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
+#include <winsock.h>
+#include <time.h>
+#include <io.h>
+#else
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <netdb.h>
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#include <sys/ioctl.h>
+#include <signal.h>
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+
+#endif
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "transfer.h"
+#include "sendf.h"
+
+#include "progress.h"
+#include "strequal.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+CURLcode Curl_dict(struct connectdata *conn)
+{
+  int nth;
+  char *word;
+  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;
+
+  char *path = conn->path;
+  long *bytecount = &conn->bytecount;
+
+  if(conn->bits.user_passwd) {
+    /* AUTH is missing */
+  }
+
+  if (strnequal(path, DICT_MATCH, sizeof(DICT_MATCH)-1) ||
+      strnequal(path, DICT_MATCH2, sizeof(DICT_MATCH2)-1) ||
+      strnequal(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)) {
+      failf(data, "lookup word is missing");
+    }
+    if ((database == NULL) || (*database == (char)0)) {
+      database = (char *)"!";
+    }
+    if ((strategy == NULL) || (*strategy == (char)0)) {
+      strategy = (char *)".";
+    }
+    if ((nthdef == NULL) || (*nthdef == (char)0)) {
+      nth = 0;
+    }
+    else {
+      nth = atoi(nthdef);
+    }
+      
+    result = Curl_sendf(conn->firstsocket, conn,
+                        "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\n"
+                        "MATCH "
+                        "%s "    /* database */
+                        "%s "    /* strategy */
+                        "%s\n"   /* word */
+                        "QUIT\n",
+                        
+                        database,
+                        strategy,
+                        word
+                        );
+    if(result)
+      failf(data, "Failed sending DICT request");
+    else
+      result = Curl_Transfer(conn, conn->firstsocket, -1, FALSE, bytecount,
+                             -1, NULL); /* no upload */      
+    if(result)
+      return result;
+  }
+  else if (strnequal(path, DICT_DEFINE, sizeof(DICT_DEFINE)-1) ||
+           strnequal(path, DICT_DEFINE2, sizeof(DICT_DEFINE2)-1) ||
+           strnequal(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)) {
+      failf(data, "lookup word is missing");
+    }
+    if ((database == NULL) || (*database == (char)0)) {
+      database = (char *)"!";
+    }
+    if ((nthdef == NULL) || (*nthdef == (char)0)) {
+      nth = 0;
+    }
+    else {
+      nth = atoi(nthdef);
+    }
+      
+    result = Curl_sendf(conn->firstsocket, conn,
+                        "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\n"
+                        "DEFINE "
+                        "%s "     /* database */
+                        "%s\n"    /* word */
+                        "QUIT\n",
+                        database,
+                        word);
+    if(result)
+      failf(data, "Failed sending DICT request");
+    else
+      result = Curl_Transfer(conn, conn->firstsocket, -1, FALSE, bytecount,
+                             -1, NULL); /* no upload */
+    
+    if(result)
+      return result;
+      
+  }
+  else {
+      
+    ppath = strchr(path, '/');
+    if (ppath) {
+      int i;
+        
+      ppath++;
+      for (i = 0; ppath[i]; i++) {
+        if (ppath[i] == ':')
+          ppath[i] = ' ';
+      }
+      result = Curl_sendf(conn->firstsocket, conn,
+                          "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\n"
+                          "%s\n"
+                          "QUIT\n", ppath);
+      if(result)
+        failf(data, "Failed sending DICT request");
+      else
+        result = Curl_Transfer(conn, conn->firstsocket, -1, FALSE, bytecount,
+                               -1, NULL);
+      if(result)
+        return result;
+    }
+  }
+
+  return CURLE_OK;
+}
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 29 - 0
Source/CTest/Curl/dict.h

@@ -0,0 +1,29 @@
+#ifndef __DICT_H
+#define __DICT_H
+
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+CURLcode Curl_dict(struct connectdata *conn);
+CURLcode Curl_dict_done(struct connectdata *conn);
+
+#endif

+ 96 - 0
Source/CTest/Curl/dllinit.c

@@ -0,0 +1,96 @@
+#ifdef WIN32
+/* dllinit.c -- Portable DLL initialization. 
+   Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+   Contributed by Mumit Khan ([email protected]).
+
+   I've used DllMain as the DLL "main" since that's the most common 
+   usage. MSVC and Mingw32 both default to DllMain as the standard
+   callback from the linker entry point. Cygwin, as of b20.1, also
+   uses DllMain as the default callback from the entry point.
+
+   The real entry point is typically always defined by the runtime 
+   library, and usually never overridden by (casual) user. What you can 
+   override however is the callback routine that the entry point calls, 
+   and this file provides such a callback function, DllMain.
+
+   Mingw32: The default entry point for mingw32 is DllMainCRTStartup
+   which is defined in libmingw32.a This in turn calls DllMain which is
+   defined here. If not defined, there is a stub in libmingw32.a which
+   does nothing.
+
+   Cygwin: The default entry point for Cygwin b20.1 or newer is
+   __cygwin_dll_entry which is defined in libcygwin.a. This in turn
+   calls the routine DllMain. If not defined, there is a stub in
+   libcygwin.a which does nothing. 
+
+   MSVC: MSVC runtime calls DllMain, just like Mingw32.
+
+   Summary: If you need to do anything special in DllMain, just add it
+   here. Otherwise, the default setup should be just fine for 99%+ of
+   the time. I strongly suggest that you *not* change the entry point,
+   but rather change DllMain as appropriate.
+
+ */
+
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+#include <stdio.h>
+
+BOOL APIENTRY DllMain (HINSTANCE hInst, DWORD reason, 
+                       LPVOID reserved /* Not used. */ );
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DllMain --
+ *
+ *      This routine is called by the Mingw32, Cygwin32 or VC++ C run 
+ *      time library init code, or the Borland DllEntryPoint routine. It 
+ *      is responsible for initializing various dynamically loaded 
+ *      libraries.
+ *
+ * Results:
+ *      TRUE on sucess, FALSE on failure.
+ *
+ * Side effects:
+ *
+ *----------------------------------------------------------------------
+ */
+BOOL APIENTRY
+DllMain (
+         HINSTANCE hInst /* Library instance handle. */ ,
+         DWORD reason /* Reason this function is being called. */ ,
+         LPVOID reserved /* Not used. */ )
+{
+
+  switch (reason)
+    {
+    case DLL_PROCESS_ATTACH:
+      break;
+
+    case DLL_PROCESS_DETACH:
+      break;
+
+    case DLL_THREAD_ATTACH:
+      break;
+
+    case DLL_THREAD_DETACH:
+      break;
+    }
+  return TRUE;
+}
+#else
+#ifdef VMS
+int VOID_VAR_DLLINIT;   
+#endif
+#endif
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 340 - 0
Source/CTest/Curl/easy.c

@@ -0,0 +1,340 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+/* -- WIN32 approved -- */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+
+#include "strequal.h"
+
+#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
+#include <winsock.h>
+#include <time.h>
+#include <io.h>
+#else
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <netdb.h>
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#include <sys/ioctl.h>
+#include <signal.h>
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#endif
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "transfer.h"
+#include "ssluse.h"
+#include "url.h"
+#include "getinfo.h"
+#include "hostip.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+
+/* Silly win32 socket initialization functions */
+
+#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
+static void win32_cleanup(void)
+{
+  WSACleanup();
+}
+
+static CURLcode win32_init(void)
+{
+  WORD wVersionRequested;  
+  WSADATA wsaData; 
+  int err; 
+  wVersionRequested = MAKEWORD(2, 0); 
+    
+  err = WSAStartup(wVersionRequested, &wsaData); 
+    
+  if (err != 0) 
+    /* Tell the user that we couldn't find a useable */ 
+    /* winsock.dll.     */ 
+    return CURLE_FAILED_INIT; 
+    
+  /* Confirm that the Windows Sockets DLL supports 2.0.*/ 
+  /* Note that if the DLL supports versions greater */ 
+  /* than 2.0 in addition to 2.0, it will still return */ 
+  /* 2.0 in wVersion since that is the version we */ 
+  /* requested. */ 
+    
+  if ( LOBYTE( wsaData.wVersion ) != 2 || 
+       HIBYTE( wsaData.wVersion ) != 0 ) { 
+    /* Tell the user that we couldn't find a useable */ 
+
+    /* winsock.dll. */ 
+    WSACleanup(); 
+    return CURLE_FAILED_INIT; 
+  }
+  return CURLE_OK;
+}
+/* The Windows Sockets DLL is acceptable. Proceed. */ 
+#else
+/* These functions exist merely to prevent compiler warnings */
+static CURLcode win32_init(void) { return CURLE_OK; }
+static void win32_cleanup(void) { }
+#endif
+
+
+/* true globals -- for curl_global_init() and curl_global_cleanup() */
+static unsigned int  initialized = 0;
+static long          init_flags  = 0;
+
+/**
+ * Globally initializes cURL given a bitwise set of 
+ * the different features to initialize.
+ */
+CURLcode curl_global_init(long flags)
+{
+  if (initialized)
+    return CURLE_OK;
+
+  if (flags & CURL_GLOBAL_SSL)
+    Curl_SSL_init();
+
+  if (flags & CURL_GLOBAL_WIN32)
+    if (win32_init() != CURLE_OK)
+      return CURLE_FAILED_INIT;
+
+  initialized = 1;
+  init_flags  = flags;
+  
+  return CURLE_OK;
+}
+
+/**
+ * Globally cleanup 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;
+
+  Curl_global_host_cache_dtor();
+
+  if (init_flags & CURL_GLOBAL_SSL)
+    Curl_SSL_cleanup();
+
+  if (init_flags & CURL_GLOBAL_WIN32)
+    win32_cleanup();
+
+  initialized = 0;
+  init_flags  = 0;
+}
+
+CURL *curl_easy_init(void)
+{
+  CURLcode res;
+  struct SessionHandle *data;
+
+  /* Make sure we inited the global SSL stuff */
+  if (!initialized)
+    curl_global_init(CURL_GLOBAL_DEFAULT);
+
+  /* We use curl_open() with undefined URL so far */
+  res = Curl_open(&data);
+  if(res != CURLE_OK)
+    return NULL;
+
+  return data;
+}
+
+typedef int (*func_T)(void);
+CURLcode curl_easy_setopt(CURL *curl, CURLoption tag, ...)
+{
+  va_list arg;
+  func_T param_func = (func_T)0;
+  long param_long = 0;
+  void *param_obj = NULL;
+  struct SessionHandle *data = curl;
+
+  va_start(arg, tag);
+
+  /* PORTING NOTE:
+     Object pointers can't necessarily be casted to function pointers and
+     therefore we need to know what type it is and read the correct type
+     at once. This should also correct problems with different sizes of
+     the types.
+  */
+
+  if(tag < CURLOPTTYPE_OBJECTPOINT) {
+    /* This is a LONG type */
+    param_long = va_arg(arg, long);
+    Curl_setopt(data, tag, param_long);
+  }
+  else if(tag < CURLOPTTYPE_FUNCTIONPOINT) {
+    /* This is a object pointer type */
+    param_obj = va_arg(arg, void *);
+    Curl_setopt(data, tag, param_obj);
+  }
+  else {
+    param_func = va_arg(arg, func_T );
+    Curl_setopt(data, tag, param_func);
+  }
+
+  va_end(arg);
+  return CURLE_OK;
+}
+
+CURLcode curl_easy_perform(CURL *curl)
+{
+  struct SessionHandle *data = (struct SessionHandle *)curl;
+
+  if (!data->hostcache) {
+    if (Curl_global_host_cache_use(data)) {
+      data->hostcache = Curl_global_host_cache_get();
+    }
+    else {
+      data->hostcache = curl_hash_alloc(7, Curl_freeaddrinfo);
+    }
+  }
+
+  return Curl_perform(data);
+}
+
+void curl_easy_cleanup(CURL *curl)
+{
+  struct SessionHandle *data = (struct SessionHandle *)curl;
+  if (!Curl_global_host_cache_use(data)) {
+    curl_hash_destroy(data->hostcache);
+  }
+  Curl_close(data);
+}
+
+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 *curl_easy_duphandle(CURL *incurl)
+{
+  struct SessionHandle *data=(struct SessionHandle *)incurl;
+
+  struct SessionHandle *outcurl = (struct SessionHandle *)
+    malloc(sizeof(struct SessionHandle));
+
+  if(NULL == outcurl)
+    return NULL; /* failure */
+
+  /* start with clearing the entire new struct */
+  memset(outcurl, 0, sizeof(struct SessionHandle));
+
+  /*
+   * 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=(char*)malloc(HEADERSIZE);
+  if(!outcurl->state.headerbuff) {
+    free(outcurl); /* free the memory again */
+    return NULL;
+  }
+  outcurl->state.headersize=HEADERSIZE;
+
+  /* copy all userdefined values */
+  outcurl->set = data->set;
+  outcurl->state.numconnects = data->state.numconnects;
+  outcurl->state.connects = (struct connectdata **)
+      malloc(sizeof(struct connectdata *) * outcurl->state.numconnects);
+
+  if(!outcurl->state.connects) {
+    free(outcurl->state.headerbuff);
+    free(outcurl);
+    return NULL;
+  }
+  memset(outcurl->state.connects, 0,
+         sizeof(struct connectdata *)*outcurl->state.numconnects);
+
+  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->cookies->filename,
+                                        outcurl->cookies);
+
+  /* duplicate all values in 'change' */
+  if(data->change.url) {
+    outcurl->change.url = strdup(data->change.url);
+    outcurl->change.url_alloc = TRUE;
+  }
+  if(data->change.proxy) {
+    outcurl->change.proxy = strdup(data->change.proxy);
+    outcurl->change.proxy_alloc = TRUE;
+  }
+  if(data->change.referer) {
+    outcurl->change.referer = strdup(data->change.referer);
+    outcurl->change.referer_alloc = TRUE;
+  }
+
+  return outcurl;
+}
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 120 - 0
Source/CTest/Curl/escape.c

@@ -0,0 +1,120 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+/* Escape and unescape URL encoding in strings. The functions return a new
+ * allocated string or NULL if an error occurred.  */
+
+#include "setup.h"
+#include <ctype.h>
+#include <curl/curl.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* The last #include file should be: */
+#ifdef MALLOCDEBUG
+#include "memdebug.h"
+#endif
+
+char *curl_escape(const char *string, int length)
+{
+  int alloc = (length?length:(int)strlen(string))+1;  
+  char *ns = malloc(alloc);
+  unsigned char in;
+  int newlen = alloc;
+  int index=0;
+
+  length = alloc-1;
+  while(length--) {
+    in = *string;
+    if(' ' == in)
+      ns[index++] = '+';
+    else if(!(in >= 'a' && in <= 'z') &&
+            !(in >= 'A' && in <= 'Z') &&
+            !(in >= '0' && in <= '9')) {
+      /* encode it */
+      newlen += 2; /* the size grows with two, since this'll become a %XX */
+      if(newlen > alloc) {
+        alloc *= 2;
+        ns = realloc(ns, alloc);
+        if(!ns)
+          return NULL;
+      }
+      sprintf(&ns[index], "%%%02X", in);
+
+      index+=3;
+    }
+    else {
+      /* just copy this */
+      ns[index++]=in;
+    }
+    string++;
+  }
+  ns[index]=0; /* terminate it */
+  return ns;
+}
+
+char *curl_unescape(const char *string, int length)
+{
+  int alloc = (length?length:(int)strlen(string))+1;
+  char *ns = malloc(alloc);
+  unsigned char in;
+  int index=0;
+  unsigned int hex;
+  char querypart=FALSE; /* everything to the right of a '?' letter is
+                           the "query part" where '+' should become ' '.
+                           RFC 2316, section 3.10 */
+  
+  while(--alloc > 0) {
+    in = *string;
+    if(querypart && ('+' == in))
+      in = ' ';
+    else if(!querypart && ('?' == in)) {
+      /* we have "walked in" to the query part */
+      querypart=TRUE;
+    }
+    else if('%' == in) {
+      /* encoded part */
+      if(sscanf(string+1, "%02X", &hex)) {
+        in = hex;
+        string+=2;
+        alloc-=2;
+      }
+    }
+    
+    ns[index++] = in;
+    string++;
+  }
+  ns[index]=0; /* terminate it */
+  return ns;
+  
+}
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 32 - 0
Source/CTest/Curl/escape.h

@@ -0,0 +1,32 @@
+#ifndef __ESCAPE_H
+#define __ESCAPE_H
+
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+/* Escape and unescape URL encoding in strings. The functions return a new
+ * allocated string or NULL if an error occurred.  */
+
+char *curl_escape(const char *string, int length);
+char *curl_unescape(const char *string, int length);
+
+#endif

+ 206 - 0
Source/CTest/Curl/file.c

@@ -0,0 +1,206 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+/* -- WIN32 approved -- */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+
+#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
+#include <winsock.h>
+#include <time.h>
+#include <io.h>
+#include <fcntl.h>
+#else
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#include <sys/time.h>
+#include <sys/resource.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.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
+#include <sys/ioctl.h>
+#include <signal.h>
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+
+#endif
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "progress.h"
+#include "sendf.h"
+#include "escape.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#ifdef MALLOCDEBUG
+#include "memdebug.h"
+#endif
+
+/* Emulate a connect-then-transfer protocol. We connect to the file here */
+CURLcode Curl_file_connect(struct connectdata *conn)
+{
+  char *actual_path = curl_unescape(conn->path, 0);
+  struct FILE *file;
+  int fd;
+#if defined(WIN32) || defined(__EMX__)
+  int i;
+#endif
+
+  file = (struct FILE *)malloc(sizeof(struct FILE));
+  if(!file)
+    return CURLE_OUT_OF_MEMORY;
+
+  memset(file, 0, sizeof(struct FILE));
+  conn->proto.file = file;
+
+#if defined(WIN32) || defined(__EMX__)
+  /* change path separators from '/' to '\\' for Windows and OS/2 */
+  for (i=0; actual_path[i] != '\0'; ++i)
+    if (actual_path[i] == '/')
+      actual_path[i] = '\\';
+
+  fd = open(actual_path, O_RDONLY | O_BINARY);  /* no CR/LF translation! */
+#else
+  fd = open(actual_path, O_RDONLY);
+#endif
+  free(actual_path);
+
+  if(fd == -1) {
+    failf(conn->data, "Couldn't open file %s", conn->path);
+    return CURLE_FILE_COULDNT_READ_FILE;
+  }
+  file->fd = fd;
+
+  return CURLE_OK;
+}
+
+/* This is the do-phase, separated from the connect-phase above */
+
+CURLcode Curl_file(struct connectdata *conn)
+{
+  /* 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;
+  ssize_t expected_size=-1;
+  ssize_t nread;
+  struct SessionHandle *data = conn->data;
+  char *buf = data->state.buffer;
+  int bytecount = 0;
+  struct timeval start = Curl_tvnow();
+  struct timeval now = start;
+  int fd;
+
+  /* get the fd from the connection phase */
+  fd = conn->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;
+  }
+
+  /* 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(expected_size != -1)
+    Curl_pgrsSetDownloadSize(data, expected_size);
+
+  while (res == CURLE_OK) {
+    nread = read(fd, buf, BUFSIZE-1);
+
+    if ( nread > 0)
+      buf[nread] = 0;
+
+    if (nread <= 0)
+      break;
+
+    bytecount += nread;
+    /* NOTE: The following call to fwrite does CR/LF translation on
+       Windows systems if the target is stdout. Use -O or -o parameters
+       to prevent CR/LF translation (this then goes to a binary mode
+       file descriptor). */
+
+    res = Curl_client_write(data, CLIENTWRITE_BODY, buf, nread);
+    if(res)
+      return res;
+
+    now = Curl_tvnow();
+    if(Curl_pgrsUpdate(conn))
+      res = CURLE_ABORTED_BY_CALLBACK;
+  }
+  now = Curl_tvnow();
+  if(Curl_pgrsUpdate(conn))
+    res = CURLE_ABORTED_BY_CALLBACK;
+
+  close(fd);
+
+  return res;
+}
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 28 - 0
Source/CTest/Curl/file.h

@@ -0,0 +1,28 @@
+#ifndef __FILE_H
+#define __FILE_H
+
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+CURLcode Curl_file(struct connectdata *conn);
+CURLcode Curl_file_connect(struct connectdata *conn);
+#endif

+ 1501 - 0
Source/CTest/Curl/formdata.c

@@ -0,0 +1,1501 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2001, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+/*
+  Debug the form generator stand-alone by compiling this source file with:
+
+  gcc -DHAVE_CONFIG_H -I../ -g -D_FORM_DEBUG -o formdata -I../include formdata.c strequal.c
+
+  run the 'formdata' executable the output should end with:
+  All Tests seem to have worked ...
+  and the following parts should be there:
+
+Content-Disposition: form-data; name="simple_COPYCONTENTS"
+value for simple COPYCONTENTS
+
+Content-Disposition: form-data; name="COPYCONTENTS_+_CONTENTTYPE"
+Content-Type: image/gif
+value for COPYCONTENTS + CONTENTTYPE
+
+Content-Disposition: form-data; name="PRNAME_+_NAMELENGTH_+_COPYNAME_+_CONTENTSLENGTH"
+vlue for PTRNAME + NAMELENGTH + COPYNAME + CONTENTSLENGTH
+(or you might see P^@RNAME and v^@lue at the start)
+
+Content-Disposition: form-data; name="simple_PTRCONTENTS"
+value for simple PTRCONTENTS
+
+Content-Disposition: form-data; name="PTRCONTENTS_+_CONTENTSLENGTH"
+vlue for PTRCONTENTS + CONTENTSLENGTH
+(or you might see v^@lue at the start)
+
+Content-Disposition: form-data; name="PTRCONTENTS_+_CONTENTSLENGTH_+_CONTENTTYPE"
+Content-Type: text/plain
+vlue for PTRCOTNENTS + CONTENTSLENGTH + CONTENTTYPE
+(or you might see v^@lue at the start)
+
+Content-Disposition: form-data; name="FILE1_+_CONTENTTYPE"; filename="inet_ntoa_r.h"
+Content-Type: text/html
+...
+
+Content-Disposition: form-data; name="FILE1_+_FILE2"
+Content-Type: multipart/mixed, boundary=curlz1s0dkticx49MV1KGcYP5cvfSsz
+...
+Content-Disposition: attachment; filename="inet_ntoa_r.h"
+Content-Type: text/plain
+...
+Content-Disposition: attachment; filename="Makefile.b32.resp"
+Content-Type: text/plain
+...
+
+Content-Disposition: form-data; name="FILE1_+_FILE2_+_FILE3"
+Content-Type: multipart/mixed, boundary=curlirkYPmPwu6FrJ1vJ1u1BmtIufh1
+...
+Content-Disposition: attachment; filename="inet_ntoa_r.h"
+Content-Type: text/plain
+...
+Content-Disposition: attachment; filename="Makefile.b32.resp"
+Content-Type: text/plain
+...
+Content-Disposition: attachment; filename="inet_ntoa_r.h"
+Content-Type: text/plain
+...
+
+
+Content-Disposition: form-data; name="ARRAY: FILE1_+_FILE2_+_FILE3"
+Content-Type: multipart/mixed, boundary=curlirkYPmPwu6FrJ1vJ1u1BmtIufh1
+...
+Content-Disposition: attachment; filename="inet_ntoa_r.h"
+Content-Type: text/plain
+...
+Content-Disposition: attachment; filename="Makefile.b32.resp"
+Content-Type: text/plain
+...
+Content-Disposition: attachment; filename="inet_ntoa_r.h"
+Content-Type: text/plain
+...
+
+Content-Disposition: form-data; name="FILECONTENT"
+...
+
+  For the old FormParse used by curl_formparse use:
+
+  gcc -DHAVE_CONFIG_H -I../ -g -D_OLD_FORM_DEBUG -o formdata -I../include formdata.c strequal.c
+
+  run the 'formdata' executable and make sure the output is ok!
+
+  try './formdata "name=Daniel" "poo=noo" "foo=bar"' and similarly
+
+ */
+
+#include "setup.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <time.h>
+
+#include <curl/curl.h>
+#include "formdata.h"
+
+#include "strequal.h"
+
+/* The last #include file should be: */
+#ifdef MALLOCDEBUG
+#include "memdebug.h"
+#endif
+
+/* Length of the random boundary string. The risk of this being used
+   in binary data is very close to zero, 64^32 makes
+   6277101735386680763835789423207666416102355444464034512896
+   combinations... */
+#define BOUNDARY_LENGTH 32
+
+/* What kind of Content-Type to use on un-specified files with unrecognized
+   extensions. */
+#define HTTPPOST_CONTENTTYPE_DEFAULT "text/plain"
+
+/* This is a silly duplicate of the function in main.c to enable this source
+   to compile stand-alone for better debugging */
+static void GetStr(char **string,
+                   const char *value)
+{
+  if(*string)
+    free(*string);
+  *string = strdup(value);
+}
+
+/***************************************************************************
+ *
+ * FormParse()
+ *      
+ * Reads a 'name=value' paramter and builds the appropriate linked list.
+ *
+ * Specify files to upload with 'name=@filename'. Supports specified
+ * given Content-Type of the files. Such as ';type=<content-type>'.
+ *
+ * You may specify more than one file for a single name (field). Specify
+ * multiple files by writing it like:
+ *
+ * 'name=@filename,filename2,filename3'
+ *
+ * If you want content-types specified for each too, write them like:
+ *
+ * 'name=@filename;type=image/gif,filename2,filename3'
+ *
+ ***************************************************************************/
+
+#define FORM_FILE_SEPARATOR ','
+#define FORM_TYPE_SEPARATOR ';'
+
+static
+int FormParse(char *input,
+              struct HttpPost **httppost,
+              struct HttpPost **last_post)
+{
+  /* nextarg MUST be a string in the format 'name=contents' and we'll
+     build a linked list with the info */
+  char name[256];
+  char *contents;
+  char major[128];
+  char minor[128];
+  long flags = 0;
+  char *contp;
+  const char *type = NULL;
+  char *prevtype = NULL;
+  char *sep;
+  char *sep2;
+  struct HttpPost *post;
+  struct HttpPost *subpost; /* a sub-node */
+  unsigned int i;
+
+  /* Preallocate contents to the length of input to make sure we don't
+     overwrite anything. */
+  contents = malloc(strlen(input));
+  contents[0] = '\000';
+ 
+  if(1 <= sscanf(input, "%255[^=]=%[^\n]", name, contents)) {
+    /* the input was using the correct format */
+    contp = contents;
+
+    if('@' == contp[0]) {
+      /* we use the @-letter to indicate file name(s) */
+      
+      flags = HTTPPOST_FILENAME;
+      contp++;
+
+      post=NULL;
+
+      do {
+        /* since this was a file, it may have a content-type specifier
+           at the end too */
+
+        sep=strchr(contp, FORM_TYPE_SEPARATOR);
+        sep2=strchr(contp, FORM_FILE_SEPARATOR);
+
+        /* pick the closest */
+        if(sep2 && (sep2 < sep)) {
+          sep = sep2;
+
+          /* no type was specified! */
+        }
+        if(sep) {
+
+          /* if we got here on a comma, don't do much */
+          if(FORM_FILE_SEPARATOR != *sep)
+            type = strstr(sep+1, "type=");
+          else
+            type=NULL;
+
+          *sep=0; /* terminate file name at separator */
+
+          if(type) {
+            type += strlen("type=");
+            
+            if(2 != sscanf(type, "%127[^/]/%127[^,\n]",
+                           major, minor)) {
+              free(contents);
+              return 2; /* illegal content-type syntax! */
+            }
+            /* now point beyond the content-type specifier */
+            sep = (char *)type + strlen(major)+strlen(minor)+1;
+
+            /* find the following comma */
+            sep=strchr(sep, FORM_FILE_SEPARATOR);
+          }
+        }
+        else {
+          type=NULL;
+          sep=strchr(contp, FORM_FILE_SEPARATOR);
+        }
+        if(sep) {
+          /* the next file name starts here */
+          *sep =0;
+          sep++;
+        }
+        if(!type) {
+          /*
+           * No type was specified, we scan through a few well-known
+           * extensions and pick the first we match!
+           */
+          struct ContentType {
+            const char *extension;
+            const char *type;
+          };
+          static struct ContentType ctts[]={
+            {".gif",  "image/gif"},
+            {".jpg",  "image/jpeg"},
+            {".jpeg", "image/jpeg"},
+            {".txt",  "text/plain"},
+            {".html", "text/plain"}
+          };
+
+          if(prevtype)
+            /* default to the previously set/used! */
+            type = prevtype;
+          else
+            /* It seems RFC1867 defines no Content-Type to default to
+               text/plain so we don't actually need to set this: */
+            type = HTTPPOST_CONTENTTYPE_DEFAULT;
+
+          for(i=0; i<sizeof(ctts)/sizeof(ctts[0]); i++) {
+            if(strlen(contp) >= strlen(ctts[i].extension)) {
+              if(strequal(contp +
+                          strlen(contp) - strlen(ctts[i].extension),
+                          ctts[i].extension)) {
+                type = ctts[i].type;
+                break;
+              }       
+            }
+          }
+          /* we have a type by now */
+        }
+
+        if(NULL == post) {
+          /* For the first file name, we allocate and initiate the main list
+             node */
+
+          post = (struct HttpPost *)malloc(sizeof(struct HttpPost));
+          if(post) {
+            memset(post, 0, sizeof(struct HttpPost));
+            GetStr(&post->name, name);      /* get the name */
+            GetStr(&post->contents, contp); /* get the contents */
+            post->contentslength = 0;
+            post->flags = flags;
+            if(type) {
+              GetStr(&post->contenttype, (char *)type); /* get type */
+              prevtype=post->contenttype; /* point to the allocated string! */
+            }
+            /* make the previous point to this */
+            if(*last_post)
+              (*last_post)->next = post;
+            else
+              (*httppost) = post;
+
+            (*last_post) = post;          
+          }
+
+        }
+        else {
+          /* we add a file name to the previously allocated node, known as
+             'post' now */
+          subpost =(struct HttpPost *)malloc(sizeof(struct HttpPost));
+          if(subpost) {
+             memset(subpost, 0, sizeof(struct HttpPost));
+             GetStr(&subpost->name, name);      /* get the name */
+             GetStr(&subpost->contents, contp); /* get the contents */
+             subpost->contentslength = 0;
+             subpost->flags = flags;
+             if(type) {
+               GetStr(&subpost->contenttype, (char *)type); /* get type */
+               prevtype=subpost->contenttype; /* point to allocated string! */
+             }
+             /* now, point our 'more' to the original 'more' */
+             subpost->more = post->more;
+
+             /* then move the original 'more' to point to ourselves */
+             post->more = subpost;           
+          }
+        }
+        contp = sep; /* move the contents pointer to after the separator */
+      } while(sep && *sep); /* loop if there's another file name */
+    }
+    else {
+      post = (struct HttpPost *)malloc(sizeof(struct HttpPost));
+      if(post) {
+        memset(post, 0, sizeof(struct HttpPost));
+        GetStr(&post->name, name);      /* get the name */
+        if( contp[0]=='<' ) {
+          GetStr(&post->contents, contp+1); /* get the contents */
+          post->contentslength = 0;
+          post->flags = HTTPPOST_READFILE;
+        }
+        else {
+          GetStr(&post->contents, contp); /* get the contents */
+          post->contentslength = 0;
+          post->flags = 0;
+        }
+
+        /* make the previous point to this */
+        if(*last_post)
+          (*last_post)->next = post;
+        else
+          (*httppost) = post;
+
+        (*last_post) = post;      
+      }
+
+    }
+
+  }
+  else {
+    free(contents);
+    return 1;
+  }
+  free(contents);
+  return 0;
+}
+
+int curl_formparse(char *input,
+                   struct HttpPost **httppost,
+                   struct HttpPost **last_post)
+{
+  return FormParse(input, httppost, last_post);
+}
+
+/***************************************************************************
+ *
+ * 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 HttpPost * AddHttpPost(char * name,
+                                     long namelength,
+                                     char * value,
+                                     long contentslength,
+                                     char *contenttype,
+                                     long flags,
+                                     struct curl_slist* contentHeader,
+                                     struct HttpPost *parent_post,
+                                     struct HttpPost **httppost,
+                                     struct HttpPost **last_post)
+{
+  struct HttpPost *post;
+  post = (struct HttpPost *)malloc(sizeof(struct HttpPost));
+  if(post) {
+    memset(post, 0, sizeof(struct HttpPost));
+    post->name = name;
+    post->namelength = name?(namelength?namelength:(long)strlen(name)):0;
+    post->contents = value;
+    post->contentslength = contentslength;
+    post->contenttype = contenttype;
+    post->contentheader = contentHeader;
+    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 = (FormInfo *)malloc(sizeof(FormInfo));
+  if(form_info) {
+    memset(form_info, 0, sizeof(FormInfo));
+    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;
+  }
+  else
+    return NULL;
+
+  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 {
+    const char *extension;
+    const char *type;
+  };
+  static struct ContentType ctts[]={
+    {".gif",  "image/gif"},
+    {".jpg",  "image/jpeg"},
+    {".jpeg", "image/jpeg"},
+    {".txt",  "text/plain"},
+    {".html", "text/plain"}
+  };
+  
+  if(prevtype)
+    /* default to the previously set/used! */
+    contenttype = prevtype;
+  else
+    /* It seems RFC1867 defines no Content-Type to default to
+       text/plain so we don't actually need to set this: */
+    contenttype = HTTPPOST_CONTENTTYPE_DEFAULT;
+  
+  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;
+}
+
+/***************************************************************************
+ *
+ * AllocAndCopy()
+ *      
+ * Copies the data currently available under *buffer using newly allocated
+ * buffer (that becomes *buffer). Uses buffer_length if not null, else
+ * uses strlen to determine the length of the buffer to be copied
+ *
+ * Returns 0 on success and 1 if the malloc failed.
+ *
+ ***************************************************************************/
+static int AllocAndCopy (char **buffer, int buffer_length)
+{
+  const char *src = *buffer;
+  int length, add = 0;
+  if (buffer_length)
+    length = buffer_length;
+  else {
+    length = strlen(*buffer);
+    add = 1;
+  }
+  *buffer = (char*)malloc(length+add);
+  if (!*buffer)
+    return 1;
+  memcpy(*buffer, src, length);
+  /* if length unknown do null termination */
+  if (add)
+    (*buffer)[length] = '\0';
+  return 0;
+}
+
+/***************************************************************************
+ *
+ * FormAdd()
+ *      
+ * Stores a 'name=value' 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:
+ * FORMADD_OK             on success
+ * FORMADD_MEMORY         if the FormInfo allocation fails
+ * FORMADD_OPTION_TWICE   if one option is given twice for one Form
+ * FORMADD_NULL           if a null pointer was given for a char
+ * FORMADD_MEMORY         if the allocation of a FormInfo struct failed
+ * FORMADD_UNKNOWN_OPTION if an unknown option was used
+ * FORMADD_INCOMPLETE     if the some FormInfo is not complete (or an error)
+ * FORMADD_MEMORY         if a HttpPost struct cannot be allocated
+ * FORMADD_MEMORY         if some allocation for string copying failed.
+ * FORMADD_ILLEGAL_ARRAY  if an illegal option is used in an array
+ *
+ ***************************************************************************/
+
+typedef enum {
+  FORMADD_OK, /* first, no error */
+
+  FORMADD_MEMORY,
+  FORMADD_OPTION_TWICE,
+  FORMADD_NULL,
+  FORMADD_UNKNOWN_OPTION,
+  FORMADD_INCOMPLETE,
+  FORMADD_ILLEGAL_ARRAY,
+
+  FORMADD_LAST /* last */
+} FORMcode;
+
+static
+FORMcode FormAdd(struct HttpPost **httppost,
+                 struct HttpPost **last_post,
+                 va_list params)
+{
+  FormInfo *first_form, *current_form, *form;
+  FORMcode return_value = FORMADD_OK;
+  const char *prevtype = NULL;
+  struct HttpPost *post = NULL;
+  CURLformoption option;
+  struct curl_forms *forms = NULL;
+  const char *array_value; /* 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 = (FormInfo *)malloc(sizeof(struct FormInfo));
+  if(first_form) {
+    memset(first_form, 0, sizeof(FormInfo));
+    current_form = first_form;
+  }
+  else
+    return FORMADD_MEMORY;
+
+  /*
+   * Loop through all the options set.
+   */
+  while (1) {
+
+    /* break if we have an error to report */
+    if (return_value != FORMADD_OK)
+      break;
+
+    /* first see if we have more parts of the array param */
+    if ( array_state ) {
+      /* get the upcoming option from the given array */
+      option = forms->option;
+      array_value = forms->value;
+
+      forms++; /* advance this to next entry */
+      if (CURLFORM_END == option) {
+        /* end of array state */
+        array_state = FALSE;
+        continue;
+      }
+      else {
+        /* check that the option is OK in an array */
+
+        /* Daniel's note: do we really need to do this? */
+        if ( (option <= CURLFORM_ARRAY_START) ||
+             (option >= CURLFORM_ARRAY_END) ) {
+          return_value = FORMADD_ILLEGAL_ARRAY;
+          break;
+        }
+      }
+    }
+    else {
+      /* This is not array-state, get next option */
+      option = va_arg(params, CURLformoption);
+      if (CURLFORM_END == option)
+        break;
+    }
+
+    switch (option) {
+    case CURLFORM_ARRAY:
+      forms = va_arg(params, struct curl_forms *);
+      if (forms)
+        array_state = TRUE;
+      else
+        return_value = FORMADD_NULL;
+      break;
+
+      /*
+       * Set the Name property.
+       */
+    case CURLFORM_PTRNAME:
+      current_form->flags |= HTTPPOST_PTRNAME; /* fall through */
+    case CURLFORM_COPYNAME:
+      if (current_form->name)
+        return_value = FORMADD_OPTION_TWICE;
+      else {
+        char *name = va_arg(params, char *);
+        if (name)
+          current_form->name = name; /* store for the moment */
+        else
+          return_value = FORMADD_NULL;
+      }
+      break;
+    case CURLFORM_NAMELENGTH:
+      if (current_form->namelength)
+        return_value = FORMADD_OPTION_TWICE;
+      else
+        current_form->namelength = 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 = FORMADD_OPTION_TWICE;
+      else {
+        char *value = va_arg(params, char *);
+        if (value)
+          current_form->value = value; /* store for the moment */
+        else
+          return_value = FORMADD_NULL;
+      }
+      break;
+    case CURLFORM_CONTENTSLENGTH:
+      if (current_form->contentslength)
+        return_value = FORMADD_OPTION_TWICE;
+      else
+        current_form->contentslength = va_arg(params, long);
+      break;
+
+      /* Get contents from a given file name */
+    case CURLFORM_FILECONTENT:
+      if (current_form->flags != 0)
+        return_value = FORMADD_OPTION_TWICE;
+      else {
+        char *filename = va_arg(params, char *);
+        if (filename) {
+          current_form->value = strdup(filename);
+          current_form->flags |= HTTPPOST_READFILE;
+        }
+        else
+          return_value = FORMADD_NULL;
+      }
+      break;
+
+      /* We upload a file */
+    case CURLFORM_FILE:
+      {
+        const char *filename = NULL;
+        if (array_state)
+          filename = array_value;
+        else
+          filename = va_arg(params, const char *);
+        if (current_form->value) {
+          if (current_form->flags & HTTPPOST_FILENAME) {
+            if (filename) {
+              if (!(current_form = AddFormInfo(strdup(filename),
+                                               NULL, current_form)))
+                return_value = FORMADD_MEMORY;
+            }
+            else
+              return_value = FORMADD_NULL;
+          }
+          else
+            return_value = FORMADD_OPTION_TWICE;
+        }
+        else {
+          if (filename)
+            current_form->value = strdup(filename);
+          else
+            return_value = FORMADD_NULL;
+          current_form->flags |= HTTPPOST_FILENAME;
+        }
+        break;
+      }
+    case CURLFORM_CONTENTTYPE:
+      {
+        const char *contenttype = NULL;
+        if (array_state)
+          contenttype = array_value;
+        else
+          contenttype = va_arg(params, const char *);
+        if (current_form->contenttype) {
+          if (current_form->flags & HTTPPOST_FILENAME) {
+            if (contenttype) {
+              if (!(current_form = AddFormInfo(NULL,
+                                               strdup(contenttype),
+                                               current_form)))
+                return_value = FORMADD_MEMORY;
+            }
+            else
+              return_value = FORMADD_NULL;
+          }
+          else
+            return_value = FORMADD_OPTION_TWICE;
+        }
+        else {
+          if (contenttype)
+            current_form->contenttype = strdup(contenttype);
+          else
+            return_value = FORMADD_NULL;
+        }
+        break;
+      }
+    case CURLFORM_CONTENTHEADER:
+      {
+        struct curl_slist* list = NULL;
+        if( array_state )
+          list = (struct curl_slist*)array_value;
+        else
+          list = va_arg(params,struct curl_slist*);
+        
+        if( current_form->contentheader )
+          return_value = FORMADD_OPTION_TWICE;
+        else
+          current_form->contentheader = list;
+        
+        break;
+      }
+    default:
+      return_value = FORMADD_UNKNOWN_OPTION;
+    }
+  }
+
+  if(FORMADD_OK == return_value) {
+    /* go through the list, check for copleteness 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->flags & HTTPPOST_READFILE) &&
+             (form->flags & HTTPPOST_PTRCONTENTS) )
+           ) {
+        return_value = FORMADD_INCOMPLETE;
+        break;
+      }
+      else {
+        if ( (form->flags & HTTPPOST_FILENAME) &&
+             !form->contenttype ) {
+          /* our contenttype is missing */
+          form->contenttype
+            = strdup(ContentTypeForFilename(form->value, prevtype));
+        }
+        if ( !(form->flags & HTTPPOST_PTRNAME) &&
+             (form == first_form) ) {
+          /* copy name (without strdup; possibly contains null characters) */
+          if (AllocAndCopy(&form->name, form->namelength)) {
+            return_value = FORMADD_MEMORY;
+            break;
+          }
+        }
+        if ( !(form->flags & HTTPPOST_FILENAME) &&
+             !(form->flags & HTTPPOST_READFILE) && 
+             !(form->flags & HTTPPOST_PTRCONTENTS) ) {
+          /* copy value (without strdup; possibly contains null characters) */
+          if (AllocAndCopy(&form->value, form->contentslength)) {
+            return_value = FORMADD_MEMORY;
+            break;
+          }
+        }
+        post = AddHttpPost(form->name, form->namelength,
+                           form->value, form->contentslength,
+                           form->contenttype, form->flags,
+                           form->contentheader,
+                           post, httppost,
+                           last_post);
+        
+        if(!post)
+          return_value = FORMADD_MEMORY;
+
+        if (form->contenttype)
+          prevtype = form->contenttype;
+      }
+    }
+  }
+
+  /* always delete the allocated memory before returning */
+  form = first_form;
+  while (form != NULL) {
+    FormInfo *delete_form;
+    
+    delete_form = form;
+    form = form->more;
+    free (delete_form);
+  }
+
+  return return_value;
+}
+
+int curl_formadd(struct HttpPost **httppost,
+                 struct HttpPost **last_post,
+                 ...)
+{
+  va_list arg;
+  int result;
+  va_start(arg, last_post);
+  result = FormAdd(httppost, last_post, arg);
+  va_end(arg);
+  return result;
+}
+
+static int AddFormData(struct FormData **formp,
+                       const void *line,
+                       long length)
+{
+  struct FormData *newform = (struct FormData *)
+    malloc(sizeof(struct FormData));
+  newform->next = NULL;
+
+  /* we make it easier for plain strings: */
+  if(!length)
+    length = strlen((char *)line);
+
+  newform->line = (char *)malloc(length+1);
+  memcpy(newform->line, line, length);
+  newform->length = length;
+  newform->line[length]=0; /* zero terminate for easier debugging */
+  
+  if(*formp) {
+    (*formp)->next = newform;
+    *formp = newform;
+  }
+  else
+    *formp = newform;
+
+  return length;
+}
+
+
+static int AddFormDataf(struct FormData **formp,
+                        const char *fmt, ...)
+{
+  char s[4096];
+  va_list ap;
+  va_start(ap, fmt);
+  vsprintf(s, fmt, ap);
+  va_end(ap);
+
+  return AddFormData(formp, s, 0);
+}
+
+
+char *Curl_FormBoundary(void)
+{
+  char *retstring;
+  static int randomizer=0; /* this is just so that two boundaries within
+                              the same form won't be identical */
+  int i;
+
+  static char table64[]=
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+  retstring = (char *)malloc(BOUNDARY_LENGTH);
+
+  if(!retstring)
+    return NULL; /* failed */
+
+  srand(time(NULL)+randomizer++); /* seed */
+
+  strcpy(retstring, "curl"); /* bonus commercials 8*) */
+
+  for(i=4; i<(BOUNDARY_LENGTH-1); i++) {
+    retstring[i] = table64[rand()%64];
+  }
+  retstring[BOUNDARY_LENGTH-1]=0; /* zero terminate */
+
+  return retstring;
+}
+
+/* Used from http.c, this cleans a built FormData linked list */ 
+void Curl_formclean(struct FormData *form)
+{
+  struct FormData *next;
+
+  do {
+    next=form->next;  /* the following form line */
+    free(form->line); /* free the line */
+    free(form);       /* free the struct */
+  
+  } while((form=next)); /* continue */
+}
+
+/* external function to free up a whole form post chain */
+void curl_formfree(struct HttpPost *form)
+{
+  struct 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) && form->contents)
+      free(form->contents); /* free the contents */
+    if(form->contenttype)
+      free(form->contenttype); /* free the content type */
+    free(form);       /* free the struct */
+
+  } while((form=next)); /* continue */
+}
+
+struct FormData *Curl_getFormData(struct HttpPost *post,
+                                  int *sizep)
+{
+  struct FormData *form = NULL;
+  struct FormData *firstform;
+
+  struct HttpPost *file;
+
+  int size =0;
+  char *boundary;
+  char *fileboundary=NULL;
+  struct curl_slist* curList;
+
+
+  if(!post)
+    return NULL; /* no input => no output! */
+
+  boundary = Curl_FormBoundary();
+  
+  /* Make the first line of the output */
+  AddFormDataf(&form,
+               "Content-Type: multipart/form-data;"
+               " boundary=%s\r\n",
+               boundary);
+  /* we DO NOT count that line since that'll be part of the header! */
+
+  firstform = form;
+  
+  do {
+
+    if(size)
+      size += AddFormDataf(&form, "\r\n");
+
+    /* boundary */
+    size += AddFormDataf(&form, "--%s\r\n", boundary);
+
+    size += AddFormData(&form,
+                        "Content-Disposition: form-data; name=\"", 0);
+
+    size += AddFormData(&form, post->name, post->namelength);
+
+    size += AddFormData(&form, "\"", 0);
+
+    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 */
+
+      fileboundary = Curl_FormBoundary();
+
+      size += AddFormDataf(&form,
+                           "\r\nContent-Type: multipart/mixed,"
+                           " boundary=%s\r\n",
+                           fileboundary);
+    }
+
+    file = post;
+
+    do {
+      if(post->more) {
+        /* if multiple-file */
+        size += AddFormDataf(&form,
+                             "\r\n--%s\r\nContent-Disposition: attachment; filename=\"%s\"",
+                             fileboundary, file->contents);
+      }
+      else if(post->flags & HTTPPOST_FILENAME) {
+        size += AddFormDataf(&form,
+                             "; filename=\"%s\"",
+                             post->contents);
+      }
+      
+      if(file->contenttype) {
+        /* we have a specified type */
+        size += AddFormDataf(&form,
+                             "\r\nContent-Type: %s",
+                             file->contenttype);
+      }
+
+      curList = file->contentheader;
+      while( curList ) {
+        /* Process the additional headers specified for this form */
+        size += AddFormDataf( &form, "\r\n%s", curList->data );
+        curList = curList->next;
+      }
+
+#if 0
+      /* The header Content-Transfer-Encoding: seems to confuse some receivers
+       * (like the built-in PHP engine). While I can't see any reason why it
+       * should, I can just as well skip this to the benefit of the users who
+       * are using such confused receivers.
+       */
+      
+      if(file->contenttype &&
+         !strnequal("text/", file->contenttype, 5)) {
+        /* this is not a text content, mention our binary encoding */
+        size += AddFormData(&form, "\r\nContent-Transfer-Encoding: binary", 0);
+      }
+#endif
+
+      size += AddFormData(&form, "\r\n\r\n", 0);
+
+      if((post->flags & HTTPPOST_FILENAME) ||
+         (post->flags & HTTPPOST_READFILE)) {
+        /* we should include the contents from the specified file */
+        FILE *fileread;
+        char buffer[1024];
+        int nread;
+
+        fileread = strequal("-", file->contents)?stdin:
+          /* binary read for win32 crap */
+/*VMS??*/ fopen(file->contents, "rb");  /* ONLY ALLOWS FOR STREAM FILES ON VMS */
+/*VMS?? Stream files are OK, as are FIXED & VAR files WITHOUT implied CC */
+/*VMS?? For implied CC, every record needs to have a \n appended & 1 added to SIZE */
+        if(fileread) {
+          while((nread = fread(buffer, 1, 1024, fileread))) {
+            size += AddFormData(&form,
+                                buffer,
+                                nread);
+          }
+          if(fileread != stdin)
+            fclose(fileread);
+        }
+        else {
+          /* File wasn't found, add a nothing field! */
+          size += AddFormData(&form, "", 0);
+        }
+      }
+ else {
+        /* include the contents we got */
+        size += AddFormData(&form, post->contents, post->contentslength);
+      }
+    } while((file = file->more)); /* for each specified file for this field */
+
+    if(post->more) {
+      /* this was a multiple-file inclusion, make a termination file
+         boundary: */
+      size += AddFormDataf(&form,
+                           "\r\n--%s--",
+                           fileboundary);     
+      free(fileboundary);
+    }
+
+  } while((post=post->next)); /* for each field */
+
+  /* end-boundary for everything */
+  size += AddFormDataf(&form,
+                       "\r\n--%s--\r\n",
+                       boundary);
+
+  *sizep = size;
+
+  free(boundary);
+
+  return firstform;
+}
+
+int Curl_FormInit(struct Form *form, struct FormData *formdata )
+{
+  if(!formdata)
+    return 1; /* error */
+
+  form->data = formdata;
+  form->sent = 0;
+
+  return 0;
+}
+
+/* fread() emulation */
+int Curl_FormReader(char *buffer,
+                    size_t size,
+                    size_t nitems,
+                    FILE *mydata)
+{
+  struct Form *form;
+  int wantedsize;
+  int gotsize = 0;
+
+  form=(struct Form *)mydata;
+
+  wantedsize = size * nitems;
+
+  if(!form->data)
+    return -1; /* nothing, error, empty */
+
+  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);
+  /* 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.
+     This is the bug reported November 22 1999 on curl 6.3. (Daniel) */
+
+  return gotsize;
+}
+
+/* possible (old) fread() emulation that copies at most one line */
+int Curl_FormReadOneLine(char *buffer,
+                         size_t size,
+                         size_t nitems,
+                         FILE *mydata)
+{
+  struct Form *form;
+  int wantedsize;
+  int gotsize;
+
+  form=(struct Form *)mydata;
+
+  wantedsize = size * nitems;
+
+  if(!form->data)
+    return -1; /* nothing, error, empty */
+
+  do {
+  
+    if( (form->data->length - form->sent ) > wantedsize ) {
+
+      memcpy(buffer, form->data->line + form->sent, wantedsize);
+
+      form->sent += wantedsize;
+
+      return wantedsize;
+    }
+
+    memcpy(buffer,
+           form->data->line + form->sent,
+           gotsize = (form->data->length - form->sent) );
+
+    form->sent = 0;
+
+    form->data = form->data->next; /* advance */
+
+  } while(!gotsize && form->data);
+  /* 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.
+     This is the bug reported November 22 1999 on curl 6.3. (Daniel) */
+
+  return gotsize;
+}
+
+
+#ifdef _FORM_DEBUG
+int FormAddTest(const char * errormsg,
+                 struct HttpPost **httppost,
+                 struct HttpPost **last_post,
+                 ...)
+{
+  int result;
+  va_list arg;
+  va_start(arg, last_post);
+  if ((result = FormAdd(httppost, last_post, arg)))
+    fprintf (stderr, "ERROR doing FormAdd ret: %d action: %s\n", result,
+             errormsg);
+  va_end(arg);
+  return result;
+}
+
+
+int main()
+{
+  char name1[] = "simple_COPYCONTENTS";
+  char name2[] = "COPYCONTENTS_+_CONTENTTYPE";
+  char name3[] = "PTRNAME_+_NAMELENGTH_+_COPYNAME_+_CONTENTSLENGTH";
+  char name4[] = "simple_PTRCONTENTS";
+  char name5[] = "PTRCONTENTS_+_CONTENTSLENGTH";
+  char name6[] = "PTRCONTENTS_+_CONTENTSLENGTH_+_CONTENTTYPE";
+  char name7[] = "FILE1_+_CONTENTTYPE";
+  char name8[] = "FILE1_+_FILE2";
+  char name9[] = "FILE1_+_FILE2_+_FILE3";
+  char name10[] = "ARRAY: FILE1_+_FILE2_+_FILE3";
+  char name11[] = "FILECONTENT";
+  char value1[] = "value for simple COPYCONTENTS";
+  char value2[] = "value for COPYCONTENTS + CONTENTTYPE";
+  char value3[] = "value for PTRNAME + NAMELENGTH + COPYNAME + CONTENTSLENGTH";
+  char value4[] = "value for simple PTRCONTENTS";
+  char value5[] = "value for PTRCONTENTS + CONTENTSLENGTH";
+  char value6[] = "value for PTRCOTNENTS + CONTENTSLENGTH + CONTENTTYPE";
+  char value7[] = "inet_ntoa_r.h";
+  char value8[] = "Makefile.b32.resp";
+  char type2[] = "image/gif";
+  char type6[] = "text/plain";
+  char type7[] = "text/html";
+  int name3length = strlen(name3);
+  int value3length = strlen(value3);
+  int value5length = strlen(value4);
+  int value6length = strlen(value5);
+  int errors = 0;
+  int size;
+  int nread;
+  char buffer[4096];
+  struct HttpPost *httppost=NULL;
+  struct HttpPost *last_post=NULL;
+  struct curl_forms forms[4];
+
+  struct FormData *form;
+  struct Form formread;
+
+  if (FormAddTest("simple COPYCONTENTS test", &httppost, &last_post,
+                  CURLFORM_COPYNAME, name1, CURLFORM_COPYCONTENTS, value1,
+                  CURLFORM_END))
+    ++errors;
+  if (FormAddTest("COPYCONTENTS  + CONTENTTYPE test", &httppost, &last_post,
+                  CURLFORM_COPYNAME, name2, CURLFORM_COPYCONTENTS, value2,
+                  CURLFORM_CONTENTTYPE, type2, CURLFORM_END))
+    ++errors;
+  /* make null character at start to check that contentslength works
+     correctly */
+  name3[1] = '\0';
+  value3[1] = '\0';
+  if (FormAddTest("PTRNAME + NAMELENGTH + COPYNAME + CONTENTSLENGTH test",
+                  &httppost, &last_post,
+                  CURLFORM_PTRNAME, name3, CURLFORM_COPYCONTENTS, value3,
+                  CURLFORM_CONTENTSLENGTH, value3length,
+                  CURLFORM_NAMELENGTH, name3length, CURLFORM_END))
+    ++errors;
+  if (FormAddTest("simple PTRCONTENTS test", &httppost, &last_post,
+                  CURLFORM_COPYNAME, name4, CURLFORM_PTRCONTENTS, value4,
+                  CURLFORM_END))
+    ++errors;
+  /* make null character at start to check that contentslength works
+     correctly */
+  value5[1] = '\0';
+  if (FormAddTest("PTRCONTENTS + CONTENTSLENGTH test", &httppost, &last_post,
+                  CURLFORM_COPYNAME, name5, CURLFORM_PTRCONTENTS, value5,
+                  CURLFORM_CONTENTSLENGTH, value5length, CURLFORM_END))
+    ++errors;
+  /* make null character at start to check that contentslength works
+     correctly */
+  value6[1] = '\0';
+  if (FormAddTest("PTRCONTENTS + CONTENTSLENGTH + CONTENTTYPE test",
+                  &httppost, &last_post,
+                  CURLFORM_COPYNAME, name6, CURLFORM_PTRCONTENTS, value6,
+                  CURLFORM_CONTENTSLENGTH, value6length,
+                  CURLFORM_CONTENTTYPE, type6, CURLFORM_END))
+    ++errors;
+  if (FormAddTest("FILE + CONTENTTYPE test", &httppost, &last_post,
+                  CURLFORM_COPYNAME, name7, CURLFORM_FILE, value7,
+                  CURLFORM_CONTENTTYPE, type7, CURLFORM_END))
+    ++errors;
+  if (FormAddTest("FILE1 + FILE2 test", &httppost, &last_post,
+                  CURLFORM_COPYNAME, name8, CURLFORM_FILE, value7,
+                  CURLFORM_FILE, value8, CURLFORM_END))
+    ++errors;
+  if (FormAddTest("FILE1 + FILE2 + FILE3 test", &httppost, &last_post,
+                  CURLFORM_COPYNAME, name9, CURLFORM_FILE, value7,
+                  CURLFORM_FILE, value8, CURLFORM_FILE, value7, CURLFORM_END))
+    ++errors;
+  forms[0].option = CURLFORM_FILE;
+  forms[0].value  = value7;
+  forms[1].option = CURLFORM_FILE;
+  forms[1].value  = value8;
+  forms[2].option = CURLFORM_FILE;
+  forms[2].value  = value7;
+  forms[3].option  = CURLFORM_END;
+  if (FormAddTest("FILE1 + FILE2 + FILE3 ARRAY test", &httppost, &last_post,
+                  CURLFORM_COPYNAME, name10, CURLFORM_ARRAY, forms,
+                  CURLFORM_END))
+    ++errors;
+  if (FormAddTest("FILECONTENT test", &httppost, &last_post,
+                  CURLFORM_COPYNAME, name11, CURLFORM_FILECONTENT, value7,
+                  CURLFORM_END))
+    ++errors;
+
+  form=Curl_getFormData(httppost, &size);
+
+  Curl_FormInit(&formread, form);
+
+  do {
+    nread = Curl_FormReader(buffer, 1, sizeof(buffer),
+                            (FILE *)&formread);
+
+    if(-1 == nread)
+      break;
+    fwrite(buffer, nread, 1, stdout);
+  } while(1);
+
+  fprintf(stdout, "size: %d\n", size);
+  if (errors)
+    fprintf(stdout, "\n==> %d Test(s) failed!\n", errors);
+  else
+    fprintf(stdout, "\nAll Tests seem to have worked (please check output)\n");
+
+  return 0;
+}
+
+#endif
+
+#ifdef _OLD_FORM_DEBUG
+
+int main(int argc, char **argv)
+{
+#if 0
+  char *testargs[]={
+    "name1 = data in number one",
+    "name2 = number two data",
+    "test = @upload"
+  };
+#endif
+  int i;
+  char *nextarg;
+  struct HttpPost *httppost=NULL;
+  struct HttpPost *last_post=NULL;
+  struct HttpPost *post;
+  int size;
+  int nread;
+  char buffer[4096];
+
+  struct FormData *form;
+  struct Form formread;
+
+  for(i=1; i<argc; i++) {
+
+    if( FormParse( argv[i],
+                   &httppost,
+                   &last_post)) {
+      fprintf(stderr, "Illegally formatted input field: '%s'!\n",
+              argv[i]);
+      return 1;
+    }
+  }
+
+  form=Curl_getFormData(httppost, &size);
+
+  Curl_FormInit(&formread, form);
+
+  do {
+    nread = Curl_FormReader(buffer, 1, sizeof(buffer),
+                            (FILE *)&formread);
+
+    if(-1 == nread)
+      break;
+    fwrite(buffer, nread, 1, stderr);
+  } while(1);
+
+  fprintf(stderr, "size: %d\n", size);
+
+  return 0;
+}
+
+#endif
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 73 - 0
Source/CTest/Curl/formdata.h

@@ -0,0 +1,73 @@
+#ifndef __FORMDATA_H
+#define __FORMDATA_H
+
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+/* plain and simple linked list with lines to send */
+struct FormData {
+  struct FormData *next;
+  char *line;
+  long length;
+};
+
+struct Form {
+  struct FormData *data; /* current form line to send */
+  int sent; /* number of bytes of the current line that has already
+               been sent in a previous invoke */
+};
+
+/* used by FormAdd for temporary storage */
+typedef struct FormInfo {
+  char *name;
+  long namelength;
+  char *value;
+  long contentslength;
+  char *contenttype;
+  long flags;
+  struct curl_slist* contentheader;
+  struct FormInfo *more;
+} FormInfo;
+
+int Curl_FormInit(struct Form *form, struct FormData *formdata );
+
+struct FormData *Curl_getFormData(struct HttpPost *post,
+                                  int *size);
+
+/* fread() emulation */
+int Curl_FormReader(char *buffer,
+                    size_t size,
+                    size_t nitems,
+                    FILE *mydata);
+
+/* possible (old) fread() emulation that copies at most one line */
+int Curl_FormReadOneLine(char *buffer,
+                         size_t size,
+                         size_t nitems,
+                         FILE *mydata);
+
+char *Curl_FormBoundary(void);
+
+void Curl_formclean(struct FormData *);
+
+#endif
+

+ 2138 - 0
Source/CTest/Curl/ftp.c

@@ -0,0 +1,2138 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2001, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <errno.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
+#include <winsock.h>
+#else /* some kind of unix */
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <sys/types.h>
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#include <sys/utsname.h>
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef  VMS
+#include <in.h>
+#include <inet.h>
+#endif
+#endif
+
+#if defined(WIN32) && defined(__GNUC__) || defined(__MINGW32__)
+#include <errno.h>
+#endif
+
+#include <curl/curl.h>
+#include "urldata.h"
+#include "sendf.h"
+
+#include "if2ip.h"
+#include "hostip.h"
+#include "progress.h"
+#include "transfer.h"
+#include "escape.h"
+#include "http.h" /* for HTTP proxy tunnel stuff */
+#include "ftp.h"
+
+#ifdef KRB4
+#include "security.h"
+#include "krb4.h"
+#endif
+
+#include "strequal.h"
+#include "ssluse.h"
+#include "connect.h"
+
+#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
+#include "inet_ntoa_r.h"
+#endif
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#ifdef MALLOCDEBUG
+#include "memdebug.h"
+#endif
+
+/* Local API functions */
+static CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote);
+static CURLcode ftp_cwd(struct connectdata *conn, char *path);
+
+/* easy-to-use macro: */
+#define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z))) return result
+
+/***********************************************************************
+ *
+ * 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.
+ *
+ */
+static CURLcode AllowServerConnect(struct SessionHandle *data,
+                                   struct connectdata *conn,
+                                   int sock)
+{
+  fd_set rdset;
+  struct timeval dt;
+  
+  FD_ZERO(&rdset);
+
+  FD_SET(sock, &rdset);
+
+  /* we give the server 10 seconds to connect to us */
+  dt.tv_sec = 10;
+  dt.tv_usec = 0;
+
+  switch (select(sock+1, &rdset, NULL, NULL, &dt)) {
+  case -1: /* error */
+    /* let's die here */
+    failf(data, "Error while waiting for server connect");
+    return CURLE_FTP_PORT_FAILED;
+  case 0:  /* timeout */
+    /* let's die here */
+    failf(data, "Timeout while waiting for server connect");
+    return CURLE_FTP_PORT_FAILED;
+  default:
+    /* we have received data here */
+    {
+      int s;
+      size_t size = sizeof(struct sockaddr_in);
+      struct sockaddr_in add;
+
+      getsockname(sock, (struct sockaddr *) &add, (socklen_t *)&size);
+      s=accept(sock, (struct sockaddr *) &add, (socklen_t *)&size);
+
+      sclose(sock); /* close the first socket */
+
+      if (-1 == s) {
+        /* DIE! */
+        failf(data, "Error accept()ing server connect");
+        return CURLE_FTP_PORT_FAILED;
+      }
+      infof(data, "Connection accepted from server\n");
+
+      conn->secondarysocket = s;
+    }
+    break;
+  }
+  return CURLE_OK;
+}
+
+
+/* --- parse FTP server responses --- */
+
+/*
+ * Curl_GetFTPResponse() is supposed to be invoked after each command sent to
+ * a remote FTP server. This function will wait and read all lines of the
+ * response and extract the relevant return code for the invoking function.
+ */
+
+int Curl_GetFTPResponse(char *buf,
+                        struct connectdata *conn,
+                        int *ftpcode)
+{
+  /* Brand new implementation.
+   * We cannot read just one byte per read() and then go back to select()
+   * as it seems that the OpenSSL read() stuff 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.  */
+
+  int sockfd = conn->firstsocket;
+  int nread;   /* total size read */
+  int perline; /* count bytes per line */
+  bool keepon=TRUE;
+  ssize_t gotbytes;
+  char *ptr;
+  int timeout = 3600; /* default timeout in seconds */
+  struct timeval interval;
+  fd_set rkeepfd;
+  fd_set readfd;
+  struct SessionHandle *data = conn->data;
+  char *line_start;
+  int code=0; /* default "error code" to return */
+
+#define SELECT_OK       0
+#define SELECT_ERROR    1 /* select() problems */
+#define SELECT_TIMEOUT  2 /* took too long */
+#define SELECT_MEMORY   3 /* no available memory */
+#define SELECT_CALLBACK 4 /* aborted by callback */
+
+  int error = SELECT_OK;
+
+  struct FTP *ftp = conn->proto.ftp;
+
+  if (ftpcode)
+    *ftpcode = 0; /* 0 for errors */
+
+  if(data->set.timeout) {
+    /* if timeout is requested, find out how much remaining time we have */
+    timeout = data->set.timeout - /* timeout time */
+      Curl_tvdiff(Curl_tvnow(), conn->now)/1000; /* spent time */
+    if(timeout <=0 ) {
+      failf(data, "Transfer aborted due to timeout");
+      return -SELECT_TIMEOUT; /* already too little time */
+    }
+  }
+
+  FD_ZERO (&readfd);            /* clear it */
+  FD_SET (sockfd, &readfd);     /* read socket */
+
+  /* get this in a backup variable to be able to restore it on each lap in the
+     select() loop */
+  rkeepfd = readfd;
+
+  ptr=buf;
+  line_start = buf;
+
+  nread=0;
+  perline=0;
+  keepon=TRUE;
+
+  while((nread<BUFSIZE) && (keepon && !error)) {
+    readfd = rkeepfd;              /* set every lap */
+    interval.tv_sec = timeout;
+    interval.tv_usec = 0;
+
+    if(!ftp->cache)
+      switch (select (sockfd+1, &readfd, NULL, NULL, &interval)) {
+      case -1: /* select() error, stop reading */
+        error = SELECT_ERROR;
+        failf(data, "Transfer aborted due to select() error");
+        break;
+      case 0: /* timeout */
+        error = SELECT_TIMEOUT;
+        failf(data, "Transfer aborted due to timeout");
+        break;
+      default:
+        error = SELECT_OK;
+        break;
+      }
+    if(SELECT_OK == error) {
+      /*
+       * This code previously didn't use the kerberos sec_read() code
+       * to read, but when we use Curl_read() it may do so. Do confirm
+       * that this is still ok and then remove this comment!
+       */
+      if(ftp->cache) {
+        /* we had data in the "cache", copy that instead of doing an actual
+           read */
+        memcpy(ptr, ftp->cache, ftp->cache_size);
+        gotbytes = ftp->cache_size;
+        free(ftp->cache);    /* free the cache */
+        ftp->cache = NULL;   /* clear the pointer */
+        ftp->cache_size = 0; /* zero the size just in case */
+      }
+      else {
+        int res = Curl_read(conn, sockfd, ptr,
+                            BUFSIZE-nread, &gotbytes);
+        if(res < 0)
+          /* EWOULDBLOCK */
+          continue; /* go looping again */
+
+        if(CURLE_OK != res)
+          keepon = FALSE;
+      }
+
+      if(!keepon)
+        ;
+      else if(gotbytes <= 0) {
+        keepon = FALSE;
+        error = SELECT_ERROR;
+        failf(data, "Connection aborted");
+      }
+      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 */
+        int i;
+
+        nread += gotbytes;
+        for(i = 0; i < gotbytes; ptr++, i++) {
+          perline++;
+          if(*ptr=='\n') {
+            /* a newline is CRLF in ftp-talk, so the CR is ignored as
+               the line isn't really terminated until the LF comes */
+            CURLcode result;
+
+            /* output debug output if that is requested */
+            if(data->set.verbose) {
+              fputs("< ", data->set.err);
+              fwrite(line_start, perline, 1, data->set.err);
+              /* no need to output LF here, it is part of the data */
+            }
+
+            /*
+             * 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(data, CLIENTWRITE_HEADER,
+                                       line_start, perline);
+            if(result)
+              return -SELECT_CALLBACK;
+                                       
+#define lastline(line) (isdigit((int)line[0]) && isdigit((int)line[1]) && \
+                        isdigit((int)line[2]) && (' ' == line[3]))
+
+            if(perline>3 && lastline(line_start)) {
+              /* 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=line_start, n=0; meow<ptr; meow++, n++)
+                buf[n] = *meow;
+              *meow=0; /* zero terminate */
+              keepon=FALSE;
+              line_start = ptr+1; /* advance pointer */
+              i++; /* skip this before getting out */
+              break;
+            }
+            perline=0; /* line starts over here */
+            line_start = 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!  Cleverly figured out by Eric Lavigne in December
+             2001. */
+          ftp->cache_size = gotbytes - i;
+          ftp->cache = (char *)malloc(ftp->cache_size);
+          if(ftp->cache)
+            memcpy(ftp->cache, line_start, ftp->cache_size);
+          else
+            return -SELECT_MEMORY; /**BANG**/
+        }
+      } /* there was data */
+    } /* if(no error) */
+  } /* while there's buffer left and loop is requested */
+
+  if(!error)
+    code = atoi(buf);
+
+#ifdef KRB4
+  /* handle the security-oriented responses 6xx ***/
+  /* FIXME: some errorchecking perhaps... ***/
+  switch(code) {
+  case 631:
+    Curl_sec_read_msg(conn, buf, prot_safe);
+    break;
+  case 632:
+    Curl_sec_read_msg(conn, buf, prot_private);
+    break;
+  case 633:
+    Curl_sec_read_msg(conn, buf, prot_confidential);
+    break;
+  default:
+    /* normal ftp stuff we pass through! */
+    break;
+  }
+#endif
+
+  if(error)
+    return -error;
+
+  if(ftpcode)
+    *ftpcode=code; /* return the initial number like this */
+
+  return nread; /* total amount of bytes read */
+}
+
+#ifndef ENABLE_IPV6
+/*
+ * This function is only used by code that works on IPv4. When we add proper
+ * support for that functionality with IPv6, this function can go in again.
+ */
+/* -- who are we? -- */
+static char *getmyhost(char *buf, int buf_size)
+{
+#if defined(HAVE_GETHOSTNAME)
+  gethostname(buf, buf_size);
+#elif defined(HAVE_UNAME)
+  struct utsname ugnm;
+  strncpy(buf, uname(&ugnm) < 0 ? "localhost" : ugnm.nodename, buf_size - 1);
+  buf[buf_size - 1] = '\0';
+#else
+  /* We have no means of finding the local host name! */
+  strncpy(buf, "localhost", buf_size);
+  buf[buf_size - 1] = '\0';
+#endif
+  return buf;
+}
+
+#endif /* ipv4-only function */
+
+
+/* ftp_connect() should do everything that is to be considered a part
+   of the connection phase. */
+CURLcode Curl_ftp_connect(struct connectdata *conn)
+{
+  /* this is FTP and no proxy */
+  int nread;
+  struct SessionHandle *data=conn->data;
+  char *buf = data->state.buffer; /* this is our buffer */
+  struct FTP *ftp;
+  CURLcode result;
+  int ftpcode;
+
+  ftp = (struct FTP *)malloc(sizeof(struct FTP));
+  if(!ftp)
+    return CURLE_OUT_OF_MEMORY;
+
+  memset(ftp, 0, sizeof(struct FTP));
+  conn->proto.ftp = ftp;
+
+  /* We always support persistant connections on ftp */
+  conn->bits.close = FALSE;
+
+  /* get some initial data into the ftp struct */
+  ftp->bytecountp = &conn->bytecount;
+
+  /* no need to duplicate them, the data struct won't change */
+  ftp->user = data->state.user;
+  ftp->passwd = data->state.passwd;
+
+  if (data->set.tunnel_thru_httpproxy) {
+    /* We want "seamless" FTP operations through HTTP proxy tunnel */
+    result = Curl_ConnectHTTPProxyTunnel(conn, conn->firstsocket,
+                                         conn->hostname, conn->remote_port);
+    if(CURLE_OK != result)
+      return result;
+  }
+
+  if(conn->protocol & PROT_FTPS) {
+    /* FTPS is simply ftp with SSL for the control channel */
+    /* now, perform the SSL initialization for this socket */
+    result = Curl_SSLConnect(conn);
+    if(result)
+      return result;
+  }
+
+
+  /* The first thing we do is wait for the "220*" line: */
+  nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
+  if(nread < 0)
+    return CURLE_OPERATION_TIMEOUTED;
+
+  if(ftpcode != 220) {
+    failf(data, "This doesn't seem like a nice ftp-server response");
+    return CURLE_FTP_WEIRD_SERVER_REPLY;
+  }
+
+#ifdef KRB4
+  /* if not anonymous login, try a secure login */
+  if(data->set.krb4) {
+
+    /* request data protection level (default is 'clear') */
+    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.krb4_level);
+
+    if(Curl_sec_login(conn) != 0)
+      infof(data, "Logging in with password in cleartext!\n");
+    else
+      infof(data, "Authentication successful\n");
+  }
+#endif
+  
+  /* send USER */
+  FTPSENDF(conn, "USER %s", ftp->user);
+
+  /* wait for feedback */
+  nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
+  if(nread < 0)
+    return CURLE_OPERATION_TIMEOUTED;
+
+  if(ftpcode == 530) {
+    /* 530 User ... access denied
+       (the server denies to log the specified user) */
+    failf(data, "Access denied: %s", &buf[4]);
+    return CURLE_FTP_ACCESS_DENIED;
+  }
+  else if(ftpcode == 331) {
+    /* 331 Password required for ...
+       (the server requires to send the user's password too) */
+    FTPSENDF(conn, "PASS %s", ftp->passwd);
+    nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
+    if(nread < 0)
+      return CURLE_OPERATION_TIMEOUTED;
+
+    if(ftpcode == 530) {
+      /* 530 Login incorrect.
+         (the username and/or the password are incorrect) */
+      failf(data, "the username and/or the password are incorrect");
+      return CURLE_FTP_USER_PASSWORD_INCORRECT;
+    }
+    else if(ftpcode == 230) {
+      /* 230 User ... logged in.
+         (user successfully logged in) */
+        
+      infof(data, "We have successfully logged in\n");
+    }
+    else {
+      failf(data, "Odd return code after PASS");
+      return CURLE_FTP_WEIRD_PASS_REPLY;
+    }
+  }
+  else if(buf[0] == '2') {
+    /* 230 User ... logged in.
+       (the user logged in without password) */
+    infof(data, "We have successfully logged in\n");
+#ifdef KRB4
+        /* we are logged in (with Kerberos)
+         * now set the requested protection level
+         */
+    if(conn->sec_complete)
+      Curl_sec_set_protection_level(conn);
+
+    /* we may need to issue a KAUTH here to have access to the files
+     * do it if user supplied a password
+     */
+    if(data->state.passwd && *data->state.passwd)
+      Curl_krb_kauth(conn);
+#endif
+  }
+  else {
+    failf(data, "Odd return code after USER");
+    return CURLE_FTP_WEIRD_USER_REPLY;
+  }
+
+  /* send PWD to discover our entry point */
+  FTPSENDF(conn, "PWD", NULL);
+
+  /* wait for feedback */
+  nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
+  if(nread < 0)
+    return CURLE_OPERATION_TIMEOUTED;
+
+  if(ftpcode == 257) {
+    char *dir = (char *)malloc(nread+1);
+    char *store=dir;
+    char *ptr=&buf[4]; /* start on the first letter */
+    
+    /* 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++;
+      while(ptr && *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++;
+      }
+      ftp->entrypath =dir; /* remember this */
+      infof(data, "Entry path is '%s'\n", ftp->entrypath);
+    }
+    else {
+      /* couldn't get the path */
+    }
+
+  }
+  else {
+    /* We couldn't read the PWD response! */
+  }
+
+  return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * Curl_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.
+ */
+CURLcode Curl_ftp_done(struct connectdata *conn)
+{
+  struct SessionHandle *data = conn->data;
+  struct FTP *ftp = conn->proto.ftp;
+  ssize_t nread;
+  char *buf = data->state.buffer; /* this is our buffer */
+  int ftpcode;
+  CURLcode result=CURLE_OK;
+
+  if(data->set.upload) {
+    if((-1 != data->set.infilesize) && (data->set.infilesize != *ftp->bytecountp)) {
+      failf(data, "Wrote only partial file (%d out of %d bytes)",
+            *ftp->bytecountp, data->set.infilesize);
+      return CURLE_PARTIAL_FILE;
+    }
+  }
+  else {
+    if((-1 != conn->size) && (conn->size != *ftp->bytecountp) &&
+       (conn->maxdownload != *ftp->bytecountp)) {
+      failf(data, "Received only partial file: %d bytes", *ftp->bytecountp);
+      return CURLE_PARTIAL_FILE;
+    }
+    else if(!conn->bits.resume_done &&
+            !data->set.no_body &&
+            (0 == *ftp->bytecountp)) {
+      /* We consider this an error, but there's no true FTP error received
+         why we need to continue to "read out" the server response too.
+         We don't want to leave a "waiting" server reply if we'll get told
+         to make a second request on this same connection! */
+      failf(data, "No data was received!");
+      result = CURLE_FTP_COULDNT_RETR_FILE;
+    }
+  }
+
+#ifdef KRB4
+  Curl_sec_fflush_fd(conn, conn->secondarysocket);
+#endif
+  /* shut down the socket to inform the server we're done */
+  sclose(conn->secondarysocket);
+  conn->secondarysocket = -1;
+
+  if(!data->set.no_body && !conn->bits.resume_done) {  
+    /* now let's see what the server says about the transfer we
+       just performed: */
+    nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
+    if(nread < 0)
+      return CURLE_OPERATION_TIMEOUTED;
+
+    /* 226 Transfer complete, 250 Requested file action okay, completed. */
+    if((ftpcode != 226) && (ftpcode != 250)) {
+      failf(data, "server did not report OK, got %d", ftpcode);
+      return CURLE_FTP_WRITE_ERROR;
+    }
+  }
+
+  conn->bits.resume_done = FALSE; /* clean this for next connection */
+
+  /* Send any post-transfer QUOTE strings? */
+  if(!result && 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.
+ */
+
+static 
+CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
+{
+  struct curl_slist *item;
+  ssize_t nread;
+  int ftpcode;
+  CURLcode result;
+
+  item = quote;
+  while (item) {
+    if (item->data) {
+      FTPSENDF(conn, "%s", item->data);
+
+      nread = Curl_GetFTPResponse(conn->data->state.buffer, conn, &ftpcode);
+      if (nread < 0)
+        return CURLE_OPERATION_TIMEOUTED;
+
+      if (ftpcode >= 400) {
+        failf(conn->data, "QUOT string not accepted: %s", item->data);
+        return CURLE_FTP_QUOTE_ERROR;
+      }
+    }
+
+    item = item->next;
+  }
+
+  return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * ftp_cwd()
+ *
+ * Send 'CWD' to the remote server to Change Working Directory.
+ * It is the ftp version of the unix 'cd' command.
+ */
+static 
+CURLcode ftp_cwd(struct connectdata *conn, char *path)
+{
+  ssize_t nread;
+  int     ftpcode;
+  CURLcode result;
+  
+  FTPSENDF(conn, "CWD %s", path);
+  nread = Curl_GetFTPResponse(
+                              conn->data->state.buffer, conn, &ftpcode);
+  if (nread < 0)
+    return CURLE_OPERATION_TIMEOUTED;
+
+  if (ftpcode != 250) {
+    failf(conn->data, "Couldn't cd to %s", path);
+    return CURLE_FTP_ACCESS_DENIED;
+  }
+
+  return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * ftp_getfiletime()
+ *
+ * Get the timestamp of the given file.
+ */
+static
+CURLcode ftp_getfiletime(struct connectdata *conn, char *file)
+{
+  CURLcode result=CURLE_OK;
+  int ftpcode; /* for ftp status */
+  ssize_t nread;
+  char *buf = conn->data->state.buffer;
+
+  /* we have requested to get the modified-time of the file, this is yet
+     again a grey area as the MDTM is not kosher RFC959 */
+  FTPSENDF(conn, "MDTM %s", file);
+
+  nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
+  if(nread < 0)
+    return CURLE_OPERATION_TIMEOUTED;
+
+  if(ftpcode == 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;
+    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);
+      sprintf(buf, "%04d%02d%02d %02d:%02d:%02d",
+              year, month, day, hour, minute, second);
+      /* now, convert this into a time() value: */
+      conn->data->info.filetime = curl_getdate(buf, &secs);
+    }
+    else {
+      infof(conn->data, "unsupported MDTM reply format\n");
+    }
+  }
+  return  result;
+}
+
+/***********************************************************************
+ *
+ * ftp_transfertype()
+ *
+ * Set transfer type. We only deal with ASCII or BINARY so this function
+ * sets one of them.
+ */
+static CURLcode ftp_transfertype(struct connectdata *conn,
+                                  bool ascii)
+{
+  struct SessionHandle *data = conn->data;
+  int ftpcode;
+  ssize_t nread;
+  char *buf=data->state.buffer;
+  CURLcode result;
+
+  FTPSENDF(conn, "TYPE %s", ascii?"A":"I");
+
+  nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
+  if(nread < 0)
+    return CURLE_OPERATION_TIMEOUTED;
+  
+  if(ftpcode != 200) {
+    failf(data, "Couldn't set %s mode",
+          ascii?"ASCII":"binary");
+    return ascii? CURLE_FTP_COULDNT_SET_ASCII:CURLE_FTP_COULDNT_SET_BINARY;
+  }
+
+  return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * ftp_getsize()
+ *
+ * Returns the file size (in bytes) of the given remote file.
+ */
+
+static
+CURLcode ftp_getsize(struct connectdata *conn, char *file,
+                      ssize_t *size)
+{
+  struct SessionHandle *data = conn->data;
+  int ftpcode;
+  ssize_t nread;
+  char *buf=data->state.buffer;
+  CURLcode result;
+
+  FTPSENDF(conn, "SIZE %s", file);
+  nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
+  if(nread < 0)
+    return CURLE_OPERATION_TIMEOUTED;
+
+  if(ftpcode == 213) {
+    /* get the size from the ascii string: */
+    *size = atoi(buf+4);
+  }
+  else
+    return CURLE_FTP_COULDNT_GET_SIZE;
+
+  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.
+ *
+ */
+static void
+ftp_pasv_verbose(struct connectdata *conn,
+                 Curl_ipconnect *addr,
+                 char *newhost, /* ascii version */
+                 int port)
+{
+#ifndef ENABLE_IPV6
+  /*****************************************************************
+   *
+   * IPv4-only code section
+   */
+
+  struct in_addr in;
+  struct hostent * answer;
+
+#ifdef HAVE_INET_NTOA_R
+  char ntoa_buf[64];
+#endif
+  /* The array size trick below is to make this a large chunk of memory
+     suitably 8-byte aligned on 64-bit platforms. This was thoughtfully
+     suggested by Philip Gladstone. */
+  long bigbuf[9000 / sizeof(long)];
+
+#if defined(HAVE_INET_ADDR)
+  in_addr_t address;
+# if defined(HAVE_GETHOSTBYADDR_R)
+  int h_errnop;
+# endif
+  char *hostent_buf = (char *)bigbuf; /* get a char * to the buffer */
+
+  address = inet_addr(newhost);
+# ifdef HAVE_GETHOSTBYADDR_R
+
+#  ifdef HAVE_GETHOSTBYADDR_R_5
+  /* AIX, Digital Unix (OSF1, Tru64) style:
+     extern int gethostbyaddr_r(char *addr, size_t len, int type,
+     struct hostent *htent, struct hostent_data *ht_data); */
+
+  /* Fred Noz helped me try this out, now it at least compiles! */
+
+  /* Bjorn Reese (November 28 2001):
+     The Tru64 man page on gethostbyaddr_r() says that
+     the hostent struct must be filled with zeroes before the call to
+     gethostbyaddr_r(). */
+
+  memset(hostent_buf, 0, sizeof(struct hostent));
+
+  if(gethostbyaddr_r((char *) &address,
+                     sizeof(address), AF_INET,
+                     (struct hostent *)hostent_buf,
+                     hostent_buf + sizeof(*answer)))
+    answer=NULL;
+                           
+#  endif
+#  ifdef HAVE_GETHOSTBYADDR_R_7
+  /* Solaris and IRIX */
+  answer = gethostbyaddr_r((char *) &address, sizeof(address), AF_INET,
+                           (struct hostent *)bigbuf,
+                           hostent_buf + sizeof(*answer),
+                           sizeof(hostent_buf) - sizeof(*answer),
+                           &h_errnop);
+#  endif
+#  ifdef HAVE_GETHOSTBYADDR_R_8
+  /* Linux style */
+  if(gethostbyaddr_r((char *) &address, sizeof(address), AF_INET,
+                     (struct hostent *)hostent_buf,
+                     hostent_buf + sizeof(*answer),
+                     sizeof(hostent_buf) - sizeof(*answer),
+                     &answer,
+                     &h_errnop))
+    answer=NULL; /* error */
+#  endif
+        
+# else
+  answer = gethostbyaddr((char *) &address, sizeof(address), AF_INET);
+# endif
+#else
+  answer = NULL;
+#endif
+  (void) memcpy(&in.s_addr, addr, sizeof (Curl_ipconnect));
+  infof(conn->data, "Connecting to %s (%s) port %u\n",
+        answer?answer->h_name:newhost,
+#if defined(HAVE_INET_NTOA_R)
+        inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf)),
+#else
+        inet_ntoa(in),
+#endif
+        port);
+
+#else
+  /*****************************************************************
+   *
+   * IPv6-only code section
+   */
+  char hbuf[NI_MAXHOST]; /* ~1KB */
+  char nbuf[NI_MAXHOST]; /* ~1KB */
+  char sbuf[NI_MAXSERV]; /* around 32 */
+#ifdef NI_WITHSCOPEID
+  const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
+#else
+  const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
+#endif
+  port = 0; /* unused, prevent warning */
+  if (getnameinfo(addr->ai_addr, addr->ai_addrlen,
+                  nbuf, sizeof(nbuf), sbuf, sizeof(sbuf), niflags)) {
+    snprintf(nbuf, sizeof(nbuf), "?");
+    snprintf(sbuf, sizeof(sbuf), "?");
+  }
+        
+  if (getnameinfo(addr->ai_addr, addr->ai_addrlen,
+                  hbuf, sizeof(hbuf), NULL, 0, 0)) {
+    infof(conn->data, "Connecting to %s (%s) port %s\n", nbuf, newhost, sbuf);
+  }
+  else {
+    infof(conn->data, "Connecting to %s (%s) port %s\n", hbuf, nbuf, sbuf);
+  }
+#endif
+}
+
+/***********************************************************************
+ *
+ * ftp_use_port()
+ *
+ * Send the proper PORT command. PORT is the ftp client's way of telling the
+ * server that *WE* open a port that we listen on an awaits the server to
+ * connect to. This is the opposite of PASV.
+ */
+
+static
+CURLcode ftp_use_port(struct connectdata *conn)
+{
+  struct SessionHandle *data=conn->data;
+  int portsock=-1;
+  ssize_t nread;
+  char *buf = data->state.buffer; /* this is our buffer */
+  int ftpcode; /* receive FTP response codes in this */
+  CURLcode result;
+
+#ifdef ENABLE_IPV6
+  /******************************************************************
+   *
+   * Here's a piece of IPv6-specific code coming up
+   *
+   */
+
+  struct addrinfo hints, *res, *ai;
+  struct sockaddr_storage ss;
+  socklen_t sslen;
+  char hbuf[NI_MAXHOST];
+
+  struct sockaddr *sa=(struct sockaddr *)&ss;
+#ifdef NI_WITHSCOPEID
+  const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
+#else
+  const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
+#endif
+  unsigned char *ap;
+  unsigned char *pp;
+  int alen, plen;
+  char portmsgbuf[4096], tmp[4096];
+
+  const char *mode[] = { "EPRT", "LPRT", "PORT", NULL };
+  char **modep;
+
+  /*
+   * we should use Curl_if2ip?  given pickiness of recent ftpd,
+   * I believe we should use the same address as the control connection.
+   */
+  sslen = sizeof(ss);
+  if (getsockname(conn->firstsocket, (struct sockaddr *)&ss, &sslen) < 0)
+    return CURLE_FTP_PORT_FAILED;
+  
+  if (getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL, 0,
+                  niflags))
+    return CURLE_FTP_PORT_FAILED;
+
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_family = sa->sa_family;
+  /*hints.ai_family = ss.ss_family;
+    this way can be used if sockaddr_storage is properly defined, as glibc 
+    2.1.X doesn't do*/
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_flags = AI_PASSIVE;
+
+  if (getaddrinfo(hbuf, (char *)"0", &hints, &res))
+    return CURLE_FTP_PORT_FAILED;
+  
+  portsock = -1;
+  for (ai = res; ai; ai = ai->ai_next) {
+    portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+    if (portsock < 0)
+      continue;
+
+    if (bind(portsock, ai->ai_addr, ai->ai_addrlen) < 0) {
+      sclose(portsock);
+      portsock = -1;
+      continue;
+    }
+      
+    if (listen(portsock, 1) < 0) {
+      sclose(portsock);
+      portsock = -1;
+      continue;
+    }
+    
+    break;
+  }
+  freeaddrinfo(res);
+  if (portsock < 0) {
+    failf(data, strerror(errno));
+    return CURLE_FTP_PORT_FAILED;
+  }
+
+  sslen = sizeof(ss);
+  if (getsockname(portsock, sa, &sslen) < 0) {
+    failf(data, strerror(errno));
+    return CURLE_FTP_PORT_FAILED;
+  }
+
+  for (modep = (char **)mode; modep && *modep; modep++) {
+    int lprtaf, eprtaf;
+    
+    switch (sa->sa_family) {
+    case AF_INET:
+      ap = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_addr;
+      alen = sizeof(((struct sockaddr_in *)&ss)->sin_addr);
+      pp = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_port;
+      plen = sizeof(((struct sockaddr_in *)&ss)->sin_port);
+      lprtaf = 4;
+      eprtaf = 1;
+      break;
+    case AF_INET6:
+      ap = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_addr;
+      alen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_addr);
+      pp = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_port;
+      plen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_port);
+      lprtaf = 6;
+      eprtaf = 2;
+      break;
+    default:
+      ap = pp = NULL;
+      lprtaf = eprtaf = -1;
+      break;
+    }
+
+    if (strcmp(*modep, "EPRT") == 0) {
+      if (eprtaf < 0)
+        continue;
+      if (getnameinfo((struct sockaddr *)&ss, sslen,
+                      portmsgbuf, sizeof(portmsgbuf), tmp, sizeof(tmp), niflags))
+        continue;
+
+      /* do not transmit IPv6 scope identifier to the wire */
+      if (sa->sa_family == AF_INET6) {
+        char *q = strchr(portmsgbuf, '%');
+          if (q)
+            *q = '\0';
+      }
+
+      result = Curl_ftpsendf(conn, "%s |%d|%s|%s|", *modep, eprtaf,
+                             portmsgbuf, tmp);
+      if(result)
+        return result;
+    } else if (strcmp(*modep, "LPRT") == 0 ||
+               strcmp(*modep, "PORT") == 0) {
+      int i;
+      
+      if (strcmp(*modep, "LPRT") == 0 && lprtaf < 0)
+        continue;
+      if (strcmp(*modep, "PORT") == 0 && sa->sa_family != AF_INET)
+        continue;
+
+      portmsgbuf[0] = '\0';
+      if (strcmp(*modep, "LPRT") == 0) {
+        snprintf(tmp, sizeof(tmp), "%d,%d", lprtaf, alen);
+        if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
+            sizeof(portmsgbuf)) {
+          continue;
+        }
+      }
+
+      for (i = 0; i < alen; i++) {
+        if (portmsgbuf[0])
+          snprintf(tmp, sizeof(tmp), ",%u", ap[i]);
+        else
+          snprintf(tmp, sizeof(tmp), "%u", ap[i]);
+        
+        if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
+            sizeof(portmsgbuf)) {
+          continue;
+        }
+      }
+      
+      if (strcmp(*modep, "LPRT") == 0) {
+        snprintf(tmp, sizeof(tmp), ",%d", plen);
+        
+        if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf))
+          continue;
+      }
+
+      for (i = 0; i < plen; i++) {
+        snprintf(tmp, sizeof(tmp), ",%u", pp[i]);
+        
+        if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
+            sizeof(portmsgbuf)) {
+          continue;
+        }
+      }
+      
+      result = Curl_ftpsendf(conn, "%s %s", *modep, portmsgbuf);
+      if(result)
+        return result;
+    }
+    
+    nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
+    if(nread < 0)
+      return CURLE_OPERATION_TIMEOUTED;
+    
+    if (ftpcode != 200) {
+      failf(data, "Server does not grok %s", *modep);
+      continue;
+    }
+    else
+      break;
+  }
+  
+  if (!*modep) {
+    sclose(portsock);
+    return CURLE_FTP_PORT_FAILED;
+  }
+  /* 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 */
+  conn->secondarysocket = portsock;
+  
+#else
+  /******************************************************************
+   *
+   * Here's a piece of IPv4-specific code coming up
+   *
+   */
+  struct sockaddr_in sa;
+  struct hostent *h=NULL;
+  char *hostdataptr=NULL;
+  unsigned short porttouse;
+  char myhost[256] = "";
+
+  if(data->set.ftpport) {
+    if(Curl_if2ip(data->set.ftpport, myhost, sizeof(myhost))) {
+      h = Curl_resolv(data, myhost, 0, &hostdataptr);
+    }
+    else {
+      int len = strlen(data->set.ftpport);
+      if(len>1)
+        h = Curl_resolv(data, data->set.ftpport, 0, &hostdataptr);
+      if(h)
+        strcpy(myhost, data->set.ftpport); /* buffer overflow risk */
+    }
+  }
+  if(! *myhost) {
+    char *tmp_host = getmyhost(myhost, sizeof(myhost));
+    h=Curl_resolv(data, tmp_host, 0, &hostdataptr);
+  }
+  infof(data, "We connect from %s\n", myhost);
+  
+  if ( h ) {
+    if( (portsock = socket(AF_INET, SOCK_STREAM, 0)) >= 0 ) {
+      int size;
+      
+      /* 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 */
+      conn->secondarysocket = portsock;
+
+      memset((char *)&sa, 0, sizeof(sa));
+      memcpy((char *)&sa.sin_addr,
+             h->h_addr,
+             h->h_length);
+      sa.sin_family = AF_INET;
+      sa.sin_addr.s_addr = INADDR_ANY;
+      sa.sin_port = 0;
+      size = sizeof(sa);
+      
+      if(bind(portsock, (struct sockaddr *)&sa, size) >= 0) {
+        /* we succeeded to bind */
+        struct sockaddr_in add;
+        socklen_t socksize = sizeof(add);
+
+        if(getsockname(portsock, (struct sockaddr *) &add,
+                       &socksize)<0) {
+          failf(data, "getsockname() failed");
+          return CURLE_FTP_PORT_FAILED;
+        }
+        porttouse = ntohs(add.sin_port);
+        
+        if ( listen(portsock, 1) < 0 ) {
+          failf(data, "listen(2) failed on socket");
+          free(hostdataptr);
+          return CURLE_FTP_PORT_FAILED;
+        }
+      }
+      else {
+        failf(data, "bind(2) failed on socket");
+        free(hostdataptr);
+        return CURLE_FTP_PORT_FAILED;
+      }
+    }
+    else {
+      failf(data, "socket(2) failed (%s)");
+      free(hostdataptr);
+      return CURLE_FTP_PORT_FAILED;
+    }
+  }
+  else {
+    failf(data, "could't find my own IP address (%s)", myhost);
+    return CURLE_FTP_PORT_FAILED;
+  }
+  {
+#ifdef HAVE_INET_NTOA_R
+    char ntoa_buf[64];
+#endif
+    struct in_addr in;
+    unsigned short ip[5];
+    (void) memcpy(&in.s_addr, *h->h_addr_list, sizeof (in.s_addr));
+#ifdef HAVE_INET_NTOA_R
+    /* ignore the return code from inet_ntoa_r() as it is int or
+       char * depending on system */
+    inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf));
+    sscanf( ntoa_buf, "%hu.%hu.%hu.%hu",
+            &ip[0], &ip[1], &ip[2], &ip[3]);
+#else
+    sscanf( inet_ntoa(in), "%hu.%hu.%hu.%hu",
+            &ip[0], &ip[1], &ip[2], &ip[3]);
+#endif
+    result=Curl_ftpsendf(conn, "PORT %d,%d,%d,%d,%d,%d",
+                         ip[0], ip[1], ip[2], ip[3],
+                         porttouse >> 8,
+                         porttouse & 255);
+    if(result)
+      return result;
+  }
+
+  nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
+  if(nread < 0)
+    return CURLE_OPERATION_TIMEOUTED;
+
+  if(ftpcode != 200) {
+    failf(data, "Server does not grok PORT, try without it!");
+    return CURLE_FTP_PORT_FAILED;
+  }
+#endif /* end of ipv4-specific code */
+
+  return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * ftp_use_pasv()
+ *
+ * Send the PASV command. PASV is the ftp client's way of asking the server to
+ * open a second port that we can connect to (for the data transfer). This is
+ * the opposite of PORT.
+ */
+
+static
+CURLcode ftp_use_pasv(struct connectdata *conn)
+{
+  struct SessionHandle *data = conn->data;
+  ssize_t nread;
+  char *buf = data->state.buffer; /* this is our buffer */
+  int ftpcode; /* receive FTP response codes in this */
+  CURLcode result;
+  Curl_addrinfo *addr=NULL;
+  Curl_ipconnect *conninfo;
+
+  /*
+    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|)
+
+  */
+
+#if 1
+  const char *mode[] = { "EPSV", "PASV", NULL };
+  int results[] = { 229, 227, 0 };
+#else
+#if 0
+  char *mode[] = { "EPSV", "LPSV", "PASV", NULL };
+  int results[] = { 229, 228, 227, 0 };
+#else
+  const char *mode[] = { "PASV", NULL };
+  int results[] = { 227, 0 };
+#endif
+#endif
+  int modeoff;
+  unsigned short connectport; /* the local port connect() should use! */
+  unsigned short newport; /* remote port, not necessary the local one */
+  char *hostdataptr=NULL;
+  
+  /* newhost must be able to hold a full IP-style address in ASCII, which
+     in the IPv6 case means 5*8-1 = 39 letters */
+  char newhost[48];
+  char *newhostp=NULL;
+  
+  for (modeoff = (data->set.ftp_use_epsv?0:1);
+       mode[modeoff]; modeoff++) {
+    result = Curl_ftpsendf(conn, mode[modeoff]);
+    if(result)
+      return result;
+    nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
+    if(nread < 0)
+      return CURLE_OPERATION_TIMEOUTED;
+    if (ftpcode == results[modeoff])
+      break;
+  }
+
+  if (!mode[modeoff]) {
+    failf(data, "Odd return code after PASV");
+    return CURLE_FTP_WEIRD_PASV_REPLY;
+  }
+  else if (227 == results[modeoff]) {
+    int ip[4];
+    int port[2];
+    char *str=buf;
+
+    /*
+     * New 227-parser June 3rd 1999.
+     * It now scans for a sequence of six comma-separated numbers and
+     * will take 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 this 227-reply: %s", buf);
+      return CURLE_FTP_WEIRD_227_FORMAT;
+    }
+
+    sprintf(newhost, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
+    newhostp = newhost;
+    newport = (port[0]<<8) + port[1];
+  }
+#if 1
+  else if (229 == results[modeoff]) {
+    char *ptr = strchr(buf, '(');
+    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])) {
+        /* the four separators should be identical */
+        newport = num;
+
+        /* we should use the same host we already are connected to */
+        newhostp = conn->name;
+      }                      
+      else
+        ptr=NULL;
+    }
+    if(!ptr) {
+      failf(data, "Weirdly formatted EPSV reply");
+      return CURLE_FTP_WEIRD_PASV_REPLY;
+    }
+  }
+#endif
+  else
+    return CURLE_FTP_CANT_RECONNECT;
+
+  if(data->change.proxy) {
+    /*
+     * This is a tunnel through a http proxy and we need to connect to the
+     * proxy again here. We already have the name info for it since the
+     * previous lookup.
+     */
+    addr = conn->hostaddr;
+    connectport =
+      (unsigned short)conn->port; /* we connect to the proxy's port */
+  }
+  else {
+    /* normal, direct, ftp connection */
+    addr = Curl_resolv(data, newhostp, newport, &hostdataptr);
+    if(!addr) {
+      failf(data, "Can't resolve new host %s", newhost);
+      return CURLE_FTP_CANT_GET_HOST;
+    }
+    connectport = newport; /* we connect to the remote port */
+  }
+    
+  result = Curl_connecthost(conn,
+                            addr,
+                            connectport,
+                            &conn->secondarysocket,
+                            &conninfo);
+  
+  if((CURLE_OK == result) &&       
+     data->set.verbose)
+    /* this just dumps information about this second connection */
+    ftp_pasv_verbose(conn, conninfo, newhost, connectport);
+  
+  if(CURLE_OK != result)
+    return result;
+
+  if (data->set.tunnel_thru_httpproxy) {
+    /* We want "seamless" FTP operations through HTTP proxy tunnel */
+    result = Curl_ConnectHTTPProxyTunnel(conn, conn->secondarysocket,
+                                         newhost, newport);
+    if(CURLE_OK != result)
+      return result;
+  }
+
+  return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * 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)
+{
+  /* this is FTP and no proxy */
+  ssize_t nread;
+  CURLcode result;
+  struct SessionHandle *data=conn->data;
+  char *buf = data->state.buffer; /* this is our buffer */
+
+  /* the ftp struct is already inited in ftp_connect() */
+  struct FTP *ftp = conn->proto.ftp;
+
+  long *bytecountp = ftp->bytecountp;
+  int ftpcode; /* for ftp status */
+
+  /* Send any QUOTE strings? */
+  if(data->set.quote) {
+    if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK)
+      return result;
+  }
+    
+  /* This is a re-used connection. Since we change directory to where the
+     transfer is taking place, we must now get back to the original dir
+     where we ended up after login: */
+  if (conn->bits.reuse) {
+    if ((result = ftp_cwd(conn, ftp->entrypath)) != CURLE_OK)
+      return result;
+  }
+
+  /* change directory first! */
+  if(ftp->dir && ftp->dir[0]) {
+    if ((result = ftp_cwd(conn, ftp->dir)) != CURLE_OK)
+        return result;
+  }
+
+  /* Requested time of file? */
+  if(data->set.get_filetime && ftp->file) {
+    result = ftp_getfiletime(conn, ftp->file);
+    if(result)
+      return result;
+  }
+
+  /* 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.no_body && data->set.include_header) {
+    /* 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! */
+    ssize_t filesize;
+
+    /* Some servers return different sizes for different modes, and thus we
+       must set the proper type before we check the size */
+    result = ftp_transfertype(conn, data->set.ftp_ascii);
+    if(result)
+      return result;
+
+    /* failing to get size is not a serious error */
+    result = ftp_getsize(conn, ftp->file, &filesize);
+
+    if(CURLE_OK == result) {
+      sprintf(buf, "Content-Length: %d\r\n", filesize);
+      result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
+      if(result)
+        return result;
+    }
+
+    /* 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. */
+
+#ifdef HAVE_STRFTIME
+    if(data->set.get_filetime && data->info.filetime) {
+      struct tm *tm;
+#ifdef HAVE_LOCALTIME_R
+      struct tm buffer;
+      tm = (struct tm *)localtime_r(&data->info.filetime, &buffer);
+#else
+      tm = localtime((unsigned long *)&data->info.filetime);
+#endif
+      /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
+      strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S %Z\r\n",
+               tm);
+      result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
+      if(result)
+        return result;
+    }
+#endif
+
+    return CURLE_OK;
+  }
+
+  if(data->set.no_body)
+    /* don't transfer the data */
+    ;
+  /* Get us a second connection up and connected */
+  else if(data->set.ftp_use_port) {
+    /* We have chosen to use the PORT command */
+    result = ftp_use_port(conn);
+    if(CURLE_OK == result)
+      /* we have the data connection ready */
+      infof(data, "Connected the data stream with PORT!\n");
+  }
+  else {
+    /* We have chosen (this is default) to use the PASV command */
+    result = ftp_use_pasv(conn);
+    if(CURLE_OK == result)
+      infof(data, "Connected the data stream with PASV!\n");
+  }
+  
+  if(result)
+    return result;
+
+  if(data->set.upload) {
+
+    /* Set type to binary (unless specified ASCII) */
+    result = ftp_transfertype(conn, data->set.ftp_ascii);
+    if(result)
+      return result;
+
+    /* Send any PREQUOTE strings after transfer type is set? (Wesley Laxton)*/
+    if(data->set.prequote) {
+      if ((result = ftp_sendquote(conn, data->set.prequote)) != CURLE_OK)
+        return result;
+    }
+
+    if(conn->resume_from) {
+      /* 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(conn->resume_from < 0 ) {
+        /* we could've got a specified offset from the command line,
+           but now we know we didn't */
+        ssize_t gottensize;
+
+        if(CURLE_OK != ftp_getsize(conn, ftp->file, &gottensize)) {
+          failf(data, "Couldn't get remote file size");
+          return CURLE_FTP_COULDNT_GET_SIZE;
+        }
+        conn->resume_from = gottensize;
+      }
+
+      if(conn->resume_from) {
+        /* do we still game? */
+        int passed=0;
+        /* enable append instead */
+        data->set.ftp_append = 1;
+
+        /* Now, let's read off the proper amount of bytes from the
+           input. If we knew it was a proper file we could've just
+           fseek()ed but we only have a stream here */
+        do {
+          int readthisamountnow = (conn->resume_from - passed);
+          int actuallyread;
+
+          if(readthisamountnow > BUFSIZE)
+            readthisamountnow = BUFSIZE;
+
+          actuallyread =
+            data->set.fread(data->state.buffer, 1, readthisamountnow,
+                            data->set.in);
+
+          passed += actuallyread;
+          if(actuallyread != readthisamountnow) {
+            failf(data, "Could only read %d bytes from the input", passed);
+            return CURLE_FTP_COULDNT_USE_REST;
+          }
+        }
+        while(passed != conn->resume_from);
+
+        /* now, decrease the size of the read */
+        if(data->set.infilesize>0) {
+          data->set.infilesize -= conn->resume_from;
+
+          if(data->set.infilesize <= 0) {
+            infof(data, "File already completely uploaded\n");
+
+            /* no data to transfer */
+            result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+            
+            /* Set resume done so that we won't get any error in
+             * Curl_ftp_done() because we didn't transfer the amount of bytes
+             * that the local file file obviously is */
+            conn->bits.resume_done = TRUE; 
+
+            return CURLE_OK;
+          }
+        }
+        /* we've passed, proceed as normal */
+      }
+    }
+
+    /* Send everything on data->set.in to the socket */
+    if(data->set.ftp_append) {
+      /* we append onto the file instead of rewriting it */
+      FTPSENDF(conn, "APPE %s", ftp->file);
+    }
+    else {
+      FTPSENDF(conn, "STOR %s", ftp->file);
+    }
+
+    nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
+    if(nread < 0)
+      return CURLE_OPERATION_TIMEOUTED;
+
+    if(ftpcode>=400) {
+      failf(data, "Failed FTP upload:%s", buf+3);
+      /* oops, we never close the sockets! */
+      return CURLE_FTP_COULDNT_STOR_FILE;
+    }
+
+    if(data->set.ftp_use_port) {
+      /* PORT means we are now awaiting the server to connect to us. */
+      result = AllowServerConnect(data, conn, conn->secondarysocket);
+      if( result )
+        return result;
+    }
+
+    *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);
+
+    result = Curl_Transfer(conn, -1, -1, FALSE, NULL, /* no download */
+                      conn->secondarysocket, bytecountp);
+    if(result)
+      return result;
+      
+  }
+  else if(!data->set.no_body) {
+    /* Retrieve file or directory */
+    bool dirlist=FALSE;
+    long downloadsize=-1;
+
+    if(conn->bits.use_range && conn->range) {
+      long from, to;
+      int totalsize=-1;
+      char *ptr;
+      char *ptr2;
+
+      from=strtol(conn->range, &ptr, 0);
+      while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-')))
+        ptr++;
+      to=strtol(ptr, &ptr2, 0);
+      if(ptr == ptr2) {
+        /* we didn't get any digit */
+        to=-1;
+      }
+      if((-1 == to) && (from>=0)) {
+        /* X - */
+        conn->resume_from = from;
+        infof(data, "FTP RANGE %d to end of file\n", from);
+      }
+      else if(from < 0) {
+        /* -Y */
+        totalsize = -from;
+        conn->maxdownload = -from;
+        conn->resume_from = from;
+        infof(data, "FTP RANGE the last %d bytes\n", totalsize);
+      }
+      else {
+        /* X-Y */
+        totalsize = to-from;
+        conn->maxdownload = totalsize+1; /* include the last mentioned byte */
+        conn->resume_from = from;
+        infof(data, "FTP RANGE from %d getting %d bytes\n", from,
+              conn->maxdownload);
+      }
+      infof(data, "range-download from %d to %d, totally %d bytes\n",
+            from, to, totalsize);
+    }
+
+    if((data->set.ftp_list_only) || !ftp->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. */
+      dirlist = TRUE;
+
+      /* Set type to ASCII */
+      result = ftp_transfertype(conn, TRUE /* ASCII enforced */);
+      if(result)
+        return result;
+
+      /* if this output is to be machine-parsed, the NLST command will be
+         better used since the LIST command output is not specified or
+         standard in any way */
+
+      FTPSENDF(conn, "%s",
+            data->set.customrequest?data->set.customrequest:
+            (data->set.ftp_list_only?"NLST":"LIST"));
+    }
+    else {
+      ssize_t foundsize;
+
+      /* Set type to binary (unless specified ASCII) */
+      result = ftp_transfertype(conn, data->set.ftp_ascii);
+      if(result)
+        return result;
+
+      /* Send any PREQUOTE strings after transfer type is set? (Wesley Laxton)*/
+      if(data->set.prequote) {
+        if ((result = ftp_sendquote(conn, data->set.prequote)) != CURLE_OK)
+          return result;
+      }
+
+      /* Attempt to get the size, it'll be useful in some cases: for resumed
+         downloads and when talking to servers that don't give away the size
+         in the RETR response line. */
+      result = ftp_getsize(conn, ftp->file, &foundsize);
+      if(CURLE_OK == result)
+        downloadsize = foundsize;
+
+      if(conn->resume_from) {
+
+        /* Daniel: (August 4, 1999)
+         *
+         * We start with trying to use the SIZE command to figure out the size
+         * of the file we're gonna get. If we can get the size, this is by far
+         * the best way to know if we're trying to resume beyond the EOF.
+         *
+         * Daniel, November 28, 2001. We *always* get the size on downloads
+         * now, so it is done before this even when not doing resumes. I saved
+         * the comment above for nostalgical reasons! ;-)
+         */
+        if(CURLE_OK != result) {
+          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(conn->resume_from< 0) {
+            /* We're supposed to download the last abs(from) bytes */
+            if(foundsize < -conn->resume_from) {
+              failf(data, "Offset (%d) was beyond file size (%d)",
+                    conn->resume_from, foundsize);
+              return CURLE_FTP_BAD_DOWNLOAD_RESUME;
+            }
+            /* convert to size to download */
+            downloadsize = -conn->resume_from;
+            /* download from where? */
+            conn->resume_from = foundsize - downloadsize;
+          }
+          else {
+            if(foundsize < conn->resume_from) {
+              failf(data, "Offset (%d) was beyond file size (%d)",
+                    conn->resume_from, foundsize);
+              return CURLE_FTP_BAD_DOWNLOAD_RESUME;
+            }
+            /* Now store the number of bytes we are expected to download */
+            downloadsize = foundsize-conn->resume_from;
+          }
+        }
+
+        if (downloadsize == 0) {
+          /* no data to transfer */
+          result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+          infof(data, "File already completely downloaded\n");
+
+          /* Set resume done so that we won't get any error in Curl_ftp_done()
+           * because we didn't transfer the amount of bytes that the remote
+           * file obviously is */
+          conn->bits.resume_done = TRUE; 
+
+          return CURLE_OK;
+        }
+        
+        /* Set resume file transfer offset */
+        infof(data, "Instructs server to resume from offset %d\n",
+              conn->resume_from);
+
+        FTPSENDF(conn, "REST %d", conn->resume_from);
+
+        nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
+        if(nread < 0)
+          return CURLE_OPERATION_TIMEOUTED;
+
+        if(ftpcode != 350) {
+          failf(data, "Couldn't use REST: %s", buf+4);
+          return CURLE_FTP_COULDNT_USE_REST;
+        }
+      }
+
+      FTPSENDF(conn, "RETR %s", ftp->file);
+    }
+
+    nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
+    if(nread < 0)
+      return CURLE_OPERATION_TIMEOUTED;
+
+    if((ftpcode == 150) || (ftpcode == 125)) {
+
+      /*
+        A;
+        150 Opening BINARY mode data connection for /etc/passwd (2241
+        bytes).  (ok, the file is being transfered)
+        
+        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 /linux/fisk/kpanelrc (0.0.0.0,0) (545 bytes).
+          
+        E:
+        125 Data connection already open; Transfer starting. */
+
+      int size=-1; /* default unknown size */
+
+      if(!dirlist &&
+         !data->set.ftp_ascii &&
+         (-1 == downloadsize)) {
+        /*
+         * 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 transfered 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--) {
+          int index=bytes-buf;
+          /* this is a hint there is size information in there! ;-) */
+          while(--index) {
+            /* scan for the parenthesis and break there */
+            if('(' == *bytes)
+              break;
+            /* if only skip digits, or else we're in deep trouble */
+            if(!isdigit((int)*bytes)) {
+              bytes=NULL;
+              break;
+            }
+            /* one more estep backwards */
+            bytes--;
+          }
+          /* only if we have nothing but digits: */
+          if(bytes++) {
+            /* get the number! */
+            size = atoi(bytes);
+          }
+            
+        }
+      }
+      else if(downloadsize > -1)
+        size = downloadsize;
+
+      if(data->set.ftp_use_port) {
+        result = AllowServerConnect(data, conn, conn->secondarysocket);
+        if( result )
+          return result;
+      }
+
+      infof(data, "Getting file with size: %d\n", size);
+
+      /* FTP download: */
+      result=Curl_Transfer(conn, conn->secondarysocket, size, FALSE,
+                           bytecountp,
+                           -1, NULL); /* no upload here */
+      if(result)
+        return result;
+    }
+    else {
+      failf(data, "%s", buf+4);
+      return CURLE_FTP_COULDNT_RETR_FILE;
+    }
+        
+  }
+  /* end of transfer */
+
+  return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * Curl_ftp()
+ *
+ * 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.
+ */
+CURLcode Curl_ftp(struct connectdata *conn)
+{
+  CURLcode retcode;
+
+  struct SessionHandle *data = conn->data;
+  struct FTP *ftp;
+  int dirlength=0; /* 0 forces strlen() */
+
+  /* the ftp struct is already inited in ftp_connect() */
+  ftp = conn->proto.ftp;
+
+  /* We split the path into dir and file parts *before* we URLdecode
+     it */
+  ftp->file = strrchr(conn->ppath, '/');
+  if(ftp->file) {
+    if(ftp->file != conn->ppath)
+      dirlength=ftp->file-conn->ppath; /* don't count the traling slash */
+
+    ftp->file++; /* point to the first letter in the file name part or
+                    remain NULL */
+  }
+  else {
+    ftp->file = conn->ppath; /* there's only a file part */
+  }
+
+  if(*ftp->file) {
+    ftp->file = curl_unescape(ftp->file, 0);
+    if(NULL == ftp->file) {
+      failf(data, "no memory");
+      return CURLE_OUT_OF_MEMORY;
+    }
+  }
+  else
+    ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL
+                       pointer */
+
+  ftp->urlpath = conn->ppath;
+  if(dirlength) {
+    ftp->dir = curl_unescape(ftp->urlpath, dirlength);
+    if(NULL == ftp->dir) {
+      if(ftp->file)
+        free(ftp->file);
+      failf(data, "no memory");
+      return CURLE_OUT_OF_MEMORY; /* failure */
+    }
+  }
+  else
+    ftp->dir = NULL;
+
+  retcode = ftp_perform(conn);
+
+  /* clean up here, success or error doesn't matter */
+  if(ftp->file)
+    free(ftp->file);
+  if(ftp->dir)
+    free(ftp->dir);
+
+  ftp->file = ftp->dir = NULL; /* zero */
+
+  return retcode;
+}
+
+/***********************************************************************
+ *
+ * Curl_ftpsendf()
+ *
+ * Sends the formated string as a ftp command to a ftp server
+ *
+ * NOTE: we build the command in a fixed-length buffer, which sets length
+ * restrictions on the command!
+ */
+CURLcode Curl_ftpsendf(struct connectdata *conn,
+                       const char *fmt, ...)
+{
+  ssize_t bytes_written;
+  char s[256];
+  ssize_t write_len;
+  char *sptr=s;
+  CURLcode res = CURLE_OK;
+
+  va_list ap;
+  va_start(ap, fmt);
+  vsnprintf(s, 250, fmt, ap);
+  va_end(ap);
+
+  if(conn->data->set.verbose)
+    fprintf(conn->data->set.err, "> %s\n", s);
+
+  strcat(s, "\r\n"); /* append a trailing CRLF */
+
+  bytes_written=0;
+  write_len = strlen(s);
+
+  do {
+    res = Curl_write(conn, conn->firstsocket, sptr, write_len,
+                     &bytes_written);
+
+    if(CURLE_OK != res)
+      break;
+
+    if(bytes_written != write_len) {
+      write_len -= bytes_written;
+      sptr += bytes_written;
+    }
+    else
+      break;
+  } while(1);
+
+  return res;
+}
+
+/***********************************************************************
+ *
+ * Curl_ftp_disconnect()
+ *
+ * Disconnect from an FTP server. Cleanup protocol-specific per-connection
+ * resources
+ */
+CURLcode Curl_ftp_disconnect(struct connectdata *conn)
+{
+  struct FTP *ftp= conn->proto.ftp;
+
+  /* The FTP session may or may not have been allocated/setup at this point! */
+  if(ftp) {
+    if(ftp->entrypath)
+      free(ftp->entrypath);
+    if(ftp->cache)
+      free(ftp->cache);
+  }
+  return CURLE_OK;
+}
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 37 - 0
Source/CTest/Curl/ftp.h

@@ -0,0 +1,37 @@
+#ifndef __FTP_H
+#define __FTP_H
+
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+CURLcode Curl_ftp(struct connectdata *conn);
+CURLcode Curl_ftp_done(struct connectdata *conn);
+CURLcode Curl_ftp_connect(struct connectdata *conn);
+CURLcode Curl_ftp_disconnect(struct connectdata *conn);
+
+CURLcode Curl_ftpsendf(struct connectdata *, const char *fmt, ...);
+
+/* The kerberos stuff needs this: */
+int Curl_GetFTPResponse(char *buf, struct connectdata *conn,
+                        int *ftpcode);
+
+#endif

+ 2149 - 0
Source/CTest/Curl/getdate.c

@@ -0,0 +1,2149 @@
+
+/*  A Bison parser, made from getdate.y
+    by GNU Bison version 1.28  */
+
+#define YYBISON 1  /* Identify Bison output.  */
+
+#define tAGO    257
+#define tDAY    258
+#define tDAY_UNIT       259
+#define tDAYZONE        260
+#define tDST    261
+#define tHOUR_UNIT      262
+#define tID     263
+#define tMERIDIAN       264
+#define tMINUTE_UNIT    265
+#define tMONTH  266
+#define tMONTH_UNIT     267
+#define tSEC_UNIT       268
+#define tSNUMBER        269
+#define tUNUMBER        270
+#define tYEAR_UNIT      271
+#define tZONE   272
+
+#line 1 "getdate.y"
+
+/*
+**  Originally written by Steven M. Bellovin <[email protected]> while
+**  at the University of North Carolina at Chapel Hill.  Later tweaked by
+**  a couple of people on Usenet.  Completely overhauled by Rich $alz
+**  <[email protected]> and Jim Berets <[email protected]> in August, 1990.
+**
+**  This code is in the public domain and has no copyright.
+*/
+
+#include "setup.h"
+
+# ifdef HAVE_ALLOCA_H
+#  include <alloca.h>
+# endif
+
+# ifdef HAVE_TIME_H
+#  include <time.h>
+# endif
+
+#ifndef YYDEBUG
+  /* to satisfy gcc -Wundef, we set this to 0 */
+#define YYDEBUG 0
+#endif
+
+/* Since the code of getdate.y is not included in the Emacs executable
+   itself, there is no need to #define static in this file.  Even if
+   the code were included in the Emacs executable, it probably
+   wouldn't do any harm to #undef it here; this will only cause
+   problems if we try to write to a static variable, which I don't
+   think this code needs to do.  */
+#ifdef emacs
+# undef static
+#endif
+
+#ifdef __APPLE__
+#include <sys/types.h>
+#include <sys/malloc.h>
+#else
+
+#endif
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#if HAVE_STDLIB_H
+# include <stdlib.h> /* for `free'; used by Bison 1.27 */
+#else
+
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+
+#endif
+
+#if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII))
+# define IN_CTYPE_DOMAIN(c) 1
+#else
+# define IN_CTYPE_DOMAIN(c) isascii(c)
+#endif
+
+#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
+#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
+#define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c))
+#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
+
+/* ISDIGIT differs from ISDIGIT_LOCALE, as follows:
+   - Its arg may be any int or unsigned int; it need not be an unsigned char.
+   - It's guaranteed to evaluate its argument exactly once.
+   - It's typically faster.
+   Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that
+   only '0' through '9' are digits.  Prefer ISDIGIT to ISDIGIT_LOCALE unless
+   it's important to use the locale's definition of `digit' even when the
+   host does not conform to Posix.  */
+#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
+
+#if defined (STDC_HEADERS) || defined (USG)
+# include <string.h>
+#endif
+
+/* The last #include file should be: */
+#ifdef MALLOCDEBUG
+#include "memdebug.h"
+#endif
+
+#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
+# define __attribute__(x)
+#endif
+
+#ifndef ATTRIBUTE_UNUSED
+# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+#endif
+
+/* Some old versions of bison generate parsers that use bcopy.
+   That loses on systems that don't provide the function, so we have
+   to redefine it here.  */
+#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
+# define bcopy(from, to, len) memcpy ((to), (from), (len))
+#endif
+
+/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
+   as well as gratuitiously global symbol names, so we can have multiple
+   yacc generated parsers in the same program.  Note that these are only
+   the variables produced by yacc.  If other parser generators (bison,
+   byacc, etc) produce additional global names that conflict at link time,
+   then those parser generators need to be fixed instead of adding those
+   names to this list. */
+
+#define yymaxdepth Curl_gd_maxdepth
+#define yyparse Curl_gd_parse
+#define yylex   Curl_gd_lex
+#define yyerror Curl_gd_error
+#define yylval  Curl_gd_lval
+#define yychar  Curl_gd_char
+#define yydebug Curl_gd_debug
+#define yypact  Curl_gd_pact
+#define yyr1    Curl_gd_r1
+#define yyr2    Curl_gd_r2
+#define yydef   Curl_gd_def
+#define yychk   Curl_gd_chk
+#define yypgo   Curl_gd_pgo
+#define yyact   Curl_gd_act
+#define yyexca  Curl_gd_exca
+#define yyerrflag Curl_gd_errflag
+#define yynerrs Curl_gd_nerrs
+#define yyps    Curl_gd_ps
+#define yypv    Curl_gd_pv
+#define yys     Curl_gd_s
+#define yy_yys  Curl_gd_yys
+#define yystate Curl_gd_state
+#define yytmp   Curl_gd_tmp
+#define yyv     Curl_gd_v
+#define yy_yyv  Curl_gd_yyv
+#define yyval   Curl_gd_val
+#define yylloc  Curl_gd_lloc
+#define yyreds  Curl_gd_reds          /* With YYDEBUG defined */
+#define yytoks  Curl_gd_toks          /* With YYDEBUG defined */
+#define yylhs   Curl_gd_yylhs
+#define yylen   Curl_gd_yylen
+#define yydefred Curl_gd_yydefred
+#define yydgoto Curl_gd_yydgoto
+#define yysindex Curl_gd_yysindex
+#define yyrindex Curl_gd_yyrindex
+#define yygindex Curl_gd_yygindex
+#define yytable  Curl_gd_yytable
+#define yycheck  Curl_gd_yycheck
+
+static int yylex ();
+static int yyerror ();
+
+#define EPOCH           1970
+#define HOUR(x)         ((x) * 60)
+
+#define MAX_BUFF_LEN    128   /* size of buffer to read the date into */
+
+/*
+**  An entry in the lexical lookup table.
+*/
+typedef struct _TABLE {
+    const char  *name;
+    int         type;
+    int         value;
+} TABLE;
+
+
+/*
+**  Meridian:  am, pm, or 24-hour style.
+*/
+typedef enum _MERIDIAN {
+    MERam, MERpm, MER24
+} MERIDIAN;
+
+/* parse results and input string */
+typedef struct _CONTEXT {
+    const char  *yyInput;
+    int         yyDayOrdinal;
+    int         yyDayNumber;
+    int         yyHaveDate;
+    int         yyHaveDay;
+    int         yyHaveRel;
+    int         yyHaveTime;
+    int         yyHaveZone;
+    int         yyTimezone;
+    int         yyDay;
+    int         yyHour;
+    int         yyMinutes;
+    int         yyMonth;
+    int         yySeconds;
+    int         yyYear;
+    MERIDIAN    yyMeridian;
+    int         yyRelDay;
+    int         yyRelHour;
+    int         yyRelMinutes;
+    int         yyRelMonth;
+    int         yyRelSeconds;
+    int         yyRelYear;
+} CONTEXT;
+
+/* enable use of extra argument to yyparse and yylex which can be used to pass
+**  in a user defined value (CONTEXT struct in our case)
+*/
+#define YYPARSE_PARAM cookie
+#define YYLEX_PARAM cookie
+#define context ((CONTEXT *) cookie)
+
+#line 215 "getdate.y"
+typedef union {
+    int                 Number;
+    enum _MERIDIAN      Meridian;
+} YYSTYPE;
+#include <stdio.h>
+
+#ifndef __cplusplus
+#ifndef __STDC__
+#define const
+#endif
+#endif
+
+
+
+#define YYFINAL         61
+#define YYFLAG          -32768
+#define YYNTBASE        22
+
+#define YYTRANSLATE(x) ((unsigned)(x) <= 272 ? yytranslate[x] : 32)
+
+static const char yytranslate[] = {     0,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,    20,     2,     2,    21,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,    19,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     1,     3,     4,     5,     6,
+     7,     8,     9,    10,    11,    12,    13,    14,    15,    16,
+    17,    18
+};
+
+#if YYDEBUG != 0
+static const short yyprhs[] = {     0,
+     0,     1,     4,     6,     8,    10,    12,    14,    16,    19,
+    24,    29,    36,    43,    45,    47,    50,    52,    55,    58,
+    62,    68,    72,    76,    79,    84,    87,    91,    94,    96,
+    99,   102,   104,   107,   110,   112,   115,   118,   120,   123,
+   126,   128,   131,   134,   136,   139,   142,   144,   146,   147
+};
+
+static const short yyrhs[] = {    -1,
+    22,    23,     0,    24,     0,    25,     0,    27,     0,    26,
+     0,    28,     0,    30,     0,    16,    10,     0,    16,    19,
+    16,    31,     0,    16,    19,    16,    15,     0,    16,    19,
+    16,    19,    16,    31,     0,    16,    19,    16,    19,    16,
+    15,     0,    18,     0,     6,     0,    18,     7,     0,     4,
+     0,     4,    20,     0,    16,     4,     0,    16,    21,    16,
+     0,    16,    21,    16,    21,    16,     0,    16,    15,    15,
+     0,    16,    12,    15,     0,    12,    16,     0,    12,    16,
+    20,    16,     0,    16,    12,     0,    16,    12,    16,     0,
+    29,     3,     0,    29,     0,    16,    17,     0,    15,    17,
+     0,    17,     0,    16,    13,     0,    15,    13,     0,    13,
+     0,    16,     5,     0,    15,     5,     0,     5,     0,    16,
+     8,     0,    15,     8,     0,     8,     0,    16,    11,     0,
+    15,    11,     0,    11,     0,    16,    14,     0,    15,    14,
+     0,    14,     0,    16,     0,     0,    10,     0
+};
+
+#endif
+
+#if YYDEBUG != 0
+static const short yyrline[] = { 0,
+   231,   232,   235,   238,   241,   244,   247,   250,   253,   259,
+   265,   274,   280,   292,   295,   298,   304,   308,   312,   318,
+   322,   340,   346,   352,   356,   361,   365,   372,   380,   383,
+   386,   389,   392,   395,   398,   401,   404,   407,   410,   413,
+   416,   419,   422,   425,   428,   431,   434,   439,   473,   477
+};
+#endif
+
+
+#if YYDEBUG != 0 || defined (YYERROR_VERBOSE)
+
+static const char * const yytname[] = {   "$","error","$undefined.","tAGO","tDAY",
+"tDAY_UNIT","tDAYZONE","tDST","tHOUR_UNIT","tID","tMERIDIAN","tMINUTE_UNIT",
+"tMONTH","tMONTH_UNIT","tSEC_UNIT","tSNUMBER","tUNUMBER","tYEAR_UNIT","tZONE",
+"':'","','","'/'","spec","item","time","zone","day","date","rel","relunit","number",
+"o_merid", NULL
+};
+#endif
+
+static const short yyr1[] = {     0,
+    22,    22,    23,    23,    23,    23,    23,    23,    24,    24,
+    24,    24,    24,    25,    25,    25,    26,    26,    26,    27,
+    27,    27,    27,    27,    27,    27,    27,    28,    28,    29,
+    29,    29,    29,    29,    29,    29,    29,    29,    29,    29,
+    29,    29,    29,    29,    29,    29,    29,    30,    31,    31
+};
+
+static const short yyr2[] = {     0,
+     0,     2,     1,     1,     1,     1,     1,     1,     2,     4,
+     4,     6,     6,     1,     1,     2,     1,     2,     2,     3,
+     5,     3,     3,     2,     4,     2,     3,     2,     1,     2,
+     2,     1,     2,     2,     1,     2,     2,     1,     2,     2,
+     1,     2,     2,     1,     2,     2,     1,     1,     0,     1
+};
+
+static const short yydefact[] = {     1,
+     0,    17,    38,    15,    41,    44,     0,    35,    47,     0,
+    48,    32,    14,     2,     3,     4,     6,     5,     7,    29,
+     8,    18,    24,    37,    40,    43,    34,    46,    31,    19,
+    36,    39,     9,    42,    26,    33,    45,     0,    30,     0,
+     0,    16,    28,     0,    23,    27,    22,    49,    20,    25,
+    50,    11,     0,    10,     0,    49,    21,    13,    12,     0,
+     0
+};
+
+static const short yydefgoto[] = {     1,
+    14,    15,    16,    17,    18,    19,    20,    21,    54
+};
+
+static const short yypact[] = {-32768,
+     0,   -19,-32768,-32768,-32768,-32768,   -13,-32768,-32768,    30,
+    15,-32768,    14,-32768,-32768,-32768,-32768,-32768,-32768,    19,
+-32768,-32768,     4,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
+-32768,-32768,-32768,-32768,    -6,-32768,-32768,    16,-32768,    17,
+    23,-32768,-32768,    24,-32768,-32768,-32768,    27,    28,-32768,
+-32768,-32768,    29,-32768,    32,    -8,-32768,-32768,-32768,    50,
+-32768
+};
+
+static const short yypgoto[] = {-32768,
+-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,    -5
+};
+
+
+#define YYLAST          51
+
+
+static const short yytable[] = {    60,
+    22,    51,    23,     2,     3,     4,    58,     5,    45,    46,
+     6,     7,     8,     9,    10,    11,    12,    13,    30,    31,
+    42,    43,    32,    44,    33,    34,    35,    36,    37,    38,
+    47,    39,    48,    40,    24,    41,    51,    25,    49,    50,
+    26,    52,    27,    28,    56,    53,    29,    57,    55,    61,
+    59
+};
+
+static const short yycheck[] = {     0,
+    20,    10,    16,     4,     5,     6,    15,     8,    15,    16,
+    11,    12,    13,    14,    15,    16,    17,    18,     4,     5,
+     7,     3,     8,    20,    10,    11,    12,    13,    14,    15,
+    15,    17,    16,    19,     5,    21,    10,     8,    16,    16,
+    11,    15,    13,    14,    16,    19,    17,    16,    21,     0,
+    56
+};
+#define YYPURE 1
+
+/* -*-C-*-  Note some compilers choke on comments on `#line' lines.  */
+#line 3 "/usr/local/share/bison.simple"
+/* This file comes from bison-1.28.  */
+
+/* Skeleton output parser for bison,
+   Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, when this file is copied by Bison into a
+   Bison output file, you may use that output file without restriction.
+   This special exception was added by the Free Software Foundation
+   in version 1.24 of Bison.  */
+
+/* This is the parser code that is written into each bison parser
+  when the %semantic_parser declaration is not specified in the grammar.
+  It was written by Richard Stallman by simplifying the hairy parser
+  used when %semantic_parser is specified.  */
+
+#ifndef YYSTACK_USE_ALLOCA
+#ifdef alloca
+#define YYSTACK_USE_ALLOCA
+#else /* alloca not defined */
+#ifdef __GNUC__
+#define YYSTACK_USE_ALLOCA
+#define alloca __builtin_alloca
+#else /* not GNU C.  */
+#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi) || (defined (__sun) && defined (__i386))
+#define YYSTACK_USE_ALLOCA
+#include <alloca.h>
+#else /* not sparc */
+/* We think this test detects Watcom and Microsoft C.  */
+/* This used to test MSDOS, but that is a bad idea
+   since that symbol is in the user namespace.  */
+#if (defined (_MSDOS) || defined (_MSDOS_)) && !defined (__TURBOC__)
+#if 0 /* No need for malloc.h, which pollutes the namespace;
+         instead, just don't use alloca.  */
+#include <malloc.h>
+#endif
+#else /* not MSDOS, or __TURBOC__ */
+#if defined(_AIX)
+/* I don't know what this was needed for, but it pollutes the namespace.
+   So I turned it off.   rms, 2 May 1997.  */
+/* #include <malloc.h>  */
+ #pragma alloca
+#define YYSTACK_USE_ALLOCA
+#else /* not MSDOS, or __TURBOC__, or _AIX */
+#if 0
+#ifdef __hpux /* [email protected] says this works for HPUX 9.05 and up,
+                 and on HPUX 10.  Eventually we can turn this on.  */
+#define YYSTACK_USE_ALLOCA
+#define alloca __builtin_alloca
+#endif /* __hpux */
+#endif
+#endif /* not _AIX */
+#endif /* not MSDOS, or __TURBOC__ */
+#endif /* not sparc */
+#endif /* not GNU C */
+#endif /* alloca not defined */
+#endif /* YYSTACK_USE_ALLOCA not defined */
+
+#ifdef YYSTACK_USE_ALLOCA
+#define YYSTACK_ALLOC alloca
+#else
+#define YYSTACK_ALLOC malloc
+#endif
+
+/* Note: there must be only one dollar sign in this file.
+   It is replaced by the list of actions, each action
+   as one case of the switch.  */
+
+#define yyerrok         (yyerrstatus = 0)
+#define yyclearin       (yychar = YYEMPTY)
+#define YYEMPTY         -2
+#define YYEOF           0
+#define YYACCEPT        goto yyacceptlab
+#define YYABORT         goto yyabortlab
+#define YYERROR         goto yyerrlab1
+/* Like YYERROR except do call yyerror.
+   This remains here temporarily to ease the
+   transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+#define YYFAIL          goto yyerrlab
+#define YYRECOVERING()  (!!yyerrstatus)
+#define YYBACKUP(token, value) \
+do                                                              \
+  if (yychar == YYEMPTY && yylen == 1)                          \
+    { yychar = (token), yylval = (value);                       \
+      yychar1 = YYTRANSLATE (yychar);                           \
+      YYPOPSTACK;                                               \
+      goto yybackup;                                            \
+    }                                                           \
+  else                                                          \
+    { yyerror ("syntax error: cannot back up"); YYERROR; }      \
+while (0)
+
+#define YYTERROR        1
+#define YYERRCODE       256
+
+#ifndef YYPURE
+#define YYLEX           yylex()
+#endif
+
+#ifdef YYPURE
+#ifdef YYLSP_NEEDED
+#ifdef YYLEX_PARAM
+#define YYLEX           yylex(&yylval, &yylloc, YYLEX_PARAM)
+#else
+#define YYLEX           yylex(&yylval, &yylloc)
+#endif
+#else /* not YYLSP_NEEDED */
+#ifdef YYLEX_PARAM
+#define YYLEX           yylex(&yylval, YYLEX_PARAM)
+#else
+#define YYLEX           yylex(&yylval)
+#endif
+#endif /* not YYLSP_NEEDED */
+#endif
+
+/* If nonreentrant, generate the variables here */
+
+#ifndef YYPURE
+
+int     yychar;                 /*  the lookahead symbol                */
+YYSTYPE yylval;                 /*  the semantic value of the           */
+                                /*  lookahead symbol                    */
+
+#ifdef YYLSP_NEEDED
+YYLTYPE yylloc;                 /*  location data for the lookahead     */
+                                /*  symbol                              */
+#endif
+
+int yynerrs;                    /*  number of parse errors so far       */
+#endif  /* not YYPURE */
+
+#if YYDEBUG != 0
+int yydebug;                    /*  nonzero means print parse trace     */
+/* Since this is uninitialized, it does not stop multiple parsers
+   from coexisting.  */
+#endif
+
+/*  YYINITDEPTH indicates the initial size of the parser's stacks       */
+
+#ifndef YYINITDEPTH
+#define YYINITDEPTH 200
+#endif
+
+/*  YYMAXDEPTH is the maximum size the stacks can grow to
+    (effective only if the built-in stack extension method is used).  */
+
+#if YYMAXDEPTH == 0
+#undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 10000
+#endif
+
+/* Define __yy_memcpy.  Note that the size argument
+   should be passed with type unsigned int, because that is what the non-GCC
+   definitions require.  With GCC, __builtin_memcpy takes an arg
+   of type size_t, but it can handle unsigned int.  */
+
+#if __GNUC__ > 1                /* GNU C and GNU C++ define this.  */
+#define __yy_memcpy(TO,FROM,COUNT)      __builtin_memcpy(TO,FROM,COUNT)
+#else                           /* not GNU C or C++ */
+#ifndef __cplusplus
+
+/* This is the most reliable way to avoid incompatibilities
+   in available built-in functions on various systems.  */
+static void
+__yy_memcpy (to, from, count)
+     char *to;
+     char *from;
+     unsigned int count;
+{
+  register char *f = from;
+  register char *t = to;
+  register int i = count;
+
+  while (i-- > 0)
+    *t++ = *f++;
+}
+
+#else /* __cplusplus */
+
+/* This is the most reliable way to avoid incompatibilities
+   in available built-in functions on various systems.  */
+static void
+__yy_memcpy (char *to, char *from, unsigned int count)
+{
+  register char *t = to;
+  register char *f = from;
+  register int i = count;
+
+  while (i-- > 0)
+    *t++ = *f++;
+}
+
+#endif
+#endif
+
+#line 217 "/usr/local/share/bison.simple"
+
+/* The user can define YYPARSE_PARAM as the name of an argument to be passed
+   into yyparse.  The argument should have type void *.
+   It should actually point to an object.
+   Grammar actions can access the variable by casting it
+   to the proper pointer type.  */
+
+#ifdef YYPARSE_PARAM
+#ifdef __cplusplus
+#define YYPARSE_PARAM_ARG void *YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL
+#else /* not __cplusplus */
+#define YYPARSE_PARAM_ARG YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM;
+#endif /* not __cplusplus */
+#else /* not YYPARSE_PARAM */
+#define YYPARSE_PARAM_ARG
+#define YYPARSE_PARAM_DECL
+#endif /* not YYPARSE_PARAM */
+
+/* Prevent warning if -Wstrict-prototypes.  */
+#ifdef __GNUC__
+#ifdef YYPARSE_PARAM
+int yyparse (void *);
+#else
+int yyparse (void);
+#endif
+#endif
+
+int
+yyparse(YYPARSE_PARAM_ARG)
+     YYPARSE_PARAM_DECL
+{
+  register int yystate;
+  register int yyn;
+  register short *yyssp;
+  register YYSTYPE *yyvsp;
+  int yyerrstatus;      /*  number of tokens to shift before error messages enabled */
+  int yychar1 = 0;              /*  lookahead token as an internal (translated) token number */
+
+  short yyssa[YYINITDEPTH];     /*  the state stack                     */
+  YYSTYPE yyvsa[YYINITDEPTH];   /*  the semantic value stack            */
+
+  short *yyss = yyssa;          /*  refer to the stacks thru separate pointers */
+  YYSTYPE *yyvs = yyvsa;        /*  to allow yyoverflow to reallocate them elsewhere */
+
+#ifdef YYLSP_NEEDED
+  YYLTYPE yylsa[YYINITDEPTH];   /*  the location stack                  */
+  YYLTYPE *yyls = yylsa;
+  YYLTYPE *yylsp;
+
+#define YYPOPSTACK   (yyvsp--, yyssp--, yylsp--)
+#else
+#define YYPOPSTACK   (yyvsp--, yyssp--)
+#endif
+
+  int yystacksize = YYINITDEPTH;
+  int yyfree_stacks = 0;
+
+#ifdef YYPURE
+  int yychar;
+  YYSTYPE yylval;
+  int yynerrs;
+#ifdef YYLSP_NEEDED
+  YYLTYPE yylloc;
+#endif
+#endif
+
+  YYSTYPE yyval;                /*  the variable used to return         */
+                                /*  semantic values from the action     */
+                                /*  routines                            */
+
+  int yylen;
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Starting parse\n");
+#endif
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;             /* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss - 1;
+  yyvsp = yyvs;
+#ifdef YYLSP_NEEDED
+  yylsp = yyls;
+#endif
+
+/* Push a new state, which is found in  yystate  .  */
+/* In all cases, when you get here, the value and location stacks
+   have just been pushed. so pushing a state here evens the stacks.  */
+yynewstate:
+
+  *++yyssp = yystate;
+
+  if (yyssp >= yyss + yystacksize - 1)
+    {
+      /* Give user a chance to reallocate the stack */
+      /* Use copies of these so that the &'s don't force the real ones into memory. */
+      YYSTYPE *yyvs1 = yyvs;
+      short *yyss1 = yyss;
+#ifdef YYLSP_NEEDED
+      YYLTYPE *yyls1 = yyls;
+#endif
+
+      /* Get the current used size of the three stacks, in elements.  */
+      int size = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      /* Each stack pointer address is followed by the size of
+         the data in use in that stack, in bytes.  */
+#ifdef YYLSP_NEEDED
+      /* This used to be a conditional around just the two extra args,
+         but that might be undefined if yyoverflow is a macro.  */
+      yyoverflow("parser stack overflow",
+                 &yyss1, size * sizeof (*yyssp),
+                 &yyvs1, size * sizeof (*yyvsp),
+                 &yyls1, size * sizeof (*yylsp),
+                 &yystacksize);
+#else
+      yyoverflow("parser stack overflow",
+                 &yyss1, size * sizeof (*yyssp),
+                 &yyvs1, size * sizeof (*yyvsp),
+                 &yystacksize);
+#endif
+
+      yyss = yyss1; yyvs = yyvs1;
+#ifdef YYLSP_NEEDED
+      yyls = yyls1;
+#endif
+#else /* no yyoverflow */
+      /* Extend the stack our own way.  */
+      if (yystacksize >= YYMAXDEPTH)
+        {
+          yyerror("parser stack overflow");
+          if (yyfree_stacks)
+            {
+              free (yyss);
+              free (yyvs);
+#ifdef YYLSP_NEEDED
+              free (yyls);
+#endif
+            }
+          return 2;
+        }
+      yystacksize *= 2;
+      if (yystacksize > YYMAXDEPTH)
+        yystacksize = YYMAXDEPTH;
+#ifndef YYSTACK_USE_ALLOCA
+      yyfree_stacks = 1;
+#endif
+      yyss = (short *) YYSTACK_ALLOC (yystacksize * sizeof (*yyssp));
+      __yy_memcpy ((char *)yyss, (char *)yyss1,
+                   size * (unsigned int) sizeof (*yyssp));
+      yyvs = (YYSTYPE *) YYSTACK_ALLOC (yystacksize * sizeof (*yyvsp));
+      __yy_memcpy ((char *)yyvs, (char *)yyvs1,
+                   size * (unsigned int) sizeof (*yyvsp));
+#ifdef YYLSP_NEEDED
+      yyls = (YYLTYPE *) YYSTACK_ALLOC (yystacksize * sizeof (*yylsp));
+      __yy_memcpy ((char *)yyls, (char *)yyls1,
+                   size * (unsigned int) sizeof (*yylsp));
+#endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + size - 1;
+      yyvsp = yyvs + size - 1;
+#ifdef YYLSP_NEEDED
+      yylsp = yyls + size - 1;
+#endif
+
+#if YYDEBUG != 0
+      if (yydebug)
+        fprintf(stderr, "Stack size increased to %d\n", yystacksize);
+#endif
+
+      if (yyssp >= yyss + yystacksize - 1)
+        YYABORT;
+    }
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Entering state %d\n", yystate);
+#endif
+
+  goto yybackup;
+ yybackup:
+
+/* Do appropriate processing given the current state.  */
+/* Read a lookahead token if we need one and don't already have one.  */
+/* yyresume: */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+
+  yyn = yypact[yystate];
+  if (yyn == YYFLAG)
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* yychar is either YYEMPTY or YYEOF
+     or a valid token in external form.  */
+
+  if (yychar == YYEMPTY)
+    {
+#if YYDEBUG != 0
+      if (yydebug)
+        fprintf(stderr, "Reading a token: ");
+#endif
+      yychar = YYLEX;
+    }
+
+  /* Convert token to internal form (in yychar1) for indexing tables with */
+
+  if (yychar <= 0)              /* This means end of input. */
+    {
+      yychar1 = 0;
+      yychar = YYEOF;           /* Don't call YYLEX any more */
+
+#if YYDEBUG != 0
+      if (yydebug)
+        fprintf(stderr, "Now at end of input.\n");
+#endif
+    }
+  else
+    {
+      yychar1 = YYTRANSLATE(yychar);
+
+#if YYDEBUG != 0
+      if (yydebug)
+        {
+          fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]);
+          /* Give the individual parser a way to print the precise meaning
+             of a token, for further debugging info.  */
+#ifdef YYPRINT
+          YYPRINT (stderr, yychar, yylval);
+#endif
+          fprintf (stderr, ")\n");
+        }
+#endif
+    }
+
+  yyn += yychar1;
+  if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
+    goto yydefault;
+
+  yyn = yytable[yyn];
+
+  /* yyn is what to do for this token type in this state.
+     Negative => reduce, -yyn is rule number.
+     Positive => shift, yyn is new state.
+       New state is final state => don't bother to shift,
+       just return success.
+     0, or most negative number => error.  */
+
+  if (yyn < 0)
+    {
+      if (yyn == YYFLAG)
+        goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+  else if (yyn == 0)
+    goto yyerrlab;
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  /* Shift the lookahead token.  */
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
+#endif
+
+  /* Discard the token being shifted unless it is eof.  */
+  if (yychar != YYEOF)
+    yychar = YYEMPTY;
+
+  *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+  *++yylsp = yylloc;
+#endif
+
+  /* count tokens shifted since error; after three, turn off error status.  */
+  if (yyerrstatus) yyerrstatus--;
+
+  yystate = yyn;
+  goto yynewstate;
+
+/* Do the default action for the current state.  */
+yydefault:
+
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+
+/* Do a reduction.  yyn is the number of a rule to reduce with.  */
+yyreduce:
+  yylen = yyr2[yyn];
+  if (yylen > 0)
+    yyval = yyvsp[1-yylen]; /* implement default value of the action */
+
+#if YYDEBUG != 0
+  if (yydebug)
+    {
+      int i;
+
+      fprintf (stderr, "Reducing via rule %d (line %d), ",
+               yyn, yyrline[yyn]);
+
+      /* Print the symbols being reduced, and their result.  */
+      for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
+        fprintf (stderr, "%s ", yytname[yyrhs[i]]);
+      fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
+    }
+#endif
+
+
+  switch (yyn) {
+
+case 3:
+#line 235 "getdate.y"
+{
+            context->yyHaveTime++;
+        ;
+    break;}
+case 4:
+#line 238 "getdate.y"
+{
+            context->yyHaveZone++;
+        ;
+    break;}
+case 5:
+#line 241 "getdate.y"
+{
+            context->yyHaveDate++;
+        ;
+    break;}
+case 6:
+#line 244 "getdate.y"
+{
+            context->yyHaveDay++;
+        ;
+    break;}
+case 7:
+#line 247 "getdate.y"
+{
+            context->yyHaveRel++;
+        ;
+    break;}
+case 9:
+#line 253 "getdate.y"
+{
+            context->yyHour = yyvsp[-1].Number;
+            context->yyMinutes = 0;
+            context->yySeconds = 0;
+            context->yyMeridian = yyvsp[0].Meridian;
+        ;
+    break;}
+case 10:
+#line 259 "getdate.y"
+{
+            context->yyHour = yyvsp[-3].Number;
+            context->yyMinutes = yyvsp[-1].Number;
+            context->yySeconds = 0;
+            context->yyMeridian = yyvsp[0].Meridian;
+        ;
+    break;}
+case 11:
+#line 265 "getdate.y"
+{
+            context->yyHour = yyvsp[-3].Number;
+            context->yyMinutes = yyvsp[-1].Number;
+            context->yyMeridian = MER24;
+            context->yyHaveZone++;
+            context->yyTimezone = (yyvsp[0].Number < 0
+                                   ? -yyvsp[0].Number % 100 + (-yyvsp[0].Number / 100) * 60
+                                   : - (yyvsp[0].Number % 100 + (yyvsp[0].Number / 100) * 60));
+        ;
+    break;}
+case 12:
+#line 274 "getdate.y"
+{
+            context->yyHour = yyvsp[-5].Number;
+            context->yyMinutes = yyvsp[-3].Number;
+            context->yySeconds = yyvsp[-1].Number;
+            context->yyMeridian = yyvsp[0].Meridian;
+        ;
+    break;}
+case 13:
+#line 280 "getdate.y"
+{
+            context->yyHour = yyvsp[-5].Number;
+            context->yyMinutes = yyvsp[-3].Number;
+            context->yySeconds = yyvsp[-1].Number;
+            context->yyMeridian = MER24;
+            context->yyHaveZone++;
+            context->yyTimezone = (yyvsp[0].Number < 0
+                                   ? -yyvsp[0].Number % 100 + (-yyvsp[0].Number / 100) * 60
+                                   : - (yyvsp[0].Number % 100 + (yyvsp[0].Number / 100) * 60));
+        ;
+    break;}
+case 14:
+#line 292 "getdate.y"
+{
+            context->yyTimezone = yyvsp[0].Number;
+        ;
+    break;}
+case 15:
+#line 295 "getdate.y"
+{
+            context->yyTimezone = yyvsp[0].Number - 60;
+        ;
+    break;}
+case 16:
+#line 299 "getdate.y"
+{
+            context->yyTimezone = yyvsp[-1].Number - 60;
+        ;
+    break;}
+case 17:
+#line 304 "getdate.y"
+{
+            context->yyDayOrdinal = 1;
+            context->yyDayNumber = yyvsp[0].Number;
+        ;
+    break;}
+case 18:
+#line 308 "getdate.y"
+{
+            context->yyDayOrdinal = 1;
+            context->yyDayNumber = yyvsp[-1].Number;
+        ;
+    break;}
+case 19:
+#line 312 "getdate.y"
+{
+            context->yyDayOrdinal = yyvsp[-1].Number;
+            context->yyDayNumber = yyvsp[0].Number;
+        ;
+    break;}
+case 20:
+#line 318 "getdate.y"
+{
+            context->yyMonth = yyvsp[-2].Number;
+            context->yyDay = yyvsp[0].Number;
+        ;
+    break;}
+case 21:
+#line 322 "getdate.y"
+{
+          /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY.
+             The goal in recognizing YYYY/MM/DD is solely to support legacy
+             machine-generated dates like those in an RCS log listing.  If
+             you want portability, use the ISO 8601 format.  */
+          if (yyvsp[-4].Number >= 1000)
+            {
+              context->yyYear = yyvsp[-4].Number;
+              context->yyMonth = yyvsp[-2].Number;
+              context->yyDay = yyvsp[0].Number;
+            }
+          else
+            {
+              context->yyMonth = yyvsp[-4].Number;
+              context->yyDay = yyvsp[-2].Number;
+              context->yyYear = yyvsp[0].Number;
+            }
+        ;
+    break;}
+case 22:
+#line 340 "getdate.y"
+{
+            /* ISO 8601 format.  yyyy-mm-dd.  */
+            context->yyYear = yyvsp[-2].Number;
+            context->yyMonth = -yyvsp[-1].Number;
+            context->yyDay = -yyvsp[0].Number;
+        ;
+    break;}
+case 23:
+#line 346 "getdate.y"
+{
+            /* e.g. 17-JUN-1992.  */
+            context->yyDay = yyvsp[-2].Number;
+            context->yyMonth = yyvsp[-1].Number;
+            context->yyYear = -yyvsp[0].Number;
+        ;
+    break;}
+case 24:
+#line 352 "getdate.y"
+{
+            context->yyMonth = yyvsp[-1].Number;
+            context->yyDay = yyvsp[0].Number;
+        ;
+    break;}
+case 25:
+#line 356 "getdate.y"
+{
+            context->yyMonth = yyvsp[-3].Number;
+            context->yyDay = yyvsp[-2].Number;
+            context->yyYear = yyvsp[0].Number;
+        ;
+    break;}
+case 26:
+#line 361 "getdate.y"
+{
+            context->yyMonth = yyvsp[0].Number;
+            context->yyDay = yyvsp[-1].Number;
+        ;
+    break;}
+case 27:
+#line 365 "getdate.y"
+{
+            context->yyMonth = yyvsp[-1].Number;
+            context->yyDay = yyvsp[-2].Number;
+            context->yyYear = yyvsp[0].Number;
+        ;
+    break;}
+case 28:
+#line 372 "getdate.y"
+{
+            context->yyRelSeconds = -context->yyRelSeconds;
+            context->yyRelMinutes = -context->yyRelMinutes;
+            context->yyRelHour = -context->yyRelHour;
+            context->yyRelDay = -context->yyRelDay;
+            context->yyRelMonth = -context->yyRelMonth;
+            context->yyRelYear = -context->yyRelYear;
+        ;
+    break;}
+case 30:
+#line 383 "getdate.y"
+{
+            context->yyRelYear += yyvsp[-1].Number * yyvsp[0].Number;
+        ;
+    break;}
+case 31:
+#line 386 "getdate.y"
+{
+            context->yyRelYear += yyvsp[-1].Number * yyvsp[0].Number;
+        ;
+    break;}
+case 32:
+#line 389 "getdate.y"
+{
+            context->yyRelYear += yyvsp[0].Number;
+        ;
+    break;}
+case 33:
+#line 392 "getdate.y"
+{
+            context->yyRelMonth += yyvsp[-1].Number * yyvsp[0].Number;
+        ;
+    break;}
+case 34:
+#line 395 "getdate.y"
+{
+            context->yyRelMonth += yyvsp[-1].Number * yyvsp[0].Number;
+        ;
+    break;}
+case 35:
+#line 398 "getdate.y"
+{
+            context->yyRelMonth += yyvsp[0].Number;
+        ;
+    break;}
+case 36:
+#line 401 "getdate.y"
+{
+            context->yyRelDay += yyvsp[-1].Number * yyvsp[0].Number;
+        ;
+    break;}
+case 37:
+#line 404 "getdate.y"
+{
+            context->yyRelDay += yyvsp[-1].Number * yyvsp[0].Number;
+        ;
+    break;}
+case 38:
+#line 407 "getdate.y"
+{
+            context->yyRelDay += yyvsp[0].Number;
+        ;
+    break;}
+case 39:
+#line 410 "getdate.y"
+{
+            context->yyRelHour += yyvsp[-1].Number * yyvsp[0].Number;
+        ;
+    break;}
+case 40:
+#line 413 "getdate.y"
+{
+            context->yyRelHour += yyvsp[-1].Number * yyvsp[0].Number;
+        ;
+    break;}
+case 41:
+#line 416 "getdate.y"
+{
+            context->yyRelHour += yyvsp[0].Number;
+        ;
+    break;}
+case 42:
+#line 419 "getdate.y"
+{
+            context->yyRelMinutes += yyvsp[-1].Number * yyvsp[0].Number;
+        ;
+    break;}
+case 43:
+#line 422 "getdate.y"
+{
+            context->yyRelMinutes += yyvsp[-1].Number * yyvsp[0].Number;
+        ;
+    break;}
+case 44:
+#line 425 "getdate.y"
+{
+            context->yyRelMinutes += yyvsp[0].Number;
+        ;
+    break;}
+case 45:
+#line 428 "getdate.y"
+{
+            context->yyRelSeconds += yyvsp[-1].Number * yyvsp[0].Number;
+        ;
+    break;}
+case 46:
+#line 431 "getdate.y"
+{
+            context->yyRelSeconds += yyvsp[-1].Number * yyvsp[0].Number;
+        ;
+    break;}
+case 47:
+#line 434 "getdate.y"
+{
+            context->yyRelSeconds += yyvsp[0].Number;
+        ;
+    break;}
+case 48:
+#line 440 "getdate.y"
+{
+            if (context->yyHaveTime && context->yyHaveDate &&
+                !context->yyHaveRel)
+              context->yyYear = yyvsp[0].Number;
+            else
+              {
+                if (yyvsp[0].Number>10000)
+                  {
+                    context->yyHaveDate++;
+                    context->yyDay= (yyvsp[0].Number)%100;
+                    context->yyMonth= (yyvsp[0].Number/100)%100;
+                    context->yyYear = yyvsp[0].Number/10000;
+                  }
+                else
+                  {
+                    context->yyHaveTime++;
+                    if (yyvsp[0].Number < 100)
+                      {
+                        context->yyHour = yyvsp[0].Number;
+                        context->yyMinutes = 0;
+                      }
+                    else
+                      {
+                        context->yyHour = yyvsp[0].Number / 100;
+                        context->yyMinutes = yyvsp[0].Number % 100;
+                      }
+                    context->yySeconds = 0;
+                    context->yyMeridian = MER24;
+                  }
+              }
+          ;
+    break;}
+case 49:
+#line 474 "getdate.y"
+{
+            yyval.Meridian = MER24;
+          ;
+    break;}
+case 50:
+#line 478 "getdate.y"
+{
+            yyval.Meridian = yyvsp[0].Meridian;
+          ;
+    break;}
+}
+   /* the action file gets copied in in place of this dollarsign */
+#line 543 "/usr/local/share/bison.simple"
+
+  yyvsp -= yylen;
+  yyssp -= yylen;
+#ifdef YYLSP_NEEDED
+  yylsp -= yylen;
+#endif
+
+#if YYDEBUG != 0
+  if (yydebug)
+    {
+      short *ssp1 = yyss - 1;
+      fprintf (stderr, "state stack now");
+      while (ssp1 != yyssp)
+        fprintf (stderr, " %d", *++ssp1);
+      fprintf (stderr, "\n");
+    }
+#endif
+
+  *++yyvsp = yyval;
+
+#ifdef YYLSP_NEEDED
+  yylsp++;
+  if (yylen == 0)
+    {
+      yylsp->first_line = yylloc.first_line;
+      yylsp->first_column = yylloc.first_column;
+      yylsp->last_line = (yylsp-1)->last_line;
+      yylsp->last_column = (yylsp-1)->last_column;
+      yylsp->text = 0;
+    }
+  else
+    {
+      yylsp->last_line = (yylsp+yylen-1)->last_line;
+      yylsp->last_column = (yylsp+yylen-1)->last_column;
+    }
+#endif
+
+  /* Now "shift" the result of the reduction.
+     Determine what state that goes to,
+     based on the state we popped back to
+     and the rule number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+  if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTBASE];
+
+  goto yynewstate;
+
+yyerrlab:   /* here on detecting error */
+
+  if (! yyerrstatus)
+    /* If not already recovering from an error, report this error.  */
+    {
+      ++yynerrs;
+
+#ifdef YYERROR_VERBOSE
+      yyn = yypact[yystate];
+
+      if (yyn > YYFLAG && yyn < YYLAST)
+        {
+          int size = 0;
+          char *msg;
+          int x, count;
+
+          count = 0;
+          /* Start X at -yyn if nec to avoid negative indexes in yycheck.  */
+          for (x = (yyn < 0 ? -yyn : 0);
+               x < (sizeof(yytname) / sizeof(char *)); x++)
+            if (yycheck[x + yyn] == x)
+              size += strlen(yytname[x]) + 15, count++;
+          msg = (char *) malloc(size + 15);
+          if (msg != 0)
+            {
+              strcpy(msg, "parse error");
+
+              if (count < 5)
+                {
+                  count = 0;
+                  for (x = (yyn < 0 ? -yyn : 0);
+                       x < (sizeof(yytname) / sizeof(char *)); x++)
+                    if (yycheck[x + yyn] == x)
+                      {
+                        strcat(msg, count == 0 ? ", expecting `" : " or `");
+                        strcat(msg, yytname[x]);
+                        strcat(msg, "'");
+                        count++;
+                      }
+                }
+              yyerror(msg);
+              free(msg);
+            }
+          else
+            yyerror ("parse error; also virtual memory exceeded");
+        }
+      else
+#endif /* YYERROR_VERBOSE */
+        yyerror("parse error");
+    }
+
+  goto yyerrlab1;
+yyerrlab1:   /* here on error raised explicitly by an action */
+
+  if (yyerrstatus == 3)
+    {
+      /* if just tried and failed to reuse lookahead token after an error, discard it.  */
+
+      /* return failure if at end of input */
+      if (yychar == YYEOF)
+        YYABORT;
+
+#if YYDEBUG != 0
+      if (yydebug)
+        fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
+#endif
+
+      yychar = YYEMPTY;
+    }
+
+  /* Else will try to reuse lookahead token
+     after shifting the error token.  */
+
+  yyerrstatus = 3;              /* Each real token shifted decrements this */
+
+  goto yyerrhandle;
+
+yyerrdefault:  /* current state does not do anything special for the error token. */
+
+#if 0
+  /* This is wrong; only states that explicitly want error tokens
+     should shift them.  */
+  yyn = yydefact[yystate];  /* If its default is to accept any token, ok.  Otherwise pop it.*/
+  if (yyn) goto yydefault;
+#endif
+
+yyerrpop:   /* pop the current state because it cannot handle the error token */
+
+  if (yyssp == yyss) YYABORT;
+  yyvsp--;
+  yystate = *--yyssp;
+#ifdef YYLSP_NEEDED
+  yylsp--;
+#endif
+
+#if YYDEBUG != 0
+  if (yydebug)
+    {
+      short *ssp1 = yyss - 1;
+      fprintf (stderr, "Error: state stack now");
+      while (ssp1 != yyssp)
+        fprintf (stderr, " %d", *++ssp1);
+      fprintf (stderr, "\n");
+    }
+#endif
+
+yyerrhandle:
+
+  yyn = yypact[yystate];
+  if (yyn == YYFLAG)
+    goto yyerrdefault;
+
+  yyn += YYTERROR;
+  if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
+    goto yyerrdefault;
+
+  yyn = yytable[yyn];
+  if (yyn < 0)
+    {
+      if (yyn == YYFLAG)
+        goto yyerrpop;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+  else if (yyn == 0)
+    goto yyerrpop;
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Shifting error token, ");
+#endif
+
+  *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+  *++yylsp = yylloc;
+#endif
+
+  yystate = yyn;
+  goto yynewstate;
+
+ yyacceptlab:
+  /* YYACCEPT comes here.  */
+  if (yyfree_stacks)
+    {
+      free (yyss);
+      free (yyvs);
+#ifdef YYLSP_NEEDED
+      free (yyls);
+#endif
+    }
+  return 0;
+
+ yyabortlab:
+  /* YYABORT comes here.  */
+  if (yyfree_stacks)
+    {
+      free (yyss);
+      free (yyvs);
+#ifdef YYLSP_NEEDED
+      free (yyls);
+#endif
+    }
+  return 1;
+}
+#line 483 "getdate.y"
+
+
+/* Include this file down here because bison inserts code above which
+   may define-away `const'.  We want the prototype for get_date to have
+   the same signature as the function definition does. */
+#include "getdate.h"
+
+#ifndef WIN32 /* the windows dudes don't need these, does anyone really? */
+extern struct tm        *gmtime ();
+extern struct tm        *localtime ();
+extern time_t           mktime ();
+#endif
+
+/* Month and day table. */
+static TABLE const MonthDayTable[] = {
+    { "january",        tMONTH,  1 },
+    { "february",       tMONTH,  2 },
+    { "march",          tMONTH,  3 },
+    { "april",          tMONTH,  4 },
+    { "may",            tMONTH,  5 },
+    { "june",           tMONTH,  6 },
+    { "july",           tMONTH,  7 },
+    { "august",         tMONTH,  8 },
+    { "september",      tMONTH,  9 },
+    { "sept",           tMONTH,  9 },
+    { "october",        tMONTH, 10 },
+    { "november",       tMONTH, 11 },
+    { "december",       tMONTH, 12 },
+    { "sunday",         tDAY, 0 },
+    { "monday",         tDAY, 1 },
+    { "tuesday",        tDAY, 2 },
+    { "tues",           tDAY, 2 },
+    { "wednesday",      tDAY, 3 },
+    { "wednes",         tDAY, 3 },
+    { "thursday",       tDAY, 4 },
+    { "thur",           tDAY, 4 },
+    { "thurs",          tDAY, 4 },
+    { "friday",         tDAY, 5 },
+    { "saturday",       tDAY, 6 },
+    { NULL, 0, 0 }
+};
+
+/* Time units table. */
+static TABLE const UnitsTable[] = {
+    { "year",           tYEAR_UNIT,     1 },
+    { "month",          tMONTH_UNIT,    1 },
+    { "fortnight",      tDAY_UNIT,      14 },
+    { "week",           tDAY_UNIT,      7 },
+    { "day",            tDAY_UNIT,      1 },
+    { "hour",           tHOUR_UNIT,     1 },
+    { "minute",         tMINUTE_UNIT,   1 },
+    { "min",            tMINUTE_UNIT,   1 },
+    { "second",         tSEC_UNIT,      1 },
+    { "sec",            tSEC_UNIT,      1 },
+    { NULL, 0, 0 }
+};
+
+/* Assorted relative-time words. */
+static TABLE const OtherTable[] = {
+    { "tomorrow",       tMINUTE_UNIT,   1 * 24 * 60 },
+    { "yesterday",      tMINUTE_UNIT,   -1 * 24 * 60 },
+    { "today",          tMINUTE_UNIT,   0 },
+    { "now",            tMINUTE_UNIT,   0 },
+    { "last",           tUNUMBER,       -1 },
+    { "this",           tMINUTE_UNIT,   0 },
+    { "next",           tUNUMBER,       1 },
+    { "first",          tUNUMBER,       1 },
+/*  { "second",         tUNUMBER,       2 }, */
+    { "third",          tUNUMBER,       3 },
+    { "fourth",         tUNUMBER,       4 },
+    { "fifth",          tUNUMBER,       5 },
+    { "sixth",          tUNUMBER,       6 },
+    { "seventh",        tUNUMBER,       7 },
+    { "eighth",         tUNUMBER,       8 },
+    { "ninth",          tUNUMBER,       9 },
+    { "tenth",          tUNUMBER,       10 },
+    { "eleventh",       tUNUMBER,       11 },
+    { "twelfth",        tUNUMBER,       12 },
+    { "ago",            tAGO,   1 },
+    { NULL, 0, 0 }
+};
+
+/* The timezone table. */
+static TABLE const TimezoneTable[] = {
+    { "gmt",    tZONE,     HOUR ( 0) }, /* Greenwich Mean */
+    { "ut",     tZONE,     HOUR ( 0) }, /* Universal (Coordinated) */
+    { "utc",    tZONE,     HOUR ( 0) },
+    { "wet",    tZONE,     HOUR ( 0) }, /* Western European */
+    { "bst",    tDAYZONE,  HOUR ( 0) }, /* British Summer */
+    { "wat",    tZONE,     HOUR ( 1) }, /* West Africa */
+    { "at",     tZONE,     HOUR ( 2) }, /* Azores */
+#if     0
+    /* For completeness.  BST is also British Summer, and GST is
+     * also Guam Standard. */
+    { "bst",    tZONE,     HOUR ( 3) }, /* Brazil Standard */
+    { "gst",    tZONE,     HOUR ( 3) }, /* Greenland Standard */
+#endif
+#if 0
+    { "nft",    tZONE,     HOUR (3.5) },        /* Newfoundland */
+    { "nst",    tZONE,     HOUR (3.5) },        /* Newfoundland Standard */
+    { "ndt",    tDAYZONE,  HOUR (3.5) },        /* Newfoundland Daylight */
+#endif
+    { "ast",    tZONE,     HOUR ( 4) }, /* Atlantic Standard */
+    { "adt",    tDAYZONE,  HOUR ( 4) }, /* Atlantic Daylight */
+    { "est",    tZONE,     HOUR ( 5) }, /* Eastern Standard */
+    { "edt",    tDAYZONE,  HOUR ( 5) }, /* Eastern Daylight */
+    { "cst",    tZONE,     HOUR ( 6) }, /* Central Standard */
+    { "cdt",    tDAYZONE,  HOUR ( 6) }, /* Central Daylight */
+    { "mst",    tZONE,     HOUR ( 7) }, /* Mountain Standard */
+    { "mdt",    tDAYZONE,  HOUR ( 7) }, /* Mountain Daylight */
+    { "pst",    tZONE,     HOUR ( 8) }, /* Pacific Standard */
+    { "pdt",    tDAYZONE,  HOUR ( 8) }, /* Pacific Daylight */
+    { "yst",    tZONE,     HOUR ( 9) }, /* Yukon Standard */
+    { "ydt",    tDAYZONE,  HOUR ( 9) }, /* Yukon Daylight */
+    { "hst",    tZONE,     HOUR (10) }, /* Hawaii Standard */
+    { "hdt",    tDAYZONE,  HOUR (10) }, /* Hawaii Daylight */
+    { "cat",    tZONE,     HOUR (10) }, /* Central Alaska */
+    { "ahst",   tZONE,     HOUR (10) }, /* Alaska-Hawaii Standard */
+    { "nt",     tZONE,     HOUR (11) }, /* Nome */
+    { "idlw",   tZONE,     HOUR (12) }, /* International Date Line West */
+    { "cet",    tZONE,     -HOUR (1) }, /* Central European */
+    { "met",    tZONE,     -HOUR (1) }, /* Middle European */
+    { "mewt",   tZONE,     -HOUR (1) }, /* Middle European Winter */
+    { "mest",   tDAYZONE,  -HOUR (1) }, /* Middle European Summer */
+    { "mesz",   tDAYZONE,  -HOUR (1) }, /* Middle European Summer */
+    { "swt",    tZONE,     -HOUR (1) }, /* Swedish Winter */
+    { "sst",    tDAYZONE,  -HOUR (1) }, /* Swedish Summer */
+    { "fwt",    tZONE,     -HOUR (1) }, /* French Winter */
+    { "fst",    tDAYZONE,  -HOUR (1) }, /* French Summer */
+    { "eet",    tZONE,     -HOUR (2) }, /* Eastern Europe, USSR Zone 1 */
+    { "bt",     tZONE,     -HOUR (3) }, /* Baghdad, USSR Zone 2 */
+#if 0
+    { "it",     tZONE,     -HOUR (3.5) },/* Iran */
+#endif
+    { "zp4",    tZONE,     -HOUR (4) }, /* USSR Zone 3 */
+    { "zp5",    tZONE,     -HOUR (5) }, /* USSR Zone 4 */
+#if 0
+    { "ist",    tZONE,     -HOUR (5.5) },/* Indian Standard */
+#endif
+    { "zp6",    tZONE,     -HOUR (6) }, /* USSR Zone 5 */
+#if     0
+    /* For completeness.  NST is also Newfoundland Standard, and SST is
+     * also Swedish Summer. */
+    { "nst",    tZONE,     -HOUR (6.5) },/* North Sumatra */
+    { "sst",    tZONE,     -HOUR (7) }, /* South Sumatra, USSR Zone 6 */
+#endif  /* 0 */
+    { "wast",   tZONE,     -HOUR (7) }, /* West Australian Standard */
+    { "wadt",   tDAYZONE,  -HOUR (7) }, /* West Australian Daylight */
+#if 0
+    { "jt",     tZONE,     -HOUR (7.5) },/* Java (3pm in Cronusland!) */
+#endif
+    { "cct",    tZONE,     -HOUR (8) }, /* China Coast, USSR Zone 7 */
+    { "jst",    tZONE,     -HOUR (9) }, /* Japan Standard, USSR Zone 8 */
+#if 0
+    { "cast",   tZONE,     -HOUR (9.5) },/* Central Australian Standard */
+    { "cadt",   tDAYZONE,  -HOUR (9.5) },/* Central Australian Daylight */
+#endif
+    { "east",   tZONE,     -HOUR (10) },        /* Eastern Australian Standard */
+    { "eadt",   tDAYZONE,  -HOUR (10) },        /* Eastern Australian Daylight */
+    { "gst",    tZONE,     -HOUR (10) },        /* Guam Standard, USSR Zone 9 */
+    { "nzt",    tZONE,     -HOUR (12) },        /* New Zealand */
+    { "nzst",   tZONE,     -HOUR (12) },        /* New Zealand Standard */
+    { "nzdt",   tDAYZONE,  -HOUR (12) },        /* New Zealand Daylight */
+    { "idle",   tZONE,     -HOUR (12) },        /* International Date Line East */
+    {  NULL, 0, 0  }
+};
+
+/* Military timezone table. */
+static TABLE const MilitaryTable[] = {
+    { "a",      tZONE,  HOUR (  1) },
+    { "b",      tZONE,  HOUR (  2) },
+    { "c",      tZONE,  HOUR (  3) },
+    { "d",      tZONE,  HOUR (  4) },
+    { "e",      tZONE,  HOUR (  5) },
+    { "f",      tZONE,  HOUR (  6) },
+    { "g",      tZONE,  HOUR (  7) },
+    { "h",      tZONE,  HOUR (  8) },
+    { "i",      tZONE,  HOUR (  9) },
+    { "k",      tZONE,  HOUR ( 10) },
+    { "l",      tZONE,  HOUR ( 11) },
+    { "m",      tZONE,  HOUR ( 12) },
+    { "n",      tZONE,  HOUR (- 1) },
+    { "o",      tZONE,  HOUR (- 2) },
+    { "p",      tZONE,  HOUR (- 3) },
+    { "q",      tZONE,  HOUR (- 4) },
+    { "r",      tZONE,  HOUR (- 5) },
+    { "s",      tZONE,  HOUR (- 6) },
+    { "t",      tZONE,  HOUR (- 7) },
+    { "u",      tZONE,  HOUR (- 8) },
+    { "v",      tZONE,  HOUR (- 9) },
+    { "w",      tZONE,  HOUR (-10) },
+    { "x",      tZONE,  HOUR (-11) },
+    { "y",      tZONE,  HOUR (-12) },
+    { "z",      tZONE,  HOUR (  0) },
+    { NULL, 0, 0 }
+};
+
+
+
+
+/* ARGSUSED */
+static int
+yyerror (s)
+     char *s ATTRIBUTE_UNUSED;
+{
+  return 0;
+}
+
+static int
+ToHour (Hours, Meridian)
+     int Hours;
+     MERIDIAN Meridian;
+{
+  switch (Meridian)
+    {
+    case MER24:
+      if (Hours < 0 || Hours > 23)
+        return -1;
+      return Hours;
+    case MERam:
+      if (Hours < 1 || Hours > 12)
+        return -1;
+      if (Hours == 12)
+        Hours = 0;
+      return Hours;
+    case MERpm:
+      if (Hours < 1 || Hours > 12)
+        return -1;
+      if (Hours == 12)
+        Hours = 0;
+      return Hours + 12;
+    default:
+      abort ();
+    }
+  /* NOTREACHED */
+}
+
+static int
+ToYear (Year)
+     int Year;
+{
+  if (Year < 0)
+    Year = -Year;
+
+  /* XPG4 suggests that years 00-68 map to 2000-2068, and
+     years 69-99 map to 1969-1999.  */
+  if (Year < 69)
+    Year += 2000;
+  else if (Year < 100)
+    Year += 1900;
+
+  return Year;
+}
+
+static int
+LookupWord (yylval, buff)
+     YYSTYPE *yylval;
+     char *buff;
+{
+  register char *p;
+  register char *q;
+  register const TABLE *tp;
+  int i;
+  int abbrev;
+
+  /* Make it lowercase. */
+  for (p = buff; *p; p++)
+    if (ISUPPER ((unsigned char) *p))
+      *p = tolower (*p);
+
+  if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0)
+    {
+      yylval->Meridian = MERam;
+      return tMERIDIAN;
+    }
+  if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0)
+    {
+      yylval->Meridian = MERpm;
+      return tMERIDIAN;
+    }
+
+  /* See if we have an abbreviation for a month. */
+  if (strlen (buff) == 3)
+    abbrev = 1;
+  else if (strlen (buff) == 4 && buff[3] == '.')
+    {
+      abbrev = 1;
+      buff[3] = '\0';
+    }
+  else
+    abbrev = 0;
+
+  for (tp = MonthDayTable; tp->name; tp++)
+    {
+      if (abbrev)
+        {
+          if (strncmp (buff, tp->name, 3) == 0)
+            {
+              yylval->Number = tp->value;
+              return tp->type;
+            }
+        }
+      else if (strcmp (buff, tp->name) == 0)
+        {
+          yylval->Number = tp->value;
+          return tp->type;
+        }
+    }
+
+  for (tp = TimezoneTable; tp->name; tp++)
+    if (strcmp (buff, tp->name) == 0)
+      {
+        yylval->Number = tp->value;
+        return tp->type;
+      }
+
+  if (strcmp (buff, "dst") == 0)
+    return tDST;
+
+  for (tp = UnitsTable; tp->name; tp++)
+    if (strcmp (buff, tp->name) == 0)
+      {
+        yylval->Number = tp->value;
+        return tp->type;
+      }
+
+  /* Strip off any plural and try the units table again. */
+  i = strlen (buff) - 1;
+  if (buff[i] == 's')
+    {
+      buff[i] = '\0';
+      for (tp = UnitsTable; tp->name; tp++)
+        if (strcmp (buff, tp->name) == 0)
+          {
+            yylval->Number = tp->value;
+            return tp->type;
+          }
+      buff[i] = 's';            /* Put back for "this" in OtherTable. */
+    }
+
+  for (tp = OtherTable; tp->name; tp++)
+    if (strcmp (buff, tp->name) == 0)
+      {
+        yylval->Number = tp->value;
+        return tp->type;
+      }
+
+  /* Military timezones. */
+  if (buff[1] == '\0' && ISALPHA ((unsigned char) *buff))
+    {
+      for (tp = MilitaryTable; tp->name; tp++)
+        if (strcmp (buff, tp->name) == 0)
+          {
+            yylval->Number = tp->value;
+            return tp->type;
+          }
+    }
+
+  /* Drop out any periods and try the timezone table again. */
+  for (i = 0, p = q = buff; *q; q++)
+    if (*q != '.')
+      *p++ = *q;
+    else
+      i++;
+  *p = '\0';
+  if (i)
+    for (tp = TimezoneTable; tp->name; tp++)
+      if (strcmp (buff, tp->name) == 0)
+        {
+          yylval->Number = tp->value;
+          return tp->type;
+        }
+
+  return tID;
+}
+
+static int
+yylex (yylval, cookie)
+     YYSTYPE *yylval;
+     void *cookie;
+{
+  register unsigned char c;
+  register char *p;
+  char buff[20];
+  int Count;
+  int sign;
+
+  for (;;)
+    {
+      while (ISSPACE ((unsigned char) *context->yyInput))
+        context->yyInput++;
+
+      if (ISDIGIT (c = *context->yyInput) || c == '-' || c == '+')
+        {
+          if (c == '-' || c == '+')
+            {
+              sign = c == '-' ? -1 : 1;
+              if (!ISDIGIT (*++context->yyInput))
+                /* skip the '-' sign */
+                continue;
+            }
+          else
+            sign = 0;
+          for (yylval->Number = 0; ISDIGIT (c = *context->yyInput++);)
+            yylval->Number = 10 * yylval->Number + c - '0';
+          context->yyInput--;
+          if (sign < 0)
+            yylval->Number = -yylval->Number;
+          return sign ? tSNUMBER : tUNUMBER;
+        }
+      if (ISALPHA (c))
+        {
+          for (p = buff; (c = *context->yyInput++, ISALPHA (c)) || c == '.';)
+            if (p < &buff[sizeof buff - 1])
+              *p++ = c;
+          *p = '\0';
+          context->yyInput--;
+          return LookupWord (yylval, buff);
+        }
+      if (c != '(')
+        return *context->yyInput++;
+      Count = 0;
+      do
+        {
+          c = *context->yyInput++;
+          if (c == '\0')
+            return c;
+          if (c == '(')
+            Count++;
+          else if (c == ')')
+            Count--;
+        }
+      while (Count > 0);
+    }
+}
+
+#define TM_YEAR_ORIGIN 1900
+
+/* Yield A - B, measured in seconds.  */
+static long
+difftm (struct tm *a, struct tm *b)
+{
+  int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
+  int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
+  long days = (
+  /* difference in day of year */
+                a->tm_yday - b->tm_yday
+  /* + intervening leap days */
+                + ((ay >> 2) - (by >> 2))
+                - (ay / 100 - by / 100)
+                + ((ay / 100 >> 2) - (by / 100 >> 2))
+  /* + difference in years * 365 */
+                + (long) (ay - by) * 365
+  );
+  return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
+                + (a->tm_min - b->tm_min))
+          + (a->tm_sec - b->tm_sec));
+}
+
+time_t
+curl_getdate (const char *p, const time_t *now)
+{
+  struct tm tm, tm0, *tmp;
+  time_t Start;
+  CONTEXT cookie;
+#ifdef HAVE_LOCALTIME_R
+  struct tm keeptime;
+#endif
+  cookie.yyInput = p;
+  Start = now ? *now : time ((time_t *) NULL);
+#ifdef HAVE_LOCALTIME_R
+  tmp = (struct tm *)localtime_r(&Start, &keeptime);
+#else
+  tmp = localtime (&Start);
+#endif
+  if (!tmp)
+    return -1;
+  cookie.yyYear = tmp->tm_year + TM_YEAR_ORIGIN;
+  cookie.yyMonth = tmp->tm_mon + 1;
+  cookie.yyDay = tmp->tm_mday;
+  cookie.yyHour = tmp->tm_hour;
+  cookie.yyMinutes = tmp->tm_min;
+  cookie.yySeconds = tmp->tm_sec;
+  tm.tm_isdst = tmp->tm_isdst;
+  cookie.yyMeridian = MER24;
+  cookie.yyRelSeconds = 0;
+  cookie.yyRelMinutes = 0;
+  cookie.yyRelHour = 0;
+  cookie.yyRelDay = 0;
+  cookie.yyRelMonth = 0;
+  cookie.yyRelYear = 0;
+  cookie.yyHaveDate = 0;
+  cookie.yyHaveDay = 0;
+  cookie.yyHaveRel = 0;
+  cookie.yyHaveTime = 0;
+  cookie.yyHaveZone = 0;
+
+  if (yyparse (&cookie)
+      || cookie.yyHaveTime > 1 || cookie.yyHaveZone > 1 ||
+      cookie.yyHaveDate > 1 || cookie.yyHaveDay > 1)
+    return -1;
+
+  tm.tm_year = ToYear (cookie.yyYear) - TM_YEAR_ORIGIN + cookie.yyRelYear;
+  tm.tm_mon = cookie.yyMonth - 1 + cookie.yyRelMonth;
+  tm.tm_mday = cookie.yyDay + cookie.yyRelDay;
+  if (cookie.yyHaveTime ||
+      (cookie.yyHaveRel && !cookie.yyHaveDate && !cookie.yyHaveDay))
+    {
+      tm.tm_hour = ToHour (cookie.yyHour, cookie.yyMeridian);
+      if (tm.tm_hour < 0)
+        return -1;
+      tm.tm_min = cookie.yyMinutes;
+      tm.tm_sec = cookie.yySeconds;
+    }
+  else
+    {
+      tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+    }
+  tm.tm_hour += cookie.yyRelHour;
+  tm.tm_min += cookie.yyRelMinutes;
+  tm.tm_sec += cookie.yyRelSeconds;
+
+  /* Let mktime deduce tm_isdst if we have an absolute timestamp,
+     or if the relative timestamp mentions days, months, or years.  */
+  if (cookie.yyHaveDate | cookie.yyHaveDay | cookie.yyHaveTime |
+      cookie.yyRelDay | cookie.yyRelMonth | cookie.yyRelYear)
+    tm.tm_isdst = -1;
+
+  tm0 = tm;
+
+  Start = mktime (&tm);
+
+  if (Start == (time_t) -1)
+    {
+
+      /* Guard against falsely reporting errors near the time_t boundaries
+         when parsing times in other time zones.  For example, if the min
+         time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
+         of UTC, then the min localtime value is 1970-01-01 08:00:00; if
+         we apply mktime to 1970-01-01 00:00:00 we will get an error, so
+         we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
+         zone by 24 hours to compensate.  This algorithm assumes that
+         there is no DST transition within a day of the time_t boundaries.  */
+      if (cookie.yyHaveZone)
+        {
+          tm = tm0;
+          if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN)
+            {
+              tm.tm_mday++;
+              cookie.yyTimezone -= 24 * 60;
+            }
+          else
+            {
+              tm.tm_mday--;
+              cookie.yyTimezone += 24 * 60;
+            }
+          Start = mktime (&tm);
+        }
+
+      if (Start == (time_t) -1)
+        return Start;
+    }
+
+  if (cookie.yyHaveDay && !cookie.yyHaveDate)
+    {
+      tm.tm_mday += ((cookie.yyDayNumber - tm.tm_wday + 7) % 7
+                     + 7 * (cookie.yyDayOrdinal - (0 < cookie.yyDayOrdinal)));
+      Start = mktime (&tm);
+      if (Start == (time_t) -1)
+        return Start;
+    }
+
+  if (cookie.yyHaveZone)
+    {
+      long delta;
+      struct tm *gmt;
+#ifdef HAVE_GMTIME_R
+      /* thread-safe version */
+      struct tm keeptime;
+      gmt = (struct tm *)gmtime_r(&Start, &keeptime);
+#else
+      gmt = gmtime(&Start);
+#endif
+      if (!gmt)
+        return -1;
+      delta = cookie.yyTimezone * 60L + difftm (&tm, gmt);
+      if ((Start + delta < Start) != (delta < 0))
+        return -1;              /* time_t overflow */
+      Start += delta;
+    }
+
+  return Start;
+}
+
+#if     defined (TEST)
+
+/* ARGSUSED */
+int
+main (ac, av)
+     int ac;
+     char *av[];
+{
+  char buff[MAX_BUFF_LEN + 1];
+  time_t d;
+
+  (void) printf ("Enter date, or blank line to exit.\n\t> ");
+  (void) fflush (stdout);
+
+  buff[MAX_BUFF_LEN] = 0;
+  while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
+    {
+      d = curl_getdate (buff, (time_t *) NULL);
+      if (d == -1)
+        (void) printf ("Bad format - couldn't convert.\n");
+      else
+        (void) printf ("%s", ctime (&d));
+      (void) printf ("\t> ");
+      (void) fflush (stdout);
+    }
+  exit (0);
+  /* NOTREACHED */
+}
+#endif /* defined (TEST) */

+ 37 - 0
Source/CTest/Curl/getdate.h

@@ -0,0 +1,37 @@
+/*
+**  Originally written by Steven M. Bellovin <[email protected]> while
+**  at the University of North Carolina at Chapel Hill.  Later tweaked by
+**  a couple of people on Usenet.  Completely overhauled by Rich $alz
+**  <[email protected]> and Jim Berets <[email protected]> in August, 1990.
+**
+**  This code is in the public domain and has no copyright.
+*/
+
+# include "setup.h"
+
+#ifndef PARAMS
+# if defined PROTOTYPES || (defined __STDC__ && __STDC__)
+#  define PARAMS(Args) Args
+# else
+#  define PARAMS(Args) ()
+# endif
+#endif
+
+#ifdef vms
+# include <types.h>
+# include <time.h>
+#else
+# include <sys/types.h>
+# if TIME_WITH_SYS_TIME
+#  include <sys/time.h>
+#  include <time.h>
+# else
+#  if HAVE_SYS_TIME_H
+#   include <sys/time.h>
+#  else
+#   include <time.h>
+#  endif
+# endif
+#endif /* defined (vms) */
+
+time_t curl_getdate PARAMS ((const char *p, const time_t *now));

+ 77 - 0
Source/CTest/Curl/getenv.c

@@ -0,0 +1,77 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+#ifdef VMS
+#include <unixlib.h>
+#endif
+
+#ifdef MALLOCDEBUG
+#include "memdebug.h"
+#endif
+
+static
+char *GetEnv(const char *variable)
+{
+#ifdef WIN32
+  /* This shit requires windows.h (HUGE) to be included */
+  char env[MAX_PATH]; /* MAX_PATH is from windef.h */
+  char *temp = getenv(variable);
+  env[0] = '\0';
+  if (temp != NULL)
+    ExpandEnvironmentStrings(temp, env, sizeof(env));
+#else
+#ifdef  VMS
+  char *env = getenv(variable);
+  if (env && strcmp("HOME",variable) == 0) {
+        env = decc$translate_vms(env);
+  }
+#else
+  /* no length control */
+  char *env = getenv(variable);
+#endif
+#endif
+  return (env && env[0])?strdup(env):NULL;
+}
+
+char *curl_getenv(const char *v)
+{
+  return GetEnv(v);
+}
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 161 - 0
Source/CTest/Curl/getinfo.c

@@ -0,0 +1,161 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+#include <curl/curl.h>
+
+#include "urldata.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+#ifdef  VMS
+#include        <stdlib.h>
+#endif
+
+/*
+ * This is supposed to be called in the beginning of a permform() 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_pretransfer = 0;
+  pro->t_starttransfer = 0;
+  pro->timespent = 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;
+  return CURLE_OK;
+}
+
+CURLcode Curl_getinfo(struct SessionHandle *data, CURLINFO info, ...)
+{
+  va_list arg;
+  long *param_longp;
+  double *param_doublep;
+  char **param_charp;
+  va_start(arg, info);
+
+  switch(info&CURLINFO_TYPEMASK) {
+  default:
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  case CURLINFO_STRING:
+    param_charp = va_arg(arg, char **);  
+    if(NULL == param_charp)
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    break;
+  case CURLINFO_LONG:
+    param_longp = va_arg(arg, long *);
+    if(NULL == param_longp)
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    break;
+  case CURLINFO_DOUBLE:
+    param_doublep = va_arg(arg, double *);
+    if(NULL == param_doublep)
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    break;
+  }
+  
+  switch(info) {
+  case CURLINFO_EFFECTIVE_URL:
+    *param_charp = data->change.url?data->change.url:(char *)"";
+    break;
+  case CURLINFO_HTTP_CODE:
+    *param_longp = data->info.httpcode;
+    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_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_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 =  data->progress.uploaded;
+    break;
+  case CURLINFO_SIZE_DOWNLOAD:
+    *param_doublep = data->progress.downloaded;
+    break;
+  case CURLINFO_SPEED_DOWNLOAD:
+    *param_doublep =  data->progress.dlspeed;
+    break;
+  case CURLINFO_SPEED_UPLOAD:
+    *param_doublep = data->progress.ulspeed;
+    break;
+  case CURLINFO_SSL_VERIFYRESULT:
+    *param_longp = data->set.ssl.certverifyresult;
+    break;
+  case CURLINFO_CONTENT_LENGTH_DOWNLOAD:
+    *param_doublep = data->progress.size_dl;
+    break;
+  case CURLINFO_CONTENT_LENGTH_UPLOAD:
+    *param_doublep = data->progress.size_ul;
+    break;
+  case CURLINFO_CONTENT_TYPE:
+    *param_charp = data->info.contenttype;
+    break;
+  default:
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  }
+  return CURLE_OK;
+}
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 28 - 0
Source/CTest/Curl/getinfo.h

@@ -0,0 +1,28 @@
+#ifndef __GETINFO_H
+#define __GETINFO_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2001, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+CURLcode Curl_getinfo(struct SessionHandle *data, CURLINFO info, ...);
+CURLcode Curl_initinfo(struct SessionHandle *data);
+
+#endif

+ 252 - 0
Source/CTest/Curl/getpass.c

@@ -0,0 +1,252 @@
+/* ============================================================================
+ * Copyright (C) 1998 Angus Mackay. All rights reserved; 
+ *
+ * Redistribution and use are freely permitted provided that:
+ *
+ *   1) This header remain in tact.
+ *   2) The prototypes for getpass and getpass_r are not changed from:
+ *         char *getpass(const char *prompt)
+ *         char *getpass_r(const char *prompt, char* buffer, int buflen)
+ *   3) This source code is not used outside of this(getpass.c) file.
+ *   4) Any changes to this(getpass.c) source code are made publicly available.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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
+ * AUTHOR 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.
+ * ============================================================================
+ *
+ * $Id$
+ *
+ * The spirit of this license is to allow use of this source code in any
+ * project be it open or closed but still encourage the use of the open,
+ * library based equivilents.
+ *
+ * Author(s):
+ *   Angus Mackay <[email protected]>
+ *
+ * Contributor(s):
+ *   Daniel Stenberg <[email protected]>
+ */
+
+#include "setup.h" /* setup.h is required for read() prototype */
+
+#ifndef HAVE_GETPASS_R
+
+#ifndef WIN32
+#ifdef  VMS
+#include <stdio.h>
+#include <string.h>
+#include descrip
+#include starlet
+#include iodef
+#include iosbdef
+char *getpass_r(const char *prompt, char *buffer, size_t buflen)
+{
+        long sts;
+        short chan;
+        struct _iosb iosb;
+        $DESCRIPTOR(ttdesc, "TT");
+
+        buffer[0]='\0';
+        if ((sts = sys$assign(&ttdesc, &chan,0,0)) & 1) {
+                if (((sts = sys$qiow(0, chan, IO$_READPROMPT | IO$M_NOECHO, &iosb, 0, 0, buffer, buflen, 0, 0, prompt, strlen(prompt))) & 1) && (iosb.iosb$w_status&1)) {
+                        buffer[iosb.iosb$w_bcnt] = '\0';
+                } 
+                sts = sys$dassgn(chan);
+        }
+        return buffer; /* we always return success */
+}
+#else /* VMS */
+#ifdef HAVE_TERMIOS_H
+#  if !defined(HAVE_TCGETATTR) && !defined(HAVE_TCSETATTR) 
+#    undef HAVE_TERMIOS_H
+#  endif
+#endif
+
+#ifndef RETSIGTYPE
+#  define RETSIGTYPE void
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <signal.h>
+#ifdef HAVE_TERMIOS_H
+#  include <termios.h>
+#else
+#  ifdef HAVE_TERMIO_H
+#  include <termio.h>
+#  else
+#  endif
+#endif
+
+/* The last #include file should be: */
+#ifdef MALLOCDEBUG
+#include "memdebug.h"
+#endif
+
+/* no perror? make an fprintf! */
+#ifndef HAVE_PERROR
+#  define perror(x) fprintf(stderr, "Error in: %s\n", x)
+#endif
+
+char *getpass_r(const char *prompt, char *buffer, size_t buflen)
+{
+  FILE *infp;
+  FILE *outfp;
+  RETSIGTYPE (*sigint)();
+#ifndef __EMX__
+  RETSIGTYPE (*sigtstp)();
+#endif
+  size_t bytes_read;
+  int infd;
+  int outfd;
+#ifdef HAVE_TERMIOS_H
+  struct termios orig;
+  struct termios noecho;
+#else
+#  ifdef HAVE_TERMIO_H
+  struct termio orig;
+  struct termio noecho;  
+#  else
+#  endif
+#endif
+
+  sigint = signal(SIGINT, SIG_IGN);
+  /* 20000318 mgs
+   * this is needed by the emx system, SIGTSTP is not a supported signal */
+#ifndef __EMX__
+  sigtstp = signal(SIGTSTP, SIG_IGN);
+#endif
+
+  if( (infp=fopen("/dev/tty", "r")) == NULL )
+  {
+    infp = stdin;
+  }
+  if( (outfp=fopen("/dev/tty", "w")) == NULL )
+  {
+    outfp = stderr;
+  }
+  infd = fileno(infp);
+  outfd = fileno(outfp);
+
+  /* dissable echo */
+#ifdef HAVE_TERMIOS_H
+  if(tcgetattr(outfd, &orig) != 0)
+  {
+    ; /*perror("tcgetattr");*/
+  }
+  noecho = orig;
+  noecho.c_lflag &= ~ECHO;
+  if(tcsetattr(outfd, TCSANOW, &noecho) != 0)
+  {
+    ; /*perror("tcgetattr");*/
+  }
+#else
+#  ifdef HAVE_TERMIO_H
+  if(ioctl(outfd, TCGETA, &orig) != 0)
+  {
+    ; /*perror("ioctl");*/
+  }
+  noecho = orig;
+  noecho.c_lflag &= ~ECHO;
+  if(ioctl(outfd, TCSETA, &noecho) != 0)
+  {
+    ; /*perror("ioctl");*/
+  }
+#  else
+#  endif
+#endif
+
+  fputs(prompt, outfp);
+  fflush(outfp);
+
+  bytes_read=read(infd, buffer, buflen);
+  buffer[bytes_read > 0 ? (bytes_read -1) : 0] = '\0';
+
+  /* print a new line if needed */
+#ifdef HAVE_TERMIOS_H
+  fputs("\n", outfp);
+#else
+#  ifdef HAVE_TERMIO_H
+  fputs("\n", outfp);
+#  else
+#  endif
+#endif
+
+  /*
+   * reset term charectaristics, use TCSAFLUSH incase the
+   * user types more than buflen
+   */
+#ifdef HAVE_TERMIOS_H
+  if(tcsetattr(outfd, TCSAFLUSH, &orig) != 0)
+  {
+    ; /*perror("tcgetattr");*/
+  }
+#else
+#  ifdef HAVE_TERMIO_H
+  if(ioctl(outfd, TCSETA, &orig) != 0)
+  {
+    ; /*perror("ioctl");*/
+  }
+#  else
+#  endif
+#endif
+  
+  signal(SIGINT, sigint);
+#ifndef __EMX__
+  signal(SIGTSTP, sigtstp);
+#endif
+
+  return buffer; /* we always return success */
+}
+#endif /* VMS */
+#else /* WIN32 */
+#include <stdio.h>
+#include <conio.h>
+char *getpass_r(const char *prompt, char *buffer, int buflen)
+{
+  int i;
+  printf("%s", prompt);
+ 
+  for(i=0; i<buflen; i++) {
+    buffer[i] = getch();
+    if ( buffer[i] == '\r' ) {
+      buffer[i] = 0;
+      break;
+    }
+  }
+  /* if user didn't hit ENTER, terminate buffer */
+  if (i==buflen)
+    buffer[buflen-1]=0;
+
+  return buffer; /* we always return success */
+}
+#endif
+
+#endif /* ifndef HAVE_GETPASS_R */
+
+#if 0
+/* for consistensy, here's the old-style function: */
+char *getpass(const char *prompt)
+{
+  static char buf[256];
+  return getpass_r(prompt, buf, sizeof(buf));
+}
+#endif
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 35 - 0
Source/CTest/Curl/getpass.h

@@ -0,0 +1,35 @@
+#ifndef __GETPASS_H
+#define __GETPASS_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+#ifndef HAVE_GETPASS_R
+/* If there's a system-provided function named like this, we trust it is
+   also found in one of the standard headers. */
+
+/*
+ * Returning NULL will abort the continued operation!
+ */
+char* getpass_r(const char *prompt, char* buffer, size_t buflen );
+#endif
+
+#endif

+ 285 - 0
Source/CTest/Curl/hash.c

@@ -0,0 +1,285 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2002, Daniel Stenberg, <[email protected]>, et al
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include "hash.h"
+#include "llist.h"
+
+#ifdef MALLOCDEBUG
+/* this must be the last include file */
+#include "memdebug.h"
+#endif
+
+
+static unsigned long 
+curl_hash_str(const char *key, unsigned int key_length)
+{
+  register unsigned long h = 0;
+  register unsigned long g;
+  register char *p = (char *) key;
+  register char *end = (char *) key + key_length;
+
+  while (p < end) {
+    h = (h << 4) + *p++;
+    if ((g = (h & 0xF0000000))) {
+      h = h ^ (g >> 24);
+      h = h ^ g;
+    }
+  }
+
+  return h;
+}
+
+static unsigned long 
+curl_hash_num(unsigned long key)
+{
+  key += ~(key << 15);
+  key ^= (key >> 10);
+  key += (key << 3);
+  key ^= (key >> 6);
+  key += (key << 11);
+  key ^= (key >> 16);
+
+  return key;
+}
+
+static void 
+hash_element_dtor(void *u, void *ele)
+{
+  curl_hash_element *e = (curl_hash_element *) ele; 
+  curl_hash         *h = (curl_hash *) u; 
+        
+  if (e->key.type == CURL_HASH_KEY_IS_STRING) {
+    free(e->key.value.str.val);
+  }
+  h->dtor(e->ptr);
+
+  free(e);
+  e = NULL;
+}
+
+void 
+curl_hash_init(curl_hash *h, int slots, curl_hash_dtor dtor)
+{
+  int i;
+
+  h->dtor = dtor;
+  h->size = 0;
+  h->slots = slots;  
+
+  h->table = (curl_llist **) malloc(slots * sizeof(curl_llist *));
+  for (i = 0; i < h->slots; ++i) {
+    h->table[i] = curl_llist_alloc((curl_llist_dtor) hash_element_dtor);
+  }
+}
+
+curl_hash *
+curl_hash_alloc(int slots, curl_hash_dtor dtor)
+{
+  curl_hash *h;
+
+  h = (curl_hash *)malloc(sizeof(curl_hash));
+  if(NULL == h)
+    return NULL;
+
+  curl_hash_init(h, slots, dtor);
+
+  return h;
+}
+
+#define FIND_SLOT(__h, __s_key, __s_key_len, __n_key) \
+  ((__s_key ? curl_hash_str(__s_key, __s_key_len) : curl_hash_num(__n_key)) % (__h)->slots)
+
+#define KEY_CREATE(__k, __s_key, __s_key_len, __n_key, __dup) \
+  if (__s_key) { \
+    if (__dup) { \
+      (__k)->value.str.val = (char *) malloc(__s_key_len); \
+      memcpy((__k)->value.str.val, __s_key, __s_key_len); \
+    } else { \
+      (__k)->value.str.val = __s_key; \
+    } \
+    (__k)->value.str.len = __s_key_len; \
+    (__k)->type = CURL_HASH_KEY_IS_STRING; \
+  } else { \
+    (__k)->value.num = __n_key; \
+    (__k)->type = CURL_HASH_KEY_IS_NUM; \
+  }
+
+#define MIN(a, b) (a > b ? b : a)
+
+static int 
+curl_hash_key_compare(curl_hash_key *key1, curl_hash_key *key2)
+{
+  if (key1->type == CURL_HASH_KEY_IS_NUM) {
+    if (key2->type == CURL_HASH_KEY_IS_STRING)
+      return 0;
+
+    if (key1->value.num == key2->value.num)
+      return 1;
+  } else {
+    if (key2->type == CURL_HASH_KEY_IS_NUM)
+      return 0;
+
+    if (memcmp(key1->value.str.val, key2->value.str.val, 
+               MIN(key1->value.str.len, key2->value.str.len)) == 0)
+      return 1;
+  }
+
+  return 0;
+}
+
+int 
+curl_hash_add_or_update(curl_hash *h, char *str_key, unsigned int str_key_len, 
+                        unsigned long num_key, const void *p)
+{
+  curl_hash_element  *e;
+  curl_hash_key       tmp;
+  curl_llist         *l; 
+  curl_llist_element *le;
+  int                slot;
+
+  slot = FIND_SLOT(h, str_key, str_key_len, num_key);
+  l = h->table[slot];
+  KEY_CREATE(&tmp, str_key, str_key_len, num_key, 0);
+  for (le = CURL_LLIST_HEAD(l); le != NULL; le = CURL_LLIST_NEXT(le)) {
+    if (curl_hash_key_compare(&tmp, &((curl_hash_element *) CURL_LLIST_VALP(le))->key)) {
+      curl_hash_element *to_update = CURL_LLIST_VALP(le);
+      h->dtor(to_update->ptr);
+      to_update->ptr = (void *) p;
+      return 1;
+    }
+  }
+
+  e = (curl_hash_element *) malloc(sizeof(curl_hash_element));
+  KEY_CREATE(&e->key, str_key, str_key_len, num_key, 1);
+  e->ptr = (void *) p;
+
+  if (curl_llist_insert_next(l, CURL_LLIST_TAIL(l), e)) {
+    ++h->size;
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+int 
+curl_hash_extended_delete(curl_hash *h, char *str_key, unsigned int str_key_len, 
+                          unsigned long num_key)
+{
+  curl_llist         *l;
+  curl_llist_element *le;
+  curl_hash_key       tmp;
+  int                slot;
+
+  slot = FIND_SLOT(h, str_key, str_key_len, num_key);
+  l = h->table[slot];
+
+  KEY_CREATE(&tmp, str_key, str_key_len, num_key, 0);
+  for (le = CURL_LLIST_HEAD(l); le != NULL; le = CURL_LLIST_NEXT(le)) {
+    if (curl_hash_key_compare(&tmp, &((curl_hash_element *) CURL_LLIST_VALP(le))->key)) {
+      curl_llist_remove(l, le, (void *) h);
+      --h->size;
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+int 
+curl_hash_extended_find(curl_hash *h, char *str_key, unsigned int str_key_len, 
+                        unsigned long num_key, void **p)
+{
+  curl_llist         *l;
+  curl_llist_element *le;
+  curl_hash_key       tmp;
+  int                slot;
+
+  slot = FIND_SLOT(h, str_key, str_key_len, num_key);
+  l = h->table[slot];
+
+  KEY_CREATE(&tmp, str_key, str_key_len, num_key, 0);
+  for (le = CURL_LLIST_HEAD(l); le != NULL; le = CURL_LLIST_NEXT(le)) {
+    if (curl_hash_key_compare(&tmp, &((curl_hash_element *) CURL_LLIST_VALP(le))->key)) {
+      *p = ((curl_hash_element *) CURL_LLIST_VALP(le))->ptr;
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+void 
+curl_hash_apply(curl_hash *h, void *user, void (*cb)(void *, curl_hash_element *))
+{
+  curl_llist_element  *le;
+  int                  i;
+
+  for (i = 0; i < h->slots; ++i) {
+    for (le = CURL_LLIST_HEAD(h->table[i]); le != NULL; le = CURL_LLIST_NEXT(le)) {
+      cb(user, (curl_hash_element *) CURL_LLIST_VALP(le));
+    }
+  }
+}
+
+void
+curl_hash_clean(curl_hash *h)
+{
+  int i;
+
+  for (i = 0; i < h->slots; ++i) {
+    curl_llist_destroy(h->table[i], (void *) h);
+  }
+
+  free(h->table);
+  h->table = NULL;
+}
+
+size_t 
+curl_hash_count(curl_hash *h)
+{
+  return h->size;
+}
+
+void 
+curl_hash_destroy(curl_hash *h)
+{
+  if (!h) {
+    return;
+  }
+
+  curl_hash_clean(h);
+  free(h);
+  h = NULL;
+}
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 85 - 0
Source/CTest/Curl/hash.h

@@ -0,0 +1,85 @@
+#ifndef __HASH_H
+#define __HASH_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+#include <stddef.h>
+
+#include "llist.h"
+
+#define CURL_HASH_KEY_IS_STRING 0
+#define CURL_HASH_KEY_IS_NUM    1
+
+typedef void (*curl_hash_dtor)(void *);
+
+typedef struct _curl_hash {
+  curl_llist     **table;
+  curl_hash_dtor   dtor;
+  int              slots;
+  size_t           size;
+} curl_hash;
+
+typedef struct _curl_hash_key {
+  union {
+    struct {
+      char *val;
+      unsigned int len;
+    } str;
+
+    unsigned long num;
+  } value;
+
+  int type;
+} curl_hash_key;
+
+typedef struct _curl_hash_element {
+  curl_hash_key  key;
+  void          *ptr;
+} curl_hash_element;
+
+
+void curl_hash_init(curl_hash *h, int slots, curl_hash_dtor dtor);
+curl_hash *curl_hash_alloc(int slots, curl_hash_dtor dtor);
+int curl_hash_add_or_update(curl_hash *h, char *str_key, unsigned int str_key_len, 
+                             unsigned long num_key, const void *p);
+int curl_hash_extended_delete(curl_hash *h, char *str_key, unsigned int str_key_len, 
+                               unsigned long num_key);
+int curl_hash_extended_find(curl_hash *h, char *str_key, unsigned int str_key_len, 
+                             unsigned long num_key, void **p);
+void curl_hash_apply(curl_hash *h, void *user, void (*cb)(void *, curl_hash_element *));
+size_t curl_hash_count(curl_hash *h);
+void curl_hash_clean(curl_hash *h);
+void curl_hash_destroy(curl_hash *h);
+
+#define curl_hash_find(h, key, key_len, p) curl_hash_extended_find(h, key, key_len, 0, p)
+#define curl_hash_delete(h, key, key_len) curl_hash_extended_delete(h, key, key_len, 0)
+#define curl_hash_add(h, key, key_len, p) curl_hash_add_or_update(h, key, key_len, 0, p)
+#define curl_hash_update curl_hash_add
+#define curl_hash_index_find(h, key, p) curl_hash_extended_find(h, NULL, 0, key, p)
+#define curl_hash_index_delete(h, key) curl_hash_extended_delete(h, NULL, 0, key)
+#define curl_hash_index_add(h, key, p) curl_hash_add_or_update(h, NULL, 0, key, p)
+#define curl_hash_index_update curl_hash_index_add
+
+#endif

+ 529 - 0
Source/CTest/Curl/hostip.c

@@ -0,0 +1,529 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2001, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+#include <errno.h>
+
+#define _REENTRANT
+
+#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
+#include <winsock.h>
+#else
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.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 HAVE_STDLIB_H
+#include <stdlib.h>     /* required for free() prototypes */
+#endif
+#ifdef  VMS
+#include <in.h>
+#include <inet.h>
+#include <stdlib.h>
+#endif
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
+#include "inet_ntoa_r.h"
+#endif
+
+/* The last #include file should be: */
+#ifdef MALLOCDEBUG
+#include "memdebug.h"
+#endif
+
+static curl_hash hostname_cache;
+static int host_cache_initialized;
+
+void Curl_global_host_cache_init(void)
+{
+  if (!host_cache_initialized) {
+    curl_hash_init(&hostname_cache, 7, Curl_freeaddrinfo);
+    host_cache_initialized = 1;
+  }
+}
+
+curl_hash *Curl_global_host_cache_get(void)
+{
+  return &hostname_cache;
+}
+
+void Curl_global_host_cache_dtor(void)
+{
+  if (host_cache_initialized) {
+    curl_hash_clean(&hostname_cache);
+    host_cache_initialized = 0;
+  }
+}
+
+struct curl_dns_cache_entry {
+  Curl_addrinfo *addr;
+  time_t timestamp;
+};
+
+/* count the number of characters that an integer takes up */
+static int _num_chars(int i)
+{
+  int chars = 0;
+
+  /* While the number divided by 10 is greater than one, 
+   * re-divide the number by 10, and increment the number of 
+   * characters by 1.
+   *
+   * this relies on the fact that for every multiple of 10, 
+   * a new digit is added onto every number
+   */
+  do {
+    chars++;
+
+    i = (int) i / 10;
+  } while (i >= 1);
+
+  return chars;
+}
+
+/* Create a hostcache id */
+static char *
+_create_hostcache_id(char *server, int port, ssize_t *entry_len)
+{
+  char *id = NULL;
+
+  /* Get the length of the new entry id */
+  *entry_len = *entry_len +      /* Hostname length */
+               1 +               /* The ':' seperator */
+               _num_chars(port); /* The number of characters the port will take up */
+  
+  /* Allocate the new entry id */
+  id = malloc(*entry_len + 1);
+  if (!id) {
+    return NULL;
+  }
+
+  /* Create the new entry */
+  /* If sprintf() doesn't return the entry length, that signals failure */
+  if (sprintf(id, "%s:%d", server, port) != *entry_len) {
+    /* Free the allocated id, set length to zero and return NULL */
+    *entry_len = 0;
+    free(id);
+    return NULL;
+  }
+
+  return id;
+}
+
+/* Macro to save redundant free'ing of entry_id */
+#define _hostcache_return(__v) \
+{ \
+  free(entry_id); \
+  return (__v); \
+}
+
+Curl_addrinfo *Curl_resolv(struct SessionHandle *data,
+                           char *hostname,
+                           int port,
+                           char **bufp)
+{
+  char *entry_id = NULL;
+  struct curl_dns_cache_entry *p = NULL;
+  ssize_t entry_len;
+  time_t now;
+
+  /* If the host cache timeout is 0, we don't do DNS cach'ing
+     so fall through */
+  if (data->set.dns_cache_timeout == 0) {
+    return Curl_getaddrinfo(data, hostname, port, bufp);
+  }
+
+  /* Create an entry id, based upon the hostname and port */
+  entry_len = strlen(hostname);
+  entry_id = _create_hostcache_id(hostname, port, &entry_len);
+  /* If we can't create the entry id, don't cache, just fall-through
+     to the plain Curl_getaddrinfo() */
+  if (!entry_id) {
+    return Curl_getaddrinfo(data, hostname, port, bufp);
+  }
+  
+  time(&now);
+  /* See if its already in our dns cache */
+  if (entry_id && curl_hash_find(data->hostcache, entry_id, entry_len+1, (void **) &p)) {
+    /* Do we need to check for a cache timeout? */
+    if (data->set.dns_cache_timeout != -1) {
+      /* Return if the entry has not timed out */
+      if ((now - p->timestamp) < data->set.dns_cache_timeout) {
+        _hostcache_return(p->addr);
+      }
+    }
+    else {
+      _hostcache_return(p->addr);
+    }
+  }
+
+  /* Create a new cache entry */
+  p = (struct curl_dns_cache_entry *) malloc(sizeof(struct curl_dns_cache_entry));
+  if (!p) {
+   _hostcache_return(NULL);
+  }
+
+  p->addr = Curl_getaddrinfo(data, hostname, port, bufp);
+  if (!p->addr) {
+    free(p);
+    _hostcache_return(NULL);
+  }
+  p->timestamp = now;
+
+  /* Save it in our host cache */
+  curl_hash_update(data->hostcache, entry_id, entry_len+1, (const void *) p);
+
+  _hostcache_return(p->addr);
+}
+
+/*
+ * This is a wrapper function for freeing name information in a protocol
+ * independent way. This takes care of using the appropriate underlaying
+ * proper function.
+ */
+void Curl_freeaddrinfo(void *freethis)
+{
+  struct curl_dns_cache_entry *p = (struct curl_dns_cache_entry *) freethis;
+
+#ifdef ENABLE_IPV6
+  freeaddrinfo(p->addr);
+#else
+  free(p->addr);
+#endif
+
+  free(p);
+}
+
+/* --- resolve name or IP-number --- */
+
+#ifdef ENABLE_IPV6
+
+#ifdef MALLOCDEBUG
+/* These two are strictly for memory tracing and are using the same
+ * style as the family otherwise present in memdebug.c. I put these ones
+ * here since they require a bunch of struct types I didn't wanna include
+ * in memdebug.c
+ */
+int curl_getaddrinfo(char *hostname, char *service,
+                     struct addrinfo *hints,
+                     struct addrinfo **result,
+                     int line, const char *source)
+{
+  int res=(getaddrinfo)(hostname, service, hints, result);
+  if(0 == res) {
+    /* success */
+    if(logfile)
+      fprintf(logfile, "ADDR %s:%d getaddrinfo() = %p\n",
+              source, line, (void *)*result);
+  }
+  else {
+    if(logfile)
+      fprintf(logfile, "ADDR %s:%d getaddrinfo() failed\n",
+              source, line);
+  }
+  return res;
+}
+
+void curl_freeaddrinfo(struct addrinfo *freethis,
+                       int line, const char *source)
+{
+  (freeaddrinfo)(freethis);
+  if(logfile)
+    fprintf(logfile, "ADDR %s:%d freeaddrinfo(%p)\n",
+            source, line, (void *)freethis);
+}
+
+#endif
+
+/*
+ * Return 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 meory *MUST* be freed with
+ * Curl_freeaddrinfo(), nothing else.
+ */
+Curl_addrinfo *Curl_getaddrinfo(struct SessionHandle *data,
+                                char *hostname,
+                                int port,
+                                char **bufp)
+{
+  struct addrinfo hints, *res;
+  int error;
+  char sbuf[NI_MAXSERV];
+
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_family = PF_INET;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_flags = AI_CANONNAME;
+  snprintf(sbuf, sizeof(sbuf), "%d", port);
+  error = getaddrinfo(hostname, sbuf, &hints, &res);
+  if (error) {
+    infof(data, "getaddrinfo(3) failed for %s\n", hostname);    
+    return NULL;
+  }
+  *bufp=(char *)res; /* make it point to the result struct */
+
+  return res;
+}
+#else /* following code is IPv4-only */
+
+#ifndef HAVE_GETHOSTBYNAME_R
+/**
+ * Performs a "deep" copy of a hostent into a buffer (returns a pointer to the
+ * copy). Make absolutely sure the destination buffer is big enough!
+ *
+ * Keith McGuigan 
+ * 10/3/2001 */
+static struct hostent* pack_hostent(char* buf, struct hostent* orig)
+{
+  char* bufptr;
+  struct hostent* copy;
+
+  int i;
+  char* str;
+  int len;
+
+  bufptr = buf;
+  copy = (struct hostent*)bufptr;
+
+  bufptr += sizeof(struct hostent);
+  copy->h_name = bufptr;
+  len = strlen(orig->h_name) + 1;
+  strncpy(bufptr, orig->h_name, len);
+  bufptr += len;
+
+  /* we align on even 64bit boundaries for safety */
+#define MEMALIGN(x) (((unsigned long)(x)&0xfffffff8)+8)
+
+  /* This must be aligned properly to work on many CPU architectures! */
+  copy->h_aliases = (char**)MEMALIGN(bufptr);
+
+  /* Figure out how many aliases there are */
+  for (i = 0; orig->h_aliases[i] != NULL; ++i);
+
+  /* Reserve room for the array */
+  bufptr += (i + 1) * sizeof(char*);
+
+  /* Clone all known aliases */
+  for(i = 0; (str = orig->h_aliases[i]); i++) {
+    len = strlen(str) + 1;
+    strncpy(bufptr, str, len);
+    copy->h_aliases[i] = bufptr;
+    bufptr += len;
+  }
+  /* Terminate the alias list with a NULL */
+  copy->h_aliases[i] = NULL;
+
+  copy->h_addrtype = orig->h_addrtype;
+  copy->h_length = orig->h_length;
+    
+  /* align it for (at least) 32bit accesses */
+  bufptr = (char *)MEMALIGN(bufptr);
+
+  copy->h_addr_list = (char**)bufptr;
+
+  /* Figure out how many addresses there are */
+  for (i = 0; orig->h_addr_list[i] != NULL; ++i);
+
+  /* Reserve room for the array */
+  bufptr += (i + 1) * sizeof(char*);
+
+  i = 0;
+  len = orig->h_length;
+  str = orig->h_addr_list[i];
+  while (str != NULL) {
+    memcpy(bufptr, str, len);
+    copy->h_addr_list[i] = bufptr;
+    bufptr += len;
+    str = orig->h_addr_list[++i];
+  }
+  copy->h_addr_list[i] = NULL;
+
+  return copy;
+}
+#endif
+
+static char *MakeIP(unsigned long num,char *addr, int addr_len)
+{
+#if defined(HAVE_INET_NTOA) || defined(HAVE_INET_NTOA_R)
+  struct in_addr in;
+  in.s_addr = htonl(num);
+
+#if defined(HAVE_INET_NTOA_R)
+  inet_ntoa_r(in,addr,addr_len);
+#else
+  strncpy(addr,inet_ntoa(in),addr_len);
+#endif
+#else
+  unsigned char *paddr;
+
+  num = htonl(num);  /* htonl() added to avoid endian probs */
+  paddr = (unsigned char *)&num;
+  sprintf(addr, "%u.%u.%u.%u", paddr[0], paddr[1], paddr[2], paddr[3]);
+#endif
+  return (addr);
+}
+
+/* The original code to this function was once stolen from the Dancer source
+   code, written by Bjorn Reese, it has since been patched and modified
+   considerably. */
+
+#ifndef INADDR_NONE
+#define INADDR_NONE (in_addr_t) ~0
+#endif
+
+Curl_addrinfo *Curl_getaddrinfo(struct SessionHandle *data,
+                                char *hostname,
+                                int port,
+                                char **bufp)
+{
+  struct hostent *h = NULL;
+  in_addr_t in;
+  int ret; /* this variable is unused on several platforms but used on some */
+
+#define CURL_NAMELOOKUP_SIZE 9000
+  /* Allocate enough memory to hold the full name information structs and
+   * everything. OSF1 is known to require at least 8872 bytes. The buffer
+   * required for storing all possible aliases and IP numbers is according to
+   * Stevens' Unix Network Programming 2nd editor, p. 304: 8192 bytes! */
+  int *buf = (int *)malloc(CURL_NAMELOOKUP_SIZE);
+  if(!buf)
+    return NULL; /* major failure */
+  *bufp = (char *)buf;
+
+  port=0; /* unused in IPv4 code */
+  ret = 0; /* to prevent the compiler warning */
+
+  if ( (in=inet_addr(hostname)) != INADDR_NONE ) {
+    struct in_addr *addrentry;
+
+    h = (struct hostent*)buf;
+    h->h_addr_list = (char**)(buf + sizeof(*h));
+    addrentry = (struct in_addr*)(h->h_addr_list + 2);
+    addrentry->s_addr = in;
+    h->h_addr_list[0] = (char*)addrentry;
+    h->h_addr_list[1] = NULL;
+    h->h_addrtype = AF_INET;
+    h->h_length = sizeof(*addrentry);
+    h->h_name = *(h->h_addr_list) + h->h_length;
+    /* bad one h->h_name = (char*)(h->h_addr_list + h->h_length); */
+    MakeIP(ntohl(in),h->h_name, CURL_NAMELOOKUP_SIZE - (long)(h->h_name) + (long)buf);
+  }
+#if defined(HAVE_GETHOSTBYNAME_R)
+  else {
+    int h_errnop;
+     /* Workaround for gethostbyname_r bug in qnx nto. It is also _required_
+        for some of these functions. */
+    memset(buf, 0, CURL_NAMELOOKUP_SIZE);
+#ifdef HAVE_GETHOSTBYNAME_R_5
+    /* Solaris, IRIX and more */
+    if ((h = gethostbyname_r(hostname,
+                             (struct hostent *)buf,
+                             (char *)buf + sizeof(struct hostent),
+                             CURL_NAMELOOKUP_SIZE - sizeof(struct hostent),
+                             &h_errnop)) == NULL )
+#endif
+#ifdef HAVE_GETHOSTBYNAME_R_6
+    /* Linux */
+    if( gethostbyname_r(hostname,
+                        (struct hostent *)buf,
+                        (char *)buf + sizeof(struct hostent),
+                        CURL_NAMELOOKUP_SIZE - sizeof(struct hostent),
+                        &h, /* DIFFERENCE */
+                        &h_errnop))
+#endif
+#ifdef HAVE_GETHOSTBYNAME_R_3
+    /* AIX, Digital Unix, HPUX 10, more? */
+
+    if(CURL_NAMELOOKUP_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. */
+
+      ret = gethostbyname_r(hostname,
+                          (struct hostent *)buf,
+                          (struct hostent_data *)(buf + sizeof(struct hostent)));
+    else
+      ret = -1; /* failure, too smallish buffer size */
+    
+    /* result expected in h */
+    h = (struct hostent*)buf;
+    h_errnop= errno; /* we don't deal with this, but set it anyway */
+    if(ret)
+#endif
+      {
+      infof(data, "gethostbyname_r(2) failed for %s\n", hostname);
+      h = NULL; /* set return code to NULL */
+      free(buf);
+      *bufp=NULL;
+    }
+#else
+  else {
+    if ((h = gethostbyname(hostname)) == NULL ) {
+      infof(data, "gethostbyname(2) failed for %s\n", hostname);
+      free(buf);
+      *bufp=NULL;
+    }
+    else 
+      /* we make a copy of the hostent right now, right here, as the
+         static one we got a pointer to might get removed when we don't
+         want/expect that */
+      h = pack_hostent((char *)buf, h);
+#endif
+  }
+  return (h);
+}
+
+#endif /* end of IPv4-specific code */
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */
+ 

+ 60 - 0
Source/CTest/Curl/hostip.h

@@ -0,0 +1,60 @@
+#ifndef __HOSTIP_H
+#define __HOSTIP_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "hash.h"
+
+struct addrinfo;
+struct hostent;
+struct SessionHandle;
+
+void Curl_global_host_cache_init(void);
+void Curl_global_host_cache_dtor(void);
+curl_hash *Curl_global_host_cache_get(void);
+
+#define Curl_global_host_cache_use(__p) ((__p)->set.global_dns_cache)
+
+Curl_addrinfo *Curl_resolv(struct SessionHandle *data,
+                           char *hostname,
+                           int port,
+                           char **bufp);
+
+/* Get name info */
+Curl_addrinfo *Curl_getaddrinfo(struct SessionHandle *data,
+                                char *hostname,
+                                int port,
+                                char **bufp);
+/* free name info */
+void Curl_freeaddrinfo(void *freethis);
+
+#ifdef MALLOCDEBUG
+void curl_freeaddrinfo(struct addrinfo *freethis,
+                       int line, const char *source);
+int curl_getaddrinfo(char *hostname, char *service,
+                     struct addrinfo *hints,
+                     struct addrinfo **result,
+                     int line, const char *source);
+#endif
+
+#endif

+ 985 - 0
Source/CTest/Curl/http.c

@@ -0,0 +1,985 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+/* -- WIN32 approved -- */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+
+#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
+#include <winsock.h>
+#include <time.h>
+#include <io.h>
+#else
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#include <sys/time.h>
+
+#ifdef HAVE_TIME_H
+#ifdef TIME_WITH_SYS_TIME
+#include <time.h>
+#endif
+#endif
+
+#include <sys/resource.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <netdb.h>
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#include <sys/ioctl.h>
+#include <signal.h>
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+
+#endif
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "transfer.h"
+#include "sendf.h"
+#include "formdata.h"
+#include "progress.h"
+#include "base64.h"
+#include "cookie.h"
+#include "strequal.h"
+#include "ssluse.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#ifdef MALLOCDEBUG
+#include "memdebug.h"
+#endif
+
+/* ------------------------------------------------------------------------- */
+/*
+ * The add_buffer series of functions are used to build one large memory chunk
+ * from repeated function invokes. Used so that the entire HTTP request can
+ * be sent in one go.
+ */
+static CURLcode
+ add_buffer(send_buffer *in, const void *inptr, size_t size);
+
+/*
+ * add_buffer_init() returns a fine buffer struct
+ */
+static
+send_buffer *add_buffer_init(void)
+{
+  send_buffer *blonk;
+  blonk=(send_buffer *)malloc(sizeof(send_buffer));
+  if(blonk) {
+    memset(blonk, 0, sizeof(send_buffer));
+    return blonk;
+  }
+  return NULL; /* failed, go home */
+}
+
+/*
+ * add_buffer_send() sends a buffer and frees all associated memory.
+ */
+static
+CURLcode add_buffer_send(int sockfd, struct connectdata *conn, send_buffer *in,
+                         long *bytes_written)
+{
+  ssize_t amount;
+  CURLcode res;
+  char *ptr;
+  int size;
+
+  if(conn->data->set.verbose) {
+    fputs("> ", conn->data->set.err);
+    /* this data _may_ contain binary stuff */
+    fwrite(in->buffer, in->size_used, 1, conn->data->set.err);
+  }
+
+  /* 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;
+  do {
+    res = Curl_write(conn, sockfd, ptr, size, &amount);
+
+    if(CURLE_OK != res)
+      break;
+
+    if(amount != size) {
+      size -= amount;
+      ptr += amount;
+    }
+    else
+      break;
+
+  } while(1);
+
+  if(in->buffer)
+    free(in->buffer);
+  free(in);
+
+  *bytes_written = amount;
+
+  return res;
+}
+
+
+/* 
+ * add_bufferf() builds a buffer from the formatted input
+ */
+static
+CURLcode add_bufferf(send_buffer *in, const char *fmt, ...)
+{
+  CURLcode result = CURLE_OUT_OF_MEMORY;
+  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) {
+    result = add_buffer(in, s, strlen(s));
+    free(s);
+  }
+  return result;
+}
+
+/*
+ * add_buffer() appends a memory chunk to the existing one
+ */
+static
+CURLcode add_buffer(send_buffer *in, const void *inptr, size_t size)
+{
+  char *new_rb;
+  int new_size;
+
+  if(!in->buffer ||
+     ((in->size_used + size) > (in->size_max - 1))) {
+    new_size = (in->size_used+size)*2;
+    if(in->buffer)
+      /* we have a buffer, enlarge the existing one */
+      new_rb = (char *)realloc(in->buffer, new_size);
+    else
+      /* create a new buffer */
+      new_rb = (char *)malloc(new_size);
+
+    if(!new_rb)
+      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 */
+/* ------------------------------------------------------------------------- */
+
+/*
+ * This function checks the linked list of custom HTTP headers for a particular
+ * header (prefix).
+ */
+static bool 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(strnequal(head->data, thisheader, thislen)) {
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+/*
+ * ConnectHTTPProxyTunnel() 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.
+ */
+
+CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn,
+                                     int tunnelsocket,
+                                     char *hostname, int remote_port)
+{
+  int httperror=0;
+  int subversion=0;
+  struct SessionHandle *data=conn->data;
+  CURLcode result;
+  int res;
+
+  int nread;   /* total size read */
+  int perline; /* count bytes per line */
+  bool keepon=TRUE;
+  ssize_t gotbytes;
+  char *ptr;
+  int timeout = 3600; /* default timeout in seconds */
+  struct timeval interval;
+  fd_set rkeepfd;
+  fd_set readfd;
+  char *line_start;
+
+#define SELECT_OK      0
+#define SELECT_ERROR   1
+#define SELECT_TIMEOUT 2
+  int error = SELECT_OK;
+
+  infof(data, "Establish HTTP proxy tunnel to %s:%d\n", hostname, remote_port);
+
+  /* OK, now send the connect request to the proxy */
+  result =
+    Curl_sendf(tunnelsocket, conn,
+               "CONNECT %s:%d HTTP/1.0\015\012"
+               "%s"
+               "%s"
+               "\r\n",
+               hostname, remote_port,
+               (conn->bits.proxy_user_passwd)?conn->allocptr.proxyuserpwd:"",
+               (data->set.useragent?conn->allocptr.uagent:"")
+               );
+  if(result) {
+    failf(data, "Failed sending CONNECT to proxy");
+    return result;
+  }
+
+  /* Now, read the full reply we get from the proxy */
+
+
+  if(data->set.timeout) {
+    /* if timeout is requested, find out how much remaining time we have */
+    timeout = data->set.timeout - /* timeout time */
+      Curl_tvdiff(Curl_tvnow(), conn->now)/1000; /* spent time */
+    if(timeout <=0 ) {
+      failf(data, "Transfer aborted due to timeout");
+      return -SELECT_TIMEOUT; /* already too little time */
+    }
+  }
+
+  FD_ZERO (&readfd);            /* clear it */
+  FD_SET (tunnelsocket, &readfd);     /* read socket */
+
+  /* get this in a backup variable to be able to restore it on each lap in the
+     select() loop */
+  rkeepfd = readfd;
+
+  ptr=data->state.buffer;
+  line_start = ptr;
+
+  nread=0;
+  perline=0;
+  keepon=TRUE;
+
+  while((nread<BUFSIZE) && (keepon && !error)) {
+    readfd = rkeepfd;              /* set every lap */
+    interval.tv_sec = timeout;
+    interval.tv_usec = 0;
+
+    switch (select (tunnelsocket+1, &readfd, NULL, NULL, &interval)) {
+    case -1: /* select() error, stop reading */
+      error = SELECT_ERROR;
+      failf(data, "Transfer aborted due to select() error");
+      break;
+    case 0: /* timeout */
+      error = SELECT_TIMEOUT;
+      failf(data, "Transfer aborted due to timeout");
+      break;
+    default:
+      /*
+       * This code previously didn't use the kerberos sec_read() code
+       * to read, but when we use Curl_read() it may do so. Do confirm
+       * that this is still ok and then remove this comment!
+       */
+      res= Curl_read(conn, tunnelsocket, ptr, BUFSIZE-nread,
+                     &gotbytes);
+      if(res< 0)
+        /* EWOULDBLOCK */
+        continue; /* go loop yourself */
+      else if(res)
+        keepon = FALSE;
+      else if(gotbytes <= 0) {
+        keepon = FALSE;
+        error = SELECT_ERROR;
+        failf(data, "Connection 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;
+        for(i = 0; i < gotbytes; ptr++, i++) {
+          perline++; /* amount of bytes in this line so far */
+          if(*ptr=='\n') {
+            /* a newline is CRLF in ftp-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(data->set.verbose) {
+              fputs("< ", data->set.err);
+              fwrite(line_start, perline, 1, data->set.err);
+              /* no need to output LF here, it is part of the data */
+            }
+            
+            if('\r' == line_start[0]) {
+              /* end of headers */
+              keepon=FALSE;
+              break; /* breaks out of loop, not switch */
+            }
+
+            if(2 == sscanf(line_start, "HTTP/1.%d %d",
+                           &subversion,
+                           &httperror)) {
+              ;
+            }
+
+            perline=0; /* line starts over here */
+            line_start = ptr+1;
+          }
+        }
+      }
+      break;
+    } /* switch */
+  } /* while there's buffer left and loop is requested */
+
+  if(error)
+    return CURLE_READ_ERROR;
+
+  if(200 != httperror) {
+    if(407 == httperror)
+      /* Added Nov 6 1998 */
+      failf(data, "Proxy requires authorization!");
+    else 
+      failf(data, "Received error code %d from proxy", httperror);
+    return CURLE_READ_ERROR;
+  }
+
+  infof (data, "Proxy replied to CONNECT request\n");
+  return CURLE_OK;
+}
+
+/*
+ * HTTP stuff to do at connect-time.
+ */
+CURLcode Curl_http_connect(struct connectdata *conn)
+{
+  struct SessionHandle *data;
+  CURLcode result;
+
+  data=conn->data;
+
+  /* If we are not using a proxy and we want a secure connection,
+   * perform SSL initialization & connection now.
+   * If using a proxy with https, then we must tell the proxy to CONNECT
+   * us to the host we want to talk to.  Only after the connect
+   * has occured, can we start talking SSL
+   */
+
+  if(data->change.proxy &&
+     ((conn->protocol & PROT_HTTPS) || data->set.tunnel_thru_httpproxy)) {
+
+    /* either HTTPS over proxy, OR explicitly asked for a tunnel */
+    result = Curl_ConnectHTTPProxyTunnel(conn, conn->firstsocket,
+                                         conn->hostname, conn->remote_port);
+    if(CURLE_OK != result)
+      return result;
+  }    
+
+  if(conn->protocol & PROT_HTTPS) {
+    /* now, perform the SSL initialization for this socket */
+    result = Curl_SSLConnect(conn);
+    if(result)
+      return result;
+  }
+
+  if(conn->bits.user_passwd && !data->state.this_is_a_follow) {
+    /* Authorization: is requested, this is not a followed location, get the
+       original host name */
+    data->state.auth_host = strdup(conn->hostname);
+  }
+
+  return CURLE_OK;
+}
+
+CURLcode Curl_http_done(struct connectdata *conn)
+{
+  struct SessionHandle *data;
+  long *bytecount = &conn->bytecount;
+  struct HTTP *http;
+
+  data=conn->data;
+  http=conn->proto.http;
+
+  if(HTTPREQ_POST_FORM == data->set.httpreq) {
+    *bytecount = http->readbytecount + http->writebytecount;
+      
+    Curl_formclean(http->sendit); /* Now free that whole lot */
+
+    data->set.fread = http->storefread; /* restore */
+    data->set.in = http->in; /* restore */
+  }
+  else if(HTTPREQ_PUT == data->set.httpreq) {
+    *bytecount = http->readbytecount + http->writebytecount;
+  }
+
+  if(0 == (http->readbytecount + conn->headerbytecount)) {
+    /* nothing was read from the HTTP server, this can't be right
+       so we return an error here */
+    failf(data, "Empty reply from server");
+    return CURLE_GOT_NOTHING;
+  }
+
+  return CURLE_OK;
+}
+
+
+CURLcode Curl_http(struct connectdata *conn)
+{
+  struct SessionHandle *data=conn->data;
+  char *buf = data->state.buffer; /* this is a short cut to the buffer */
+  CURLcode result=CURLE_OK;
+  struct HTTP *http;
+  struct Cookie *co=NULL; /* no cookies from start */
+  char *ppath = conn->ppath; /* three previous function arguments */
+  char *host = conn->name;
+  long *bytecount = &conn->bytecount;
+
+  if(!conn->proto.http) {
+    /* Only allocate this struct if we don't already have it! */
+
+    http = (struct HTTP *)malloc(sizeof(struct HTTP));
+    if(!http)
+      return CURLE_OUT_OF_MEMORY;
+    memset(http, 0, sizeof(struct HTTP));
+    conn->proto.http = http;
+  }
+  else
+    http = conn->proto.http;
+
+  /* We default to persistant connections */
+  conn->bits.close = FALSE;
+
+  if ( (conn->protocol&(PROT_HTTP|PROT_FTP)) &&
+       data->set.upload) {
+    data->set.httpreq = HTTPREQ_PUT;
+  }
+  
+  /* The User-Agent string has been built in 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(checkheaders(data, "User-Agent:") && conn->allocptr.uagent) {
+    free(conn->allocptr.uagent);
+    conn->allocptr.uagent=NULL;
+  }
+
+  if((conn->bits.user_passwd) && !checkheaders(data, "Authorization:")) {
+    char *authorization;
+
+    /* To prevent the user+password to get sent to other than the original
+       host due to a location-follow, we do some weirdo checks here */
+    if(!data->state.this_is_a_follow ||
+       !data->state.auth_host ||
+       strequal(data->state.auth_host, conn->hostname)) {
+      sprintf(data->state.buffer, "%s:%s",
+              data->state.user, data->state.passwd);
+      if(Curl_base64_encode(data->state.buffer, strlen(data->state.buffer),
+                            &authorization) >= 0) {
+        if(conn->allocptr.userpwd)
+          free(conn->allocptr.userpwd);
+        conn->allocptr.userpwd = aprintf( "Authorization: Basic %s\015\012",
+                                          authorization);
+        free(authorization);
+      }
+    }
+  }
+  if((data->change.referer) && !checkheaders(data, "Referer:")) {
+    if(conn->allocptr.ref)
+      free(conn->allocptr.ref);
+    conn->allocptr.ref = aprintf("Referer: %s\015\012", data->change.referer);
+  }
+  if(data->set.cookie && !checkheaders(data, "Cookie:")) {
+    if(conn->allocptr.cookie)
+      free(conn->allocptr.cookie);
+    conn->allocptr.cookie = aprintf("Cookie: %s\015\012", data->set.cookie);
+  }
+
+  if(data->cookies) {
+    co = Curl_cookie_getlist(data->cookies,
+                             host, ppath,
+                             conn->protocol&PROT_HTTPS?TRUE:FALSE);
+  }
+  if (data->change.proxy &&
+      !data->set.tunnel_thru_httpproxy &&
+      !(conn->protocol&PROT_HTTPS))  {
+    /* The path sent to the proxy is in fact the entire URL */
+    ppath = data->change.url;
+  }
+  if(HTTPREQ_POST_FORM == data->set.httpreq) {
+    /* we must build the whole darned post sequence first, so that we have
+       a size of the whole shebang before we start to send it */
+    http->sendit = Curl_getFormData(data->set.httppost, &http->postsize);
+  }
+
+  if(!checkheaders(data, "Host:")) {
+    /* if ptr_host is already set, it is almost OK since we only re-use
+       connections to the very same host and port, but when we use a HTTP
+       proxy we have a persistant connect and yet we must change the Host:
+       header! */
+
+    if(conn->allocptr.host)
+      free(conn->allocptr.host);
+
+    if(((conn->protocol&PROT_HTTPS) && (conn->remote_port == PORT_HTTPS)) ||
+       (!(conn->protocol&PROT_HTTPS) && (conn->remote_port == PORT_HTTP)) )
+      /* If (HTTPS on port 443) OR (non-HTTPS on port 80) then don't include
+         the port number in the host string */
+      conn->allocptr.host = aprintf("Host: %s\r\n", host);
+    else
+      conn->allocptr.host = aprintf("Host: %s:%d\r\n", host,
+                                    conn->remote_port);
+  }
+
+  if(!checkheaders(data, "Pragma:"))
+    http->p_pragma = "Pragma: no-cache\r\n";
+
+  if(!checkheaders(data, "Accept:"))
+    http->p_accept = "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*\r\n";
+
+  if(( (HTTPREQ_POST == data->set.httpreq) ||
+       (HTTPREQ_POST_FORM == data->set.httpreq) ||
+       (HTTPREQ_PUT == data->set.httpreq) ) &&
+     conn->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(conn->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!
+       */
+       conn->resume_from = 0;
+    }
+
+    if(conn->resume_from) {
+      /* do we still game? */
+      int passed=0;
+
+      /* Now, let's read off the proper amount of bytes from the
+         input. If we knew it was a proper file we could've just
+         fseek()ed but we only have a stream here */
+      do {
+        int readthisamountnow = (conn->resume_from - passed);
+        int actuallyread;
+
+        if(readthisamountnow > BUFSIZE)
+          readthisamountnow = BUFSIZE;
+
+        actuallyread =
+          data->set.fread(data->state.buffer, 1, readthisamountnow,
+                          data->set.in);
+
+        passed += actuallyread;
+        if(actuallyread != readthisamountnow) {
+          failf(data, "Could only read %d bytes from the input",
+                passed);
+          return CURLE_READ_ERROR;
+        }
+      } while(passed != conn->resume_from); /* loop until done */
+
+      /* now, decrease the size of the read */
+      if(data->set.infilesize>0) {
+        data->set.infilesize -= conn->resume_from;
+
+        if(data->set.infilesize <= 0) {
+          failf(data, "File already completely uploaded");
+          return CURLE_PARTIAL_FILE;
+        }
+      }
+      /* we've passed, proceed as normal */
+    }
+  }
+  if(conn->bits.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((data->set.httpreq == HTTPREQ_GET) &&
+       !checkheaders(data, "Range:")) {
+      conn->allocptr.rangeline = aprintf("Range: bytes=%s\r\n", conn->range);
+    }
+    else if((data->set.httpreq != HTTPREQ_GET) &&
+            !checkheaders(data, "Content-Range:")) {
+
+      if(conn->resume_from) {
+        /* This is because "resume" was selected */
+        long total_expected_size= conn->resume_from + data->set.infilesize;
+        conn->allocptr.rangeline = aprintf("Content-Range: bytes %s%ld/%ld\r\n",
+                                      conn->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/%d\r\n",
+                                      conn->range, data->set.infilesize);
+      }
+    }
+  }
+
+  do {
+    /* Use 1.1 unless the use specificly asked for 1.0 */
+    const char *httpstring=
+      data->set.httpversion==CURL_HTTP_VERSION_1_0?"1.0":"1.1";
+
+    send_buffer *req_buffer;
+    struct curl_slist *headers=data->set.headers;
+
+    /* initialize a dynamic send-buffer */
+    req_buffer = add_buffer_init();
+
+    /* add the main request stuff */
+    add_bufferf(req_buffer,
+                "%s " /* GET/HEAD/POST/PUT */
+                "%s HTTP/%s\r\n" /* path */
+                "%s" /* proxyuserpwd */
+                "%s" /* userpwd */
+                "%s" /* range */
+                "%s" /* user agent */
+                "%s" /* cookie */
+                "%s" /* host */
+                "%s" /* pragma */
+                "%s" /* accept */
+                "%s", /* referer */
+
+                data->set.customrequest?data->set.customrequest:
+                (data->set.no_body?"HEAD":
+                 ((HTTPREQ_POST == data->set.httpreq) ||
+                  (HTTPREQ_POST_FORM == data->set.httpreq))?"POST":
+                 (HTTPREQ_PUT == data->set.httpreq)?"PUT":"GET"),
+                ppath, httpstring,
+                (conn->bits.proxy_user_passwd &&
+                 conn->allocptr.proxyuserpwd)?conn->allocptr.proxyuserpwd:"",
+                (conn->bits.user_passwd && conn->allocptr.userpwd)?
+                conn->allocptr.userpwd:"",
+                (conn->bits.use_range && conn->allocptr.rangeline)?
+                conn->allocptr.rangeline:"",
+                (data->set.useragent && *data->set.useragent && conn->allocptr.uagent)?
+                conn->allocptr.uagent:"",
+                (conn->allocptr.cookie?conn->allocptr.cookie:""), /* Cookie: <data> */
+                (conn->allocptr.host?conn->allocptr.host:""), /* Host: host */
+                http->p_pragma?http->p_pragma:"",
+                http->p_accept?http->p_accept:"",
+                (data->change.referer && conn->allocptr.ref)?conn->allocptr.ref:"" /* Referer: <data> <CRLF> */
+                );
+
+    if(co) {
+      int count=0;
+      struct Cookie *store=co;
+      /* now loop through all cookies that matched */
+      while(co) {
+        if(co->value && strlen(co->value)) {
+          if(0 == count) {
+            add_bufferf(req_buffer, "Cookie: ");
+          }
+          add_bufferf(req_buffer,
+                      "%s%s=%s", count?"; ":"", co->name, co->value);
+          count++;
+        }
+        co = co->next; /* next cookie please */
+      }
+      if(count) {
+        add_buffer(req_buffer, "\r\n", 2);
+      }
+      Curl_cookie_freelist(store); /* free the cookie list */
+      co=NULL;
+    }
+
+    if(data->set.timecondition) {
+      struct tm *thistime;
+
+      /* Phil Karn (Fri, 13 Apr 2001) pointed out that 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).
+       */
+
+#ifdef HAVE_GMTIME_R
+      /* thread-safe version */
+      struct tm keeptime;
+      thistime = (struct tm *)gmtime_r(&data->set.timevalue, &keeptime);
+#else
+      thistime = gmtime(&data->set.timevalue);
+#endif
+      if(NULL == thistime) {
+        failf(data, "localtime() failed!");
+        return CURLE_OUT_OF_MEMORY;
+      }
+
+#ifdef HAVE_STRFTIME
+      /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
+      strftime(buf, BUFSIZE-1, "%a, %d %b %Y %H:%M:%S GMT", thistime);
+#else
+      /* TODO: Right, we *could* write a replacement here */
+      strcpy(buf, "no strftime() support");
+#endif
+      switch(data->set.timecondition) {
+      case TIMECOND_IFMODSINCE:
+      default:
+        add_bufferf(req_buffer,
+                    "If-Modified-Since: %s\r\n", buf);
+        break;
+      case TIMECOND_IFUNMODSINCE:
+        add_bufferf(req_buffer,
+                    "If-Unmodified-Since: %s\r\n", buf);
+        break;
+      case TIMECOND_LASTMOD:
+        add_bufferf(req_buffer,
+                    "Last-Modified: %s\r\n", buf);
+        break;
+      }
+    }
+
+    while(headers) {
+      char *ptr = strchr(headers->data, ':');
+      if(ptr) {
+        /* we require a colon for this to be a true header */
+
+        ptr++; /* pass the colon */
+        while(*ptr && isspace((int)*ptr))
+          ptr++;
+
+        if(*ptr) {
+          /* only send this if the contents was non-blank */
+
+          add_bufferf(req_buffer, "%s\r\n", headers->data);
+        }
+      }
+      headers = headers->next;
+    }
+
+    if(HTTPREQ_POST_FORM == data->set.httpreq) {
+      if(Curl_FormInit(&http->form, http->sendit)) {
+        failf(data, "Internal HTTP POST error!");
+        return CURLE_HTTP_POST_ERROR;
+      }
+
+      http->storefread = data->set.fread; /* backup */
+      http->in = data->set.in; /* backup */
+          
+      data->set.fread = (curl_read_callback)
+        Curl_FormReader; /* set the read function to read from the
+                            generated form data */
+      data->set.in = (FILE *)&http->form;
+
+      add_bufferf(req_buffer,
+                  "Content-Length: %d\r\n", http->postsize);
+
+      if(!checkheaders(data, "Expect:")) {
+        /* if not 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) */
+        add_bufferf(req_buffer,
+                    "Expect: 100-continue\r\n");
+        data->set.expect100header = TRUE;
+      }
+
+      if(!checkheaders(data, "Content-Type:")) {
+        /* Get Content-Type: line from Curl_FormReadOneLine, which happens
+           to always be the first line. We can know this for sure since
+           we always build the formpost linked list the same way!
+
+           The Content-Type header line also contains the MIME boundary
+           string etc why disabling this header is likely to not make things
+           work, but we support it anyway.
+        */
+        char contentType[256];
+        int linelength=0;
+        linelength = Curl_FormReadOneLine (contentType,
+                                           sizeof(contentType),
+                                           1,
+                                           (FILE *)&http->form);
+        if(linelength == -1) {
+          failf(data, "Could not get Content-Type header line!");
+          return CURLE_HTTP_POST_ERROR;
+        }
+        add_buffer(req_buffer, contentType, linelength);
+      }
+
+      /* make the request end in a true CRLF */
+      add_buffer(req_buffer, "\r\n", 2);
+
+      /* set upload size to the progress meter */
+      Curl_pgrsSetUploadSize(data, http->postsize);
+
+      /* fire away the whole request to the server */
+      result = add_buffer_send(conn->firstsocket, conn, req_buffer,
+                               &data->info.request_size);
+      if(result)
+        failf(data, "Failed sending POST request");
+      else
+        /* setup variables for the upcoming transfer */
+        result = Curl_Transfer(conn, conn->firstsocket, -1, TRUE,
+                               &http->readbytecount,
+                               conn->firstsocket,
+                               &http->writebytecount);
+      if(result) {
+        Curl_formclean(http->sendit); /* free that whole lot */
+        return result;
+      }
+    }
+    else if(HTTPREQ_PUT == data->set.httpreq) {
+      /* Let's PUT the data to the server! */
+
+      if(data->set.infilesize>0) {
+        add_bufferf(req_buffer,
+                    "Content-Length: %d\r\n\r\n", /* file size */
+                    data->set.infilesize );
+      }
+      else
+        add_bufferf(req_buffer, "\015\012");
+
+      /* set the upload size to the progress meter */
+      Curl_pgrsSetUploadSize(data, data->set.infilesize);
+
+      /* this sends the buffer and frees all the buffer resources */
+      result = add_buffer_send(conn->firstsocket, conn, req_buffer,
+                               &data->info.request_size);
+      if(result)
+        failf(data, "Faied sending POST request");
+      else
+        /* prepare for transfer */
+        result = Curl_Transfer(conn, conn->firstsocket, -1, TRUE,
+                               &http->readbytecount,
+                               conn->firstsocket,
+                               &http->writebytecount);
+      if(result)
+        return result;
+      
+    }
+    else {
+      if(HTTPREQ_POST == data->set.httpreq) {
+        /* this is the simple POST, using x-www-form-urlencoded style */
+
+        if(!data->set.postfields) {
+          /*
+           * This is an attempt to do a POST without having anything to
+           * actually send. Let's make a NULL pointer equal "" here. Good/bad
+           * ?
+           */
+          data->set.postfields = (char *)"";
+          data->set.postfieldsize = 0; /* it might been set to something illegal,
+                                      anything > 0 would be! */
+        }
+
+        if(!checkheaders(data, "Content-Length:"))
+          /* we allow replacing this header, although it isn't very wise to
+             actually set your own */
+          add_bufferf(req_buffer,
+                      "Content-Length: %d\r\n",
+                      (data->set.postfieldsize?data->set.postfieldsize:
+                       strlen(data->set.postfields)) );
+
+        if(!checkheaders(data, "Content-Type:"))
+          add_bufferf(req_buffer,
+                      "Content-Type: application/x-www-form-urlencoded\r\n");
+
+        /* and here comes the actual data */
+        if(data->set.postfieldsize) {
+          add_buffer(req_buffer, "\r\n", 2);
+          add_buffer(req_buffer, data->set.postfields,
+                     data->set.postfieldsize);
+        }
+        else {
+          add_bufferf(req_buffer,
+                      "\r\n"
+                      "%s",
+                      data->set.postfields );
+        }
+      }
+      else
+        add_buffer(req_buffer, "\r\n", 2);
+
+      /* issue the request */
+      result = add_buffer_send(conn->firstsocket, conn, req_buffer,
+                               &data->info.request_size);
+
+      if(result)
+        failf(data, "Failed sending HTTP request");
+      else
+        /* HTTP GET/HEAD download: */
+        result = Curl_Transfer(conn, conn->firstsocket, -1, TRUE, bytecount,
+                               -1, NULL); /* nothing to upload */
+    }
+    if(result)
+      return result;
+  } while (0); /* this is just a left-over from the multiple document download
+                  attempts */
+
+  return CURLE_OK;
+}
+
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 42 - 0
Source/CTest/Curl/http.h

@@ -0,0 +1,42 @@
+#ifndef __HTTP_H
+#define __HTTP_H
+
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+/* ftp can use this as well */
+CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn,
+                                     int tunnelsocket,
+                                     char *hostname, int remote_port);
+
+/* protocol-specific functions set up to be called by the main engine */
+CURLcode Curl_http(struct connectdata *conn);
+CURLcode Curl_http_done(struct connectdata *conn);
+CURLcode Curl_http_connect(struct connectdata *conn);
+
+/* The following functions are defined in http_chunks.c */
+void Curl_httpchunk_init(struct connectdata *conn);
+CHUNKcode Curl_httpchunk_read(struct connectdata *conn, char *datap,
+                              ssize_t length, ssize_t *wrote);
+
+#endif

+ 230 - 0
Source/CTest/Curl/http_chunks.c

@@ -0,0 +1,230 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2001, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+#include "setup.h"
+
+/* -- WIN32 approved -- */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "urldata.h" /* it includes http_chunks.h */
+#include "sendf.h"   /* for the client write stuff */
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#ifdef MALLOCDEBUG
+#include "memdebug.h"
+#endif
+
+/* 
+ * 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.
+
+ */
+
+
+void Curl_httpchunk_init(struct connectdata *conn)
+{
+  struct Curl_chunker *chunk = &conn->proto.http->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.
+ */
+CHUNKcode Curl_httpchunk_read(struct connectdata *conn,
+                              char *datap,
+                              size_t length,
+                              size_t *wrote)
+{
+  CURLcode result;
+  struct Curl_chunker *ch = &conn->proto.http->chunk;
+  int piece;
+  *wrote = 0; /* nothing yet */
+
+  while(length) {
+    switch(ch->state) {
+    case CHUNK_HEX:
+      if(isxdigit((int)*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;
+        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 == '\r')
+        ch->state = CHUNK_CR;
+      length--;
+      datap++;
+      break;
+
+    case CHUNK_CR:
+      /* waiting for the LF */
+      if(*datap == '\n') {
+        /* we're now expecting data to come, unless size was zero! */
+        if(0 == ch->datasize) {
+          ch->state = CHUNK_STOP; /* stop reading! */
+          if(1 == length) {
+            /* This was the final byte, return right now */
+            return CHUNKE_STOP;
+          }
+        }
+        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 */
+      result = Curl_client_write(conn->data, CLIENTWRITE_BODY, datap, piece);
+      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 == '\r') {
+        ch->state = CHUNK_POSTLF;
+        datap++;
+        length--;
+      }
+      else
+        return CHUNKE_BAD_CHUNK;
+      break;
+
+    case CHUNK_POSTLF:
+      if(*datap == '\n') {
+        /*
+         * 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_STOP:
+      /* If we arrive here, there is 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 */
+    default:
+      return CHUNKE_STATE_ERROR;
+    }
+  }
+  return CHUNKE_OK;
+}
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 87 - 0
Source/CTest/Curl/http_chunks.h

@@ -0,0 +1,87 @@
+#ifndef __HTTP_CHUNKS_H
+#define __HTTP_CHUNKS_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2001, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+/*
+ * The longest possible hexadecimal number we support in a chunked transfer.
+ * Weird enough, RFC2616 doesn't set a maximum size! Since we use strtoul()
+ * to convert it, we "only" support 2^32 bytes chunk data.
+ */
+#define MAXNUM_SIZE 16
+
+typedef enum {
+  CHUNK_FIRST, /* never use */
+
+  /* In this we await and buffer all hexadecimal digits until we get one
+     that isn't a hexadecimal digit. When done, we go POSTHEX */
+  CHUNK_HEX,
+
+  /* We have received the hexadecimal digit and we eat all characters until
+     we get a CRLF pair. When we see a CR we go to the CR state. */
+  CHUNK_POSTHEX,
+
+  /* A single CR has been found and we should get a LF right away in this
+     state or we go back to POSTHEX. When LF is received, we go to DATA.
+     If the size given was zero, we set state to STOP and return. */
+  CHUNK_CR,
+
+  /* We eat the amount of data specified. When done, we move on to the
+     POST_CR state. */
+  CHUNK_DATA,
+
+  /* POSTCR should get a CR and nothing else, then move to POSTLF */
+  CHUNK_POSTCR,
+
+  /* POSTLF should get a LF and nothing else, then move back to HEX as
+     the CRLF combination marks the end of a chunk */
+  CHUNK_POSTLF,
+
+  /* This is mainly used to really mark that we're out of the game.
+     NOTE: that there's a 'dataleft' field in the struct that will tell how
+     many bytes that were not passed to the client in the end of the last
+     buffer! */
+  CHUNK_STOP,
+
+  CHUNK_LAST /* never use */
+} ChunkyState;
+
+typedef enum {
+  CHUNKE_STOP = -1,
+  CHUNKE_OK = 0,
+  CHUNKE_TOO_LONG_HEX = 1,
+  CHUNKE_ILLEGAL_HEX,
+  CHUNKE_BAD_CHUNK,
+  CHUNKE_WRITE_ERROR,
+  CHUNKE_STATE_ERROR,
+  CHUNKE_LAST
+} CHUNKcode;
+
+struct Curl_chunker {
+  char hexbuffer[ MAXNUM_SIZE + 1];
+  int hexindex;
+  ChunkyState state;
+  size_t datasize;
+  size_t dataleft; /* untouched data amount at the end of the last buffer */
+};
+
+#endif

+ 134 - 0
Source/CTest/Curl/if2ip.c

@@ -0,0 +1,134 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if ! defined(WIN32) && ! defined(__BEOS__) && !defined(__CYGWIN32__)
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+/* This must be before net/if.h for AIX 3.2 to enjoy life */
+#include <sys/time.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#include <sys/ioctl.h>
+
+/* -- if2ip() -- */
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+
+#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL) 
+#include "inet_ntoa_r.h"
+#endif
+
+#ifdef  VMS
+#define IOCTL_3_ARGS
+#include <inet.h>
+#endif
+
+/* The last #include file should be: */
+#ifdef MALLOCDEBUG
+#include "memdebug.h"
+#endif
+
+#define SYS_ERROR -1
+
+char *Curl_if2ip(char *interface, char *buf, int buf_size)
+{
+  int dummy;
+  char *ip=NULL;
+  
+  if(!interface)
+    return NULL;
+
+  dummy = socket(AF_INET, SOCK_STREAM, 0);
+  if (SYS_ERROR == dummy) {
+    return NULL;
+  }
+  else {
+    struct ifreq req;
+    memset(&req, 0, sizeof(req));
+    strcpy(req.ifr_name, interface);
+    req.ifr_addr.sa_family = AF_INET;
+#ifdef  IOCTL_3_ARGS
+    if (SYS_ERROR == ioctl(dummy, SIOCGIFADDR, &req)) {
+#else
+    if (SYS_ERROR == ioctl(dummy, SIOCGIFADDR, &req, sizeof(req))) {
+#endif
+      sclose(dummy);
+      return NULL;
+    }
+    else {
+      struct in_addr in;
+
+      struct sockaddr_in *s = (struct sockaddr_in *)&req.ifr_dstaddr;
+      memcpy(&in, &(s->sin_addr.s_addr), sizeof(in));
+#if defined(HAVE_INET_NTOA_R)
+      ip = inet_ntoa_r(in,buf,buf_size);
+#else
+      ip = strncpy(buf,inet_ntoa(in),buf_size);
+      ip[buf_size - 1] = 0;
+#endif
+    }
+    sclose(dummy);
+  }
+  return ip;
+}
+
+/* -- end of if2ip() -- */
+#else
+#define if2ip(x) NULL
+#endif
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 33 - 0
Source/CTest/Curl/if2ip.h

@@ -0,0 +1,33 @@
+#ifndef __IF2IP_H
+#define __IF2IP_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+#include "setup.h"
+
+#if ! defined(WIN32) && ! defined(__BEOS__) && !defined(__CYGWIN32__)
+extern char *Curl_if2ip(char *interface, char *buf, int buf_size);
+#else
+#define Curl_if2ip(a,b,c) NULL
+#endif
+
+#endif

+ 9 - 0
Source/CTest/Curl/inet_ntoa_r.h

@@ -0,0 +1,9 @@
+#ifndef __INET_NTOA_R_H
+#define __INET_NTOA_R_H
+/*
+ * My solaris 5.6 system running gcc 2.8.1 does *not* have this prototype
+ * in any system include file! Isn't that weird?
+ */
+char *inet_ntoa_r(const struct in_addr in, char *buffer, int buflen);
+
+#endif

+ 401 - 0
Source/CTest/Curl/krb4.c

@@ -0,0 +1,401 @@
+/* This source code was modified by Martin Hedenfalk <[email protected]> for
+ * use in Curl. His latest changes were done 2000-09-18.
+ *
+ * It has since been patched away like a madman by Daniel Stenberg
+ * <[email protected]> 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) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * 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 "setup.h"
+
+#ifdef KRB4
+
+#include "security.h"
+#include "base64.h"
+#include <stdlib.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <string.h>
+#include <krb.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for getpid() */
+#endif
+
+#include "ftp.h"
+#include "sendf.h"
+
+#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
+#include "inet_ntoa_r.h"
+#endif
+
+/* The last #include file should be: */
+#ifdef MALLOCDEBUG
+#include "memdebug.h"
+#endif
+
+#define LOCAL_ADDR (&conn->local_addr)
+#define REMOTE_ADDR (&conn->serv_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){
+        syslog(LOG_ERR, "krb4_decode: %s", 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, void *from, int length, int level, void **to,
+            struct connectdata *conn)
+{
+    struct krb4_data *d = app_data;
+    *to = malloc(length + 31);
+    if(level == prot_safe)
+        return krb_mk_safe(from, *to, length, &d->key, 
+                           (struct sockaddr_in *)LOCAL_ADDR,
+                           (struct sockaddr_in *)REMOTE_ADDR);
+    else if(level == prot_private)
+        return krb_mk_priv(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;
+}
+
+static int
+krb4_auth(void *app_data, struct connectdata *conn)
+{
+  int ret;
+  char *p;
+  int len;
+  KTEXT_ST adat;
+  MSG_DAT msg_data;
+  int checksum;
+  u_int32_t cs;
+  struct krb4_data *d = app_data;
+  char *host = conn->hostaddr->h_name;
+  ssize_t nread;
+  int l = sizeof(conn->local_addr);
+  struct SessionHandle *data = conn->data;
+
+  if(getsockname(conn->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) {
+    Curl_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)
+      Curl_infof(data, "Can't get address for realm %s\n",
+                 krb_realmofhost(host));
+    else {
+      if (natAddr.s_addr != localaddr->sin_addr.s_addr) {
+#ifdef HAVE_INET_NTOA_R
+        char ntoa_buf[64];
+        char *ip = (char *)inet_ntoa_r(natAddr, ntoa_buf, sizeof(ntoa_buf));
+#else
+        char *ip = (char *)inet_ntoa(natAddr);
+#endif
+        Curl_infof(data, "Using NAT IP address (%s) for kerberos 4\n", ip);
+        localaddr->sin_addr = natAddr;
+      }
+    }
+  }
+#endif
+
+  if(Curl_base64_encode(adat.dat, adat.length, &p) < 0) {
+    Curl_failf(data, "Out of memory base64-encoding");
+    return AUTH_CONTINUE;
+  }
+
+  if(Curl_ftpsendf(conn, "ADAT %s", p))
+    return -2;
+
+  nread = Curl_GetFTPResponse(data->state.buffer, conn, NULL);
+  if(nread < 0)
+    return -1;
+  free(p);
+
+  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;
+  len = Curl_base64_decode(p, adat.dat);
+  if(len < 0) {
+    Curl_failf(data, "Failed to decode base64 from server");
+    return AUTH_ERROR;
+  }
+  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
+};
+
+void Curl_krb_kauth(struct connectdata *conn)
+{
+  des_cblock key;
+  des_key_schedule schedule;
+  KTEXT_ST tkt, tktcopy;
+  char *name;
+  char *p;
+  char passwd[100];
+  int tmp;
+  ssize_t nread;
+        
+  int save;
+
+  save = Curl_set_command_prot(conn, prot_private);
+
+  if(Curl_ftpsendf(conn, "SITE KAUTH %s", conn->data->state.user))
+    return;
+
+  nread = Curl_GetFTPResponse(conn->data->state.buffer,
+                              conn, NULL);
+  if(nread < 0)
+    return /*CURLE_OPERATION_TIMEOUTED*/;
+
+  if(conn->data->state.buffer[0] != '3'){
+    Curl_set_command_prot(conn, save);
+    return;
+  }
+
+  p = strstr(conn->data->state.buffer, "T=");
+  if(!p) {
+    Curl_failf(conn->data, "Bad reply from server");
+    Curl_set_command_prot(conn, save);
+    return;
+  }
+
+  p += 2;
+  tmp = Curl_base64_decode(p, &tkt.dat);
+  if(tmp < 0) {
+    Curl_failf(conn->data, "Failed to decode base64 in reply.\n");
+    Curl_set_command_prot(conn, save);
+    return;
+  }
+  tkt.length = tmp;
+  tktcopy.length = tkt.length;
+    
+  p = strstr(conn->data->state.buffer, "P=");
+  if(!p) {
+    Curl_failf(conn->data, "Bad reply from server");
+    Curl_set_command_prot(conn, save);
+    return;
+  }
+  name = p + 2;
+  for(; *p && *p != ' ' && *p != '\r' && *p != '\n'; p++);
+  *p = 0;
+
+  des_string_to_key (conn->data->state.passwd, &key);
+  des_key_sched(&key, schedule);
+    
+  des_pcbc_encrypt((des_cblock*)tkt.dat, (des_cblock*)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->hostaddr->h_name),
+                       &key);
+    des_key_sched (&key, schedule);
+    des_pcbc_encrypt((des_cblock*)tkt.dat, (des_cblock*)tktcopy.dat,
+                     tkt.length,
+                     schedule, &key, DES_DECRYPT);
+  }
+  memset(key, 0, sizeof(key));
+  memset(schedule, 0, sizeof(schedule));
+  memset(passwd, 0, sizeof(passwd));
+  if(Curl_base64_encode(tktcopy.dat, tktcopy.length, &p) < 0) {
+    failf(conn->data, "Out of memory base64-encoding.");
+    Curl_set_command_prot(conn, save);
+    return;
+  }
+  memset (tktcopy.dat, 0, tktcopy.length);
+
+  if(Curl_ftpsendf(conn, "SITE KAUTH %s %s", name, p))
+    return;
+
+  nread = Curl_GetFTPResponse(conn->data->state.buffer,
+                              conn, NULL);
+  if(nread < 0)
+    return /*CURLE_OPERATION_TIMEOUTED*/;
+  free(p);
+  Curl_set_command_prot(conn, save);
+}
+
+#endif /* KRB4 */
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 27 - 0
Source/CTest/Curl/krb4.h

@@ -0,0 +1,27 @@
+#ifndef __KRB4_H
+#define __KRB4_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+void Curl_krb_kauth(struct connectdata *conn);
+
+#endif

+ 231 - 0
Source/CTest/Curl/ldap.c

@@ -0,0 +1,231 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2001, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+/* -- WIN32 approved -- */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#if defined(WIN32) && !defined(__GNUC__)
+#else
+# ifdef HAVE_UNISTD_H
+#  include <unistd.h>
+# endif
+# ifdef HAVE_DLFCN_H
+#  include <dlfcn.h>
+# endif
+#endif
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "sendf.h"
+#include "escape.h"
+#include "transfer.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+
+#define DYNA_GET_FUNCTION(type, fnc) \
+  (fnc) = (type)DynaGetFunction(#fnc); \
+  if ((fnc) == NULL) { \
+    return CURLE_FUNCTION_NOT_FOUND; \
+  } \
+
+/***********************************************************************
+ */
+static void *libldap = NULL;
+static void *liblber = NULL;
+
+static void DynaOpen(void)
+{
+#if defined(HAVE_DLOPEN) || defined(HAVE_LIBDL)
+  if (libldap == NULL) {
+    /*
+     * libldap.so should be able to resolve its dependency on
+     * liblber.so automatically, but since it does not we will
+     * handle it here by opening liblber.so as global.
+     */
+    dlopen("liblber.so",
+#ifdef RTLD_LAZY_GLOBAL /* It turns out some systems use this: */
+           RTLD_LAZY_GLOBAL
+#else
+#ifdef RTLD_GLOBAL
+           RTLD_LAZY | RTLD_GLOBAL
+#else
+           /* and some systems don't have the RTLD_GLOBAL symbol */
+           RTLD_LAZY
+#endif
+#endif
+           );
+    libldap = dlopen("libldap.so", RTLD_LAZY);
+  }
+#endif
+}
+
+static void DynaClose(void)
+{
+#if defined(HAVE_DLOPEN) || defined(HAVE_LIBDL)
+  if (libldap) {
+    dlclose(libldap);
+    libldap=NULL;
+  }
+  if (liblber) {
+    dlclose(liblber);
+    liblber=NULL;
+  }
+#endif
+}
+
+static void * DynaGetFunction(const char *name)
+{
+  void *func = NULL;
+
+#if defined(HAVE_DLOPEN) || defined(HAVE_LIBDL)
+  if (libldap) {
+    func = dlsym(libldap, name);
+  }
+#endif
+  
+  return func;
+}
+
+static int WriteProc(void *param, char *text, int len)
+{
+  struct SessionHandle *data = (struct SessionHandle *)param;
+  len = 0; /* prevent compiler warning */
+  Curl_client_write(data, CLIENTWRITE_BODY, text, 0);
+  return 0;
+}
+
+/***********************************************************************
+ */
+CURLcode Curl_ldap(struct connectdata *conn)
+{
+  CURLcode status = CURLE_OK;
+  int rc;
+  void *(*ldap_open)(char *, int);
+  int (*ldap_simple_bind_s)(void *, char *, char *);
+  int (*ldap_unbind_s)(void *);
+  int (*ldap_url_search_s)(void *, char *, int, void **);
+  void *(*ldap_first_entry)(void *, void *);
+  void *(*ldap_next_entry)(void *, void *);
+  char *(*ldap_err2string)(int);
+  int (*ldap_entry2text)(void *, char *, void *, void *, char **, char **, int (*)(void *, char *, int), void *, char *, int, unsigned long);
+  int (*ldap_entry2html)(void *, char *, void *, void *, char **, char **, int (*)(void *, char *, int), void *, char *, int, unsigned long, char *, char *);
+  void *server;
+  void *result;
+  void *entryIterator;
+
+  int ldaptext;
+  struct SessionHandle *data=conn->data;
+  
+  infof(data, "LDAP: %s %s\n", data->change.url);
+
+  DynaOpen();
+  if (libldap == NULL) {
+    failf(data, "The needed LDAP library/libraries couldn't be opened");
+    return CURLE_LIBRARY_NOT_FOUND;
+  }
+
+  ldaptext = data->set.ftp_ascii; /* This is a dirty hack */
+  
+  /* The types are needed because ANSI C distinguishes between
+   * pointer-to-object (data) and pointer-to-function.
+   */
+  DYNA_GET_FUNCTION(void *(*)(char *, int), ldap_open);
+  DYNA_GET_FUNCTION(int (*)(void *, char *, char *), ldap_simple_bind_s);
+  DYNA_GET_FUNCTION(int (*)(void *), ldap_unbind_s);
+  DYNA_GET_FUNCTION(int (*)(void *, char *, int, void **), ldap_url_search_s);
+  DYNA_GET_FUNCTION(void *(*)(void *, void *), ldap_first_entry);
+  DYNA_GET_FUNCTION(void *(*)(void *, void *), ldap_next_entry);
+  DYNA_GET_FUNCTION(char *(*)(int), ldap_err2string);
+  DYNA_GET_FUNCTION(int (*)(void *, char *, void *, void *, char **, char **, int (*)(void *, char *, int), void *, char *, int, unsigned long), ldap_entry2text);
+  DYNA_GET_FUNCTION(int (*)(void *, char *, void *, void *, char **, char **, int (*)(void *, char *, int), void *, char *, int, unsigned long, char *, char *), ldap_entry2html);
+  
+  server = ldap_open(conn->hostname, conn->port);
+  if (server == NULL) {
+    failf(data, "LDAP: Cannot connect to %s:%d",
+          conn->hostname, conn->port);
+    status = CURLE_COULDNT_CONNECT;
+  } else {
+    rc = ldap_simple_bind_s(server,
+                            conn->bits.user_passwd?data->state.user:NULL,
+                            conn->bits.user_passwd?data->state.passwd:NULL);
+    if (rc != 0) {
+      failf(data, "LDAP: %s", ldap_err2string(rc));
+      status = CURLE_LDAP_CANNOT_BIND;
+    } else {
+      rc = ldap_url_search_s(server, data->change.url, 0, &result);
+      if (rc != 0) {
+        failf(data, "LDAP: %s", ldap_err2string(rc));
+        status = CURLE_LDAP_SEARCH_FAILED;
+      } else {
+        for (entryIterator = ldap_first_entry(server, result);
+             entryIterator;
+             entryIterator = ldap_next_entry(server, entryIterator))
+          {
+            if (ldaptext) {
+              rc = ldap_entry2text(server, NULL, entryIterator, NULL,
+                                   NULL, NULL, WriteProc, data,
+                                   (char *)"", 0, 0);
+              if (rc != 0) {
+                failf(data, "LDAP: %s", ldap_err2string(rc));
+                status = CURLE_LDAP_SEARCH_FAILED;
+              }
+            } else {
+              rc = ldap_entry2html(server, NULL, entryIterator, NULL,
+                                   NULL, NULL, WriteProc, data,
+                                   (char *)"", 0, 0, NULL, NULL);
+              if (rc != 0) {
+                failf(data, "LDAP: %s", ldap_err2string(rc));
+                status = CURLE_LDAP_SEARCH_FAILED;
+              }
+            }
+          }
+      }
+      ldap_unbind_s(server);
+    }
+  }
+  DynaClose();
+
+  /* no data to transfer */
+  Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+  
+  return status;
+}
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 29 - 0
Source/CTest/Curl/ldap.h

@@ -0,0 +1,29 @@
+#ifndef __LDAP_H
+#define __LDAP_H
+
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+CURLcode Curl_ldap(struct connectdata *conn);
+CURLcode Curl_ldap_done(struct connectdata *conn);
+
+#endif /* __LDAP_H */

+ 168 - 0
Source/CTest/Curl/llist.c

@@ -0,0 +1,168 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2002, Daniel Stenberg, <[email protected]>, et al
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "llist.h"
+
+#ifdef MALLOCDEBUG
+/* this must be the last include file */
+#include "memdebug.h"
+#endif
+void 
+curl_llist_init(curl_llist *l, curl_llist_dtor dtor)
+{
+  l->size = 0;
+  l->dtor = dtor;
+  l->head = NULL;
+  l->tail = NULL;
+}
+
+curl_llist *
+curl_llist_alloc(curl_llist_dtor dtor)
+{
+  curl_llist *list;
+
+  list = (curl_llist *)malloc(sizeof(curl_llist));
+  if(NULL == list)
+    return NULL;
+
+  curl_llist_init(list, dtor);
+
+  return list;
+}
+
+int
+curl_llist_insert_next(curl_llist *list, curl_llist_element *e, const void *p)
+{
+  curl_llist_element  *ne;
+
+  ne = (curl_llist_element *) malloc(sizeof(curl_llist_element));
+  ne->ptr = (void *) p;
+  if (list->size == 0) {
+    list->head = ne;
+    list->head->prev = NULL;
+    list->head->next = NULL;
+    list->tail = ne;
+  } else {
+    ne->next = e->next;
+    ne->prev = e;
+    if (e->next) {
+      e->next->prev = ne;
+    } else {
+      list->tail = ne;
+    }
+    e->next = ne;
+  }
+
+  ++list->size;
+
+  return 1;
+}
+
+int 
+curl_llist_insert_prev(curl_llist *list, curl_llist_element *e, const void *p)
+{
+  curl_llist_element *ne;
+
+  ne = (curl_llist_element *) malloc(sizeof(curl_llist_element));
+  ne->ptr = (void *) p;
+  if (list->size == 0) {
+    list->head = ne;
+    list->head->prev = NULL;
+    list->head->next = NULL;
+    list->tail = ne;
+  } else {
+    ne->next = e;
+    ne->prev = e->prev;
+    if (e->prev)
+      e->prev->next = ne;
+    else
+      list->head = ne;
+    e->prev = ne;
+  }
+
+  ++list->size;
+
+  return 1;
+}
+
+int 
+curl_llist_remove(curl_llist *list, 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);
+  free(e);
+  --list->size;
+
+  return 1;
+}
+
+int 
+curl_llist_remove_next(curl_llist *list, curl_llist_element *e, void *user)
+{
+  return curl_llist_remove(list, e->next, user);
+}
+
+int 
+curl_llist_remove_prev(curl_llist *list, curl_llist_element *e, void *user)
+{
+  return curl_llist_remove(list, e->prev, user);
+}
+
+size_t 
+curl_llist_count(curl_llist *list)
+{
+  return list->size;
+}
+
+void 
+curl_llist_destroy(curl_llist *list, void *user)
+{
+  while (list->size > 0) {
+    curl_llist_remove(list, CURL_LLIST_TAIL(list), user);
+  }
+
+  free(list);
+  list = NULL;
+}

+ 64 - 0
Source/CTest/Curl/llist.h

@@ -0,0 +1,64 @@
+#ifndef __LLIST_H
+#define __LLIST_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2001, Daniel Stenberg, <[email protected]>, et al
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+#include <stddef.h>
+
+typedef void (*curl_llist_dtor)(void *, void *);
+
+typedef struct _curl_llist_element {
+  void *ptr;
+
+  struct _curl_llist_element *prev;
+  struct _curl_llist_element *next;
+} curl_llist_element;
+
+typedef struct _curl_llist {
+  curl_llist_element *head;
+  curl_llist_element *tail;
+
+  curl_llist_dtor dtor;
+
+  size_t size;
+} curl_llist;
+
+void curl_llist_init(curl_llist *, curl_llist_dtor);
+curl_llist *curl_llist_alloc(curl_llist_dtor);
+int curl_llist_insert_next(curl_llist *, curl_llist_element *, const void *);
+int curl_llist_insert_prev(curl_llist *, curl_llist_element *, const void *);
+int curl_llist_remove(curl_llist *, curl_llist_element *, void *);
+int curl_llist_remove_next(curl_llist *, curl_llist_element *, void *);
+size_t curl_llist_count(curl_llist *);
+void curl_llist_destroy(curl_llist *, void *);
+
+#define CURL_LLIST_HEAD(__l) ((__l)->head)
+#define CURL_LLIST_TAIL(__l) ((__l)->tail)
+#define CURL_LLIST_NEXT(__e) ((__e)->next)
+#define CURL_LLIST_PREV(__e) ((__e)->prev)
+#define CURL_LLIST_VALP(__e) ((__e)->ptr)
+#define CURL_LLIST_IS_TAIL(__e) ((__e)->next ? 0 : 1)
+#define CURL_LLIST_IS_HEAD(__e) ((__e)->prev ? 0 : 1)
+
+#endif

+ 221 - 0
Source/CTest/Curl/memdebug.c

@@ -0,0 +1,221 @@
+#ifdef MALLOCDEBUG
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+#include <curl/curl.h>
+
+#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
+#include <winsock.h>
+#else /* some kind of unix */
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#endif
+
+#define _MPRINTF_REPLACE
+#include <curl/mprintf.h>
+#include "urldata.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/* DONT include memdebug.h here! */
+
+struct memdebug {
+  int size;
+  char mem[1];
+};
+
+/*
+ * 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!
+ */
+
+FILE *logfile;
+
+/* this sets the log file name */
+void curl_memdebug(const char *logname)
+{
+  if(logname)
+    logfile = fopen(logname, "w");
+  else
+    logfile = stderr;
+}
+
+
+void *curl_domalloc(size_t wantedsize, int line, const char *source)
+{
+  struct memdebug *mem;
+  size_t size;
+
+  /* alloc at least 64 bytes */
+  size = sizeof(struct memdebug)+wantedsize;
+
+  mem=(struct memdebug *)(malloc)(size);
+  if(mem) {
+    /* fill memory with junk */
+    memset(mem->mem, 0xA5, wantedsize);
+    mem->size = wantedsize;
+  }
+
+  if(logfile && source)
+    fprintf(logfile, "MEM %s:%d malloc(%d) = %p\n",
+            source, line, wantedsize, mem->mem);
+  return mem->mem;
+}
+
+char *curl_dostrdup(const char *str, int line, const char *source)
+{
+  char *mem;
+  size_t len;
+  
+  if(NULL ==str) {
+    fprintf(stderr, "ILLEGAL strdup() on NULL at %s:%d\n",
+            source, line);
+    exit(2);
+  }
+
+  len=strlen(str)+1;
+
+  mem=curl_domalloc(len, 0, NULL); /* NULL prevents logging */
+  memcpy(mem, str, len);
+
+  if(logfile)
+    fprintf(logfile, "MEM %s:%d strdup(%p) (%d) = %p\n",
+            source, line, str, len, mem);
+
+  return mem;
+}
+
+void *curl_dorealloc(void *ptr, size_t wantedsize,
+                     int line, const char *source)
+{
+  struct memdebug *mem;
+
+  size_t size = sizeof(struct memdebug)+wantedsize;
+
+  mem = (struct memdebug *)((char *)ptr - offsetof(struct memdebug, mem));
+
+  mem=(struct memdebug *)(realloc)(mem, size);
+  if(logfile)
+    fprintf(logfile, "MEM %s:%d realloc(%p, %d) = %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;
+
+  if(NULL == ptr) {
+    fprintf(stderr, "ILLEGAL free() on NULL at %s:%d\n",
+            source, line);
+    exit(2);
+  }
+  mem = (struct memdebug *)((char *)ptr - offsetof(struct memdebug, mem));
+
+  /* destroy  */
+  memset(mem->mem, 0x13, mem->size);
+  
+  /* free for real */
+  (free)(mem);
+
+  if(logfile)
+    fprintf(logfile, "MEM %s:%d free(%p)\n", source, line, ptr);
+}
+
+int curl_socket(int domain, int type, int protocol, int line, char *source)
+{
+  int sockfd=(socket)(domain, type, protocol);
+  if(logfile)
+    fprintf(logfile, "FD %s:%d socket() = %d\n",
+            source, line, sockfd);
+  return sockfd;
+}
+
+int curl_accept(int s, struct sockaddr *addr, socklen_t *addrlen,
+                int line, const char *source)
+{
+  int sockfd=(accept)(s, addr, addrlen);
+  if(logfile)
+    fprintf(logfile, "FD %s:%d accept() = %d\n",
+            source, line, sockfd);
+  return sockfd;
+}
+
+/* this is our own defined way to close sockets on *ALL* platforms */
+int curl_sclose(int sockfd, int line, char *source)
+{
+  int res=sclose(sockfd);
+  if(logfile)
+    fprintf(logfile, "FD %s:%d sclose(%d)\n",
+            source, line, sockfd);
+  return res;
+}
+
+FILE *curl_fopen(const char *file, const char *mode,
+                 int line, const char *source)
+{
+  FILE *res=(fopen)(file, mode);
+  if(logfile)
+    fprintf(logfile, "FILE %s:%d fopen(\"%s\") = %p\n",
+            source, line, file, res);
+  return res;
+}
+
+int curl_fclose(FILE *file, int line, const char *source)
+{
+  int res=(fclose)(file);
+  if(logfile)
+    fprintf(logfile, "FILE %s:%d fclose(%p)\n",
+            source, line, file);
+  return res;
+}
+#else
+#ifdef VMS
+int VOID_VAR_MEMDEBUG;  
+#endif
+#endif /* MALLOCDEBUG */
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 61 - 0
Source/CTest/Curl/memdebug.h

@@ -0,0 +1,61 @@
+#ifdef MALLOCDEBUG
+
+#include "setup.h"
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+
+extern FILE *logfile;
+
+/* memory functions */
+void *curl_domalloc(size_t size, int line, const char *source);
+void *curl_dorealloc(void *ptr, size_t size, int line, const char *source);
+void curl_dofree(void *ptr, int line, const char *source);
+char *curl_dostrdup(const char *str, int line, const char *source);
+void curl_memdebug(const char *logname);
+
+/* file descriptor manipulators */
+int curl_socket(int domain, int type, int protocol, int, const char *);
+int curl_sclose(int sockfd, int, const char *source);
+int curl_accept(int s, struct sockaddr *addr, socklen_t *addrlen,
+                int line, const char *source);
+
+/* FILE functions */
+FILE *curl_fopen(const char *file, const char *mode, int line,
+                 const char *source);
+int curl_fclose(FILE *file, int line, const char *source);
+
+/* Set this symbol on the command-line, recompile all lib-sources */
+#define strdup(ptr) curl_dostrdup(ptr, __LINE__, __FILE__)
+#define malloc(size) curl_domalloc(size, __LINE__, __FILE__)
+#define realloc(ptr,size) curl_dorealloc(ptr, size, __LINE__, __FILE__)
+#define free(ptr) curl_dofree(ptr, __LINE__, __FILE__)
+
+#define socket(domain,type,protocol)\
+ curl_socket(domain,type,protocol,__LINE__,__FILE__)
+#define accept(sock,addr,len)\
+ curl_accept(sock,addr,len,__LINE__,__FILE__)
+
+#define getaddrinfo(host,serv,hint,res) \
+  curl_getaddrinfo(host,serv,hint,res,__LINE__,__FILE__)
+#define freeaddrinfo(data) \
+  curl_freeaddrinfo(data,__LINE__,__FILE__)
+
+/* sclose is probably already defined, redefine it! */
+#undef sclose
+#define sclose(sockfd) curl_sclose(sockfd,__LINE__,__FILE__)
+
+#undef fopen
+#define fopen(file,mode) curl_fopen(file,mode,__LINE__,__FILE__)
+#define fclose(file) curl_fclose(file,__LINE__,__FILE__)
+
+#endif

+ 1181 - 0
Source/CTest/Curl/mprintf.c

@@ -0,0 +1,1181 @@
+/****************************************************************************
+ *
+ * $Id$
+ *
+ *************************************************************************
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
+ * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
+ *
+ * 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 "setup.h"
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <string.h>
+
+#ifndef SIZEOF_LONG_LONG
+/* prevents warnings on picky compilers */
+#define SIZEOF_LONG_LONG 0
+#endif
+#ifndef SIZEOF_LONG_DOUBLE
+#define SIZEOF_LONG_DOUBLE 0
+#endif
+
+
+/* The last #include file should be: */
+#ifdef MALLOCDEBUG
+#include "memdebug.h"
+#endif
+
+#define BUFFSIZE 256 /* buffer for long-to-str and float-to-str calcs */
+#define MAX_PARAMETERS 128 /* lame static limit */
+
+#undef TRUE
+#undef FALSE
+#undef BOOL
+#ifdef __cplusplus
+# define TRUE true
+# define FALSE false
+# define BOOL bool
+#else
+# define TRUE  ((char)(1 == 1))
+# define FALSE ((char)(0 == 1))
+# define BOOL char
+#endif
+
+
+/* Lower-case digits.  */
+static const char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+
+/* Upper-case digits.  */
+static const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+#define OUTCHAR(x) done+=(stream(x, (FILE *)data)==-1?0:1)
+
+/* 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;
+
+/* convertion 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;
+  int width;     /* width OR width parameter number */
+  int precision; /* precision OR precision parameter number */
+  union {
+    char *str;
+    void *ptr;
+    long num;
+#if SIZEOF_LONG_LONG /* if this is non-zero */
+    long long lnum;
+#endif
+    double dnum;
+#if SIZEOF_LONG_DOUBLE
+    long double ldnum;
+#endif
+  } 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 curl_msprintf(char *buffer, const char *format, ...);
+
+static int dprintf_DollarString(char *input, char **end)
+{
+  int number=0;
+  while(isdigit((int)*input)) {
+    number *= 10;
+    number += *input-'0';
+    input++;
+  }
+  if(number && ('$'==*input++)) {
+    *end = input;
+    return number;
+  }
+  return 0;
+}
+
+static BOOL 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':
+    return TRUE;
+  default:
+    return FALSE;
+  }
+}
+
+#ifdef DPRINTF_DEBUG2
+int dprintf_Pass1Report(va_stack_t *vto, int max)
+{
+  int i;
+  char buffer[128];
+  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_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 int dprintf_Pass1(char *format, va_stack_t *vto, char **endpos, va_list arglist)
+{
+  char *fmt = format;
+  int param_num = 0;
+  int this_param;
+  int width;
+  int precision;
+  int flags;
+  int max_param=0;
+  int 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':
+          if (sizeof(size_t) > sizeof(unsigned long int))
+            flags |= FLAGS_LONGLONG;
+          if (sizeof(size_t) > sizeof(unsigned int))
+            flags |= FLAGS_LONG;
+          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': case 'E':
+        vto[i].type = FORMAT_DOUBLE;
+        flags |= FLAGS_FLOATE| (('E' == *fmt)?FLAGS_UPPER:0);
+        break;
+      case 'g': case 'G':
+        vto[i].type = FORMAT_DOUBLE;
+        flags |= FLAGS_FLOATG| (('G' == *fmt)?FLAGS_UPPER:0);
+        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 = 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:
+#if SIZEOF_LONG_LONG
+        if(vto[i].flags & FLAGS_LONGLONG)
+          vto[i].data.lnum = va_arg(arglist, long long);
+        else
+#endif
+          if(vto[i].flags & FLAGS_LONG)
+            vto[i].data.num = va_arg(arglist, long);
+        else
+          vto[i].data.num = va_arg(arglist, int);
+        break;
+        
+      case FORMAT_DOUBLE:
+#if SIZEOF_LONG_DOUBLE
+        if(vto[i].flags & FLAGS_LONG)
+          vto[i].data.ldnum = va_arg(arglist, long double);
+        else
+#endif
+          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 first argument */
+             int (*stream)(int, FILE *), /* function pointer called for each
+                                            output character */
+             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.  */
+  register size_t 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((char *)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.  */
+    char alt;
+    
+    /* Width of a field.  */
+    register long width;
+    /* Precision of a field.  */
+    long prec;
+    
+    /* Decimal integer is negative.  */
+    char is_neg;
+    
+    /* Base of a number to be written.  */
+    long base;
+
+    /* Integral values to be written.  */
+#if SIZEOF_LONG_LONG
+    unsigned long long num;
+#else
+    unsigned long num;
+#endif
+    long 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 imediately
+       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 = vto[p->width].data.num;
+    else
+      width = p->width;
+
+    /* pick up the specified precision */
+    if(p->flags & FLAGS_PRECPARAM)
+      prec = vto[p->precision].data.num;
+    else if(p->flags & FLAGS_PREC)
+      prec = p->precision;
+    else
+      prec = -1;
+
+    alt = p->flags & FLAGS_ALT;
+    
+    switch (p->type) {
+    case FORMAT_INT:
+      num = p->data.num;
+      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;
+
+#if SIZEOF_LONG_LONG
+      if(p->flags & FLAGS_LONGLONG) {
+         /* long long */
+        is_neg = p->data.lnum < 0;
+        num = is_neg ? (- p->data.lnum) : p->data.lnum;
+      }
+      else
+#endif
+      {
+        signed_num = (long) num;
+      
+        is_neg = signed_num < 0;
+        num = is_neg ? (- signed_num) : signed_num;
+      }
+      goto number;
+      
+    unsigned_number:;
+      /* Unsigned number of base BASE.  */
+      is_neg = 0;
+      
+    number:;
+      /* Number of base BASE.  */
+      {
+        char *workend = &work[sizeof(work) - 1];
+        register 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 -= workend - w;
+        prec -= workend - w;
+        
+        if (alt && base == 8 && prec <= 0) {
+          *w-- = '0';
+          --width;
+        }
+        
+        if (prec > 0) {
+          width -= prec;
+          while (prec-- > 0)
+            *w-- = '0';
+        }
+        
+        if (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 (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 char null[] = "(nil)";
+        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 = (char *)"";
+            len = 0;
+          }
+        }
+        else
+          len = strlen(str);
+        
+        if (prec != -1 && (size_t) prec < len)
+          len = prec;
+        width -= 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;
+          alt = 1;
+          num = (unsigned long) ptr;
+          is_neg = 0;
+          goto number;
+        }
+        else {
+          /* Write "(nil)" for a nil pointer.  */
+          static char strnil[] = "(nil)";
+          register char *point;
+          
+          width -= 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;
+        
+        width = -1;
+        if (p->flags & FLAGS_WIDTH)
+          width = p->width;
+        else if (p->flags & FLAGS_WIDTHPARAM)
+          width = vto[p->width].data.num;
+
+        prec = -1;
+        if (p->flags & FLAGS_PREC)
+          prec = p->precision;
+        else if (p->flags & FLAGS_PRECPARAM)
+          prec = vto[p->precision].data.num;
+
+        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 */
+          fptr += curl_msprintf(fptr, "%d", width);
+        }
+        if(prec >= 0) {
+          /* RECURSIVE USAGE */
+          fptr += curl_msprintf(fptr, ".%d", prec);
+        }
+        if (p->flags & FLAGS_LONG)
+          strcat(fptr, "l");
+
+        if (p->flags & FLAGS_FLOATE)
+          strcat(fptr, p->flags&FLAGS_UPPER?"E":"e");
+        else if (p->flags & FLAGS_FLOATG)
+          strcat(fptr, (p->flags & FLAGS_UPPER) ? "G" : "g");
+        else
+          strcat(fptr, "f");
+
+        /* NOTE NOTE NOTE!! Not all sprintf() implementations returns number
+           of output characters */
+#if SIZEOF_LONG_DOUBLE
+        if (p->flags & FLAGS_LONG)
+          /* This is for support of the 'long double' type */
+          (sprintf)(work, formatbuf, p->data.ldnum);
+        else
+#endif
+          (sprintf)(work, formatbuf, p->data.dnum);
+
+        for(fptr=work; *fptr; fptr++)
+          OUTCHAR(*fptr);
+      }
+      break;
+
+    case FORMAT_INTPTR:
+      /* Answer the count of characters written.  */
+#if SIZEOF_LONG_LONG
+      if (p->flags & FLAGS_LONGLONG)
+        *(long long int *) p->data.ptr = done;
+      else
+#endif
+        if (p->flags & FLAGS_LONG)
+          *(long int *) p->data.ptr = done;
+      else if (!(p->flags & FLAGS_SHORT))
+        *(int *) p->data.ptr = done;
+      else
+        *(short int *) p->data.ptr = 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;
+ 
+  if(infop->length < infop->max) {
+    /* only do this if we haven't reached max length yet */
+    infop->buffer[0] = (char)output; /* store */
+    infop->buffer++; /* increase pointer */
+    infop->length++; /* we are now one byte larger */
+    return output; /* fputc() returns like this on success */
+  }
+  return -1;
+}
+
+int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...)
+{
+  va_list ap_save; /* argument pointer */
+  int retcode;
+  struct nsprintf info;
+
+  info.buffer = buffer;
+  info.length = 0;
+  info.max = maxlength;
+
+  va_start(ap_save, format);
+  retcode = dprintf_formatf(&info, addbyter, format, ap_save);
+  va_end(ap_save);
+  info.buffer[0] = 0; /* we terminate this with a zero byte */
+
+  /* we could even return things like */
+  
+  return retcode;
+}
+
+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);
+  info.buffer[0] = 0; /* we terminate this with a zero byte */
+  return retcode;
+}
+
+
+/* fputc() look-alike */
+static int alloc_addbyter(int output, FILE *data)
+{
+  struct asprintf *infop=(struct asprintf *)data;
+ 
+  if(!infop->buffer) {
+    infop->buffer=(char *)malloc(32);
+    if(!infop->buffer)
+      return -1; /* fail */
+    infop->alloc = 32;
+    infop->len =0;
+  }
+  else if(infop->len+1 >= infop->alloc) {
+    char *newptr;
+
+    newptr = (char *)realloc(infop->buffer, infop->alloc*2);
+
+    if(!newptr) {
+      return -1;
+    }
+    infop->buffer = newptr;
+    infop->alloc *= 2;
+  }
+
+  infop->buffer[ infop->len ] = output;
+
+  infop->len++;
+
+  return output; /* 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;
+
+  va_start(ap_save, format);
+  retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save);
+  va_end(ap_save);
+  if(info.len) {
+    info.buffer[info.len] = 0; /* we terminate this with a zero byte */
+    return info.buffer;
+  }
+  else
+    return NULL;
+}
+
+char *curl_mvaprintf(const char *format, va_list ap_save)
+{
+  int retcode;
+  struct asprintf info;
+
+  info.buffer = NULL;
+  info.len = 0;
+  info.alloc = 0;
+
+  retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save);
+  info.buffer[info.len] = 0; /* we terminate this with a zero byte */
+  if(info.len) {
+    info.buffer[info.len] = 0; /* we terminate this with a zero byte */
+    return info.buffer;
+  }
+  else
+    return NULL;
+}
+
+static int storebuffer(int output, FILE *data)
+{
+  char **buffer = (char **)data;
+  **buffer = (char)output;
+  (*buffer)++;
+  return output; /* 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;
+}
+
+#ifndef WIN32 /* not needed on win32 */
+extern int fputc(int, FILE *);
+#endif
+
+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);
+}
+
+#ifdef DPRINTF_DEBUG
+int main()
+{
+  char buffer[129];
+  char *ptr;
+#if SIZEOF_LONG_LONG>0
+  long long hullo;
+  dprintf("%3$12s %1$s %2$qd %4$d\n", "daniel", hullo, "stenberg", 65);
+#endif
+
+  mprintf("%3d %5d\n", 10, 1998);
+  
+  ptr=maprintf("test this then baby %s%s%s%s%s%s %d %d %d loser baby get a hit in yer face now!", "", "pretty long string pretty long string pretty long string pretty long string pretty long string", "/", "/", "/", "pretty long string", 1998, 1999, 2001);
+
+  puts(ptr);
+
+  memset(ptr, 55, strlen(ptr)+1);
+
+  free(ptr);
+
+#if 1
+  mprintf(buffer, "%s %s %d", "daniel", "stenberg", 19988);
+  puts(buffer);
+
+  mfprintf(stderr, "%s %#08x\n", "dummy", 65);
+
+  printf("%s %#08x\n", "dummy", 65);
+  {
+    double tryout = 3.14156592;
+    mprintf(buffer, "%.2g %G %f %e %E", tryout, tryout, tryout, tryout, tryout);
+    puts(buffer);
+    printf("%.2g %G %f %e %E\n", tryout, tryout, tryout, tryout, tryout);
+  }
+#endif
+
+  return 0;
+}
+
+#endif
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 361 - 0
Source/CTest/Curl/multi.c

@@ -0,0 +1,361 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2001, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+#include <stdlib.h>
+#include <string.h>
+#include <curl/curl.h>
+
+#include "multi.h" /* will become <curl/multi.h> soon */
+
+#include "urldata.h"
+#include "transfer.h"
+#include "url.h"
+
+struct Curl_message {
+  /* the 'CURLMsg' is the part that is visible to the external user */
+  struct CURLMsg extmsg;
+  struct Curl_message *next;
+};
+
+typedef enum {
+  CURLM_STATE_INIT,
+  CURLM_STATE_CONNECT,
+  CURLM_STATE_DO,
+  CURLM_STATE_PERFORM,
+  CURLM_STATE_DONE,
+  CURLM_STATE_COMPLETED,
+
+  CURLM_STATE_LAST /* not a true state, never use this */
+} CURLMstate;
+
+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 */
+};
+
+
+#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)
+
+/* 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 linked list with easy handles */
+  struct Curl_one_easy easy; 
+  /* This is the amount of entries in the linked list above. */
+  int num_easy;
+
+  /* this is a linked list of posted messages */
+  struct Curl_message *msgs;
+  /* amount of messages in the queue */
+  int num_msgs;
+  /* Hostname cache */
+  curl_hash *hostcache;
+};
+
+
+CURLM *curl_multi_init(void)
+{
+  struct Curl_multi *multi;
+
+  multi = (void *)malloc(sizeof(struct Curl_multi));
+
+  if(multi) {
+    memset(multi, 0, sizeof(struct Curl_multi));
+    multi->type = CURL_MULTI_HANDLE;
+  }
+  
+  return (CURLM *) multi;
+}
+
+CURLMcode curl_multi_add_handle(CURLM *multi_handle,
+                                CURL *easy_handle)
+{
+  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
+  struct Curl_one_easy *easy;
+
+  /* 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;
+
+  /* Now, time to add an easy handle to the multi stack */
+  easy = (struct Curl_one_easy *)malloc(sizeof(struct Curl_one_easy));
+  if(!easy)
+    return CURLM_OUT_OF_MEMORY;
+  
+  /* clean it all first (just to be sure) */
+  memset(easy, 0, sizeof(struct Curl_one_easy));
+
+  /* set the easy handle */
+  easy->easy_handle = easy_handle;
+  easy->state = CURLM_STATE_INIT;
+  
+  /* We add this new entry first in the list. We make our 'next' point to the
+     previous next and our 'prev' point back to the 'first' struct */
+  easy->next = multi->easy.next;
+  easy->prev = &multi->easy; 
+
+  /* make 'easy' the first node in the chain */
+  multi->easy.next = easy;
+
+  /* if there was a next node, make sure its 'prev' pointer links back to
+     the new node */
+  if(easy->next)
+    easy->next->prev = easy;
+
+  /* increase the node-counter */
+  multi->num_easy++;
+
+  return CURLM_CALL_MULTI_PERFORM;
+}
+
+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;
+
+  /* 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;
+
+  /* scan through the list and remove the 'curl_handle' */
+  easy = multi->easy.next;
+  while(easy) {
+    if(easy->easy_handle == curl_handle)
+      break;
+    easy=easy->next;
+  }
+  if(easy) {
+    /* 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. */
+
+    /* 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;
+    
+    /* NOTE NOTE NOTE
+       We do not touch the easy handle here! */
+    free(easy);
+
+    multi->num_easy--; /* one less to care about now */
+
+    return CURLM_OK;
+  }
+  else
+    return CURLM_BAD_EASY_HANDLE; /* twasn't found */
+}
+
+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;
+
+  if(!GOOD_MULTI_HANDLE(multi))
+    return CURLM_BAD_HANDLE;
+
+  *max_fd = -1; /* so far none! */
+
+  easy=multi->easy.next;
+  while(easy) {
+    switch(easy->state) {
+    default:
+      break;
+    case CURLM_STATE_PERFORM:
+      /* This should have a set of file descriptors for us to set.  */
+      /* after the transfer is done, go DONE */
+
+      Curl_single_fdset(easy->easy_conn,
+                        read_fd_set, write_fd_set,
+                        exc_fd_set, &this_max_fd);
+
+      /* remember the maximum file descriptor */
+      if(this_max_fd > *max_fd)
+        *max_fd = this_max_fd;
+
+      break;
+    }
+    easy = easy->next; /* check next handle */
+  }
+
+  return CURLM_OK;
+}
+
+CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
+{
+  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
+  struct Curl_one_easy *easy;
+  bool done;
+  CURLMcode result=CURLM_OK;
+
+  *running_handles = 0; /* bump this once for every living handle */
+
+  if(!GOOD_MULTI_HANDLE(multi))
+    return CURLM_BAD_HANDLE;
+
+  easy=multi->easy.next;
+  while(easy) {
+    switch(easy->state) {
+    case CURLM_STATE_INIT:
+      /* init this transfer. */
+      easy->result=Curl_pretransfer(easy->easy_handle);
+      if(CURLE_OK == easy->result) {
+        /* after init, go CONNECT */
+        easy->state = CURLM_STATE_CONNECT;
+        result = CURLM_CALL_MULTI_PERFORM; 
+      }
+      break;
+    case CURLM_STATE_CONNECT:
+      if (Curl_global_host_cache_use(easy->easy_handle)) {
+        easy->easy_handle->hostcache = Curl_global_host_cache_get();
+      }
+      else {
+        if (multi->hostcache == NULL) {
+          multi->hostcache = curl_hash_alloc(7, Curl_freeaddrinfo);
+        }
+
+        easy->easy_handle->hostcache = multi->hostcache;
+      }
+
+      /* Connect. We get a connection identifier filled in. */
+      easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn);
+
+      /* after connect, go DO */
+      if(CURLE_OK == easy->result) {
+        easy->state = CURLM_STATE_DO;
+        result = CURLM_CALL_MULTI_PERFORM; 
+      }
+      break;
+    case CURLM_STATE_DO:
+      /* Do the fetch or put request */
+      easy->result = Curl_do(&easy->easy_conn);
+      /* after do, go PERFORM */
+      if(CURLE_OK == easy->result) {
+        if(CURLE_OK == Curl_readwrite_init(easy->easy_conn)) {
+          easy->state = CURLM_STATE_PERFORM;
+          result = CURLM_CALL_MULTI_PERFORM; 
+        }
+      }
+      break;
+    case CURLM_STATE_PERFORM:
+      /* read/write data if it is ready to do so */
+      easy->result = Curl_readwrite(easy->easy_conn, &done);
+      /* hm, when we follow redirects, we may need to go back to the CONNECT
+         state */
+      /* after the transfer is done, go DONE */
+      if(TRUE == done) {
+        /* call this even if the readwrite function returned error */
+        easy->result = Curl_posttransfer(easy->easy_handle);
+        easy->state = CURLM_STATE_DONE;
+        result = CURLM_CALL_MULTI_PERFORM; 
+      }
+      break;
+    case CURLM_STATE_DONE:
+      /* post-transfer command */
+      easy->result = Curl_done(easy->easy_conn);
+      /* after we have DONE what we're supposed to do, go COMPLETED */
+      if(CURLE_OK == easy->result)
+        easy->state = 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. */
+      break;
+    default:
+      return CURLM_INTERNAL_ERROR;
+    }
+
+    if((CURLM_STATE_COMPLETED != easy->state) &&
+       (CURLE_OK != easy->result)) {
+      /*
+       * If an error was returned, and we aren't in completed now,
+       * then we go to completed and consider this transfer aborted.
+       */
+      easy->state = CURLM_STATE_COMPLETED;
+    }
+    else if(CURLM_STATE_COMPLETED != easy->state)
+      /* this one still lives! */
+      (*running_handles)++;
+
+    easy = easy->next; /* operate on next handle */
+  }
+  return result;
+}
+
+CURLMcode curl_multi_cleanup(CURLM *multi_handle)
+{
+  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
+  if(GOOD_MULTI_HANDLE(multi)) {
+    multi->type = 0; /* not good anymore */
+    curl_hash_destroy(multi->hostcache);
+    /* remove all easy handles */
+
+    free(multi);
+
+    return CURLM_OK;
+  }
+  else
+    return CURLM_BAD_HANDLE;
+}
+
+CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue);
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 189 - 0
Source/CTest/Curl/multi.h

@@ -0,0 +1,189 @@
+#ifndef __CURL_MULTI_H
+#define __CURL_MULTI_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2001, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+/*
+  This is meant to be the "external" header file. Don't give away any
+  internals here!
+
+  This document presents a mixture of ideas from at least:
+  - Daniel Stenberg
+  - Steve Dekorte
+  - Sterling Hughes
+  - Ben Greear
+
+  -------------------------------------------
+  GOALS
+
+  o Enable a "pull" interface. The application that uses libcurl decides where
+    and when to ask libcurl to get/send data.
+
+  o Enable multiple simultaneous transfers in the same thread without making it
+    complicated for the application.
+
+  o Enable the application to select() on its own file descriptors and curl's
+    file descriptors simultaneous easily.
+  
+  Example sources using this interface is here: ../multi/
+
+*/
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
+#include <winsock.h>
+#endif
+
+#include <curl/curl.h>
+
+typedef void CURLM;
+
+typedef enum {
+  CURLM_CALL_MULTI_PERFORM=-1, /* please call curl_multi_perform() soon */
+  CURLM_OK,
+  CURLM_BAD_HANDLE,      /* the passed-in handle is not a valid CURLM handle */
+  CURLM_BAD_EASY_HANDLE, /* an easy handle was not good/valid */
+  CURLM_OUT_OF_MEMORY,   /* if you ever get this, you're in deep sh*t */
+  CURLM_INTERNAL_ERROR,  /* this is a libcurl bug */
+  CURLM_LAST
+} CURLMcode;
+
+typedef enum {
+  CURLMSG_NONE, /* first, not used */
+  CURLMSG_DONE, /* This easy handle has completed. 'whatever' points to
+                   the CURLcode of the transfer */
+  CURLMSG_LAST /* last, not used */
+} CURLMSG;
+
+struct CURLMsg {
+  CURLMSG msg;       /* what this message means */
+  CURL *easy_handle; /* the handle it concerns */
+  union {
+    void *whatever;    /* message-specific data */
+    CURLcode result;   /* return code for transfer */
+  } data;
+};
+typedef struct CURLMsg CURLMsg;
+
+/*
+ * Name:    curl_multi_init()
+ *
+ * Desc:    inititalize multi-style curl usage
+ * Returns: a new CURLM handle to use in all 'curl_multi' functions.
+ */
+CURLM *curl_multi_init(void);
+
+/*
+ * Name:    curl_multi_add_handle()
+ *
+ * Desc:    add a standard curl handle to the multi stack
+ * Returns: CURLMcode type, general multi error code.
+ */
+CURLMcode curl_multi_add_handle(CURLM *multi_handle,
+                                CURL *curl_handle);
+
+ /*
+  * Name:    curl_multi_remove_handle()
+  *
+  * Desc:    removes a curl handle from the multi stack again
+  * Returns: CURLMcode type, general multi error code.
+  */
+CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
+                                   CURL *curl_handle);
+
+ /*
+  * Name:    curl_multi_fdset()
+  *
+  * Desc:    Ask curl for its fd_set sets. The app can use these to select() or
+  *          poll() on. We want curl_multi_perform() called as soon as one of
+  *          them are ready.
+  * Returns: CURLMcode type, general multi error code.
+  */
+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);
+
+ /*
+  * Name:    curl_multi_perform()
+  *
+  * Desc:    When the app thinks there's data available for curl it calls this
+  *          function to read/write whatever there is right now. This returns
+  *          as soon as the reads and writes are done. This function does not
+  *          require that there actually is data available for reading or that
+  *          data can be written, it can be called just in case. It returns
+  *          the number of handles that still transfer data in the second
+  *          argument's integer-pointer.
+  *
+  * Returns: CURLMcode type, general multi error code. *NOTE* that this only
+  *          returns errors etc regarding the whole multi stack. There might
+  *          still have occurred problems on invidual transfers even when this
+  *          returns OK.
+  */
+CURLMcode curl_multi_perform(CURLM *multi_handle,
+                             int *running_handles);
+
+ /*
+  * Name:    curl_multi_cleanup()
+  *
+  * Desc:    Cleans up and removes a whole multi stack. It does not free or
+  *          touch any individual easy handles in any way. We need to define
+  *          in what state those handles will be if this function is called
+  *          in the middle of a transfer.
+  * Returns: CURLMcode type, general multi error code.
+  */
+CURLMcode curl_multi_cleanup(CURLM *multi_handle);
+
+/*
+ * Name:    curl_multi_info_read()
+ *
+ * Desc:    Ask the multi handle if there's any messages/informationals from
+ *          the individual transfers. Messages include informationals such as
+ *          error code from the transfer or just the fact that a transfer is
+ *          completed. More details on these should be written down as well.
+ *
+ *          Repeated calls to this function will return a new struct each
+ *          time, until a special "end of msgs" struct is returned as a signal
+ *          that there is no more to get at this point.
+ *
+ *          The data the returned pointer points to will not survive calling
+ *          curl_multi_cleanup().
+ *
+ *          The 'CURLMsg' struct is meant to be very simple and only contain
+ *          very basic informations. If more involved information is wanted,
+ *          we will provide the particular "transfer handle" in that struct
+ *          and that should/could/would be used in subsequent
+ *          curl_easy_getinfo() calls (or similar). The point being that we
+ *          must never expose complex structs to applications, as then we'll
+ *          undoubtably get backwards compatibility problems in the future.
+ *
+ * Returns: A pointer to a filled-in struct, or NULL if it failed or ran out
+ *          of structs. It also writes the number of messages left in the
+ *          queue (after this read) in the integer the second argument points
+ *          to.
+ */
+CURLMsg *curl_multi_info_read(CURLM *multi_handle,
+                              int *msgs_in_queue);
+
+#endif

+ 211 - 0
Source/CTest/Curl/netrc.c

@@ -0,0 +1,211 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef VMS
+#include <unixlib.h>
+#endif
+
+#include <curl/curl.h>
+
+#include "strequal.h"
+#include "strtok.h"
+
+/* The last #include file should be: */
+#ifdef MALLOCDEBUG
+#include "memdebug.h"
+#endif
+
+/* Debug this single source file with:
+   'make netrc' then run './netrc'!
+
+   Oh, make sure you have a .netrc file too ;-)
+ */
+
+/* Get user and password from .netrc when given a machine name */
+
+enum {
+  NOTHING,
+  HOSTFOUND,    /* the 'machine' keyword was found */
+  HOSTCOMPLETE, /* the machine name following the keyword was found too */
+  HOSTVALID,    /* this is "our" machine! */
+
+  HOSTEND /* LAST enum */
+};
+
+/* make sure we have room for at least this size: */
+#define LOGINSIZE 64
+#define PASSWORDSIZE 64
+
+int Curl_parsenetrc(char *host,
+                    char *login,
+                    char *password)
+{
+  FILE *file;
+  char netrcbuffer[256];
+  int retcode=1;
+  
+  char *home = NULL; 
+  int state=NOTHING;
+
+  char state_login=0;
+  char state_password=0;
+
+#define NETRC DOT_CHAR "netrc"
+
+#if defined(HAVE_GETPWUID) && defined(HAVE_GETEUID)
+  struct passwd *pw;
+  pw= getpwuid(geteuid());
+  if (pw) {
+#ifdef  VMS
+    home = decc$translate_vms(pw->pw_dir);
+#else
+    home = pw->pw_dir;
+#endif
+  }
+#else
+  void *pw=NULL;
+#endif
+  
+  if(NULL == pw) {
+    home = curl_getenv("HOME"); /* portable environment reader */
+    if(!home) {
+      return -1;
+    }
+  }
+
+  if(strlen(home)>(sizeof(netrcbuffer)-strlen(NETRC))) {
+    if(NULL==pw)
+      free(home);
+    return -1;
+  }
+
+  sprintf(netrcbuffer, "%s%s%s", home, DIR_CHAR, NETRC);
+
+  file = fopen(netrcbuffer, "r");
+  if(file) {
+    char *tok;
+        char *tok_buf;
+    while(fgets(netrcbuffer, sizeof(netrcbuffer), file)) {
+      tok=strtok_r(netrcbuffer, " \t\n", &tok_buf);
+      while(tok) {
+        switch(state) {
+        case NOTHING:
+          if(strequal("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(strequal(host, tok)) {
+            /* and yes, this is our host! */
+            state=HOSTVALID;
+#ifdef _NETRC_DEBUG
+            printf("HOST: %s\n", tok);
+#endif
+            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) {
+            strncpy(login, tok, LOGINSIZE-1);
+#ifdef _NETRC_DEBUG
+            printf("LOGIN: %s\n", login);
+#endif
+            state_login=0;
+          }
+          else if(state_password) {
+            strncpy(password, tok, PASSWORDSIZE-1);
+#ifdef _NETRC_DEBUG
+            printf("PASSWORD: %s\n", password);
+#endif
+            state_password=0;
+          }
+          else if(strequal("login", tok))
+            state_login=1;
+          else if(strequal("password", tok))
+            state_password=1;
+          else if(strequal("machine", tok)) {
+            /* ok, there's machine here go => */
+            state = HOSTFOUND;
+          }
+          break;
+        } /* switch (state) */
+        tok = strtok_r(NULL, " \t\n", &tok_buf);
+      } /* while (tok) */
+    } /* while fgets() */
+
+    fclose(file);
+  }
+
+  if(NULL==pw)
+    free(home);
+
+  return retcode;
+}
+
+#ifdef _NETRC_DEBUG
+int main(int argc, char **argv)
+{
+  char login[64]="";
+  char password[64]="";
+
+  if(argc<2)
+    return -1;
+
+  if(0 == ParseNetrc(argv[1], login, password)) {
+    printf("HOST: %s LOGIN: %s PASSWORD: %s\n",
+           argv[1], login, password);
+  }
+}
+
+#endif
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 28 - 0
Source/CTest/Curl/netrc.h

@@ -0,0 +1,28 @@
+#ifndef __NETRC_H
+#define __NETRC_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+int Curl_parsenetrc(char *host,
+                    char *login,
+                    char *password);
+#endif

+ 384 - 0
Source/CTest/Curl/progress.c

@@ -0,0 +1,384 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2001, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+
+#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
+#if defined(__MINGW32__)
+#include <winsock.h>
+#endif
+#include <time.h>
+#endif
+
+/* 20000318 mgs
+ * later we use _scrsize to determine the screen width, this emx library
+ * function needs stdlib.h to be included */
+#if defined(__EMX__)
+#include <stdlib.h>
+#endif
+
+#include <curl/curl.h>
+#include "urldata.h"
+#include "sendf.h"
+
+#include "progress.h"
+
+static void time2str(char *r, int t)
+{
+  int h = (t/3600);
+  int m = (t-(h*3600))/60;
+  int s = (t-(h*3600)-(m*60));
+  sprintf(r,"%2d:%02d:%02d",h,m,s);
+}
+
+/* The point of this function would be to return a string of the input data,
+   but never longer than 5 columns. Add suffix k, M, G when suitable... */
+static char *max5data(double bytes, char *max5)
+{
+#define ONE_KILOBYTE 1024
+#define ONE_MEGABYTE (1024*1024)
+
+  if(bytes < 100000) {
+    sprintf(max5, "%5d", (int)bytes);
+    return max5;
+  }
+  if(bytes < (9999*ONE_KILOBYTE)) {
+    sprintf(max5, "%4dk", (int)bytes/ONE_KILOBYTE);
+    return max5;
+  }
+  if(bytes < (100*ONE_MEGABYTE)) {
+    /* 'XX.XM' is good as long as we're less than 100 megs */
+    sprintf(max5, "%4.1fM", bytes/ONE_MEGABYTE);
+    return max5;
+  }
+  sprintf(max5, "%4dM", (int)bytes/ONE_MEGABYTE);
+  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
+
+*/
+
+void Curl_pgrsDone(struct connectdata *conn)
+{
+  struct SessionHandle *data = conn->data;
+  if(!(data->progress.flags & PGRS_HIDE)) {
+    data->progress.lastshow=0;
+    Curl_pgrsUpdate(conn); /* the final (forced) update */
+    if(!data->progress.callback)
+      /* only output if we don't use progress callback */
+      fprintf(data->set.err, "\n");
+  }
+}
+
+void Curl_pgrsTime(struct SessionHandle *data, timerid timer)
+{
+  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 = Curl_tvnow();
+    break;
+
+  case TIMER_NAMELOOKUP:
+    data->progress.t_nslookup =
+      (double)Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle)/1000.0;
+    break;
+  case TIMER_CONNECT:
+    data->progress.t_connect =
+      (double)Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle)/1000.0;
+    break;
+  case TIMER_PRETRANSFER:
+    data->progress.t_pretransfer =
+      (double)Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle)/1000.0;
+    break;
+  case TIMER_STARTTRANSFER:
+    data->progress.t_starttransfer =
+      (double)Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle)/1000.0;
+    break;
+  case TIMER_POSTRANSFER:
+    /* this is the normal end-of-transfer thing */
+    break;
+  }
+}
+
+void Curl_pgrsStartNow(struct SessionHandle *data)
+{
+  data->progress.speeder_c = 0; /* reset the progress meter display */
+  data->progress.start = Curl_tvnow();
+}
+
+void Curl_pgrsSetDownloadCounter(struct SessionHandle *data, double size)
+{
+  data->progress.downloaded = size;
+}
+
+void Curl_pgrsSetUploadCounter(struct SessionHandle *data, double size)
+{
+  data->progress.uploaded = size;
+}
+
+void Curl_pgrsSetDownloadSize(struct SessionHandle *data, double size)
+{
+  if(size > 0) {
+    data->progress.size_dl = size;
+    data->progress.flags |= PGRS_DL_SIZE_KNOWN;
+  }
+}
+
+void Curl_pgrsSetUploadSize(struct SessionHandle *data, double size)
+{
+  if(size > 0) {
+    data->progress.size_ul = size;
+    data->progress.flags |= PGRS_UL_SIZE_KNOWN;
+  }
+}
+
+/* EXAMPLE OUTPUT to follow:
+
+  % Total    % Received % Xferd  Average Speed          Time             Curr.
+                                 Dload  Upload Total    Current  Left    Speed
+100 12345  100 12345  100 12345  12345  12345 12:12:12 12:12:12 12:12:12 12345
+
+ */
+
+int Curl_pgrsUpdate(struct connectdata *conn)
+{
+  struct timeval now;
+  int result;
+
+  char max5[6][10];
+  double dlpercen=0;
+  double ulpercen=0;
+  double total_percen=0;
+
+  double total_transfer;
+  double total_expected_transfer;
+  double 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_current[10];
+      
+  double ulestimate=0;
+  double dlestimate=0;
+  
+  double total_estimate;
+
+
+  if(data->progress.flags & PGRS_HIDE)
+    ; /* We do enter this function even if we don't wanna see anything, since
+         this is were lots of the calculations are being made that will be used
+         even when not displayed! */
+  else if(!(data->progress.flags & PGRS_HEADERS_OUT)) {
+    if (!data->progress.callback) {
+      if(conn->resume_from)
+        fprintf(data->set.err, "** Resuming transfer from byte position %d\n",
+                conn->resume_from);
+      fprintf(data->set.err,
+              "  %% Total    %% Received %% Xferd  Average Speed          Time             Curr.\n"
+              "                                 Dload  Upload Total    Current  Left    Speed\n");
+    }
+    data->progress.flags |= PGRS_HEADERS_OUT; /* headers are shown */
+  }
+
+  now = Curl_tvnow(); /* what time is it */
+
+  /* The exact time spent so far (from the start) */
+  timespent = (double)Curl_tvdiff (now, data->progress.start)/1000;
+
+  data->progress.timespent = timespent;
+
+  /* The average download speed this far */
+  data->progress.dlspeed =
+    data->progress.downloaded/(timespent>0.01?timespent:1);
+
+  /* The average upload speed this far */
+  data->progress.ulspeed =
+    data->progress.uploaded/(timespent>0.01?timespent:1);
+
+  if(data->progress.lastshow == Curl_tvlong(now))
+    return 0; /* never update this more than once a second if the end isn't 
+                 reached */
+  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 fasted 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 'countindex' seconds */
+    data->progress.current_speed =
+      (data->progress.speeder[nowindex]-
+       data->progress.speeder[checkindex])/((double)span_ms/1000);
+  }
+  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;
+
+  if(data->progress.flags & PGRS_HIDE)
+    return 0;
+
+  else 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,
+                                data->progress.size_dl,
+                                data->progress.downloaded,
+                                data->progress.size_ul,
+                                data->progress.uploaded);
+    if(result)
+      failf(data, "Callback aborted");
+    return result;
+  }
+
+  /* Figure out the estimated time of arrival for the upload */
+  if((data->progress.flags & PGRS_UL_SIZE_KNOWN) && data->progress.ulspeed){
+    ulestimate = data->progress.size_ul / data->progress.ulspeed;
+    ulpercen = (data->progress.uploaded / data->progress.size_ul)*100;
+  }
+
+  /* ... and the download */
+  if((data->progress.flags & PGRS_DL_SIZE_KNOWN) && data->progress.dlspeed) {
+    dlestimate = data->progress.size_dl / data->progress.dlspeed;
+    dlpercen = (data->progress.downloaded / data->progress.size_dl)*100;
+  }
+    
+  /* Now figure out which of them that is slower and use for the for
+     total estimate! */
+  total_estimate = ulestimate>dlestimate?ulestimate:dlestimate;
+
+
+  /* If we have a total estimate, we can display that and the expected
+     time left */
+  if(total_estimate) {
+    time2str(time_left, total_estimate-(int) data->progress.timespent); 
+    time2str(time_total, total_estimate);
+  }
+  else {
+    /* otherwise we blank those times */
+    strcpy(time_left,  "--:--:--");
+    strcpy(time_total, "--:--:--");
+  }
+  /* The time spent so far is always known */
+  time2str(time_current, data->progress.timespent);
+
+  /* Get the total amount of data expected to get transfered */
+  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 transfered this much so far */
+  total_transfer = data->progress.downloaded + data->progress.uploaded;
+
+  /* Get the percentage of data transfered so far */
+  if(total_expected_transfer)
+    total_percen=(double)(total_transfer/total_expected_transfer)*100;
+
+  fprintf(data->set.err,
+          "\r%3d %s  %3d %s  %3d %s  %s  %s %s %s %s %s",
+          (int)total_percen,                            /* total % */
+          max5data(total_expected_transfer, max5[2]),   /* total size */
+          (int)dlpercen,                                /* rcvd % */
+          max5data(data->progress.downloaded, max5[0]), /* rcvd size */
+          (int)ulpercen,                                /* 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,                           /* total time */
+          time_current,                         /* current time */
+          time_left,                            /* 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);
+
+  return 0;
+}
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 68 - 0
Source/CTest/Curl/progress.h

@@ -0,0 +1,68 @@
+#ifndef __PROGRESS_H
+#define __PROGRESS_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "timeval.h"
+
+
+typedef enum {
+  TIMER_NONE,
+  TIMER_NAMELOOKUP,
+  TIMER_CONNECT,
+  TIMER_PRETRANSFER,
+  TIMER_STARTTRANSFER,
+  TIMER_POSTRANSFER,
+  TIMER_STARTSINGLE,
+  TIMER_LAST /* must be last */
+} timerid;
+  
+void Curl_pgrsDone(struct connectdata *);
+void Curl_pgrsStartNow(struct SessionHandle *data);
+void Curl_pgrsSetDownloadSize(struct SessionHandle *data, double size);
+void Curl_pgrsSetUploadSize(struct SessionHandle *data, double size);
+void Curl_pgrsSetDownloadCounter(struct SessionHandle *data, double size);
+void Curl_pgrsSetUploadCounter(struct SessionHandle *data, double size);
+int Curl_pgrsUpdate(struct connectdata *);
+void Curl_pgrsTime(struct SessionHandle *data, timerid timer);
+
+
+/* Don't show progress for sizes smaller than: */
+#define LEAST_SIZE_PROGRESS BUFSIZE
+
+#define PROGRESS_DOWNLOAD (1<<0)
+#define PROGRESS_UPLOAD   (1<<1)
+#define PROGRESS_DOWN_AND_UP (PROGRESS_UPLOAD | PROGRESS_DOWNLOAD)
+
+#define PGRS_SHOW_DL (1<<0)
+#define PGRS_SHOW_UL (1<<1)
+#define PGRS_DONE_DL (1<<2)
+#define PGRS_DONE_UL (1<<3)
+#define PGRS_HIDE    (1<<4)
+#define PGRS_UL_SIZE_KNOWN (1<<5)
+#define PGRS_DL_SIZE_KNOWN (1<<6)
+
+#define PGRS_HEADERS_OUT (1<<7) /* set when the headers have been written */
+
+
+#endif /* __PROGRESS_H */

+ 562 - 0
Source/CTest/Curl/security.c

@@ -0,0 +1,562 @@
+/* This source code was modified by Martin Hedenfalk <[email protected]> for
+ * use in Curl. His latest changes were done 2000-09-18.
+ *
+ * It has since been patched and modified a lot by Daniel Stenberg
+ * <[email protected]> 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).
+ * 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 "setup.h"
+
+#ifdef KRB4
+
+#define _MPRINTF_REPLACE /* we want curl-functions instead of native ones */
+#include <curl/mprintf.h>
+
+#include "security.h"
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "base64.h"
+#include "sendf.h"
+#include "ftp.h"
+
+/* The last #include file should be: */
+#ifdef MALLOCDEBUG
+#include "memdebug.h"
+#endif
+
+#define min(a, b)   ((a) < (b) ? (a) : (b))
+
+static 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(!strncasecmp(level_names[i].name, name, strlen(name)))
+      return level_names[i].level;
+  return (enum protection_level)-1;
+}
+
+static struct Curl_sec_client_mech *mechs[] = {
+#ifdef KRB5
+  /* not supported */
+#endif
+#ifdef KRB4
+    &Curl_krb4_client_mech,
+#endif
+    NULL
+};
+
+int
+Curl_sec_getc(struct connectdata *conn, FILE *F)
+{
+  if(conn->sec_complete && conn->data_prot) {
+    char c;
+    if(Curl_sec_read(conn, fileno(F), &c, 1) <= 0)
+      return EOF;
+    return c;
+  }
+  else
+    return getc(F);
+}
+
+static int
+block_read(int fd, void *buf, size_t len)
+{
+  unsigned char *p = buf;
+  int b;
+  while(len) {
+    b = read(fd, p, len);
+    if (b == 0)
+      return 0;
+    else if (b < 0)
+      return -1;
+    len -= b;
+    p += b;
+  }
+  return p - (unsigned char*)buf;
+}
+
+static int
+block_write(int fd, void *buf, size_t len)
+{
+  unsigned char *p = buf;
+  int b;
+  while(len) {
+    b = write(fd, p, len);
+    if(b < 0)
+      return -1;
+    len -= b;
+    p += b;
+  }
+  return p - (unsigned char*)buf;
+}
+
+static int
+sec_get_data(struct connectdata *conn,
+             int fd, struct krb4buffer *buf)
+{
+  int len;
+  int b;
+  
+  b = block_read(fd, &len, sizeof(len));
+  if (b == 0)
+    return 0;
+  else if (b < 0)
+    return -1;
+  len = ntohl(len);
+  buf->data = realloc(buf->data, len);
+  b = block_read(fd, buf->data, len);
+  if (b == 0)
+    return 0;
+  else if (b < 0)
+    return -1;
+  buf->size = (conn->mech->decode)(conn->app_data, buf->data, len,
+                                   conn->data_prot, conn);
+  buf->index = 0;
+  return 0;
+}
+
+static size_t
+buffer_read(struct krb4buffer *buf, void *data, size_t len)
+{
+    len = min(len, buf->size - buf->index);
+    memcpy(data, (char*)buf->data + buf->index, len);
+    buf->index += len;
+    return len;
+}
+
+static size_t
+buffer_write(struct krb4buffer *buf, void *data, size_t len)
+{
+    if(buf->index + len > buf->size) {
+        void *tmp;
+        if(buf->data == NULL)
+            tmp = malloc(1024);
+        else
+            tmp = realloc(buf->data, buf->index + len);
+        if(tmp == NULL)
+            return -1;
+        buf->data = tmp;
+        buf->size = buf->index + len;
+    }
+    memcpy((char*)buf->data + buf->index, data, len);
+    buf->index += len;
+    return len;
+}
+
+int
+Curl_sec_read(struct connectdata *conn, int fd, void *buffer, int length)
+{
+    size_t len;
+    int rx = 0;
+
+    if(conn->sec_complete == 0 || conn->data_prot == 0)
+      return read(fd, buffer, length);
+
+    if(conn->in_buffer.eof_flag){
+      conn->in_buffer.eof_flag = 0;
+      return 0;
+    }
+    
+    len = buffer_read(&conn->in_buffer, buffer, length);
+    length -= len;
+    rx += len;
+    buffer = (char*)buffer + len;
+    
+    while(length) {
+      if(sec_get_data(conn, fd, &conn->in_buffer) < 0)
+        return -1;
+      if(conn->in_buffer.size == 0) {
+        if(rx)
+          conn->in_buffer.eof_flag = 1;
+        return rx;
+      }
+      len = buffer_read(&conn->in_buffer, buffer, length);
+      length -= len;
+      rx += len;
+      buffer = (char*)buffer + len;
+    }
+    return rx;
+}
+
+static int
+sec_send(struct connectdata *conn, int fd, char *from, int length)
+{
+  int bytes;
+  void *buf;
+  bytes = (conn->mech->encode)(conn->app_data, from, length, conn->data_prot,
+                               &buf, conn);
+  bytes = htonl(bytes);
+  block_write(fd, &bytes, sizeof(bytes));
+  block_write(fd, buf, ntohl(bytes));
+  free(buf);
+  return length;
+}
+
+int
+Curl_sec_fflush_fd(struct connectdata *conn, int fd)
+{
+  if(conn->data_prot != prot_clear) {
+    if(conn->out_buffer.index > 0){
+      Curl_sec_write(conn, fd,
+                conn->out_buffer.data, conn->out_buffer.index);
+      conn->out_buffer.index = 0;
+    }
+    sec_send(conn, fd, NULL, 0);
+  }
+  return 0;
+}
+
+int
+Curl_sec_write(struct connectdata *conn, int fd, char *buffer, int length)
+{
+  int len = conn->buffer_size;
+  int tx = 0;
+      
+  if(conn->data_prot == prot_clear)
+    return write(fd, buffer, length);
+
+  len -= (conn->mech->overhead)(conn->app_data, conn->data_prot, len);
+  while(length){
+    if(length < len)
+      len = length;
+    sec_send(conn, fd, buffer, len);
+    length -= len;
+    buffer += len;
+    tx += len;
+  }
+  return tx;
+}
+
+int
+Curl_sec_vfprintf2(struct connectdata *conn, FILE *f, const char *fmt, va_list ap)
+{
+  char *buf;
+  int ret;
+  if(conn->data_prot == prot_clear)
+    return vfprintf(f, fmt, ap);
+  else {
+    buf = aprintf(fmt, ap);
+    ret = buffer_write(&conn->out_buffer, buf, strlen(buf));
+    free(buf);
+    return ret;
+  }
+}
+
+int
+Curl_sec_fprintf2(struct connectdata *conn, FILE *f, const char *fmt, ...)
+{
+    int ret;
+    va_list ap;
+    va_start(ap, fmt);
+    ret = Curl_sec_vfprintf2(conn, f, fmt, ap);
+    va_end(ap);
+    return ret;
+}
+
+int
+Curl_sec_putc(struct connectdata *conn, int c, FILE *F)
+{
+  char ch = c;
+  if(conn->data_prot == prot_clear)
+    return putc(c, F);
+    
+  buffer_write(&conn->out_buffer, &ch, 1);
+  if(c == '\n' || conn->out_buffer.index >= 1024 /* XXX */) {
+    Curl_sec_write(conn, fileno(F), conn->out_buffer.data, conn->out_buffer.index);
+    conn->out_buffer.index = 0;
+  }
+  return c;
+}
+
+int
+Curl_sec_read_msg(struct connectdata *conn, char *s, int level)
+{
+    int len;
+    char *buf;
+    int code;
+    
+    buf = malloc(strlen(s));
+    len = Curl_base64_decode(s + 4, buf); /* XXX */
+    
+    len = (conn->mech->decode)(conn->app_data, buf, len, level, conn);
+    if(len < 0)
+        return -1;
+    
+    buf[len] = '\0';
+
+    if(buf[3] == '-')
+        code = 0;
+    else
+        sscanf(buf, "%d", &code);
+    if(buf[len-1] == '\n')
+        buf[len-1] = '\0';
+    strcpy(s, buf);
+    free(buf);
+    return code;
+}
+
+/* modified to return how many bytes written, or -1 on error ***/
+int
+Curl_sec_vfprintf(struct connectdata *conn, FILE *f, const char *fmt, va_list ap)
+{
+    int ret = 0;
+    char *buf;
+    void *enc;
+    int len;
+    if(!conn->sec_complete)
+        return vfprintf(f, fmt, ap);
+    
+    buf = aprintf(fmt, ap);
+    len = (conn->mech->encode)(conn->app_data, buf, strlen(buf),
+                               conn->command_prot, &enc,
+                               conn);
+    free(buf);
+    if(len < 0) {
+        failf(conn->data, "Failed to encode command.");
+        return -1;
+    }
+    if(Curl_base64_encode(enc, len, &buf) < 0){
+      failf(conn->data, "Out of memory base64-encoding.");
+      return -1;
+    }
+    if(conn->command_prot == prot_safe)
+        ret = fprintf(f, "MIC %s", buf);
+    else if(conn->command_prot == prot_private)
+        ret = fprintf(f, "ENC %s", buf);
+    else if(conn->command_prot == prot_confidential)
+        ret = fprintf(f, "CONF %s", buf);
+
+    free(buf);
+    return ret;
+}
+
+int
+Curl_sec_fprintf(struct connectdata *conn, FILE *f, const char *fmt, ...)
+{
+    va_list ap;
+    int ret;
+    va_start(ap, fmt);
+    ret = Curl_sec_vfprintf(conn, f, fmt, ap);
+    va_end(ap);
+    return ret;
+}
+
+
+enum protection_level
+Curl_set_command_prot(struct connectdata *conn, enum protection_level level)
+{
+    enum protection_level old = conn->command_prot;
+    conn->command_prot = level;
+    return old;
+}
+
+static int
+sec_prot_internal(struct connectdata *conn, int level)
+{
+  char *p;
+  unsigned int s = 1048576;
+  ssize_t nread;
+
+  if(!conn->sec_complete){
+    infof(conn->data, "No security data exchange has taken place.\n");
+    return -1;
+  }
+
+  if(level){
+    if(Curl_ftpsendf(conn, "PBSZ %u", s))
+      return -1;
+
+    nread = Curl_GetFTPResponse(conn->data->state.buffer, conn, NULL);
+    if(nread < 0)
+      return -1;
+
+    if(conn->data->state.buffer[0] != '2'){
+      failf(conn->data, "Failed to set protection buffer size.");
+      return -1;
+    }
+    conn->buffer_size = s;
+
+    p = strstr(conn->data->state.buffer, "PBSZ=");
+    if(p)
+      sscanf(p, "PBSZ=%u", &s);
+    if(s < conn->buffer_size)
+      conn->buffer_size = s;
+  }
+
+  if(Curl_ftpsendf(conn, "PROT %c", level["CSEP"]))
+    return -1;
+
+  nread = Curl_GetFTPResponse(conn->data->state.buffer, conn, NULL);
+  if(nread < 0)
+    return -1;
+
+  if(conn->data->state.buffer[0] != '2'){
+    failf(conn->data, "Failed to set protection level.");
+    return -1;
+  }
+    
+  conn->data_prot = (enum protection_level)level;
+  return 0;
+}
+
+void
+Curl_sec_set_protection_level(struct connectdata *conn)
+{
+  if(conn->sec_complete && conn->data_prot != conn->request_data_prot)
+    sec_prot_internal(conn, conn->request_data_prot);
+}
+
+
+int
+Curl_sec_request_prot(struct connectdata *conn, const char *level)
+{
+  int l = name_to_level(level);
+  if(l == -1)
+    return -1;
+  conn->request_data_prot = (enum protection_level)l;
+  return 0;
+}
+
+int
+Curl_sec_login(struct connectdata *conn)
+{
+  int ret;
+  struct Curl_sec_client_mech **m;
+  ssize_t nread;
+  struct SessionHandle *data=conn->data;
+  int ftpcode;
+
+  for(m = mechs; *m && (*m)->name; m++) {
+    void *tmp;
+
+    tmp = realloc(conn->app_data, (*m)->size);
+    if (tmp == NULL) {
+      failf (data, "realloc %u failed", (*m)->size);
+      return -1;
+    }
+    conn->app_data = tmp;
+            
+    if((*m)->init && (*(*m)->init)(conn->app_data) != 0) {
+      infof(data, "Skipping %s...\n", (*m)->name);
+      continue;
+    }
+    infof(data, "Trying %s...\n", (*m)->name);
+
+    if(Curl_ftpsendf(conn, "AUTH %s", (*m)->name))
+      return -1;
+
+    nread = Curl_GetFTPResponse(conn->data->state.buffer, conn, &ftpcode);
+    if(nread < 0)
+      return -1;
+
+    if(conn->data->state.buffer[0] != '3'){
+      switch(ftpcode) {
+      case 504:
+        infof(data,
+              "%s is not supported by the server.\n", (*m)->name);
+        break;
+      case 534:
+        infof(data, "%s rejected as security mechanism.\n", (*m)->name);
+        break;
+      default:
+        if(conn->data->state.buffer[0] == '5') {
+          infof(data, "The server doesn't support the FTP "
+                "security extensions.\n");
+          return -1;
+        }
+        break;
+      }
+      continue;
+    }
+
+    ret = (*(*m)->auth)(conn->app_data, conn);
+        
+    if(ret == AUTH_CONTINUE)
+      continue;
+    else if(ret != AUTH_OK){
+      /* mechanism is supposed to output error string */
+      return -1;
+    }
+    conn->mech = *m;
+    conn->sec_complete = 1;
+    conn->command_prot = prot_safe;
+    break;
+  }
+    
+  return *m == NULL;
+}
+
+void
+Curl_sec_end(struct connectdata *conn)
+{
+  if (conn->mech != NULL) {
+    if(conn->mech->end)
+      (conn->mech->end)(conn->app_data);
+    memset(conn->app_data, 0, conn->mech->size);
+    free(conn->app_data);
+    conn->app_data = NULL;
+  }
+  conn->sec_complete = 0;
+  conn->data_prot = (enum protection_level)0;
+  conn->mech=NULL;
+}
+
+#endif /* KRB4 */
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 72 - 0
Source/CTest/Curl/security.h

@@ -0,0 +1,72 @@
+#ifndef __SECURITY_H
+#define __SECURITY_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2001, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+/* this is a re-write */
+
+#include <stdarg.h>
+#include "urldata.h"  /* for struct connectdata * */
+
+struct Curl_sec_client_mech {
+  const char *name;
+  size_t size;
+  int (*init)(void *);
+  int (*auth)(void *, struct connectdata *);
+  void (*end)(void *);
+  int (*check_prot)(void *, int);
+  int (*overhead)(void *, int, int);
+  int (*encode)(void *, void*, int, int, void**, struct connectdata *);
+  int (*decode)(void *, void*, int, int, struct connectdata *);
+};
+
+
+#define AUTH_OK         0
+#define AUTH_CONTINUE   1
+#define AUTH_ERROR      2
+
+extern struct Curl_sec_client_mech Curl_krb4_client_mech;
+
+int Curl_sec_fflush_fd(struct connectdata *conn, int fd);
+int Curl_sec_fprintf (struct connectdata *, FILE *, const char *, ...);
+int Curl_sec_getc (struct connectdata *conn, FILE *);
+int Curl_sec_putc (struct connectdata *conn, int, FILE *);
+int Curl_sec_read (struct connectdata *conn, int, void *, int);
+int Curl_sec_read_msg (struct connectdata *conn, char *, int);
+
+int Curl_sec_vfprintf(struct connectdata *, FILE *, const char *, va_list);
+int Curl_sec_fprintf2(struct connectdata *conn, FILE *f, const char *fmt, ...);
+int Curl_sec_vfprintf2(struct connectdata *conn, FILE *, const char *, va_list);
+int Curl_sec_write (struct connectdata *conn, int, char *, int);
+
+void Curl_sec_end (struct connectdata *);
+int Curl_sec_login (struct connectdata *);
+void Curl_sec_prot (int, char **);
+int Curl_sec_request_prot (struct connectdata *conn, const char *level);
+void Curl_sec_set_protection_level(struct connectdata *conn);
+void Curl_sec_status (void);
+
+enum protection_level Curl_set_command_prot(struct connectdata *,
+                                            enum protection_level);
+
+#endif

+ 365 - 0
Source/CTest/Curl/sendf.c

@@ -0,0 +1,365 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2002, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h> /* required for send() & recv() prototypes */
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
+#include <winsock.h>
+#endif
+
+#include <curl/curl.h>
+#include "urldata.h"
+#include "sendf.h"
+
+#define _MPRINTF_REPLACE /* use the internal *printf() functions */
+#include <curl/mprintf.h>
+
+#ifdef KRB4
+#include "security.h"
+#endif
+#include <string.h>
+/* The last #include file should be: */
+#ifdef MALLOCDEBUG
+#include "memdebug.h"
+#endif
+
+/* 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;
+}
+
+/* append a struct to the linked list. It always retunrs the address of the
+ * first record, so that you can sure 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 proram. */
+struct curl_slist *curl_slist_append(struct curl_slist *list,
+                                     const char *data)
+{
+        struct curl_slist       *last;
+        struct curl_slist       *new_item;
+
+        new_item = (struct curl_slist *) malloc(sizeof(struct curl_slist));
+        if (new_item) {
+                new_item->next = NULL;
+                new_item->data = strdup(data);
+        }
+        else {
+                fprintf(stderr, "Cannot allocate memory for QUOTE list.\n");
+                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;
+}
+
+/* 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;
+                
+                if (item->data) {
+                        free(item->data);
+                }
+                free(item);
+                item = next;
+        } while (next);
+}
+
+
+/* Curl_infof() is for info message along the way */
+
+void Curl_infof(struct SessionHandle *data, const char *fmt, ...)
+{
+  va_list ap;
+  if(data->set.verbose) {
+    va_start(ap, fmt);
+    fputs("* ", data->set.err);
+    vfprintf(data->set.err, fmt, ap);
+    va_end(ap);
+  }
+}
+
+/* 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;
+  va_start(ap, fmt);
+  if(data->set.errorbuffer && !data->state.errorbuf) {
+    vsnprintf(data->set.errorbuffer, CURL_ERROR_SIZE, fmt, ap);
+    data->state.errorbuf = TRUE; /* wrote error string */
+  }
+  va_end(ap);
+}
+
+/* Curl_sendf() sends formated data to the server */
+CURLcode Curl_sendf(int sockfd, struct connectdata *conn,
+                    const char *fmt, ...)
+{
+  struct SessionHandle *data = conn->data;
+  ssize_t bytes_written;
+  CURLcode result;
+  char *s;
+  va_list ap;
+  va_start(ap, fmt);
+  s = vaprintf(fmt, ap); /* returns an allocated string */
+  va_end(ap);
+  if(!s)
+    return 0; /* failure */
+  if(data->set.verbose)
+    fprintf(data->set.err, "> %s", s);
+
+  /* Write the buffer to the socket */
+  result = Curl_write(conn, sockfd, s, strlen(s), &bytes_written);
+
+  free(s); /* free the output string */
+
+  return result;
+}
+
+/*
+ * Curl_write() is an internal write function that sends plain (binary) data
+ * to the server. Works with plain sockets, SSL or kerberos.
+ *
+ */
+CURLcode Curl_write(struct connectdata *conn, int sockfd,
+                    void *mem, size_t len,
+                    ssize_t *written)
+{
+  ssize_t bytes_written;
+
+#ifdef USE_SSLEAY
+  /* SSL_write() is said to return 'int' while write() and send() returns
+     'size_t' */
+  if (conn->ssl.use) {
+    int err;
+    int rc = SSL_write(conn->ssl.handle, mem, len);
+
+    if(rc < 0) {
+      err = SSL_get_error(conn->ssl.handle, rc);
+    
+      switch(err) {
+      case SSL_ERROR_WANT_READ:
+      case SSL_ERROR_WANT_WRITE:
+        /* this is basicly the EWOULDBLOCK equivalent */
+        *written = 0;
+        return CURLE_OK;
+      }
+      /* a true error */
+      failf(conn->data, "SSL_write() return error %d\n", err);
+      return CURLE_WRITE_ERROR;
+    }
+    bytes_written = rc;
+  }
+  else {
+#endif
+#ifdef KRB4
+    if(conn->sec_complete) {
+      bytes_written = Curl_sec_write(conn, sockfd, mem, len);
+    }
+    else
+#endif /* KRB4 */
+    {
+      bytes_written = swrite(sockfd, mem, len);
+    }
+    if(-1 == bytes_written) {
+#ifdef WIN32
+      if(WSAEWOULDBLOCK == GetLastError())
+#else
+      if(EWOULDBLOCK == errno)
+#endif
+      {
+        /* this is just a case of EWOULDBLOCK */
+        *written=0;
+        return CURLE_OK;
+      }
+    }
+#ifdef USE_SSLEAY
+  }
+#endif
+
+  *written = bytes_written;
+  return (-1 != bytes_written)?CURLE_OK:CURLE_WRITE_ERROR;
+}
+
+/* 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 sendf.h of course.
+ */
+CURLcode Curl_client_write(struct SessionHandle *data,
+                           int type,
+                           char *ptr,
+                           size_t len)
+{
+  size_t wrote;
+
+  if(0 == len)
+    len = strlen(ptr);
+
+  if(type & CLIENTWRITE_BODY) {
+    wrote = data->set.fwrite(ptr, 1, len, data->set.out);
+    if(wrote != len) {
+      failf (data, "Failed writing body");
+      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;
+
+    wrote = writeit(ptr, 1, len, data->set.writeheader);
+    if(wrote != len) {
+      failf (data, "Failed writing header");
+      return CURLE_WRITE_ERROR;
+    }
+  }
+  
+  return CURLE_OK;
+}
+
+/*
+ * Internal read-from-socket function. This is meant to deal with plain
+ * sockets, SSL sockets and kerberos sockets.
+ *
+ * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
+ * a regular CURLcode value.
+ */
+int Curl_read(struct connectdata *conn,
+              int sockfd,
+              char *buf,
+              size_t buffersize,
+              ssize_t *n)
+{
+  ssize_t nread;
+
+#ifdef USE_SSLEAY
+  if (conn->ssl.use) {
+    bool loop=TRUE;
+    int err;
+    do {
+      nread = SSL_read(conn->ssl.handle, buf, buffersize);
+
+      if(nread >= 0)
+        /* successful read */
+        break;
+
+      err = SSL_get_error(conn->ssl.handle, nread);
+
+      switch(err) {
+      case SSL_ERROR_NONE: /* this is not an error */
+      case SSL_ERROR_ZERO_RETURN: /* no more data */
+        loop=0; /* get out of loop */
+        break;
+      case SSL_ERROR_WANT_READ:
+      case SSL_ERROR_WANT_WRITE:
+        /* if there's data pending, then we re-invoke SSL_read() */
+        break;
+      }
+    } while(loop);
+    if(loop && SSL_pending(conn->ssl.handle))
+      return -1; /* basicly EWOULDBLOCK */
+  }
+  else {
+#endif
+#ifdef KRB4
+    if(conn->sec_complete)
+      nread = Curl_sec_read(conn, sockfd, buf, buffersize);
+    else
+#endif
+      nread = sread (sockfd, buf, buffersize);
+
+    if(-1 == nread) {
+#ifdef WIN32
+      if(WSAEWOULDBLOCK == GetLastError())
+#else
+      if(EWOULDBLOCK == errno)
+#endif
+        return -1;
+    }
+
+#ifdef USE_SSLEAY
+  }
+#endif /* USE_SSLEAY */
+  *n = nread;
+  return CURLE_OK;
+}
+
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 56 - 0
Source/CTest/Curl/sendf.h

@@ -0,0 +1,56 @@
+#ifndef __SENDF_H
+#define __SENDF_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+CURLcode Curl_sendf(int fd, struct connectdata *, const char *fmt, ...);
+void Curl_infof(struct SessionHandle *, const char *fmt, ...);
+void Curl_failf(struct SessionHandle *, const char *fmt, ...);
+
+#define infof Curl_infof
+#define failf Curl_failf
+
+struct send_buffer {
+  char *buffer;
+  size_t size_max;
+  size_t size_used;
+};
+typedef struct send_buffer send_buffer;
+
+#define CLIENTWRITE_BODY   1
+#define CLIENTWRITE_HEADER 2
+#define CLIENTWRITE_BOTH   (CLIENTWRITE_BODY|CLIENTWRITE_HEADER)
+
+CURLcode Curl_client_write(struct SessionHandle *data, int type, char *ptr,
+                           size_t len);
+
+/* internal read-function, does plain socket, SSL and krb4 */
+int Curl_read(struct connectdata *conn, int sockfd,
+              char *buf, size_t buffersize,
+              ssize_t *n);
+/* internal write-function, does plain socket, SSL and krb4 */
+CURLcode Curl_write(struct connectdata *conn, int sockfd,
+                    void *mem, size_t len,
+                    ssize_t *written);
+
+#endif

+ 169 - 0
Source/CTest/Curl/setup.h

@@ -0,0 +1,169 @@
+#ifndef __SETUP_H
+#define __SETUP_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+
+
+#if !defined(WIN32) && defined(_WIN32)
+/* This _might_ be a good Borland fix. Please report whether this works or
+   not! */
+#define WIN32
+#endif
+
+#ifdef HAVE_CONFIG_H
+
+#ifdef VMS
+#include "config-vms.h"
+#else
+#include "config.h" /* the configure script results */
+#endif
+
+#else
+#ifdef WIN32
+/* hand-modified win32 config.h! */
+#include "config-win32.h"
+#endif
+#ifdef macintosh
+/* hand-modified MacOS config.h! */
+#include "config-mac.h"
+#endif
+
+#endif
+
+#ifndef __cplusplus        /* (rabe) */
+typedef unsigned char bool;
+#define typedef_bool
+#endif                     /* (rabe) */
+
+#ifdef NEED_REENTRANT
+/* Solaris machines needs _REENTRANT set for a few function prototypes and
+   things to appear in the #include files. We need to #define it before all
+   #include files */
+#define _REENTRANT
+#endif
+
+
+#include <stdio.h>
+#ifndef OS
+#ifdef WIN32
+#define OS "win32"
+#else
+#define OS "unknown"
+#endif
+#endif
+
+#if defined(HAVE_X509_H) && defined(HAVE_SSL_H) && defined(HAVE_RSA_H) && \
+defined(HAVE_PEM_H) && defined(HAVE_ERR_H) && defined(HAVE_CRYPTO_H) && \
+defined(HAVE_LIBSSL) && defined(HAVE_LIBCRYPTO)
+  /* the six important includes files all exist and so do both libs,
+     defined SSLeay usage */
+#define USE_SSLEAY 1
+#endif
+#if defined(HAVE_OPENSSL_X509_H) && defined(HAVE_OPENSSL_SSL_H) && \
+defined(HAVE_OPENSSL_RSA_H) && defined(HAVE_OPENSSL_PEM_H) && \
+defined(HAVE_OPENSSL_ERR_H) && defined(HAVE_OPENSSL_CRYPTO_H) && \
+defined(HAVE_LIBSSL) && defined(HAVE_LIBCRYPTO)
+  /* the six important includes files all exist and so do both libs,
+     defined SSLeay usage */
+#define USE_SSLEAY 1
+#define USE_OPENSSL 1
+#endif
+
+#ifndef STDC_HEADERS /* no standard C headers! */
+#ifdef  VMS
+#include "../include/curl/stdcheaders.h"
+#else
+#include <curl/stdcheaders.h>
+#endif
+
+#else
+#ifdef _AIX
+#include <curl/stdcheaders.h>
+#endif
+#endif
+
+/* Below we define four functions. They should
+   1. close a socket
+   2. read from a socket
+   3. write to a socket
+
+   4. set the SIGALRM signal timeout
+   5. set dir/file naming defines
+   */
+
+#ifdef WIN32
+#if !defined(__GNUC__) || defined(__MINGW32__)
+#define sclose(x) closesocket(x)
+#define sread(x,y,z) recv(x,y,z,0)
+#define swrite(x,y,z) (size_t)send(x,y,z,0)
+#undef HAVE_ALARM
+#else
+     /* gcc-for-win is still good :) */
+#define sclose(x) close(x)
+#define sread(x,y,z) recv(x,y,z,0)
+#define swrite(x,y,z) send(x,y,z,0)
+#define HAVE_ALARM
+#endif
+
+#define PATH_CHAR     ";"
+#define DIR_CHAR      "\\"
+#define DOT_CHAR      "_"
+
+#else
+#define sclose(x) close(x)
+#define sread(x,y,z) recv(x,y,z,0)
+#define swrite(x,y,z) send(x,y,z,0)
+#define HAVE_ALARM
+
+#define PATH_CHAR     ":"
+#define DIR_CHAR      "/"
+#define DOT_CHAR      "."
+
+#ifdef HAVE_STRCASECMP
+/* this is for "-ansi -Wall -pedantic" to stop complaining! */
+extern int (strcasecmp)(const char *s1, const char *s2);
+extern int (strncasecmp)(const char *s1, const char *s2, size_t n);
+#ifndef fileno /* sunos 4 have this as a macro! */
+int fileno( FILE *stream);
+#endif
+#endif
+
+#endif
+
+/*
+ * Curl_addrinfo MUST be used for name resolving information.
+ * Information regarding a single IP witin a Curl_addrinfo MUST be stored in
+ * a Curl_ipconnect struct.
+ */
+#ifdef ENABLE_IPV6
+typedef struct addrinfo Curl_addrinfo;
+typedef struct addrinfo Curl_ipconnect;
+#else
+typedef struct hostent Curl_addrinfo;
+typedef struct in_addr Curl_ipconnect;
+#endif
+
+
+
+#endif /* __CONFIG_H */

+ 78 - 0
Source/CTest/Curl/speedcheck.c

@@ -0,0 +1,78 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+#include <stdio.h>
+#include <string.h>
+#if defined(__MINGW32__)
+#include <winsock.h>
+#endif
+
+#include <curl/curl.h>
+#include "urldata.h"
+#include "sendf.h"
+#include "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)) {
+
+    /* 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( (Curl_tvdiff(now, data->state.keeps_speed)/1000) >
+        data->set.low_speed_time) {
+      /* we have been this slow for long enough, now die */
+      failf(data,
+            "Operation too slow. "
+            "Less than %d bytes/sec transfered the last %d seconds",
+            data->set.low_speed_limit,
+            data->set.low_speed_time);
+      return CURLE_OPERATION_TIMEOUTED;
+    }
+  }
+  else {
+    /* we keep up the required speed all right */
+    data->state.keeps_speed = now;
+  }
+  return CURLE_OK;
+}
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 34 - 0
Source/CTest/Curl/speedcheck.h

@@ -0,0 +1,34 @@
+#ifndef __SPEEDCHECK_H
+#define __SPEEDCHECK_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+#include "timeval.h"
+
+void Curl_speedinit(struct SessionHandle *data);
+CURLcode Curl_speedcheck(struct SessionHandle *data,
+                         struct timeval now);
+
+#endif

+ 956 - 0
Source/CTest/Curl/ssluse.c

@@ -0,0 +1,956 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2001, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+/*
+ * The original SSL code for curl was written by
+ * Linas Vepstas <[email protected]> and Sampo Kellomaki <[email protected]>
+ */
+
+#include "setup.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "urldata.h"
+#include "sendf.h"
+#include "formdata.h" /* for the boundary function */
+
+#ifdef USE_SSLEAY
+#include <openssl/rand.h>
+
+/* The last #include file should be: */
+#ifdef MALLOCDEBUG
+#include "memdebug.h"
+#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
+
+#ifndef HAVE_USERDATA_IN_PWD_CALLBACK
+static char global_passwd[64];
+#endif
+
+static int passwd_callback(char *buf, int num, int verify
+#if 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
+                           )
+{
+  if(verify)
+    fprintf(stderr, "%s\n", buf);
+  else {
+    if(num > (int)strlen((char *)global_passwd)) {
+      strcpy(buf, global_passwd);
+      return strlen(buf);
+    }
+  }  
+  return 0;
+}
+
+static
+bool seed_enough(int nread)
+{
+#ifdef HAVE_RAND_STATUS
+  nread = 0; /* to prevent compiler warnings */
+
+  /* only available in OpenSSL 0.9.5a and later */
+  if(RAND_status())
+    return TRUE;
+#else
+  if(nread > 500)
+    /* this is a very silly decision to make */
+    return TRUE;
+#endif
+  return FALSE; /* not enough */
+}
+
+static
+int random_the_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.ssl.random_file?
+                             data->set.ssl.random_file:RANDOM_FILE),
+                            16384);
+    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.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.ssl.egdsocket?data->set.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! */
+#ifdef HAVE_RAND_SCREEN
+  /* This one gets a random value by reading the currently shown screen */
+  RAND_screen();
+  nread = 100; /* just a value */
+#else
+  {
+    int len;
+    char *area = Curl_FormBoundary();
+    if(!area)
+      return 3; /* out of memory */
+        
+    len = strlen(area);
+    RAND_seed(area, len);
+
+    free(area); /* now remove the random junk */
+  }
+#endif
+
+  /* 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, 16384);
+    if(seed_enough(nread))
+      return nread;
+  }
+
+  infof(data, "libcurl is now using a weak random seed!\n");
+  return nread;
+}
+
+#ifndef SSL_FILETYPE_ENGINE
+#define SSL_FILETYPE_ENGINE 42
+#endif
+static int do_file_type(const char *type)
+{
+  if (!type || !type[0])
+    return SSL_FILETYPE_PEM;
+  if (curl_strequal(type, "PEM"))
+    return SSL_FILETYPE_PEM;
+  if (curl_strequal(type, "DER"))
+    return SSL_FILETYPE_ASN1;
+  if (curl_strequal(type, "ENG"))
+    return SSL_FILETYPE_ENGINE;
+  return -1;
+}
+
+static
+int cert_stuff(struct connectdata *conn,
+               char *cert_file,
+               const char *cert_type,
+               char *key_file,
+               const char *key_type)
+{
+  struct SessionHandle *data = conn->data;
+  int file_type;
+
+  if (cert_file != NULL) {
+    SSL *ssl;
+    X509 *x509;
+
+    if(data->set.key_passwd) {
+#ifndef HAVE_USERDATA_IN_PWD_CALLBACK
+      /*
+       * If password has been given, we store that in the global
+       * area (*shudder*) for a while:
+       */
+      strcpy(global_passwd, data->set.key_passwd);
+#else
+      /*
+       * We set the password in the callback userdata
+       */
+      SSL_CTX_set_default_passwd_cb_userdata(conn->ssl.ctx,
+                                             data->set.key_passwd);
+#endif
+      /* Set passwd callback: */
+      SSL_CTX_set_default_passwd_cb(conn->ssl.ctx, passwd_callback);
+    }
+
+#if 0
+    if (SSL_CTX_use_certificate_file(conn->ssl.ctx,
+                                     cert_file,
+                                     SSL_FILETYPE_PEM) != 1) {
+      failf(data, "unable to set certificate file (wrong password?)");
+      return(0);
+    }
+    if (key_file == NULL)
+      key_file=cert_file;
+
+    if (SSL_CTX_use_PrivateKey_file(conn->ssl.ctx,
+                                    key_file,
+                                    SSL_FILETYPE_PEM) != 1) {
+      failf(data, "unable to set public key file");
+      return(0);
+    }
+#else
+    /* The '#ifdef 0' section above was removed on 17-dec-2001 */
+
+    file_type = do_file_type(cert_type);
+
+    switch(file_type) {
+    case SSL_FILETYPE_PEM:
+    case SSL_FILETYPE_ASN1:
+      if (SSL_CTX_use_certificate_file(conn->ssl.ctx,
+                                       cert_file,
+                                       file_type) != 1) {
+        failf(data, "unable to set certificate file (wrong password?)");
+        return 0;
+      }
+      break;
+    case SSL_FILETYPE_ENGINE:
+      failf(data, "file type ENG for certificate not implemented");
+      return 0;
+
+    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 (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(conn->ssl.ctx,
+                                      key_file,
+                                      file_type) != 1) {
+        failf(data, "unable to set private key file\n");
+        return 0;
+      }
+      break;
+    case SSL_FILETYPE_ENGINE:
+#ifdef HAVE_OPENSSL_ENGINE_H
+      {                         /* XXXX still needs some work */
+        EVP_PKEY *priv_key = NULL;
+        if (conn && conn->data && conn->data->engine) {
+          if (!key_file || !key_file[0]) {
+            failf(data, "no key set to load from crypto engine\n");
+            return 0;
+          }
+          priv_key = ENGINE_load_private_key(conn->data->engine,key_file,
+                                             data->set.key_passwd);
+          if (!priv_key) {
+            failf(data, "failed to load private key from crypto engine\n");
+            return 0;
+          }
+          if (SSL_CTX_use_PrivateKey(conn->ssl.ctx, priv_key) != 1) {
+            failf(data, "unable to set private key\n");
+            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\n");
+          return 0;
+        }
+      }
+#else
+      failf(data, "file type ENG for private key not supported\n");
+      return 0;
+#endif
+      break;
+    default:
+      failf(data, "not supported file type for private key\n");
+      return 0;
+    }
+
+#endif
+    
+    ssl=SSL_new(conn->ssl.ctx);
+    x509=SSL_get_certificate(ssl);
+    
+    if (x509 != NULL)
+      EVP_PKEY_copy_parameters(X509_get_pubkey(x509),
+                               SSL_get_privatekey(ssl));
+    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(conn->ssl.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);
+}
+
+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);
+  X509_NAME_oneline(X509_get_subject_name(err_cert),buf,256);
+
+  return ok;
+}
+
+#endif
+
+#ifdef USE_SSLEAY
+/* "global" init done? */
+static int init_ssl=0;
+
+/* we have the "SSL is seeded" boolean global for the application to
+   prevent multiple time-consuming seedings in vain */
+static bool ssl_seeded = FALSE;
+#endif
+
+/* Global init */
+void Curl_SSL_init(void)
+{
+#ifdef USE_SSLEAY
+  /* make sure this is only done once */
+  if(0 != init_ssl)
+    return;
+
+  init_ssl++; /* never again */
+
+#ifdef HAVE_ENGINE_LOAD_BUILTIN_ENGINES
+  ENGINE_load_builtin_engines();
+#endif
+
+  /* Lets get nice error messages */
+  SSL_load_error_strings();
+
+  /* Setup all the global SSL stuff */
+  SSLeay_add_ssl_algorithms();
+#else
+  /* SSL disabled, do nothing */
+#endif
+}
+
+/* Global cleanup */
+void Curl_SSL_cleanup(void)
+{
+#ifdef USE_SSLEAY
+  if(init_ssl) {
+    /* only cleanup if we did a previous init */
+
+    /* Free the SSL error strings */
+    ERR_free_strings();
+  
+    /* EVP_cleanup() removes all ciphers and digests from the
+       table. */
+    EVP_cleanup();
+
+#ifdef HAVE_ENGINE_cleanup
+    ENGINE_cleanup();
+#endif
+
+    init_ssl=0; /* not inited any more */
+  }
+#else
+  /* SSL disabled, do nothing */
+#endif
+}
+
+#ifdef USE_SSLEAY
+
+/*
+ * This function is called when an SSL connection is closed.
+ */
+void Curl_SSL_Close(struct connectdata *conn)
+{
+  if (conn->ssl.use) {
+    /*
+      ERR_remove_state() frees the error queue associated with
+      thread pid.  If pid == 0, the current thread will have its
+      error queue removed.
+
+      Since error queue data structures are allocated
+      automatically for new threads, they must be freed when
+      threads are terminated in oder to avoid memory leaks.
+    */
+    ERR_remove_state(0);
+
+    if(conn->ssl.handle) {
+      (void)SSL_shutdown(conn->ssl.handle);
+      SSL_set_connect_state(conn->ssl.handle);
+
+      SSL_free (conn->ssl.handle);
+      conn->ssl.handle = NULL;
+    }
+    if(conn->ssl.ctx) {
+      SSL_CTX_free (conn->ssl.ctx);
+      conn->ssl.ctx = NULL;
+    }
+    conn->ssl.use = FALSE; /* get back to ordinary socket usage */
+  }
+}
+
+
+/*
+ * This sets up a session cache to the specified size.
+ */
+CURLcode Curl_SSL_InitSessions(struct SessionHandle *data, long amount)
+{
+  struct curl_ssl_session *session;
+
+  if(data->state.session)
+    /* this is just a precaution to prevent multiple inits */
+    return CURLE_OK;
+
+  session = (struct curl_ssl_session *)
+    malloc(amount * sizeof(struct curl_ssl_session));
+  if(!session)
+    return CURLE_OUT_OF_MEMORY;
+
+  /* "blank out" the newly allocated memory */
+  memset(session, 0, amount * sizeof(struct curl_ssl_session));
+
+  /* store the info in the SSL section */
+  data->set.ssl.numsessions = amount;
+  data->state.session = session;
+  data->state.sessionage = 1; /* this is brand new */
+
+  return CURLE_OK;
+}
+
+/*
+ * Check if there's a session ID for the given connection in the cache,
+ * and if there's one suitable, it is returned.
+ */
+static int Get_SSL_Session(struct connectdata *conn,
+                           SSL_SESSION **ssl_sessionid)
+{
+  struct curl_ssl_session *check;
+  struct SessionHandle *data = conn->data;
+  long i;
+
+  for(i=0; i< data->set.ssl.numsessions; i++) {
+    check = &data->state.session[i];
+    if(!check->sessionid)
+      /* not session ID means blank entry */
+      continue;
+    if(strequal(conn->name, check->name) &&
+       (conn->remote_port == check->remote_port) ) {
+      /* yes, we have a session ID! */
+      data->state.sessionage++;            /* increase general age */
+      check->age = data->state.sessionage; /* set this as used in this age */
+      *ssl_sessionid = check->sessionid;
+      return FALSE;
+    }
+  }
+  *ssl_sessionid = (SSL_SESSION *)NULL;
+  return TRUE;
+}
+
+/*
+ * Kill a single session ID entry in the cache.
+ */
+static int Kill_Single_Session(struct curl_ssl_session *session)
+{
+  if(session->sessionid) {
+    /* defensive check */
+
+    /* free the ID */
+    SSL_SESSION_free(session->sessionid);
+    session->sessionid=NULL;
+    session->age = 0; /* fresh */
+    free(session->name);
+    session->name = NULL; /* no name */
+
+    return 0; /* ok */
+  }
+  else
+    return 1;
+}
+
+/*
+ * This function is called when the 'data' struct is going away. Close
+ * down everything and free all resources!
+ */
+int Curl_SSL_Close_All(struct SessionHandle *data)
+{
+  int i;
+
+  if(data->state.session) {    
+    for(i=0; i< data->set.ssl.numsessions; i++)
+      /* the single-killer function handles empty table slots */
+      Kill_Single_Session(&data->state.session[i]);
+    
+    /* free the cache data */
+    free(data->state.session);
+  }
+#ifdef HAVE_OPENSSL_ENGINE_H
+  if (data->engine)
+  {
+    ENGINE_free(data->engine);
+    data->engine = NULL;
+  }
+#endif
+  return 0;
+}
+
+/*
+ * Extract the session id and store it in the session cache.
+ */
+static int Store_SSL_Session(struct connectdata *conn)
+{
+  SSL_SESSION *ssl_sessionid;
+  int i;
+  struct SessionHandle *data=conn->data; /* the mother of all structs */
+  struct curl_ssl_session *store = &data->state.session[0];
+  int oldest_age=data->state.session[0].age; /* zero if unused */
+
+  /* ask OpenSSL, say please */
+
+#ifdef HAVE_SSL_GET1_SESSION
+  ssl_sessionid = SSL_get1_session(conn->ssl.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
+  ssl_sessionid = SSL_get_session(conn->ssl.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
+
+  /* Now we should add the session ID and the host name to the cache, (remove
+     the oldest if necessary) */
+
+  /* find an empty slot for us, or find the oldest */
+  for(i=1; (i<data->set.ssl.numsessions) &&
+        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.numsessions)
+    /* cache is full, we must "kill" the oldest entry! */
+    Kill_Single_Session(store);
+  else
+    store = &data->state.session[i]; /* use this slot */
+  
+  /* now init the session struct wisely */
+  store->sessionid = ssl_sessionid;
+  store->age = data->state.sessionage;      /* set current age */
+  store->name = strdup(conn->name);       /* clone host name */
+  store->remote_port = conn->remote_port; /* port number */
+
+  return 0;
+}
+
+static int Curl_ASN1_UTCTIME_output(struct connectdata *conn,
+                                    const char *prefix,
+                                    ASN1_UTCTIME *tm)
+{
+  char *asn1_string;
+  int gmt=FALSE;
+  int i;
+  int year=0,month=0,day=0,hour=0,minute=0,second=0;
+  struct SessionHandle *data = conn->data;
+
+  if(!data->set.verbose)
+    return 0;
+
+  i=tm->length;
+  asn1_string=(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');
+  
+  infof(data,
+        "%s%04d-%02d-%02d %02d:%02d:%02d %s\n",
+        prefix, year+1900, month, day, hour, minute, second, (gmt?"GMT":""));
+
+  return 0;
+}
+
+#endif  
+
+/* ====================================================== */
+CURLcode
+Curl_SSLConnect(struct connectdata *conn)
+{
+  CURLcode retcode = CURLE_OK;
+
+#ifdef USE_SSLEAY
+  struct SessionHandle *data = conn->data;
+  int err;
+  char * str;
+  SSL_METHOD *req_method;
+  SSL_SESSION *ssl_sessionid=NULL;
+  ASN1_TIME *certdate;
+
+  /* mark this is being ssl enabled from here on out. */
+  conn->ssl.use = TRUE;
+
+  if(!ssl_seeded || data->set.ssl.random_file || data->set.ssl.egdsocket) {
+    /* Make funny stuff to get random input */
+    random_the_seed(data);
+
+    ssl_seeded = TRUE;
+  }
+
+  /* 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:
+    /* 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_SSLv2:
+    req_method = SSLv2_client_method();
+    break;
+  case CURL_SSLVERSION_SSLv3:
+    req_method = SSLv3_client_method();
+    break;
+  }
+    
+  conn->ssl.ctx = SSL_CTX_new(req_method);
+
+  if(!conn->ssl.ctx) {
+    failf(data, "SSL: couldn't create a context!");
+    return CURLE_OUT_OF_MEMORY;
+  }
+    
+  if(data->set.cert) {
+    if (!cert_stuff(conn,
+                    data->set.cert,
+                    data->set.cert_type,
+                    data->set.key,
+                    data->set.key_type)) {
+      /* failf() is already done in cert_stuff() */
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+  }
+
+  if(data->set.ssl.cipher_list) {
+    if (!SSL_CTX_set_cipher_list(conn->ssl.ctx,
+                                 data->set.ssl.cipher_list)) {
+      failf(data, "failed setting cipher list");
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+  }
+
+  if(data->set.ssl.verifypeer){
+    SSL_CTX_set_verify(conn->ssl.ctx,
+                       SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT|
+                       SSL_VERIFY_CLIENT_ONCE,
+                       cert_verify_callback);
+    if (!SSL_CTX_load_verify_locations(conn->ssl.ctx,
+                                       data->set.ssl.CAfile,
+                                       data->set.ssl.CApath)) {
+      failf(data,"error setting cerficate verify locations");
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+  }
+  else
+    SSL_CTX_set_verify(conn->ssl.ctx, SSL_VERIFY_NONE, cert_verify_callback);
+
+
+  /* Lets make an SSL structure */
+  conn->ssl.handle = SSL_new (conn->ssl.ctx);
+  SSL_set_connect_state (conn->ssl.handle);
+
+  conn->ssl.server_cert = 0x0;
+
+  if(!conn->bits.reuse) {
+    /* We're not re-using a connection, check if there's a cached ID we
+       can/should use here! */
+    if(!Get_SSL_Session(conn, &ssl_sessionid)) {
+      /* we got a session id, use it! */
+      SSL_set_session(conn->ssl.handle, ssl_sessionid);
+      /* Informational message */
+      infof (data, "SSL re-using session ID\n");
+    }
+  }
+
+  /* pass the raw socket into the SSL layers */
+  SSL_set_fd(conn->ssl.handle, conn->firstsocket);
+
+  do {
+    int what;
+    fd_set writefd;
+    fd_set readfd;
+    struct timeval interval;
+    long timeout_ms;
+
+    err = SSL_connect(conn->ssl.handle);
+
+    what = SSL_get_error(conn->ssl.handle, err);
+
+    FD_ZERO(&writefd);
+    FD_ZERO(&readfd);
+
+    if(SSL_ERROR_WANT_READ == what)
+      FD_SET(conn->firstsocket, &readfd);
+    else if(SSL_ERROR_WANT_WRITE == what)
+      FD_SET(conn->firstsocket, &writefd);
+    else
+      break; /* untreated error */
+
+    /* Find out if any timeout is set. If not, use 300 seconds.
+       Otherwise, figure out the most strict timeout of the two possible one
+       and then how much time that has elapsed to know how much time we
+       allow for the connect call */
+    if(data->set.timeout || data->set.connecttimeout) {
+      double has_passed;
+
+      /* Evaluate in milliseconds how much time that has passed */
+      has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start);
+
+#ifndef min
+#define min(a, b)   ((a) < (b) ? (a) : (b))
+#endif
+
+      /* get the most strict timeout of the ones converted to milliseconds */
+      if(data->set.timeout &&
+         (data->set.timeout>data->set.connecttimeout))
+        timeout_ms = data->set.timeout*1000;
+      else
+        timeout_ms = data->set.connecttimeout*1000;
+      
+      /* subtract the passed time */
+      timeout_ms -= (long)has_passed;
+      
+      if(timeout_ms < 0) {
+        /* a precaution, no need to continue if time already is up */
+        failf(data, "SSL connection timeout");
+        return CURLE_OPERATION_TIMEOUTED;
+      }
+    }
+    else
+      /* no particular time-out has been set */
+      timeout_ms=300000; /* milliseconds, default to five minutes */
+
+    interval.tv_sec = timeout_ms/1000;
+    timeout_ms -= interval.tv_sec*1000;
+
+    interval.tv_usec = timeout_ms*1000;
+
+    what = select(conn->firstsocket+1, &readfd, &writefd, NULL, &interval);
+    if(what > 0)
+      /* reabable or writable, go loop yourself */
+      continue;
+    else if(0 == what) {
+      /* timeout */
+      failf(data, "SSL connection timeout");
+      return CURLE_OPERATION_TIMEOUTED;
+    }
+    else
+      break; /* get out of loop */
+  } while(1);
+
+  /* 1  is fine
+     0  is "not successful but was shut down controlled"
+     <0 is "handshake was not successful, because a fatal error occurred" */
+  if (err <= 0) {
+    err = ERR_get_error(); 
+    failf(data, "SSL: %s", ERR_error_string(err, NULL));
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  /* Informational message */
+  infof (data, "SSL connection using %s\n",
+         SSL_get_cipher(conn->ssl.handle));
+
+  if(!ssl_sessionid) {
+    /* Since this is not a cached session ID, then we want to stach this one
+       in the cache! */
+    Store_SSL_Session(conn);
+  }
+
+  
+  /* Get server's certificate (note: beware of dynamic allocation) - opt */
+  /* major serious hack alert -- we should check certificates
+   * to authenticate the server; otherwise we risk man-in-the-middle
+   * attack
+   */
+
+  conn->ssl.server_cert = SSL_get_peer_certificate (conn->ssl.handle);
+  if(!conn->ssl.server_cert) {
+    failf(data, "SSL: couldn't get peer certificate!");
+    return CURLE_SSL_PEER_CERTIFICATE;
+  }
+  infof (data, "Server certificate:\n");
+  
+  str = X509_NAME_oneline (X509_get_subject_name (conn->ssl.server_cert),
+                           NULL, 0);
+  if(!str) {
+    failf(data, "SSL: couldn't get X509-subject!");
+    X509_free(conn->ssl.server_cert);
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+  infof(data, "\t subject: %s\n", str);
+  CRYPTO_free(str);
+
+  certdate = X509_get_notBefore(conn->ssl.server_cert);
+  Curl_ASN1_UTCTIME_output(conn, "\t start date: ", certdate);
+
+  certdate = X509_get_notAfter(conn->ssl.server_cert);
+  Curl_ASN1_UTCTIME_output(conn, "\t expire date: ", certdate);
+
+  if (data->set.ssl.verifyhost) {
+    char peer_CN[257];
+    if (X509_NAME_get_text_by_NID(X509_get_subject_name(conn->ssl.server_cert),
+                                  NID_commonName,
+                                  peer_CN,
+                                  sizeof(peer_CN)) < 0) {
+      failf(data, "SSL: unable to obtain common name from peer certificate");
+      X509_free(conn->ssl.server_cert);
+      return CURLE_SSL_PEER_CERTIFICATE;
+    }
+
+    if (!strequal(peer_CN, conn->hostname)) {
+      if (data->set.ssl.verifyhost > 1) {
+        failf(data, "SSL: certificate subject name '%s' does not match "
+              "target host name '%s'",
+              peer_CN, conn->hostname);
+        X509_free(conn->ssl.server_cert);
+        return CURLE_SSL_PEER_CERTIFICATE;
+      }
+      else
+        infof(data,
+              "\t common name: %s (does not match '%s')\n",
+              peer_CN, conn->hostname);
+    }
+    else
+      infof(data, "\t common name: %s (matched)\n", peer_CN);
+  }
+
+  str = X509_NAME_oneline (X509_get_issuer_name  (conn->ssl.server_cert),
+                           NULL, 0);
+  if(!str) {
+    failf(data, "SSL: couldn't get X509-issuer name!");
+    X509_free(conn->ssl.server_cert);
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+  infof(data, "\t issuer: %s\n", str);
+  CRYPTO_free(str);
+
+  /* We could do all sorts of certificate verification stuff here before
+     deallocating the certificate. */
+
+  if(data->set.ssl.verifypeer) {
+    data->set.ssl.certverifyresult=SSL_get_verify_result(conn->ssl.handle);
+    if (data->set.ssl.certverifyresult != X509_V_OK) {
+      failf(data, "SSL certificate verify result: %d",
+            data->set.ssl.certverifyresult);
+      retcode = CURLE_SSL_PEER_CERTIFICATE;
+    }
+  }
+  else
+    data->set.ssl.certverifyresult=0;
+
+  X509_free(conn->ssl.server_cert);
+#else /* USE_SSLEAY */
+  /* this is for "-ansi -Wall -pedantic" to stop complaining!   (rabe) */
+  (void) conn;
+#endif
+  return retcode;
+}
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 38 - 0
Source/CTest/Curl/ssluse.h

@@ -0,0 +1,38 @@
+#ifndef __SSLUSE_H
+#define __SSLUSE_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+#include "urldata.h"
+CURLcode Curl_SSLConnect(struct connectdata *conn);
+
+void Curl_SSL_init(void);    /* Global SSL init */
+void Curl_SSL_cleanup(void); /* Global SSL cleanup */
+
+/* init the SSL session ID cache */
+CURLcode Curl_SSL_InitSessions(struct SessionHandle *, long);
+void Curl_SSL_Close(struct connectdata *conn); /* close a SSL connection */
+
+/* tell the SSL stuff to close down all open information regarding 
+   connections (and thus session ID caching etc) */
+int Curl_SSL_Close_All(struct SessionHandle *data);
+#endif

+ 121 - 0
Source/CTest/Curl/strequal.c

@@ -0,0 +1,121 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+#include <ctype.h>
+
+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
+}
+
+int curl_strnequal(const char *first, const char *second, size_t max)
+{
+#if defined(HAVE_STRCASECMP)
+  return !strncasecmp(first, second, max);
+#elif defined(HAVE_STRCMPI)
+  return !strncmpi(first, second, max);
+#elif defined(HAVE_STRICMP)
+  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;
+  size_t dlen;
+
+  /* Find the end of dst and adjust bytes left but don't go past end */
+  while (n-- != 0 && *d != '\0')
+    d++;
+  dlen = d - dst;
+  n = siz - dlen;
+
+  if (n == 0)
+    return(dlen + strlen(s));
+  while (*s != '\0') {
+    if (n != 1) {
+      *d++ = *s;
+      n--;
+    }
+    s++;
+  }
+  *d = '\0';
+
+  return(dlen + (s - src));     /* count does not include NUL */
+}
+#endif
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 40 - 0
Source/CTest/Curl/strequal.h

@@ -0,0 +1,40 @@
+#ifndef __STREQUAL_H
+#define __STREQUAL_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+/*
+ * These two actually are public functions.
+ */
+int curl_strequal(const char *first, const char *second);
+int curl_strnequal(const char *first, const char *second, size_t max);
+
+#define strequal(a,b) curl_strequal(a,b)
+#define strnequal(a,b,c) curl_strnequal(a,b,c)
+
+#ifndef HAVE_STRLCAT
+#define strlcat(x,y,z) Curl_strlcat(x,y,z)
+size_t Curl_strlcat(char *dst, const char *src, size_t siz);
+#endif
+
+#endif

+ 74 - 0
Source/CTest/Curl/strtok.c

@@ -0,0 +1,74 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2001, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+#ifndef HAVE_STRTOK_R
+#include <stddef.h>
+#include <string.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 */
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 38 - 0
Source/CTest/Curl/strtok.h

@@ -0,0 +1,38 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#ifndef _CURL_STRTOK_R_H
+#define _CURL_STRTOK_R_H
+
+#include "setup.h"
+#include <stddef.h>
+
+#ifndef HAVE_STRTOK_R
+char *Curl_strtok_r(char *s, const char *delim, char **last);
+#define strtok_r Curl_strtok_r
+#else
+#include <string.h>
+#endif
+
+#endif
+

+ 1213 - 0
Source/CTest/Curl/telnet.c

@@ -0,0 +1,1213 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+/* -- WIN32 approved -- */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+
+#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
+#include <winsock2.h>
+#include <time.h>
+#include <io.h>
+#else
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <netdb.h>
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#include <sys/ioctl.h>
+#include <signal.h>
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+
+#endif
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "transfer.h"
+#include "sendf.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#define  TELOPTS
+#define  TELCMDS
+
+#include "arpa_telnet.h"
+
+/* The last #include file should be: */
+#ifdef MALLOCDEBUG
+#include "memdebug.h"
+#endif
+
+#define SUBBUFSIZE 512
+
+#define  SB_CLEAR(x)  x->subpointer = x->subbuffer;
+#define  SB_TERM(x)   { x->subend = x->subpointer; SB_CLEAR(x); }
+#define  SB_ACCUM(x,c) if (x->subpointer < (x->subbuffer+sizeof x->subbuffer)) { \
+            *x->subpointer++ = (c); \
+         }
+
+#define  SB_GET(x) ((*x->subpointer++)&0xff)
+#define  SB_PEEK(x)   ((*x->subpointer)&0xff)
+#define  SB_EOF(x) (x->subpointer >= x->subend)
+#define  SB_LEN(x) (x->subend - x->subpointer)
+
+static
+void telrcv(struct connectdata *,
+            unsigned char *inbuf,       /* Data received from socket */
+            int count);                 /* Number of bytes received */
+
+static void printoption(struct SessionHandle *data,
+                        const char *direction,
+                        int cmd, int option);
+
+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, int length);
+static void suboption(struct connectdata *);
+
+/* For negotiation compliant to RFC 1143 */
+#define NO      0
+#define YES     1
+#define WANTYES 2
+#define WANTNO  3
+
+#define EMPTY    0
+#define OPPOSITE 1
+
+/*
+ * Telnet receiver states for fsm
+ */
+typedef enum
+{
+   TS_DATA = 0,
+   TS_IAC,
+   TS_WILL,
+   TS_WONT,
+   TS_DO,
+   TS_DONT,
+   TS_CR,
+   TS_SB,   /* sub-option collection */
+   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]; 
+  char subopt_ttype[32];             /* Set with suboption TTYPE */
+  char subopt_xdisploc[128];          /* Set with suboption XDISPLOC */
+  struct curl_slist *telnet_vars; /* Environment variables */
+
+  /* suboptions */
+  char subbuffer[SUBBUFSIZE];
+  char *subpointer, *subend;      /* buffer for sub-options */
+  
+  TelnetReceive telrcv_state;
+};
+
+static
+CURLcode init_telnet(struct connectdata *conn)
+{
+  struct TELNET *tn;
+
+  tn = (struct TELNET *)malloc(sizeof(struct TELNET));
+  if(!tn)
+    return CURLE_OUT_OF_MEMORY;
+  
+  conn->proto.telnet = (void *)tn; /* make us known */
+
+  memset(tn, 0, sizeof(struct TELNET));
+
+  tn->telrcv_state = TS_DATA;
+
+  /* Init suboptions */
+  SB_CLEAR(tn);
+
+  /* Set all options to NO */
+#if 0
+  /* NO is zero => default fill pattern */
+  memset(tn->us, NO, 256);
+  memset(tn->usq, NO, 256);
+  memset(tn->us_preferred, NO, 256);
+  memset(tn->him, NO, 256);
+  memset(tn->himq, NO, 256);
+  memset(tn->him_preferred, NO, 256);
+#endif
+  /* Set the options we want by default */
+  tn->us_preferred[TELOPT_BINARY] = YES;
+  tn->us_preferred[TELOPT_SGA] = YES;
+  tn->him_preferred[TELOPT_BINARY] = YES;
+  tn->him_preferred[TELOPT_SGA] = YES;
+
+  return CURLE_OK;
+}
+
+static void negotiate(struct connectdata *conn)
+{
+  int i;
+  struct TELNET *tn = (struct TELNET *)conn->proto.telnet;
+   
+  for(i = 0;i < NTELOPTS;i++)
+  {
+    if(tn->us_preferred[i] == YES)
+      set_local_option(conn, i, YES);
+      
+    if(tn->him_preferred[i] == YES)
+      set_remote_option(conn, i, YES);
+  }
+}
+
+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 == IAC)
+    {
+      if (TELCMD_OK(option))
+        Curl_infof(data, "%s IAC %s\n", direction, TELCMD(option));
+      else
+        Curl_infof(data, "%s IAC %d\n", direction, option);
+    }
+    else
+    {
+      fmt = (cmd == WILL) ? "WILL" : (cmd == WONT) ? "WONT" :
+        (cmd == DO) ? "DO" : (cmd == DONT) ? "DONT" : 0;
+      if (fmt)
+      {
+        if (TELOPT_OK(option))
+          opt = TELOPT(option);
+        else if (option == TELOPT_EXOPL)
+          opt = "EXOPL";
+        else
+          opt = NULL;
+
+        if(opt)
+          Curl_infof(data, "%s %s %s\n", direction, fmt, opt);
+        else
+          Curl_infof(data, "%s %s %d\n", direction, fmt, option);
+      }
+      else
+        Curl_infof(data, "%s %d %d\n", direction, cmd, option);
+    }
+  }
+}
+
+static void send_negotiation(struct connectdata *conn, int cmd, int option)
+{
+   unsigned char buf[3];
+
+   buf[0] = IAC;
+   buf[1] = cmd;
+   buf[2] = option;
+   
+   swrite(conn->firstsocket, buf, 3);
+   
+   printoption(conn->data, "SENT", cmd, option);
+}
+
+static
+void set_remote_option(struct connectdata *conn, int option, int newstate)
+{
+  struct TELNET *tn = (struct TELNET *)conn->proto.telnet;
+  if(newstate == YES)
+  {
+    switch(tn->him[option])
+    {
+      case NO:
+        tn->him[option] = WANTYES;
+        send_negotiation(conn, DO, option);
+        break;
+         
+      case YES:
+        /* Already enabled */
+        break;
+         
+      case WANTNO:
+        switch(tn->himq[option])
+        {
+          case EMPTY:
+            /* Already negotiating for YES, queue the request */
+            tn->himq[option] = OPPOSITE;
+            break;
+          case OPPOSITE:
+            /* Error: already queued an enable request */
+            break;
+        }
+        break;
+         
+      case WANTYES:
+        switch(tn->himq[option])
+        {
+          case EMPTY:
+            /* Error: already negotiating for enable */
+            break;
+          case OPPOSITE:
+            tn->himq[option] = EMPTY;
+            break;
+        }
+        break;
+    }
+  }
+  else /* NO */
+  {
+    switch(tn->him[option])
+    {
+      case NO:
+        /* Already disabled */
+        break;
+         
+      case YES:
+        tn->him[option] = WANTNO;
+        send_negotiation(conn, DONT, option);
+        break;
+         
+      case WANTNO:
+        switch(tn->himq[option])
+        {
+          case EMPTY:
+            /* Already negotiating for NO */
+            break;
+          case OPPOSITE:
+            tn->himq[option] = EMPTY;
+            break;
+        }
+        break;
+         
+      case WANTYES:
+        switch(tn->himq[option])
+        {
+          case EMPTY:
+            tn->himq[option] = OPPOSITE;
+            break;
+          case OPPOSITE:
+            break;
+        }
+        break;
+    }
+  }
+}
+
+static
+void rec_will(struct connectdata *conn, int option)
+{
+  struct TELNET *tn = (struct TELNET *)conn->proto.telnet;
+  switch(tn->him[option])
+  {
+    case NO:
+      if(tn->him_preferred[option] == YES)
+      {
+        tn->him[option] = YES;
+        send_negotiation(conn, DO, option);
+      }
+      else
+      {
+        send_negotiation(conn, DONT, option);
+      }
+      break;
+         
+    case YES:
+      /* Already enabled */
+      break;
+         
+    case WANTNO:
+      switch(tn->himq[option])
+      {
+        case EMPTY:
+          /* Error: DONT answered by WILL */
+          tn->him[option] = NO;
+          break;
+        case OPPOSITE:
+          /* Error: DONT answered by WILL */
+          tn->him[option] = YES;
+          tn->himq[option] = EMPTY;
+          break;
+      }
+      break;
+         
+    case WANTYES:
+      switch(tn->himq[option])
+      {
+        case EMPTY:
+          tn->him[option] = YES;
+          break;
+        case OPPOSITE:
+          tn->him[option] = WANTNO;
+          tn->himq[option] = EMPTY;
+          send_negotiation(conn, DONT, option);
+          break;
+      }
+      break;
+  }
+}
+   
+static
+void rec_wont(struct connectdata *conn, int option)
+{
+  struct TELNET *tn = (struct TELNET *)conn->proto.telnet;
+  switch(tn->him[option])
+  {
+    case NO:
+      /* Already disabled */
+      break;
+         
+    case YES:
+      tn->him[option] = NO;
+      send_negotiation(conn, DONT, option);
+      break;
+         
+    case WANTNO:
+      switch(tn->himq[option])
+      {
+        case EMPTY:
+          tn->him[option] = NO;
+          break;
+         
+        case OPPOSITE:
+          tn->him[option] = WANTYES;
+          tn->himq[option] = EMPTY;
+          send_negotiation(conn, DO, option);
+          break;
+      }
+      break;
+         
+    case WANTYES:
+      switch(tn->himq[option])
+      {
+        case EMPTY:
+          tn->him[option] = NO;
+          break;
+        case OPPOSITE:
+          tn->him[option] = NO;
+          tn->himq[option] = EMPTY;
+          break;
+      }
+      break;
+  }
+}
+   
+void set_local_option(struct connectdata *conn, int option, int newstate)
+{
+  struct TELNET *tn = (struct TELNET *)conn->proto.telnet;
+  if(newstate == YES)
+  {
+    switch(tn->us[option])
+    {
+      case NO:
+        tn->us[option] = WANTYES;
+        send_negotiation(conn, WILL, option);
+        break;
+         
+      case YES:
+        /* Already enabled */
+        break;
+         
+      case WANTNO:
+        switch(tn->usq[option])
+        {
+          case EMPTY:
+            /* Already negotiating for YES, queue the request */
+            tn->usq[option] = OPPOSITE;
+            break;
+          case OPPOSITE:
+            /* Error: already queued an enable request */
+            break;
+        }
+        break;
+         
+      case WANTYES:
+        switch(tn->usq[option])
+        {
+          case EMPTY:
+            /* Error: already negotiating for enable */
+            break;
+          case OPPOSITE:
+            tn->usq[option] = EMPTY;
+            break;
+        }
+        break;
+    }
+  }
+  else /* NO */
+  {
+    switch(tn->us[option])
+    {
+      case NO:
+        /* Already disabled */
+        break;
+         
+      case YES:
+        tn->us[option] = WANTNO;
+        send_negotiation(conn, WONT, option);
+        break;
+         
+      case WANTNO:
+        switch(tn->usq[option])
+        {
+          case EMPTY:
+            /* Already negotiating for NO */
+            break;
+          case OPPOSITE:
+            tn->usq[option] = EMPTY;
+            break;
+        }
+        break;
+         
+      case WANTYES:
+        switch(tn->usq[option])
+        {
+          case EMPTY:
+            tn->usq[option] = OPPOSITE;
+            break;
+          case OPPOSITE:
+            break;
+        }
+        break;
+    }
+  }
+}
+
+static
+void rec_do(struct connectdata *conn, int option)
+{
+  struct TELNET *tn = (struct TELNET *)conn->proto.telnet;
+  switch(tn->us[option])
+  {
+    case NO:
+      if(tn->us_preferred[option] == YES)
+      {
+        tn->us[option] = YES;
+        send_negotiation(conn, WILL, option);
+      }
+      else
+      {
+        send_negotiation(conn, WONT, option);
+      }
+      break;
+         
+    case YES:
+      /* Already enabled */
+      break;
+         
+    case WANTNO:
+      switch(tn->usq[option])
+      {
+        case EMPTY:
+          /* Error: DONT answered by WILL */
+          tn->us[option] = NO;
+          break;
+        case OPPOSITE:
+          /* Error: DONT answered by WILL */
+          tn->us[option] = YES;
+          tn->usq[option] = EMPTY;
+          break;
+      }
+      break;
+         
+    case WANTYES:
+      switch(tn->usq[option])
+      {
+        case EMPTY:
+          tn->us[option] = YES;
+          break;
+        case OPPOSITE:
+          tn->us[option] = WANTNO;
+          tn->himq[option] = EMPTY;
+          send_negotiation(conn, WONT, option);
+          break;
+      }
+      break;
+  }
+}
+
+static   
+void rec_dont(struct connectdata *conn, int option)
+{
+  struct TELNET *tn = (struct TELNET *)conn->proto.telnet;
+  switch(tn->us[option])
+  {
+    case NO:
+      /* Already disabled */
+      break;
+         
+    case YES:
+      tn->us[option] = NO;
+      send_negotiation(conn, WONT, option);
+      break;
+         
+    case WANTNO:
+      switch(tn->usq[option])
+      {
+        case EMPTY:
+          tn->us[option] = NO;
+          break;
+         
+        case OPPOSITE:
+          tn->us[option] = WANTYES;
+          tn->usq[option] = EMPTY;
+          send_negotiation(conn, WILL, option);
+          break;
+      }
+      break;
+         
+    case WANTYES:
+      switch(tn->usq[option])
+      {
+        case EMPTY:
+          tn->us[option] = NO;
+          break;
+        case OPPOSITE:
+          tn->us[option] = NO;
+          tn->usq[option] = EMPTY;
+          break;
+      }
+      break;
+  }
+}
+
+
+static void printsub(struct SessionHandle *data,
+                     int direction,             /* '<' or '>' */
+                     unsigned char *pointer,    /* where suboption data is */
+                     int length)                /* length of suboption data */
+{
+  int i = 0;
+
+  if (data->set.verbose)
+  {
+    if (direction)
+    {
+      Curl_infof(data, "%s IAC SB ", (direction == '<')? "RCVD":"SENT");
+      if (length >= 3)
+      {
+        int j;
+
+        i = pointer[length-2];
+        j = pointer[length-1];
+
+        if (i != IAC || j != SE)
+        {
+          Curl_infof(data, "(terminated by ");
+          if (TELOPT_OK(i))
+            Curl_infof(data, "%s ", TELOPT(i));
+          else if (TELCMD_OK(i))
+            Curl_infof(data, "%s ", TELCMD(i));
+          else
+            Curl_infof(data, "%d ", i);
+          if (TELOPT_OK(j))
+            Curl_infof(data, "%s", TELOPT(j));
+          else if (TELCMD_OK(j))
+            Curl_infof(data, "%s", TELCMD(j));
+          else
+            Curl_infof(data, "%d", j);
+          Curl_infof(data, ", not IAC SE!) ");
+        }
+      }
+      length -= 2;
+    }
+    if (length < 1)
+    {
+      Curl_infof(data, "(Empty suboption?)");
+      return;
+    }
+
+    if (TELOPT_OK(pointer[0])) {
+      switch(pointer[0]) {
+        case TELOPT_TTYPE:
+        case TELOPT_XDISPLOC:
+        case TELOPT_NEW_ENVIRON:
+          Curl_infof(data, "%s", TELOPT(pointer[0]));
+          break;
+        default:
+          Curl_infof(data, "%s (unsupported)", TELOPT(pointer[0]));
+          break;
+      }
+    }
+    else
+      Curl_infof(data, "%d (unknown)", pointer[i]);
+
+    switch(pointer[1]) {
+      case TELQUAL_IS:
+        Curl_infof(data, " IS");
+        break;
+      case TELQUAL_SEND:
+        Curl_infof(data, " SEND");
+        break;
+      case TELQUAL_INFO:
+        Curl_infof(data, " INFO/REPLY");
+        break;
+      case TELQUAL_NAME:
+        Curl_infof(data, " NAME");
+        break;
+    }
+      
+    switch(pointer[0]) {
+      case TELOPT_TTYPE:
+      case TELOPT_XDISPLOC:
+        pointer[length] = 0;
+        Curl_infof(data, " \"%s\"", &pointer[2]);
+        break;
+      case TELOPT_NEW_ENVIRON:
+        if(pointer[1] == TELQUAL_IS) {
+          Curl_infof(data, " ");
+          for(i = 3;i < length;i++) {
+            switch(pointer[i]) {
+              case NEW_ENV_VAR:
+                Curl_infof(data, ", ");
+                break;
+              case NEW_ENV_VALUE:
+                Curl_infof(data, " = ");
+                break;
+              default:
+                Curl_infof(data, "%c", pointer[i]);
+                break;
+            }
+          }
+        }
+        break;
+      default:
+        for (i = 2; i < length; i++)
+          Curl_infof(data, " %.2x", pointer[i]);
+        break;
+    }
+      
+    if (direction)
+    {
+      Curl_infof(data, "\n");
+    }
+  }
+}
+
+static int check_telnet_options(struct connectdata *conn)
+{
+  struct curl_slist *head;
+  char option_keyword[128];
+  char option_arg[256];
+  char *buf;
+  struct SessionHandle *data = conn->data;
+  struct TELNET *tn = (struct TELNET *)conn->proto.telnet;
+
+  /* Add the user name as an environment variable if it
+     was given on the command line */
+  if(conn->bits.user_passwd)
+  {
+    char *buf = malloc(256);
+    sprintf(buf, "USER,%s", data->state.user);
+    tn->telnet_vars = curl_slist_append(tn->telnet_vars, buf);
+
+    tn->us_preferred[TELOPT_NEW_ENVIRON] = 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(strequal(option_keyword, "TTYPE")) {
+        strncpy(tn->subopt_ttype, option_arg, 31);
+        tn->subopt_ttype[31] = 0; /* String termination */
+        tn->us_preferred[TELOPT_TTYPE] = YES;
+        continue;
+      }
+
+      /* Display variable */
+      if(strequal(option_keyword, "XDISPLOC")) {
+        strncpy(tn->subopt_xdisploc, option_arg, 127);
+        tn->subopt_xdisploc[127] = 0; /* String termination */
+        tn->us_preferred[TELOPT_XDISPLOC] = YES;
+        continue;
+      }
+
+      /* Environment variable */
+      if(strequal(option_keyword, "NEW_ENV")) {
+        buf = strdup(option_arg);
+        if(!buf)
+          return CURLE_OUT_OF_MEMORY;
+        tn->telnet_vars = curl_slist_append(tn->telnet_vars, buf);
+        tn->us_preferred[TELOPT_NEW_ENVIRON] = YES;
+        continue;
+      }
+
+      failf(data, "Unknown telnet option %s", head->data);
+      return CURLE_UNKNOWN_TELNET_OPTION;
+    } else {
+      failf(data, "Syntax error in telnet option: %s", head->data);
+      return CURLE_TELNET_OPTION_SYNTAX;
+    }
+  }
+
+  return CURLE_OK;
+}
+
+/*
+ * 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 subchar;
+  unsigned char temp[2048];
+  int len;
+  int tmplen;
+  char varname[128];
+  char varval[128];
+  struct SessionHandle *data = conn->data;
+  struct TELNET *tn = (struct TELNET *)conn->proto.telnet;
+
+  printsub(data, '<', (unsigned char *)tn->subbuffer, SB_LEN(tn)+2);
+  switch (subchar = SB_GET(tn)) {
+    case TELOPT_TTYPE:
+      len = strlen(tn->subopt_ttype) + 4 + 2;
+      snprintf((char *)temp, sizeof(temp),
+               "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
+               TELQUAL_IS, tn->subopt_ttype, IAC, SE);
+      swrite(conn->firstsocket, temp, len);
+      printsub(data, '>', &temp[2], len-2);
+      break;
+    case TELOPT_XDISPLOC:
+      len = strlen(tn->subopt_xdisploc) + 4 + 2;
+      snprintf((char *)temp, sizeof(temp),
+               "%c%c%c%c%s%c%c", IAC, SB, TELOPT_XDISPLOC,
+               TELQUAL_IS, tn->subopt_xdisploc, IAC, SE);
+      swrite(conn->firstsocket, temp, len);
+      printsub(data, '>', &temp[2], len-2);
+      break;
+    case TELOPT_NEW_ENVIRON:
+      snprintf((char *)temp, sizeof(temp),
+               "%c%c%c%c", IAC, SB, TELOPT_NEW_ENVIRON, 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[^,],%s", varname, varval);
+          snprintf((char *)&temp[len], sizeof(temp) - len,
+                   "%c%s%c%s", NEW_ENV_VAR, varname,
+                   NEW_ENV_VALUE, varval);
+          len += tmplen;
+        }
+      }
+      snprintf((char *)&temp[len], sizeof(temp) - len,
+               "%c%c", IAC, SE);
+      len += 2;
+      swrite(conn->firstsocket, temp, len);
+      printsub(data, '>', &temp[2], len-2);
+      break;
+  }
+  return;
+}
+
+static
+void telrcv(struct connectdata *conn,
+            unsigned char *inbuf,       /* Data received from socket */
+            int count)                  /* Number of bytes received */
+{
+  unsigned char c;
+  int index = 0;
+  struct SessionHandle *data = conn->data;
+  struct TELNET *tn = (struct TELNET *)conn->proto.telnet;
+
+  while(count--)
+  {
+    c = inbuf[index++];
+
+    switch (tn->telrcv_state)
+    {
+      case TS_CR:
+        tn->telrcv_state = TS_DATA;
+        if (c == '\0')
+        {
+          break;   /* Ignore \0 after CR */
+        }
+
+        Curl_client_write(data, CLIENTWRITE_BODY, (char *)&c, 1);
+        continue;
+
+      case TS_DATA:
+        if (c == IAC)
+        {
+          tn->telrcv_state = TS_IAC;
+          break;
+        }
+        else if(c == '\r')
+        {
+          tn->telrcv_state = TS_CR;
+        }
+
+        Curl_client_write(data, CLIENTWRITE_BODY, (char *)&c, 1);
+        continue;
+
+      case TS_IAC:
+      process_iac:
+      switch (c)
+      {
+        case WILL:
+          tn->telrcv_state = TS_WILL;
+          continue;
+        case WONT:
+          tn->telrcv_state = TS_WONT;
+          continue;
+        case DO:
+          tn->telrcv_state = TS_DO;
+          continue;
+        case DONT:
+          tn->telrcv_state = TS_DONT;
+          continue;
+        case SB:
+          SB_CLEAR(tn);
+          tn->telrcv_state = TS_SB;
+          continue;
+        case IAC:
+          Curl_client_write(data, CLIENTWRITE_BODY, (char *)&c, 1);
+          break;
+        case DM:
+        case NOP:
+        case GA:
+        default:
+          printoption(data, "RCVD", IAC, c);
+          break;
+      }
+      tn->telrcv_state = TS_DATA;
+      continue;
+
+      case TS_WILL:
+        printoption(data, "RCVD", WILL, c);
+        tn->please_negotiate = 1;
+        rec_will(conn, c);
+        tn->telrcv_state = TS_DATA;
+        continue;
+      
+      case TS_WONT:
+        printoption(data, "RCVD", WONT, c);
+        tn->please_negotiate = 1;
+        rec_wont(conn, c);
+        tn->telrcv_state = TS_DATA;
+        continue;
+      
+      case TS_DO:
+        printoption(data, "RCVD", DO, c);
+        tn->please_negotiate = 1;
+        rec_do(conn, c);
+        tn->telrcv_state = TS_DATA;
+        continue;
+      
+      case TS_DONT:
+        printoption(data, "RCVD", DONT, c);
+        tn->please_negotiate = 1;
+        rec_dont(conn, c);
+        tn->telrcv_state = TS_DATA;
+        continue;
+
+      case TS_SB:
+        if (c == IAC)
+        {
+          tn->telrcv_state = TS_SE;
+        }
+        else
+        {
+          SB_ACCUM(tn,c);
+        }
+        continue;
+
+      case TS_SE:
+        if (c != SE)
+        {
+          if (c != IAC)
+          {
+            /*
+             * This is an error.  We only expect to get
+             * "IAC IAC" or "IAC SE".  Several things may
+             * have happend.  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.
+             */
+            SB_ACCUM(tn, (unsigned char)IAC);
+            SB_ACCUM(tn, c);
+            tn->subpointer -= 2;
+            SB_TERM(tn);
+            
+            printoption(data, "In SUBOPTION processing, RCVD", IAC, c);
+            suboption(conn);   /* handle sub-option */
+            tn->telrcv_state = TS_IAC;
+            goto process_iac;
+          }
+          SB_ACCUM(tn,c);
+          tn->telrcv_state = TS_SB;
+        }
+        else
+        {
+          SB_ACCUM(tn, (unsigned char)IAC);
+          SB_ACCUM(tn, (unsigned char)SE);
+          tn->subpointer -= 2;
+          SB_TERM(tn);
+          suboption(conn);   /* handle sub-option */
+          tn->telrcv_state = TS_DATA;
+        }
+        break;
+    }
+  }
+}
+
+CURLcode Curl_telnet_done(struct connectdata *conn)
+{
+  struct TELNET *tn = (struct TELNET *)conn->proto.telnet;
+  curl_slist_free_all(tn->telnet_vars);
+
+  free(conn->proto.telnet);
+  conn->proto.telnet = NULL;
+
+  return CURLE_OK;
+}
+
+CURLcode Curl_telnet(struct connectdata *conn)
+{
+  CURLcode code;
+  struct SessionHandle *data = conn->data;
+  int sockfd = conn->firstsocket;
+#ifdef WIN32
+  WSAEVENT event_handle;
+  WSANETWORKEVENTS events;
+  HANDLE stdin_handle;
+  HANDLE objs[2];
+  DWORD waitret;
+#else
+  fd_set readfd;
+  fd_set keepfd;
+#endif
+  bool keepon = TRUE;
+  char *buf = data->state.buffer;
+  ssize_t nread;
+  struct TELNET *tn;
+
+  code = init_telnet(conn);
+  if(code)
+    return code;
+
+  tn = (struct TELNET *)conn->proto.telnet;
+
+  code = check_telnet_options(conn);
+  if(code)
+    return code;
+
+#ifdef WIN32
+  /* 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 = WSACreateEvent();
+
+  /* The get the Windows file handle for stdin */
+  stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
+
+  /* Create the list of objects to wait for */
+  objs[0] = stdin_handle;
+  objs[1] = event_handle;
+
+  /* Tell winsock what events we want to listen to */
+  if(WSAEventSelect(sockfd, event_handle, FD_READ|FD_CLOSE) == SOCKET_ERROR) {
+    return 0;
+  }
+
+  /* Keep on listening and act on events */
+  while(keepon) {
+    waitret = WaitForMultipleObjects(2, objs, FALSE, INFINITE);
+    switch(waitret - WAIT_OBJECT_0)
+    {
+      case 0:
+      {
+        unsigned char outbuf[2];
+        int out_count = 0;
+        ssize_t bytes_written;
+        char *buffer = buf;
+              
+        if(!ReadFile(stdin_handle, buf, 255, &nread, NULL)) {
+          keepon = FALSE;
+          break;
+        }
+        
+        while(nread--) {
+          outbuf[0] = *buffer++;
+          out_count = 1;
+          if(outbuf[0] == IAC)
+            outbuf[out_count++] = IAC;
+          
+          Curl_write(conn, conn->firstsocket, outbuf,
+                     out_count, &bytes_written);
+        }
+      }
+      break;
+      
+      case 1:
+        if(WSAEnumNetworkEvents(sockfd, event_handle, &events)
+           != SOCKET_ERROR)
+        {
+          if(events.lNetworkEvents & FD_READ)
+          {
+            /* This reallu OUGHT to check its return code. */
+            Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread);
+            
+            telrcv(conn, (unsigned char *)buf, nread);
+            
+            fflush(stdout);
+            
+            /* 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;
+    }
+  }
+#else
+  FD_ZERO (&readfd);            /* clear it */
+  FD_SET (sockfd, &readfd);
+  FD_SET (0, &readfd);
+
+  keepfd = readfd;
+
+  while (keepon) {
+    readfd = keepfd;            /* set this every lap in the loop */
+
+    switch (select (sockfd + 1, &readfd, NULL, NULL, NULL)) {
+    case -1:                    /* error, stop reading */
+      keepon = FALSE;
+      continue;
+    case 0:                     /* timeout */
+      break;
+    default:                    /* read! */
+      if(FD_ISSET(0, &readfd)) { /* read from stdin */
+        unsigned char outbuf[2];
+        int out_count = 0;
+        ssize_t bytes_written;
+        char *buffer = buf;
+        
+        nread = read(0, buf, 255);
+
+        while(nread--) {
+          outbuf[0] = *buffer++;
+          out_count = 1;
+          if(outbuf[0] == IAC)
+            outbuf[out_count++] = IAC;
+      
+          Curl_write(conn, conn->firstsocket, outbuf,
+                     out_count, &bytes_written);
+        }
+      }
+
+      if(FD_ISSET(sockfd, &readfd)) {
+        /* This OUGHT to check the return code... */
+        Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread);
+
+        /* if we receive 0 or less here, the server closed the connection and
+           we bail out from this! */
+        if (nread <= 0) {
+          keepon = FALSE;
+          break;
+        }
+
+        telrcv(conn, (unsigned char *)buf, nread);
+
+        /* 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;
+        }
+      }
+    }
+  }
+#endif
+  /* mark this as "no further transfer wanted" */
+  return Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+}
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 29 - 0
Source/CTest/Curl/telnet.h

@@ -0,0 +1,29 @@
+#ifndef __TELNET_H
+#define __TELNET_H
+
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+CURLcode Curl_telnet(struct connectdata *conn);
+CURLcode Curl_telnet_done(struct connectdata *conn);
+
+#endif

+ 89 - 0
Source/CTest/Curl/timeval.c

@@ -0,0 +1,89 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+#include "timeval.h"
+
+#ifndef HAVE_GETTIMEOFDAY
+
+#ifdef WIN32
+int
+gettimeofday (struct timeval *tp, void *nothing)
+{
+ SYSTEMTIME st;
+ time_t tt;
+ struct tm tmtm;
+ /* mktime converts local to UTC */
+ GetLocalTime (&st);
+ tmtm.tm_sec = st.wSecond;
+ tmtm.tm_min = st.wMinute;
+ tmtm.tm_hour = st.wHour;
+ tmtm.tm_mday = st.wDay;
+ tmtm.tm_mon = st.wMonth - 1;
+ tmtm.tm_year = st.wYear - 1900;
+ tmtm.tm_isdst = -1;
+ tt = mktime (&tmtm);
+ tp->tv_sec = tt;
+ tp->tv_usec = st.wMilliseconds * 1000;
+ return 1;
+}
+#define HAVE_GETTIMEOFDAY
+#endif
+#endif
+
+struct timeval Curl_tvnow (void)
+{
+ struct timeval now;
+#ifdef HAVE_GETTIMEOFDAY
+ gettimeofday (&now, NULL);
+#else
+ now.tv_sec = (long) time(NULL);
+ now.tv_usec = 0;
+#endif
+ return now;
+}
+
+/*
+ * Make sure that the first argument is the more recent time, as otherwise
+ * we'll get a weird negative time-diff back...
+ */
+long Curl_tvdiff (struct timeval newer, struct timeval older)
+{
+  return (newer.tv_sec-older.tv_sec)*1000+
+    (499+newer.tv_usec-older.tv_usec)/1000;
+}
+
+long Curl_tvlong (struct timeval t1)
+{
+ return t1.tv_sec;
+}
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 52 - 0
Source/CTest/Curl/timeval.h

@@ -0,0 +1,52 @@
+#ifndef __TIMEVAL_H
+#define __TIMEVAL_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2000, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
+#include <time.h>
+#include <winsock.h>
+#else
+#include <sys/time.h>
+#endif
+
+
+#ifndef HAVE_GETTIMEOFDAY
+#if !defined(_WINSOCKAPI_) && !defined(__MINGW32__)
+struct timeval {
+ long tv_sec;
+ long tv_usec;
+};
+#endif
+#endif
+
+struct timeval Curl_tvnow (void);
+
+/* the diff is from now on returned in number of milliseconds! */
+long Curl_tvdiff (struct timeval t1, struct timeval t2);
+
+long Curl_tvlong (struct timeval t1);
+
+#endif

+ 1357 - 0
Source/CTest/Curl/transfer.c

@@ -0,0 +1,1357 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2001, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+/* -- WIN32 approved -- */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+
+#include "strequal.h"
+
+#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
+#include <winsock.h>
+#include <time.h>
+#include <io.h>
+#else
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <netdb.h>
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#include <sys/ioctl.h>
+#include <signal.h>
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#ifndef HAVE_SELECT
+#error "We can't compile without select() support!"
+#endif
+#ifndef HAVE_SOCKET
+#error "We can't compile without socket() support!"
+#endif
+
+#endif
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include <curl/types.h>
+#include "netrc.h"
+
+#include "hostip.h"
+#include "transfer.h"
+#include "sendf.h"
+#include "speedcheck.h"
+#include "getpass.h"
+#include "progress.h"
+#include "getdate.h"
+#include "http.h"
+#include "url.h"
+#include "getinfo.h"
+#include "ssluse.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#ifdef MALLOCDEBUG
+#include "memdebug.h"
+#endif
+
+#ifndef min
+#define min(a, b)   ((a) < (b) ? (a) : (b))
+#endif
+
+enum {
+  KEEP_NONE,
+  KEEP_READ,
+  KEEP_WRITE
+};
+
+
+/*
+ * compareheader()
+ *
+ * Returns TRUE if 'headerline' contains the 'header' with given 'content'.
+ * Pass headers WITH the colon.
+ */
+static bool
+compareheader(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;
+  char *start;
+  char *end;
+
+  if(!strnequal(headerline, header, hlen))
+    return FALSE; /* doesn't start with header */
+
+  /* pass the header */
+  start = &headerline[hlen];
+
+  /* pass all white spaces */
+  while(*start && isspace((int)*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, return false and bail out! */
+      return FALSE;
+  }
+
+  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(strnequal(start, content, clen))
+      return TRUE; /* match! */
+  }
+
+  return FALSE; /* no match */
+}
+
+CURLcode Curl_readwrite(struct connectdata *conn,
+                        bool *done)
+{
+  struct Curl_transfer_keeper *k = &conn->keep;
+  struct SessionHandle *data = conn->data;
+  int result;
+  ssize_t nread; /* number of bytes read */
+  int didwhat=0;
+
+  do {
+    if((k->keepon & KEEP_READ) &&
+       FD_ISSET(conn->sockfd, &k->readfd)) {
+
+      /* read! */
+      result = Curl_read(conn, conn->sockfd, k->buf,
+                         BUFSIZE -1, &nread);
+
+      if(0>result)
+        break; /* get out of loop */
+      if(result>0)
+        return result;
+
+      if ((k->bytecount == 0) && (k->writebytecount == 0))
+        Curl_pgrsTime(data, TIMER_STARTTRANSFER);
+
+      didwhat |= KEEP_READ;
+
+      /* NULL terminate, allowing string ops to be used */
+      if (0 < nread)
+        k->buf[nread] = 0;
+
+      /* if we receive 0 or less here, the server closed the connection and
+         we bail out from this! */
+      else if (0 >= nread) {
+        k->keepon &= ~KEEP_READ;
+        FD_ZERO(&k->rkeepfd);
+        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;
+
+      /* 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 */
+
+        /* header line within buffer loop */
+        do {
+          int hbufp_index;
+              
+          /* str_start is start of line within buf */
+          k->str_start = k->str;
+              
+          k->end_ptr = strchr (k->str_start, '\n');
+              
+          if (!k->end_ptr) {
+            /* no more complete header lines within buffer */
+            /* copy what is remaining into headerbuff */
+            int str_length = (int)strlen(k->str);
+
+            /*
+             * We enlarge the header buffer if it seems to be too
+             * smallish
+             */
+            if (k->hbuflen + (int)str_length >=
+                data->state.headersize) {
+              char *newbuff;
+              long newsize=MAX((k->hbuflen+str_length)*3/2,
+                               data->state.headersize*2);
+              hbufp_index = k->hbufp - data->state.headerbuff;
+              newbuff = (char *)realloc(data->state.headerbuff, newsize);
+              if(!newbuff) {
+                failf (data, "Failed to alloc memory for big header!");
+                return CURLE_READ_ERROR;
+              }
+              data->state.headersize=newsize;
+              data->state.headerbuff = newbuff;
+              k->hbufp = data->state.headerbuff + hbufp_index;
+            }
+            strcpy (k->hbufp, k->str);
+            k->hbufp += strlen (k->str);
+            k->hbuflen += strlen (k->str);
+            break;              /* read more and try again */
+          }
+
+          k->str = k->end_ptr + 1; /* move past new line */
+
+          /*
+           * We're about to copy 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.
+           */
+          if (k->hbuflen + (k->str - k->str_start) >=
+              data->state.headersize) {
+            char *newbuff;
+            long newsize=MAX((k->hbuflen+
+                              (k->str-k->str_start))*3/2,
+                             data->state.headersize*2);
+            hbufp_index = k->hbufp - data->state.headerbuff;
+            newbuff = (char *)realloc(data->state.headerbuff, newsize);
+            if(!newbuff) {
+              failf (data, "Failed to alloc memory for big header!");
+              return CURLE_READ_ERROR;
+            }
+            data->state.headersize= newsize;
+            data->state.headerbuff = newbuff;
+            k->hbufp = data->state.headerbuff + hbufp_index;
+          }
+
+          /* copy to end of line */
+          strncpy (k->hbufp, k->str_start, k->str - k->str_start);
+          k->hbufp += k->str - k->str_start;
+          k->hbuflen += k->str - k->str_start;
+          *k->hbufp = 0;
+              
+          k->p = data->state.headerbuff;
+              
+          /****
+           * We now have a FULL header line that p points to
+           *****/
+
+          if (('\n' == *k->p) || ('\r' == *k->p)) {
+            /* Zero-length header line means end of headers! */
+
+            if ('\r' == *k->p)
+              k->p++; /* pass the \r byte */
+            if ('\n' == *k->p)
+              k->p++; /* pass the \n byte */
+
+            if(100 == 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 our stuff.
+               * 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->write_after_100_header) {
+
+                k->write_after_100_header = FALSE;
+                FD_SET (conn->writesockfd, &k->writefd); /* write */
+                k->keepon |= KEEP_WRITE;
+                k->wkeepfd = k->writefd;
+              }
+            }
+            else
+              k->header = FALSE; /* no more header to parse! */
+
+            if (417 == k->httpcode) {
+              /*
+               * we got: "417 Expectation Failed" this means:
+               * we have made a HTTP call and our Expect Header
+               * seems to cause a problem => abort the write operations
+               * (or prevent them from starting
+               */
+              k->write_after_100_header = FALSE;
+              k->keepon &= ~KEEP_WRITE;
+              FD_ZERO(&k->wkeepfd);
+            }
+
+            /* now, only output this if the header AND body are requested:
+             */
+            k->writetype = CLIENTWRITE_HEADER;
+            if (data->set.http_include_header)
+              k->writetype |= CLIENTWRITE_BODY;
+
+            result = Curl_client_write(data, k->writetype,
+                                       data->state.headerbuff,
+                                       k->p - data->state.headerbuff);
+            if(result)
+              return result;
+
+            data->info.header_size += k->p - data->state.headerbuff;
+            conn->headerbytecount += k->p - data->state.headerbuff;
+
+            if(!k->header) {
+              /*
+               * really end-of-headers.
+               *
+               * If we requested a "no body", this is a good time to get
+               * out and return home.
+               */
+              bool stop_reading = FALSE;
+
+              if(data->set.no_body)
+                stop_reading = TRUE;
+              else if(!conn->bits.close) {
+                /* If this is not the last request before a close, we must
+                   set the maximum download size to the size of the
+                   expected document or else, we won't know when to stop
+                   reading! */
+                if(-1 != conn->size)
+                  conn->maxdownload = conn->size;
+
+                /* If max download size is *zero* (nothing) we already
+                   have nothing and can safely return ok now! */
+                if(0 == conn->maxdownload)
+                  stop_reading = TRUE;
+                    
+                /* What to do if the size is *not* known? */
+              }
+
+              if(stop_reading) {
+                /* we make sure that this socket isn't read more now */
+                k->keepon &= ~KEEP_READ;
+                FD_ZERO(&k->rkeepfd);
+                /* for a progress meter/info update before going away */
+                Curl_pgrsUpdate(conn);
+                return CURLE_OK;
+              }
+
+              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 consiser this to be the body right away! */
+            int httpversion_major;
+            int nc=sscanf (k->p, " HTTP/%d.%d %3d",
+                           &httpversion_major,
+                           &k->httpversion,
+                           &k->httpcode);
+            if (nc==3) {
+              k->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 (k->p, " HTTP %3d", &k->httpcode);
+              k->httpversion = 10;
+            }
+
+            if (nc) {
+              data->info.httpcode = k->httpcode;
+              data->info.httpversion = k->httpversion;
+
+              /* 404 -> URL not found! */
+              if (data->set.http_fail_on_error &&
+                  (k->httpcode >= 400)) {
+                /* If we have been told to fail hard on HTTP-errors,
+                   here is the check for that: */
+                /* serious error, go home! */
+                failf (data, "The requested file was not found");
+                return CURLE_HTTP_NOT_FOUND;
+              }
+
+              if(k->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! */
+                conn->bits.close = 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.  */
+                conn->size=0;
+                break;
+              default:
+                /* nothing */
+                break;
+              }
+            }
+            else {
+              k->header = FALSE;        /* this is not a header line */
+              break;
+            }
+          }
+          /* check for Content-Length: header lines to get size */
+          if (strnequal("Content-Length:", k->p, 15) &&
+              sscanf (k->p+15, " %ld", &k->contentlength)) {
+            conn->size = k->contentlength;
+            Curl_pgrsSetDownloadSize(data, k->contentlength);
+          }
+          /* check for Content-Type: header lines to get the mime-type */
+          else if (strnequal("Content-Type:", k->p, 13)) {
+            char *start;
+            char *end;
+            int len;
+
+            /* Find the first non-space letter */
+            for(start=k->p+14;
+                *start && isspace((int)*start);
+                start++);
+
+            /* count all non-space letters following */
+            for(end=start, len=0;
+                *end && !isspace((int)*end);
+                end++, len++);
+
+            /* allocate memory of a cloned copy */
+            data->info.contenttype = malloc(len + 1);
+            if (NULL == data->info.contenttype)
+              return CURLE_OUT_OF_MEMORY;
+
+            /* copy the content-type string */
+            memcpy(data->info.contenttype, start, len);
+            data->info.contenttype[len] = 0; /* zero terminate */
+          }
+          else if((k->httpversion == 10) &&
+                  conn->bits.httpproxy &&
+                  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((k->httpversion == 10) &&
+                  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 (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 (compareheader(k->p, "Transfer-Encoding:", "chunked")) {
+            /*
+             * [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. */
+            conn->bits.chunk = TRUE; /* chunks coming our way */
+
+            /* init our chunky engine */
+            Curl_httpchunk_init(conn);
+          }
+          else if (strnequal("Content-Range:", k->p, 14)) {
+            if (sscanf (k->p+14, " bytes %d-", &k->offset) ||
+                sscanf (k->p+14, " bytes: %d-", &k->offset)) {
+              /* This second format was added August 1st 2000 by Igor
+                 Khristophorov since Sun's webserver JavaWebServer/1.1.1
+                 obviously sends the header this way! :-( */
+              if (conn->resume_from == k->offset) {
+                /* we asked for a resume and we got it */
+                k->content_range = TRUE;
+              }
+            }
+          }
+          else if(data->cookies &&
+                  strnequal("Set-Cookie:", k->p, 11)) {
+            Curl_cookie_add(data->cookies, TRUE, k->p+12, conn->name);
+          }
+          else if(strnequal("Last-Modified:", k->p,
+                            strlen("Last-Modified:")) &&
+                  (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 = k->timeofdoc;
+          }
+          else if ((k->httpcode >= 300 && k->httpcode < 400) &&
+                   (data->set.http_follow_location) &&
+                   strnequal("Location:", k->p, 9)) {
+            /* this is the URL that the server advices us to get instead */
+            char *ptr;
+            char *start=k->p;
+            char backup;
+
+            start += 9; /* pass "Location:" */
+
+            /* Skip spaces and tabs. We do this to support multiple
+               white spaces after the "Location:" keyword. */
+            while(*start && isspace((int)*start ))
+              start++;
+            ptr = start; /* start scanning here */
+
+            /* scan through the string to find the end */
+            while(*ptr && !isspace((int)*ptr))
+              ptr++;
+            backup = *ptr; /* store the ending letter */
+            *ptr = '\0';   /* zero terminate */
+            conn->newurl = strdup(start); /* clone string */
+            *ptr = backup; /* restore ending letter */
+          }
+
+          /*
+           * End of header-checks. Write them to the client.
+           */
+
+          k->writetype = CLIENTWRITE_HEADER;
+          if (data->set.http_include_header)
+            k->writetype |= CLIENTWRITE_BODY;
+
+          result = Curl_client_write(data, k->writetype, k->p,
+                                     k->hbuflen);
+          if(result)
+            return result;
+
+          data->info.header_size += k->hbuflen;
+          conn->headerbytecount += k->hbuflen;
+              
+          /* reset hbufp pointer && hbuflen */
+          k->hbufp = data->state.headerbuff;
+          k->hbuflen = 0;
+        }
+        while (*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. */
+
+        if (!k->header) {
+          /* the next token and forward is not part of
+             the header! */
+
+          /* we subtract the remaining header size from the buffer */
+          nread -= (k->str - k->buf);
+        }
+
+      }                 /* end if header mode */
+
+      /* 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)) {
+            
+        if(0 == k->bodywrites) {
+          /* These checks are only made the first time we are about to
+             write a piece of the body */
+          if(conn->protocol&PROT_HTTP) {
+            /* HTTP-only checks */
+            if (conn->newurl) {
+              /* abort after the headers if "follow Location" is set */
+              infof (data, "Follow to new URL: %s\n", conn->newurl);
+                k->keepon &= ~KEEP_READ;
+                FD_ZERO(&k->rkeepfd);
+              return CURLE_OK;
+            }
+            else if (conn->resume_from &&
+                     !k->content_range &&
+                     (data->set.httpreq==HTTPREQ_GET)) {
+              /* 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_HTTP_RANGE_ERROR;
+            }
+            else if(data->set.timecondition && !conn->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((k->timeofdoc > 0) && (data->set.timevalue > 0)) {
+                switch(data->set.timecondition) {
+                case TIMECOND_IFMODSINCE:
+                default:
+                  if(k->timeofdoc < data->set.timevalue) {
+                    infof(data,
+                          "The requested document is not new enough\n");
+                    return CURLE_OK;
+                  }
+                  break;
+                case TIMECOND_IFUNMODSINCE:
+                  if(k->timeofdoc > data->set.timevalue) {
+                    infof(data,
+                          "The requested document is not old enough\n");
+                    return CURLE_OK;
+                  }
+                  break;
+                } /* switch */
+              } /* two valid time strings */
+            } /* we have a time condition */
+
+          } /* this is HTTP */
+        } /* this is the first time we write a body part */
+        k->bodywrites++;
+
+        if(conn->bits.chunk) {
+          /*
+           * Bless me father for I have sinned. 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) {
+            failf(data, "Receeived problem in the chunky parser");
+            return CURLE_READ_ERROR;
+          }
+          else if(CHUNKE_STOP == res) {
+            /* we're done reading chunks! */
+            k->keepon &= ~KEEP_READ; /* read no more */
+            FD_ZERO(&k->rkeepfd);
+
+            /* There are now possibly N number of bytes at the end of the
+               str buffer that weren't written to the client, but we don't
+               care about them right now. */
+          }
+          /* If it returned OK, we just keep going */
+        }
+
+        if((-1 != conn->maxdownload) &&
+           (k->bytecount + nread >= conn->maxdownload)) {
+          nread = conn->maxdownload - k->bytecount;
+          if(nread < 0 ) /* this should be unusual */
+            nread = 0;
+
+          k->keepon &= ~KEEP_READ; /* we're done reading */
+          FD_ZERO(&k->rkeepfd);
+        }
+
+        k->bytecount += nread;
+
+        Curl_pgrsSetDownloadCounter(data, (double)k->bytecount);
+            
+        if(!conn->bits.chunk && nread) {
+          /* If this is chunky transfer, it was already written */
+          result = Curl_client_write(data, CLIENTWRITE_BODY, k->str,
+                                     nread);
+          if(result)
+            return result;
+        }
+
+      } /* if (! header and data to read ) */
+    } /* if( read from socket ) */
+
+    if((k->keepon & KEEP_WRITE) &&
+       FD_ISSET(conn->writesockfd, &k->writefd)) {
+      /* write */
+
+      int i, si;
+      ssize_t bytes_written;
+
+      if ((k->bytecount == 0) && (k->writebytecount == 0))
+        Curl_pgrsTime(data, TIMER_STARTTRANSFER);
+
+      didwhat |= KEEP_WRITE;
+
+      /* only read more data if there's no upload data already
+         present in the upload buffer */
+      if(0 == conn->upload_present) {
+        /* init the "upload from here" pointer */
+        conn->upload_fromhere = k->uploadbuf;
+
+        nread = data->set.fread(conn->upload_fromhere, 1,
+                                BUFSIZE, data->set.in);
+
+        /* the signed int typecase of nread of for systems that has
+           unsigned size_t */
+        if (nread<=0) {
+          /* done */
+          k->keepon &= ~KEEP_WRITE; /* we're done writing */
+          FD_ZERO(&k->wkeepfd);
+          break;
+        }
+
+        /* store number of bytes available for upload */
+        conn->upload_present = nread;
+
+        /* convert LF to CRLF if so asked */
+        if (data->set.crlf) {
+          for(i = 0, si = 0; i < nread; i++, si++) {
+            if (k->buf[i] == 0x0a) {
+              data->state.scratch[si++] = 0x0d;
+              data->state.scratch[si] = 0x0a;
+            }
+            else {
+              data->state.scratch[si] = k->uploadbuf[i];
+            }
+          }
+          nread = si;
+          k->buf = data->state.scratch; /* point to the new buffer */
+        }
+      }
+      else {
+        /* We have a partial buffer left from a previous "round". Use
+           that instead of reading more data */
+      }
+
+      /* write to socket */
+      result = Curl_write(conn,
+                          conn->writesockfd,
+                          conn->upload_fromhere,
+                          conn->upload_present,
+                          &bytes_written);
+      if(result)
+        return result;
+      else if(conn->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 */
+        conn->upload_present -= bytes_written;
+
+        /* advance the pointer where to find the buffer when the next send
+           is to happen */
+        conn->upload_fromhere += bytes_written;
+      }
+      else if(data->set.crlf)
+        k->buf = data->state.buffer; /* put it back on the buffer */
+      else {
+        /* we've uploaded that buffer now */
+        conn->upload_fromhere = k->uploadbuf;
+        conn->upload_present = 0; /* no more bytes left */
+      }
+
+      k->writebytecount += bytes_written;
+      Curl_pgrsSetUploadCounter(data, (double)k->writebytecount);
+
+    }
+
+  } while(0); /* just to break out from! */
+
+  if(didwhat) {
+    /* Update read/write counters */
+    if(conn->bytecountp)
+      *conn->bytecountp = k->bytecount; /* read count */
+    if(conn->writebytecountp)
+      *conn->writebytecountp = k->writebytecount; /* write count */
+  }
+  else {
+    /* no read no write, this is a timeout? */
+    if (k->write_after_100_header) {
+      /* This should allow some time for the header to arrive, but only a
+         very short time as otherwise it'll be too much wasted times too
+         often. */
+      k->write_after_100_header = FALSE;
+      FD_SET (conn->writesockfd, &k->writefd); /* write socket */
+      k->keepon |= KEEP_WRITE;
+      k->wkeepfd = k->writefd;
+    }    
+  }
+
+  k->now = Curl_tvnow();
+  if(Curl_pgrsUpdate(conn))
+    result = CURLE_ABORTED_BY_CALLBACK;
+  else
+    result = Curl_speedcheck (data, k->now);
+  if (result)
+    return result;
+    
+  if (data->set.timeout &&
+      ((Curl_tvdiff(k->now, k->start)/1000) >= data->set.timeout)) {
+    failf (data, "Operation timed out with %d out of %d bytes received",
+           k->bytecount, conn->size);
+    return CURLE_OPERATION_TIMEOUTED;
+  }
+
+  if(!k->keepon) {
+    /*
+     * The transfer has been performed. Just make some general checks before
+     * returning.
+     */
+
+    if(!(data->set.no_body) && k->contentlength &&
+       (k->bytecount != k->contentlength) &&
+       !conn->newurl) {
+      failf(data, "transfer closed with %d bytes remaining to read",
+            k->contentlength-k->bytecount);
+      return CURLE_PARTIAL_FILE;
+    }
+    else if(conn->bits.chunk && conn->proto.http->chunk.datasize) {
+      failf(data, "transfer closed with at least %d bytes remaining",
+            conn->proto.http->chunk.datasize);
+      return CURLE_PARTIAL_FILE;
+    }
+    if(Curl_pgrsUpdate(conn))
+      return CURLE_ABORTED_BY_CALLBACK;
+  }
+
+  /* Now update the "done" boolean we return */
+  *done = !k->keepon;
+
+  return CURLE_OK;
+}
+
+CURLcode Curl_readwrite_init(struct connectdata *conn)
+{
+  struct SessionHandle *data = conn->data;
+  struct Curl_transfer_keeper *k = &conn->keep;
+
+  memset(k, 0, sizeof(struct Curl_transfer_keeper));
+
+  k->start = Curl_tvnow(); /* start time */
+  k->now = k->start;   /* current time is now */
+  k->header = TRUE; /* assume header */
+  k->httpversion = -1; /* unknown at this point */
+  k->conn = (struct connectdata *)conn; /* store the connection */
+
+  data = conn->data; /* there's the root struct */
+  k->buf = data->state.buffer;
+  k->uploadbuf = data->state.uploadbuffer;
+  k->maxfd = (conn->sockfd>conn->writesockfd?
+              conn->sockfd:conn->writesockfd)+1;
+  k->hbufp = data->state.headerbuff;
+
+  Curl_pgrsTime(data, TIMER_PRETRANSFER);
+  Curl_speedinit(data);
+
+  if (!conn->getheader) {
+    k->header = FALSE;
+    if(conn->size > 0)
+      Curl_pgrsSetDownloadSize(data, conn->size);
+  }
+  /* we want header and/or body, if neither then don't do this! */
+  if(conn->getheader || !data->set.no_body) {
+
+    FD_ZERO (&k->readfd);               /* clear it */
+    if(conn->sockfd != -1) {
+      FD_SET (conn->sockfd, &k->readfd); /* read socket */
+      k->keepon |= KEEP_READ;
+    }
+
+    FD_ZERO (&k->writefd);              /* clear it */
+    if(conn->writesockfd != -1) {
+      if (data->set.expect100header)
+        /* wait with write until we either got 100-continue or a timeout */
+        k->write_after_100_header = TRUE;
+      else {
+        FD_SET (conn->writesockfd, &k->writefd); /* write socket */
+        k->keepon |= KEEP_WRITE;
+      }
+    }
+
+    /* get these in backup variables to be able to restore them on each lap in
+       the select() loop */
+    k->rkeepfd = k->readfd;
+    k->wkeepfd = k->writefd;
+
+  }
+
+  return CURLE_OK;
+}
+
+void Curl_single_fdset(struct connectdata *conn,
+                       fd_set *read_fd_set,
+                       fd_set *write_fd_set,
+                       fd_set *exc_fd_set,
+                       int *max_fd)
+{
+  *max_fd = -1; /* init */
+  if(conn->keep.keepon & KEEP_READ) {
+    FD_SET(conn->sockfd, read_fd_set);
+    *max_fd = conn->sockfd;
+  }
+  if(conn->keep.keepon & KEEP_WRITE) {
+    FD_SET(conn->writesockfd, write_fd_set);
+    if(conn->writesockfd > *max_fd)
+      *max_fd = conn->writesockfd;
+  }
+  /* we don't use exceptions, only touch that one to prevent compiler
+     warnings! */
+  *exc_fd_set = *exc_fd_set;
+}
+
+
+/*
+ * 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_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 shrinken again.
+ *
+ * Parts of this function was once written by the friendly Mark Butler
+ * <[email protected]>.
+ */
+
+static CURLcode
+Transfer(struct connectdata *conn)
+{
+  struct SessionHandle *data = conn->data;
+  CURLcode result;
+  struct Curl_transfer_keeper *k = &conn->keep;
+  bool done=FALSE;
+
+  Curl_readwrite_init(conn);
+
+  if((conn->sockfd == -1) && (conn->writesockfd == -1))
+    /* 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(!conn->getheader && data->set.no_body)
+    return CURLE_OK;
+
+  while (!done) {
+    struct timeval interval;
+    k->readfd = k->rkeepfd;  /* set these every lap in the loop */
+    k->writefd = k->wkeepfd;
+    interval.tv_sec = 1;
+    interval.tv_usec = 0;
+
+    switch (select (k->maxfd, &k->readfd, &k->writefd, NULL,
+                    &interval)) {
+    case -1: /* select() error, stop reading */
+#ifdef EINTR
+      /* The EINTR is not serious, and it seems you might get this more
+         ofen when using the lib in a multi-threaded environment! */
+      if(errno == EINTR)
+        ;
+      else
+#endif
+        done = TRUE; /* no more read or write */
+      continue;
+    case 0:  /* timeout */
+      result = Curl_readwrite(conn, &done);
+      break;
+
+    default: /* readable descriptors */
+      result = Curl_readwrite(conn, &done);
+      break;
+    }
+    if(result)
+      return result;
+    
+    /* "done" signals to us if the transfer(s) are ready */
+  }
+
+  return CURLE_OK;
+}
+
+CURLcode Curl_pretransfer(struct SessionHandle *data)
+{
+  if(!data->change.url)
+    /* we can't do anything wihout URL */
+    return CURLE_URL_MALFORMAT;
+
+#ifdef USE_SSLEAY
+  /* Init the SSL session ID cache here. We do it here since we want to
+     do it after the *_setopt() calls (that could change the size) but
+     before any transfer. */
+  Curl_SSL_InitSessions(data, data->set.ssl.numsessions);
+#endif
+
+  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 */
+
+ /* 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)
+  /*************************************************************
+   * Tell signal handler to ignore SIGPIPE
+   *************************************************************/
+  data->state.prev_signal = signal(SIGPIPE, SIG_IGN);
+#endif  
+
+  Curl_initinfo(data); /* reset session-specific information "variables" */
+  Curl_pgrsStartNow(data);
+
+  return CURLE_OK;
+}
+
+CURLcode Curl_posttransfer(struct SessionHandle *data)
+{
+#if defined(HAVE_SIGNAL) && defined(SIGPIPE)
+  /* restore the signal handler for SIGPIPE before we get back */
+  signal(SIGPIPE, data->state.prev_signal);
+#endif  
+
+  return CURLE_OK;
+}
+
+CURLcode Curl_perform(struct SessionHandle *data)
+{
+  CURLcode res;
+  CURLcode res2;
+  struct connectdata *conn=NULL;
+  char *newurl = NULL; /* possibly a new URL to follow to! */
+
+  res = Curl_pretransfer(data);
+  if(res)
+    return res;
+
+  /*
+   * It is important that there is NO 'return' from this function any any
+   * other place than falling down the bottom! 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.
+   */
+
+  do {
+    Curl_pgrsTime(data, TIMER_STARTSINGLE);
+    res = Curl_connect(data, &conn);
+    if(res == CURLE_OK) {
+      res = Curl_do(&conn);
+
+      if(res == CURLE_OK) {
+        CURLcode res2; /* just a local extra result container */
+
+        if(conn->protocol&PROT_FTPS)
+          /* FTPS, disable ssl while transfering data */
+          conn->ssl.use = FALSE;
+        res = Transfer(conn); /* now fetch that URL please */
+        if(conn->protocol&PROT_FTPS)
+          /* FTPS, enable ssl again after havving transferred data */
+          conn->ssl.use = TRUE;
+
+        if(res == CURLE_OK)
+          /*
+           * We must duplicate the new URL here as the connection data
+           * may be free()ed in the Curl_done() function.
+           */
+          newurl = conn->newurl?strdup(conn->newurl):NULL;
+        else
+          /* The transfer phase returned error, we mark the connection to get
+           * closed to prevent being re-used. This is becasue we can't
+           * possibly know if the connection is in a good shape or not now. */
+          conn->bits.close = TRUE;
+
+        /* Always run Curl_done(), even if some of the previous calls
+           failed, but return the previous (original) error code */
+        res2 = Curl_done(conn);
+
+        if(CURLE_OK == res)
+          res = res2;
+      }
+
+      /*
+       * Important: 'conn' cannot be used here, since it may have been closed
+       * in 'Curl_done' or other functions.
+       */
+
+      if((res == CURLE_OK) && newurl) {
+        /* Location: redirect
+ 
+           This is assumed to happen for HTTP(S) only!
+        */
+        char prot[16]; /* URL protocol string storage */
+        char letter;   /* used for a silly sscanf */
+
+        if (data->set.maxredirs && (data->set.followlocation >= data->set.maxredirs)) {
+          failf(data,"Maximum (%d) redirects followed", data->set.maxredirs);
+          res=CURLE_TOO_MANY_REDIRECTS;
+          break;
+        }
+
+        /* 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)
+            /* If we already have an allocated referer, free this first */
+            free(data->change.referer);
+
+          data->change.referer = strdup(data->change.url);
+          data->change.referer_alloc = TRUE; /* yes, free this later */
+        }
+
+        if(2 != sscanf(newurl, "%15[^?&/:]://%c", prot, &letter)) {
+          /***
+           *DANG* this is an RFC 2068 violation. The URL is supposed
+           to be absolute and this doesn't seem to be that!
+           ***
+           Instead, we have to 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 *protsep;
+          char *pathsep;
+          char *newest;
+
+          /* we must make our own copy of the URL to play with, as it may
+             point to read-only data */
+          char *url_clone=strdup(data->change.url);
+
+          if(!url_clone) {
+            res = CURLE_OUT_OF_MEMORY;
+            break; /* skip out of this loop 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('/' != newurl[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 = strrchr(protsep, '?');
+            if(pathsep)
+              *pathsep=0;
+
+            /* we have a relative path to append to the last slash if
+               there's one available */
+            pathsep = strrchr(protsep, '/');
+            if(pathsep)
+              *pathsep=0;
+          }
+          else {
+            /* We got a new absolute path for this server, cut off from the
+               first slash */
+            pathsep = strchr(protsep, '/');
+            if(pathsep)
+              *pathsep=0;
+          }
+
+          newest=(char *)malloc( strlen(url_clone) +
+                                 1 + /* possible slash */
+                                 strlen(newurl) + 1/* zero byte */);
+
+          if(!newest) {
+            res = CURLE_OUT_OF_MEMORY;
+            break; /* go go go out from this loop */
+          }
+          sprintf(newest, "%s%s%s", url_clone, ('/' == newurl[0])?"":"/",
+                  newurl);
+          free(newurl);
+          free(url_clone);
+          newurl = newest;
+        }
+        else
+          /* This is an absolute URL, don't allow the custom port number */
+          data->state.allow_port = FALSE;
+
+        if(data->change.url_alloc)
+          free(data->change.url);
+        else
+          data->change.url_alloc = TRUE; /* the URL is allocated */
+      
+        /* TBD: set the URL with curl_setopt() */
+        data->change.url = newurl;
+        newurl = NULL; /* don't free! */
+
+        infof(data, "Follows Location: to new URL: '%s'\n", data->change.url);
+
+        /*
+         * We get here when the HTTP code is 300-399. We need to perform
+         * differently based on exactly what return code there was.
+         * Discussed on the curl mailing list and posted about on the 26th
+         * of January 2001.
+         */
+        switch(data->info.httpcode) {
+        case 300: /* Multiple Choices */
+        case 301: /* Moved Permanently */
+        case 306: /* Not used */
+        case 307: /* Temporary Redirect */
+        default:  /* for all unknown ones */
+          /* These are explicitly mention since I've checked RFC2616 and they
+           * seem to be OK to POST to.
+           */
+          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.             
+          */
+        case 303: /* See Other */
+          /* Disable both types of POSTs, since doing a second POST when
+           * following isn't what anyone would want! */
+          data->set.httpreq = HTTPREQ_GET; /* enforce GET request */
+          infof(data, "Disables POST, goes with GET\n");
+          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;
+        }
+        continue;
+      }
+    }
+    break; /* it only reaches here when this shouldn't loop */
+
+  } while(1); /* loop if Location: */
+
+  if(newurl)
+    free(newurl);
+
+  /* run post-transfer uncondionally, 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;
+}
+
+CURLcode 
+Curl_Transfer(struct connectdata *c_conn, /* connection data */
+              int sockfd,       /* socket to read from or -1 */
+              int size,         /* -1 if unknown at this point */
+              bool getheader,   /* TRUE if header parsing is wanted */
+              long *bytecountp, /* return number of bytes read or NULL */
+              int writesockfd,  /* socket to write to, it may very well be
+                                   the same we read from. -1 disables */
+              long *writebytecountp /* return number of bytes written or
+                                       NULL */
+              )
+{
+  struct connectdata *conn = (struct connectdata *)c_conn;
+  if(!conn)
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+
+  /* now copy all input parameters */
+  conn->sockfd = sockfd;
+  conn->size = size;
+  conn->getheader = getheader;
+  conn->bytecountp = bytecountp;
+  conn->writesockfd = writesockfd;
+  conn->writebytecountp = writebytecountp;
+
+  return CURLE_OK;
+
+}
+          
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 49 - 0
Source/CTest/Curl/transfer.h

@@ -0,0 +1,49 @@
+#ifndef __TRANSFER_H
+#define __TRANSFER_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2001, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+CURLcode Curl_perform(struct SessionHandle *data);
+
+CURLcode Curl_pretransfer(struct SessionHandle *data);
+CURLcode Curl_posttransfer(struct SessionHandle *data);
+
+CURLcode Curl_readwrite(struct connectdata *conn, bool *done);
+void Curl_single_fdset(struct connectdata *conn, 
+                       fd_set *read_fd_set,
+                       fd_set *write_fd_set,
+                       fd_set *exc_fd_set,
+                       int *max_fd);
+CURLcode Curl_readwrite_init(struct connectdata *conn);
+
+/* This sets up a forthcoming transfer */
+CURLcode 
+Curl_Transfer (struct connectdata *data,
+               int sockfd,              /* socket to read from or -1 */
+               int size,                /* -1 if unknown at this point */
+               bool getheader,          /* TRUE if header parsing is wanted */
+               long *bytecountp,        /* return number of bytes read */
+               int writesockfd,      /* socket to write to, it may very well be
+                                        the same we read from. -1 disables */
+               long *writebytecountp /* return number of bytes written */
+);
+#endif

+ 2379 - 0
Source/CTest/Curl/url.c

@@ -0,0 +1,2379 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2001, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+/* -- WIN32 approved -- */
+
+#include "setup.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+
+#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
+#include <winsock.h>
+#include <time.h>
+#include <io.h>
+#else
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <netdb.h>
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#include <sys/ioctl.h>
+#include <signal.h>
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#ifdef  VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#ifndef HAVE_SELECT
+#error "We can't compile without select() support!"
+#endif
+#ifndef HAVE_SOCKET
+#error "We can't compile without socket() support!"
+#endif
+
+#endif
+
+#include "urldata.h"
+#include "netrc.h"
+
+#include "formdata.h"
+#include "base64.h"
+#include "ssluse.h"
+#include "hostip.h"
+#include "if2ip.h"
+#include "transfer.h"
+#include "sendf.h"
+#include "getpass.h"
+#include "progress.h"
+#include "cookie.h"
+#include "strequal.h"
+#include "escape.h"
+#include "strtok.h"
+
+/* And now for the protocols */
+#include "ftp.h"
+#include "dict.h"
+#include "telnet.h"
+#include "http.h"
+#include "file.h"
+#include "ldap.h"
+#include "url.h"
+#include "connect.h"
+
+#include <curl/types.h>
+
+#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
+#include "inet_ntoa_r.h"
+#endif
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#ifdef KRB4
+#include "security.h"
+#endif
+/* The last #include file should be: */
+#ifdef MALLOCDEBUG
+#include "memdebug.h"
+#endif
+
+/* Local static prototypes */
+static int ConnectionKillOne(struct SessionHandle *data);
+static bool ConnectionExists(struct SessionHandle *data,
+                             struct connectdata *needle,
+                             struct connectdata **usethis);
+static unsigned int ConnectionStore(struct SessionHandle *data,
+                                    struct connectdata *conn);
+
+
+#if !defined(WIN32)||defined(__CYGWIN32__)
+#ifndef RETSIGTYPE
+#define RETSIGTYPE void
+#endif
+static
+RETSIGTYPE alarmfunc(int signal)
+{
+  /* this is for "-ansi -Wall -pedantic" to stop complaining!   (rabe) */
+  (void)signal;
+  return;
+}
+#endif
+
+
+/*
+ * 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)
+{
+  /* Loop through all open connections and kill them one by one */
+  while(-1 != ConnectionKillOne(data));
+
+#ifdef USE_SSLEAY
+  /* Close down all open SSL info and sessions */
+  Curl_SSL_Close_All(data);
+#endif
+
+  if(data->state.auth_host)
+    free(data->state.auth_host);
+
+  if(data->change.proxy_alloc)
+    free(data->change.proxy);
+
+  if(data->change.referer_alloc)
+    free(data->change.referer);
+
+  if(data->change.url_alloc)
+    free(data->change.url);
+
+  if(data->state.headerbuff)
+    free(data->state.headerbuff);
+
+  if(data->set.cookiejar)
+    /* we have a "destination" for all the cookies to get dumped to */
+    Curl_cookie_output(data->cookies, data->set.cookiejar);
+    
+  Curl_cookie_cleanup(data->cookies);
+
+  /* free the connection cache */
+  free(data->state.connects);
+
+  if(data->info.contenttype)
+    free(data->info.contenttype);
+
+  free(data);
+  return CURLE_OK;
+}
+
+static
+int my_getpass(void *clientp, const char *prompt, char* buffer, int buflen )
+{
+  char *retbuf;
+  clientp=NULL; /* prevent compiler warning */
+
+  retbuf = getpass_r(prompt, buffer, buflen);
+  if(NULL == retbuf)
+    return 1;
+  else
+    return 0; /* success */
+}
+
+
+CURLcode Curl_open(struct SessionHandle **curl)
+{
+  /* We don't yet support specifying the URL at this point */
+  struct SessionHandle *data;
+  /* Very simple start-up: alloc the struct, init it with zeroes and return */
+  data = (struct SessionHandle *)malloc(sizeof(struct SessionHandle));
+  if(!data)
+    /* this is a very serious error */
+    return CURLE_OUT_OF_MEMORY;
+  
+  memset(data, 0, sizeof(struct SessionHandle));
+
+  /* We do some initial setup here, all those fields that can't be just 0 */
+
+  data->state.headerbuff=(char*)malloc(HEADERSIZE);
+  if(!data->state.headerbuff) {
+    free(data); /* free the memory again */
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  data->state.headersize=HEADERSIZE;
+
+  data->set.out = stdout; /* default output to stdout */
+  data->set.in  = stdin;  /* default input from stdin */
+  data->set.err  = stderr;  /* default stderr to stderr */
+  
+  /* use fwrite as default function to store output */
+  data->set.fwrite = (curl_write_callback)fwrite;
+
+  /* use fread as default function to read input */
+  data->set.fread = (curl_read_callback)fread;
+  
+  /* set the default passwd function */
+  data->set.fpasswd = my_getpass;
+
+  data->set.infilesize = -1; /* we don't know any size */
+
+  data->state.current_speed = -1; /* init to negative == impossible */
+
+  data->set.httpreq = HTTPREQ_GET; /* Default HTTP request */
+  data->set.ftp_use_epsv = TRUE;   /* FTP defaults to EPSV operations */
+
+  data->set.dns_cache_timeout = 60; /* Timeout every 60 seconds by default */
+  
+  /* make libcurl quiet by default: */
+  data->set.hide_progress = TRUE;  /* CURLOPT_NOPROGRESS changes these */
+  data->progress.flags |= PGRS_HIDE;
+
+  /* Set the default size of the SSL session ID cache */
+  data->set.ssl.numsessions = 5;
+  
+  /* create an array with connection data struct pointers */
+  data->state.numconnects = 5; /* hard-coded right now */
+  data->state.connects = (struct connectdata **)
+    malloc(sizeof(struct connectdata *) * data->state.numconnects);
+  
+  if(!data->state.connects) {
+    free(data);
+    return CURLE_OUT_OF_MEMORY;
+  }
+  
+  memset(data->state.connects, 0,
+         sizeof(struct connectdata *)*data->state.numconnects);
+
+  *curl = data;
+
+  return CURLE_OK;
+}
+
+CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...)
+{
+  va_list param;
+  char *cookiefile;
+
+  va_start(param, option);
+
+  switch(option) {
+  case CURLOPT_DNS_CACHE_TIMEOUT:
+    data->set.dns_cache_timeout = va_arg(param, int);
+    break;
+  case CURLOPT_DNS_USE_GLOBAL_CACHE: 
+    {
+      int use_cache = va_arg(param, int);
+      if (use_cache) {
+        Curl_global_host_cache_init();
+      }
+
+      data->set.global_dns_cache = use_cache;
+    }
+    break;
+  case CURLOPT_SSL_CIPHER_LIST:
+    /* set a list of cipher we want to use in the SSL connection */
+    data->set.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.
+     */
+    data->set.ssl.random_file = va_arg(param, char *);
+    break;
+  case CURLOPT_EGDSOCKET:
+    /*
+     * The Entropy Gathering Daemon socket pathname
+     */
+    data->set.ssl.egdsocket = va_arg(param, char *);
+    break;
+  case CURLOPT_MAXCONNECTS:
+    /*
+     * Set the absolute number of maximum simultaneous alive connection that
+     * libcurl is allowed to have.
+     */
+    {
+      long newconnects= va_arg(param, long);
+      struct connectdata **newptr;
+
+      if(newconnects < data->state.numconnects) {
+        /* Since this number is *decreased* from the existing number, we must
+           close the possibly open connections that live on the indexes that
+           are being removed! */
+        int i;
+        for(i=newconnects; i< data->state.numconnects; i++)
+          Curl_disconnect(data->state.connects[i]);
+      }
+      if(newconnects) {
+        newptr= (struct connectdata **)
+          realloc(data->state.connects,
+                  sizeof(struct connectdata *) * newconnects);
+        if(!newptr)
+          /* we closed a few connections in vain, but so what? */
+          return CURLE_OUT_OF_MEMORY;
+        data->state.connects = newptr;
+        data->state.numconnects = newconnects;
+      }
+      else {
+        /* zero makes NO cache at all */
+        if(data->state.connects)
+          free(data->state.connects);
+        data->state.connects=NULL;
+        data->state.numconnects=0;
+      }
+    }
+    break;
+  case CURLOPT_FORBID_REUSE:
+    /*
+     * When this transfer is done, it must not be left to be reused by a
+     * subsequent transfer but shall be closed immediately.
+     */
+    data->set.reuse_forbid = va_arg(param, long)?TRUE:FALSE;
+    break;
+  case CURLOPT_FRESH_CONNECT:
+    /*
+     * This transfer shall not use a previously cached connection but
+     * should be made with a fresh new connect!
+     */
+    data->set.reuse_fresh = va_arg(param, long)?TRUE:FALSE;
+    break;
+  case CURLOPT_VERBOSE:
+    /*
+     * Verbose means infof() calls that give a lot of information about
+     * the connection and transfer procedures as well as internal choices.
+     */
+    data->set.verbose = va_arg(param, long)?TRUE:FALSE;
+    break;
+  case CURLOPT_HEADER:
+    /*
+     * Set to include the header in the general data output stream.
+     */
+    data->set.http_include_header = va_arg(param, long)?TRUE:FALSE;
+    break;
+  case CURLOPT_NOPROGRESS:
+    /*
+     * Shut off the internal supported progress meter
+     */
+    data->set.hide_progress = 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.no_body = va_arg(param, long)?TRUE:FALSE;
+    break;
+  case CURLOPT_FAILONERROR:
+    /*
+     * Don't output the >=300 error code HTML-page, but instead only
+     * return error.
+     */
+    data->set.http_fail_on_error = va_arg(param, long)?TRUE:FALSE;
+    break;
+  case CURLOPT_UPLOAD:
+    /*
+     * We want to sent data to the remote host
+     */
+    data->set.upload = 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;
+    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 = va_arg(param, long)?TRUE:FALSE;
+    break;
+  case CURLOPT_FTPLISTONLY:
+    /*
+     * An FTP option that changes the command to one that asks for a list
+     * only, no file info details.
+     */
+    data->set.ftp_list_only = va_arg(param, long)?TRUE:FALSE;
+    break;
+  case CURLOPT_FTPAPPEND:
+    /*
+     * We want to upload and append to an existing (FTP) file.
+     */
+    data->set.ftp_append = va_arg(param, long)?TRUE:FALSE;
+    break;
+  case CURLOPT_NETRC:
+    /*
+     * Parse the $HOME/.netrc file
+     */
+    data->set.use_netrc = va_arg(param, long)?TRUE:FALSE;
+    break;
+  case CURLOPT_FOLLOWLOCATION:
+    /*
+     * Follow Location: header hints on a HTTP-server.
+     */
+    data->set.http_follow_location = va_arg(param, long)?TRUE:FALSE;
+    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_TRANSFERTEXT:
+    /*
+     * This option was previously named 'FTPASCII'. Renamed to work with
+     * more protocols than merely FTP.
+     *
+     * Transfer using ASCII (instead of BINARY).
+     */
+    data->set.ftp_ascii = va_arg(param, long)?TRUE:FALSE;
+    break;
+  case CURLOPT_PUT:
+    /*
+     * Use the HTTP PUT request to transfer data if this is TRUE.  If this is
+     * FALSE, don't set the httpreq. We can't know what to revert it to!
+     */
+    if(va_arg(param, long))
+      data->set.httpreq = HTTPREQ_PUT;
+    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 = 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 = 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;
+
+  case CURLOPT_COOKIEFILE:
+    /*
+     * Set cookie file to read and parse. Can be used multiple times.
+     */
+    cookiefile = (char *)va_arg(param, void *);
+    if(cookiefile)
+      data->cookies = Curl_cookie_init(cookiefile, data->cookies);
+    break;
+
+  case CURLOPT_COOKIEJAR:
+    /*
+     * Set cookie file name to dump all cookies to when we're done.
+     */
+    data->set.cookiejar = (char *)va_arg(param, void *);
+
+    /*
+     * Activate the cookie parser. This may or may not already
+     * have been made.
+     */
+    data->cookies = Curl_cookie_init(NULL, data->cookies);
+    break;
+  case CURLOPT_WRITEHEADER:
+    /*
+     * Custom pointer to pass the header write callback function
+     */
+    data->set.writeheader = (void *)va_arg(param, void *);
+    break;
+  case CURLOPT_COOKIE:
+    /*
+     * Cookie string to send to the remote server in the request.
+     */
+    data->set.cookie = va_arg(param, char *);
+    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
+     */
+    data->set.ftpport = va_arg(param, char *);
+    data->set.ftp_use_port = data->set.ftpport?1:0;
+    break;
+
+  case CURLOPT_FTP_USE_EPSV:
+    data->set.ftp_use_epsv = va_arg(param, long)?TRUE:FALSE;
+    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_CUSTOMREQUEST:
+    /*
+     * Set a custom string to use as request
+     */
+    data->set.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;
+  case CURLOPT_HTTPPOST:
+    /*
+     * Set to make us do HTTP POST
+     */
+    data->set.httppost = va_arg(param, struct HttpPost *);
+    if(data->set.httppost)
+      data->set.httpreq = HTTPREQ_POST_FORM;
+    break;
+
+  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 */
+    }
+    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_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_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! */
+      free(data->change.url);
+      data->change.url_alloc=FALSE;
+    }
+    data->set.set_url = va_arg(param, char *);
+    data->change.url = data->set.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_POST:
+    /* Does this option serve a purpose anymore? */
+
+    if(va_arg(param, long))
+      data->set.httpreq = HTTPREQ_POST;
+    break;
+  case CURLOPT_POSTFIELDS:
+    /*
+     * A string with POST data. Makes curl HTTP POST.
+     */
+    data->set.postfields = va_arg(param, char *);
+    if(data->set.postfields)
+      data->set.httpreq = HTTPREQ_POST;
+    break;
+  case CURLOPT_POSTFIELDSIZE:
+    /*
+     * The size of the POSTFIELD data, if curl should now do a strlen
+     * to find out. Enables binary posts.
+     */
+    data->set.postfieldsize = va_arg(param, long);
+    break;
+  case CURLOPT_REFERER:
+    /*
+     * String to set in the HTTP Referer: field.
+     */
+    if(data->change.referer_alloc) {
+      free(data->change.referer);
+      data->change.referer_alloc = FALSE;
+    }
+    data->set.set_referer = va_arg(param, char *);
+    data->change.referer = data->set.set_referer;
+    break;
+  case CURLOPT_AUTOREFERER:
+    /*
+     * Switch on automatic referer that gets set if curl follows locations.
+     */
+    data->set.http_auto_referer = va_arg(param, long)?1:0;
+    break;
+  case CURLOPT_PROXY:
+    /*
+     * Set proxy server:port to use as HTTP proxy
+     */
+    if(data->change.proxy_alloc) {
+      /*
+       * The already set string is allocated, free that first
+       */
+      data->change.proxy_alloc=FALSE;;
+      free(data->change.proxy);
+    }
+    data->set.set_proxy = va_arg(param, char *);
+    data->change.proxy = data->set.set_proxy;
+    break;
+  case CURLOPT_HTTPPROXYTUNNEL:
+    /*
+     * Tunnel operations through the proxy instead of normal proxy use
+     */
+    data->set.tunnel_thru_httpproxy = 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_TIMEOUT:
+    /*
+     * The maximum time you allow curl to use for a single transfer
+     * operation.
+     */
+    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);
+    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_USERAGENT:
+    /*
+     * String to use in the HTTP User-Agent field
+     */
+    data->set.useragent = va_arg(param, char *);
+    break;
+  case CURLOPT_USERPWD:
+    /*
+     * user:password to use in the operation
+     */
+    data->set.userpwd = 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_PROGRESSFUNCTION:
+    /*
+     * Progress callback function
+     */
+    data->set.fprogress = va_arg(param, curl_progress_callback);
+    data->progress.callback = TRUE; /* no longer internal */
+    break;
+  case CURLOPT_PROGRESSDATA:
+    /*
+     * Custom client data to pass to the progress callback
+     */
+    data->set.progress_client = va_arg(param, void *);
+    break;
+  case CURLOPT_PASSWDFUNCTION:
+    /*
+     * Password prompt callback
+     */
+    data->set.fpasswd = va_arg(param, curl_passwd_callback);
+    break;
+  case CURLOPT_PASSWDDATA:
+    /*
+     * Custom client data to pass to the password callback
+     */
+    data->set.passwd_client = va_arg(param, void *);
+    break;
+  case CURLOPT_PROXYUSERPWD:
+    /*
+     * user:password needed to use the proxy
+     */
+    data->set.proxyuserpwd = va_arg(param, char *);
+    break;
+  case CURLOPT_RANGE:
+    /*
+     * What range of the file you want to transfer
+     */
+    data->set.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_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 *);
+    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 = va_arg(param, curl_write_callback);
+    break;
+  case CURLOPT_READFUNCTION:
+    /*
+     * Read data callback
+     */
+    data->set.fread = va_arg(param, curl_read_callback);
+    break;
+  case CURLOPT_SSLCERT:
+    /*
+     * String that holds file name of the SSL certificate to use
+     */
+    data->set.cert = va_arg(param, char *);
+    break;
+  case CURLOPT_SSLCERTTYPE:
+    /*
+     * String that holds file type of the SSL certificate to use
+     */
+    data->set.cert_type = va_arg(param, char *);
+    break;
+  case CURLOPT_SSLKEY:
+    /*
+     * String that holds file name of the SSL certificate to use
+     */
+    data->set.key = va_arg(param, char *);
+    break;
+  case CURLOPT_SSLKEYTYPE:
+    /*
+     * String that holds file type of the SSL certificate to use
+     */
+    data->set.key_type = va_arg(param, char *);
+    break;
+  case CURLOPT_SSLKEYPASSWD:
+    /*
+     * String that holds the SSL private key password.
+     */
+    data->set.key_passwd = va_arg(param, char *);
+    break;
+  case CURLOPT_SSLENGINE:
+    /*
+     * String that holds the SSL crypto engine.
+     */
+#ifdef HAVE_OPENSSL_ENGINE_H
+    {
+      const char *cpTemp = va_arg(param, char *);
+      ENGINE     *e;
+      if (cpTemp && cpTemp[0]) {
+        e = ENGINE_by_id(cpTemp);
+        if (e) {
+          if (data->engine) {
+            ENGINE_free(data->engine);
+          }
+          data->engine = e;
+        }
+        else {
+          failf(data, "SSL Engine '%s' not found", cpTemp);
+          return CURLE_SSL_ENGINE_NOTFOUND;
+        }
+      }
+    }
+#else
+    return CURLE_SSL_ENGINE_NOTFOUND;
+#endif
+    break;
+  case CURLOPT_SSLENGINE_DEFAULT:
+    /*
+     * flag to set engine as default.
+     */
+#ifdef HAVE_OPENSSL_ENGINE_H
+    if (data->engine) {
+      if (ENGINE_set_default(data->engine, ENGINE_METHOD_ALL) > 0) {
+#ifdef DEBUG
+        fprintf(stderr,"set default crypto engine\n");
+#endif
+      }
+      else {
+#ifdef DEBUG
+        failf(data, "set default crypto engine failed");
+#endif
+        return CURLE_SSL_ENGINE_SETFAILED;
+      }
+    }
+#endif
+    break;
+  case CURLOPT_CRLF:
+    /*
+     * Kludgy option to enable CRLF convertions. Subject for removal.
+     */
+    data->set.crlf = va_arg(param, long)?TRUE:FALSE;
+    break;
+  case CURLOPT_INTERFACE:
+    /*
+     * Set what interface to bind to when performing an operation and thus
+     * what from-IP your connection will use.
+     */
+    data->set.device = va_arg(param, char *);
+    break;
+  case CURLOPT_KRB4LEVEL:
+    /*
+     * A string that defines the krb4 security level.
+     */
+    data->set.krb4_level = va_arg(param, char *);
+    data->set.krb4=data->set.krb4_level?TRUE:FALSE;
+    break;
+  case CURLOPT_SSL_VERIFYPEER:
+    /*
+     * Enable peer SSL verifying.
+     */
+    data->set.ssl.verifypeer = va_arg(param, long);
+    break;
+  case CURLOPT_SSL_VERIFYHOST:
+    /*
+     * Enable verification of the CN contained in the peer certificate
+     */
+    data->set.ssl.verifyhost = va_arg(param, long);
+    break;
+  case CURLOPT_CAINFO:
+    /*
+     * Set CA info for SSL connection. Specify file name of the CA certificate
+     */
+    data->set.ssl.CAfile = va_arg(param, char *);
+    data->set.ssl.CApath = NULL; /*This does not work on windows.*/
+    break;
+  case CURLOPT_TELNETOPTIONS:
+    /*
+     * Set a linked list of telnet options
+     */
+    data->set.telnet_options = va_arg(param, struct curl_slist *);
+    break;
+  default:
+    /* unknown tag and its companion, just ignore: */
+    return CURLE_READ_ERROR; /* correct this */
+  }
+  return CURLE_OK;
+}
+
+CURLcode Curl_disconnect(struct connectdata *conn)
+{
+  if(!conn)
+    return CURLE_OK; /* this is closed and fine already */
+
+  /*
+   * The range string is usually freed in curl_done(), but we might
+   * get here *instead* if we fail prematurely. Thus we need to be able
+   * to free this resource here as well.
+   */
+  if(conn->bits.rangestringalloc) {
+    free(conn->range);
+    conn->bits.rangestringalloc = FALSE;
+  }
+
+  if(-1 != conn->connectindex) {
+    /* unlink ourselves! */
+    infof(conn->data, "Closing connection #%d\n", conn->connectindex);
+    conn->data->state.connects[conn->connectindex] = NULL;
+  }
+
+  if(conn->curl_disconnect)
+    /* This is set if protocol-specific cleanups should be made */
+    conn->curl_disconnect(conn);
+
+  if(conn->proto.generic)
+    free(conn->proto.generic);
+
+  if(conn->newurl)
+    free(conn->newurl);
+
+  if(conn->path) /* the URL path part */
+    free(conn->path);
+
+#ifdef USE_SSLEAY
+  Curl_SSL_Close(conn);
+#endif /* USE_SSLEAY */
+
+  /* close possibly still open sockets */
+  if(-1 != conn->secondarysocket)
+    sclose(conn->secondarysocket);
+  if(-1 != conn->firstsocket)
+    sclose(conn->firstsocket);
+
+  if(conn->allocptr.proxyuserpwd)
+    free(conn->allocptr.proxyuserpwd);
+  if(conn->allocptr.uagent)
+    free(conn->allocptr.uagent);
+  if(conn->allocptr.userpwd)
+    free(conn->allocptr.userpwd);
+  if(conn->allocptr.rangeline)
+    free(conn->allocptr.rangeline);
+  if(conn->allocptr.ref)
+    free(conn->allocptr.ref);
+  if(conn->allocptr.cookie)
+    free(conn->allocptr.cookie);
+  if(conn->allocptr.host)
+    free(conn->allocptr.host);
+
+  if(conn->proxyhost)
+    free(conn->proxyhost);
+
+  free(conn); /* free all the connection oriented data */
+
+  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(int sock) 
+{ 
+  int sval; 
+  bool ret_val = TRUE; 
+  fd_set check_set; 
+  struct timeval to; 
+
+  FD_ZERO(&check_set); 
+  FD_SET(sock,&check_set); 
+
+  to.tv_sec = 0; 
+  to.tv_usec = 1; 
+
+  sval = select(sock + 1, &check_set, 0, 0, &to);
+  if(sval == 0)
+    /* timeout */
+    ret_val = FALSE;
+  
+  return ret_val;
+}
+
+/*
+ * Given one filled in connection struct (named needle), this function should
+ * detect if there already is one that have all the significant details
+ * exactly the same and thus should be used instead.
+ */
+static bool
+ConnectionExists(struct SessionHandle *data,
+                 struct connectdata *needle,
+                 struct connectdata **usethis)
+{
+  long i;
+  struct connectdata *check;
+
+  for(i=0; i< data->state.numconnects; i++) {
+    /*
+     * Note that if we use a HTTP proxy, we check connections to that
+     * proxy and not to the actual remote server.
+     */
+    check = data->state.connects[i];
+    if(!check)
+      /* NULL pointer means not filled-in entry */
+      continue;
+    if(!needle->bits.httpproxy || needle->protocol&PROT_SSL) {
+      /* The requested connection does not use a HTTP proxy or it
+         uses SSL. */
+
+      if(!(needle->protocol&PROT_SSL) && check->bits.httpproxy)
+        /* we don't do SSL but the cached connection has a proxy,
+           then don't match this */
+        continue;
+
+      if(strequal(needle->protostr, check->protostr) &&
+         strequal(needle->name, check->name) &&
+         (needle->remote_port == check->remote_port) ) {
+        bool dead;
+        if(strequal(needle->protostr, "FTP")) {
+          /* This is FTP, verify that we're using the same name and
+             password as well */
+          if(!strequal(needle->data->state.user, check->proto.ftp->user) ||
+             !strequal(needle->data->state.passwd, check->proto.ftp->passwd)) {
+            /* one of them was different */
+            continue;
+          }
+        }
+        dead = SocketIsDead(check->firstsocket);
+        if(dead) {
+          /*
+           * Even though the connection seems to have passed away, we could
+           * still make an effort to get the name information, as we intend to
+           * connect to the same host again.
+           *
+           * This is now subject to discussion. What do you think?
+           */
+          infof(data, "Connection %d seems to be dead!\n", i);
+          Curl_disconnect(check); /* disconnect resources */
+          data->state.connects[i]=NULL; /* nothing here */
+
+          /* There's no need to continue search, because we only store
+             one connection for each unique set of identifiers */
+          return FALSE;
+        }
+
+        *usethis = check;
+        return TRUE; /* yes, we found one to use! */
+        
+      }
+    }
+    else { /* The requested needle connection is using a proxy,
+              is the checked one using the same? */
+      if(check->bits.httpproxy &&
+         strequal(needle->proxyhost, check->proxyhost) &&
+         needle->port == check->port) {
+        /* This is the same proxy connection, use it! */
+        *usethis = check;
+        return TRUE;
+      }
+    }
+  }
+  return FALSE; /* no matching connecting exists */
+}
+
+/*
+ * This function frees/closes a connection in the connection cache. This
+ * should take the previously set policy into account when deciding which
+ * of the connections to kill.
+ */
+static int
+ConnectionKillOne(struct SessionHandle *data)
+{
+  long i;
+  struct connectdata *conn;
+  int highscore=-1;
+  int connindex=-1;
+  int score;
+  CURLcode result;
+  struct timeval now;
+
+  now = Curl_tvnow();
+
+  for(i=0; i< data->state.numconnects; i++) {
+    conn = data->state.connects[i];
+    
+    if(!conn)
+      continue;
+
+    /*
+     * By using the set policy, we score each connection.
+     */
+    switch(data->set.closepolicy) {
+    case CURLCLOSEPOLICY_LEAST_RECENTLY_USED:
+    default:
+      /*
+       * Set higher score for the age passed since the connection
+       * was used.
+       */
+      score = Curl_tvdiff(now, conn->now);
+      break;
+    case CURLCLOSEPOLICY_OLDEST:
+      /*
+       * Set higher score for the age passed since the connection
+       * was created.
+       */
+      score = Curl_tvdiff(now, conn->created);
+      break;
+    }
+
+    if(score > highscore) {
+      highscore = score;
+      connindex = i;
+    }
+  }
+  if(connindex >= 0) {
+
+    /* the winner gets the honour of being disconnected */
+    result = Curl_disconnect(data->state.connects[connindex]);
+
+    /* clean the array entry */
+    data->state.connects[connindex] = NULL;
+  }
+
+  return connindex; /* return the available index or -1 */
+}
+
+/*
+ * The given input connection struct pointer is to be stored. If the "cache"
+ * is already full, we must clean out the most suitable using the previously
+ * set policy.
+ *
+ * The given connection should be unique. That must've been checked prior to
+ * this call.
+ */
+static unsigned int
+ConnectionStore(struct SessionHandle *data,
+                struct connectdata *conn)
+{
+  long i;
+  for(i=0; i< data->state.numconnects; i++) {
+    if(!data->state.connects[i])
+      break;
+  }
+  if(i == data->state.numconnects) {
+    /* there was no room available, kill one */
+    i = ConnectionKillOne(data);
+    infof(data, "Connection (#%d) was killed to make room\n", i);
+  }
+
+  if(-1 != i) {
+    /* only do this if a true index was returned, if -1 was returned there
+       is no room in the cache for an unknown reason and we cannot store
+       this there. */
+    data->state.connects[i] = conn; /* fill in this */
+    conn->connectindex = i; /* make the child know where the pointer to this
+                               particular data is stored */
+  }
+  return i;
+}
+
+static CURLcode ConnectPlease(struct connectdata *conn)
+{
+  CURLcode result;
+  Curl_ipconnect *addr;
+
+  /*************************************************************
+   * Connect to server/proxy
+   *************************************************************/
+  result= Curl_connecthost(conn,
+                           conn->hostaddr,
+                           conn->port,
+                           &conn->firstsocket,
+                           &addr);
+  if(CURLE_OK == result) {
+    /* All is cool, then we store the current information from the hostaddr
+       struct to the serv_addr, as it might be needed later. The address
+       returned from the function above is crucial here. */
+#ifdef ENABLE_IPV6
+    conn->serv_addr = addr;
+#else
+    memset((char *) &conn->serv_addr, '\0', sizeof(conn->serv_addr));
+    memcpy((char *)&(conn->serv_addr.sin_addr),
+           (struct in_addr *)addr, sizeof(struct in_addr));
+    conn->serv_addr.sin_family = conn->hostaddr->h_addrtype;
+    conn->serv_addr.sin_port = htons(conn->port);
+#endif
+  }
+
+  return result;
+}
+
+static CURLcode CreateConnection(struct SessionHandle *data,
+                                 struct connectdata **in_connect)
+{
+  char *tmp;
+  char *buf;
+  CURLcode result=CURLE_OK;
+  char resumerange[40]="";
+  struct connectdata *conn;
+  struct connectdata *conn_temp;
+  char endbracket;
+  int urllen;
+#ifdef HAVE_INET_NTOA_R
+  char ntoa_buf[64];
+#endif
+#ifdef HAVE_ALARM
+  unsigned int prev_alarm;
+#endif
+
+#ifdef HAVE_SIGACTION
+  struct sigaction keep_sigact;   /* store the old struct here */
+  bool keep_copysig;              /* did copy it? */
+#else
+#ifdef HAVE_SIGNAL
+  void *keep_sigact;              /* store the old handler here */
+#endif
+#endif
+
+  /*************************************************************
+   * 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 = (struct connectdata *)malloc(sizeof(struct connectdata));
+  if(!conn) {
+    *in_connect = NULL; /* clear the pointer */
+    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;
+
+  /* we have to init the struct */
+  memset(conn, 0, sizeof(struct connectdata));
+
+  /* and we setup a few fields in case we end up actually using this struct */
+  conn->data = data;           /* remember our daddy */
+  conn->firstsocket = -1;     /* no file descriptor */
+  conn->secondarysocket = -1; /* no file descriptor */
+  conn->connectindex = -1;    /* no index */
+  conn->bits.httpproxy = data->change.proxy?TRUE:FALSE; /* proxy-or-not */
+  conn->bits.use_range = data->set.set_range?TRUE:FALSE; /* range status */
+  conn->range = data->set.set_range;               /* clone the range setting */
+  conn->resume_from = data->set.set_resume_from;   /* inherite resume_from */
+
+  /* Default protocol-independent behavior doesn't support persistant
+     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;
+  
+  /* inherite initial knowledge from the data struct */
+  conn->bits.user_passwd = data->set.userpwd?1:0;
+  conn->bits.proxy_user_passwd = data->set.proxyuserpwd?1:0;
+
+  /* maxdownload must be -1 on init, as 0 is a valid value! */
+  conn->maxdownload = -1;  /* might have been used previously! */
+
+  /* Store creation time to help future close decision making */
+  conn->created = Curl_tvnow();
+
+
+  /***********************************************************
+   * 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;
+  
+  conn->path=(char *)malloc(urllen);
+  if(NULL == conn->path)
+    return CURLE_OUT_OF_MEMORY; /* really bad error */
+
+  /*************************************************************
+   * 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, "%64[^:]://%[^\n]",
+                  conn->protostr,
+                  conn->path)) && strequal(conn->protostr, "file")) {
+    /*
+     * 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(conn->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(conn->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 preceeding 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++; 
+        
+        strcpy(conn->path, ptr);
+      }
+    }
+
+    strcpy(conn->protostr, "file"); /* store protocol string lowercase */
+  }
+  else {
+    /* Set default host and default path */
+    strcpy(conn->gname, "curl.haxx.se");
+    strcpy(conn->path, "/");
+
+    if (2 > sscanf(data->change.url,
+                   "%64[^\n:]://%512[^\n/]%[^\n]",
+                   conn->protostr, conn->gname, conn->path)) {
+      
+      /*
+       * The URL was badly formatted, let's try the browser-style _without_
+       * protocol specified like 'http://'.
+       */
+      if((1 > sscanf(data->change.url, "%512[^\n/]%[^\n]",
+                     conn->gname, conn->path)) ) {
+        /*
+         * We couldn't even get this format.
+         */
+        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.
+       */
+
+      if(strnequal(conn->gname, "FTP", 3)) {
+        strcpy(conn->protostr, "ftp");
+      }
+      else if(strnequal(conn->gname, "GOPHER", 6))
+        strcpy(conn->protostr, "gopher");
+#ifdef USE_SSLEAY
+      else if(strnequal(conn->gname, "HTTPS", 5))
+        strcpy(conn->protostr, "https");
+      else if(strnequal(conn->gname, "FTPS", 4))
+        strcpy(conn->protostr, "ftps");
+#endif /* USE_SSLEAY */
+      else if(strnequal(conn->gname, "TELNET", 6))
+        strcpy(conn->protostr, "telnet");
+      else if (strnequal(conn->gname, "DICT", sizeof("DICT")-1))
+        strcpy(conn->protostr, "DICT");
+      else if (strnequal(conn->gname, "LDAP", sizeof("LDAP")-1))
+        strcpy(conn->protostr, "LDAP");
+      else {
+        strcpy(conn->protostr, "http");
+      }
+
+      conn->protocol |= PROT_MISSING; /* not given in URL */
+    }
+  }
+
+  buf = data->state.buffer; /* this is our buffer */
+
+  /*************************************************************
+   * Take care of user and password authentication stuff
+   *************************************************************/
+
+  if(conn->bits.user_passwd && !data->set.use_netrc) {
+    data->state.user[0] =0;
+    data->state.passwd[0]=0;
+
+    if(*data->set.userpwd != ':') {
+      /* the name is given, get user+password */
+      sscanf(data->set.userpwd, "%127[^:]:%127[^\n]",
+             data->state.user, data->state.passwd);
+    }
+    else
+      /* no name given, get the password only */
+      sscanf(data->set.userpwd+1, "%127[^\n]", data->state.passwd);
+
+    /* check for password, if no ask for one */
+    if( !data->state.passwd[0] ) {
+      if(!data->set.fpasswd ||
+         data->set.fpasswd(data->set.passwd_client,
+                       "password:", data->state.passwd,
+                           sizeof(data->state.passwd)))
+        return CURLE_BAD_PASSWORD_ENTERED;
+    }
+  }
+
+  /*************************************************************
+   * Take care of proxy authentication stuff
+   *************************************************************/
+  if(conn->bits.proxy_user_passwd) {
+    data->state.proxyuser[0] =0;
+    data->state.proxypasswd[0]=0;
+
+    if(*data->set.proxyuserpwd != ':') {
+      /* the name is given, get user+password */
+      sscanf(data->set.proxyuserpwd, "%127[^:]:%127[^\n]",
+             data->state.proxyuser, data->state.proxypasswd);
+      }
+    else
+      /* no name given, get the password only */
+      sscanf(data->set.proxyuserpwd+1, "%127[^\n]", data->state.proxypasswd);
+
+    /* check for password, if no ask for one */
+    if( !data->state.proxypasswd[0] ) {
+      if(!data->set.fpasswd ||
+         data->set.fpasswd( data->set.passwd_client,
+                        "proxy password:",
+                        data->state.proxypasswd,
+                        sizeof(data->state.proxypasswd)))
+        return CURLE_BAD_PASSWORD_ENTERED;
+    }
+
+  }
+
+  /*************************************************************
+   * Set a few convenience pointers 
+   *************************************************************/
+  conn->name = conn->gname;
+  conn->ppath = conn->path;
+  conn->hostname = conn->name;
+
+
+  /*************************************************************
+   * Detect what (if any) proxy to use
+   *************************************************************/
+  if(!data->change.proxy) {
+    /* 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/
+     * gopher_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 *no_proxy_tok_buf;
+    char *proxy=NULL;
+    char proxy_env[128];
+
+    no_proxy=curl_getenv("no_proxy");
+    if(!no_proxy)
+      no_proxy=curl_getenv("NO_PROXY");
+
+    if(!no_proxy || !strequal("*", no_proxy)) {
+      /* NO_PROXY wasn't specified or it wasn't just an asterisk */
+      char *nope;
+
+      nope=no_proxy?strtok_r(no_proxy, ", ", &no_proxy_tok_buf):NULL;
+      while(nope) {
+        if(strlen(nope) <= strlen(conn->name)) {
+          char *checkn=
+            conn->name + strlen(conn->name) - strlen(nope);
+          if(strnequal(nope, checkn, strlen(nope))) {
+            /* no proxy for this host! */
+            break;
+          }
+        }
+        nope=strtok_r(NULL, ", ", &no_proxy_tok_buf);
+      }
+      if(!nope) {
+        /* It was not listed as without proxy */
+        char *protop = conn->protostr;
+        char *envp = proxy_env;
+        char *prox;
+
+        /* Now, build <protocol>_proxy and check for such a one to use */
+        while(*protop)
+          *envp++ = tolower(*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 && !strequal("http_proxy", proxy_env)) {
+          /* There was no lowercase variable, try the uppercase version: */
+          for(envp = proxy_env; *envp; envp++)
+            *envp = toupper(*envp);
+          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(proxy && *proxy) {
+          /* we have a proxy here to set */
+          data->change.proxy = proxy;
+          data->change.proxy_alloc=TRUE; /* this needs to be freed later */
+          conn->bits.httpproxy = TRUE;
+        }
+      } /* if (!nope) - it wasn't specified non-proxy */
+    } /* NO_PROXY wasn't specified or '*' */
+    if(no_proxy)
+      free(no_proxy);
+  } /* if not using proxy */
+
+  /*************************************************************
+   * No protocol part in URL was used, add it!
+   *************************************************************/
+  if(conn->protocol&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->protostr, data->change.url);
+
+    if(!reurl)
+      return CURLE_OUT_OF_MEMORY;
+
+    data->change.url = reurl;
+    data->change.url_alloc = TRUE; /* free this later */
+    conn->protocol &= ~PROT_MISSING; /* switch that one off again */
+  }
+
+  /************************************************************
+   * RESUME on a HTTP page is a tricky business. First, let's just check that
+   * 'range' isn't used, then set the range parameter and leave the resume as
+   * it is to inform about this situation for later use. We will then
+   * "attempt" to resume, and if we're talking to a HTTP/1.1 (or later)
+   * server, we will get the document resumed. If we talk to a HTTP/1.0
+   * server, we just fail since we can't rewind the file writing from within
+   * this function.
+   ***********************************************************/
+  if(conn->resume_from) {
+    if(!conn->bits.use_range) {
+      /* if it already was in use, we just skip this */
+      snprintf(resumerange, sizeof(resumerange), "%d-", conn->resume_from);
+      conn->range=strdup(resumerange); /* tell ourselves to fetch this range */
+      conn->bits.rangestringalloc = TRUE; /* mark as allocated */
+      conn->bits.use_range = 1; /* switch on range usage */
+    }
+  }
+
+  /*************************************************************
+   * Setup internals depending on protocol
+   *************************************************************/
+
+  if (strequal(conn->protostr, "HTTP")) {
+    conn->port = (data->set.use_port && data->state.allow_port)?
+      data->set.use_port:PORT_HTTP;
+    conn->remote_port = PORT_HTTP;
+    conn->protocol |= PROT_HTTP;
+    conn->curl_do = Curl_http;
+    conn->curl_done = Curl_http_done;
+    conn->curl_connect = Curl_http_connect;
+  }
+  else if (strequal(conn->protostr, "HTTPS")) {
+#ifdef USE_SSLEAY
+
+    conn->port = (data->set.use_port && data->state.allow_port)?
+      data->set.use_port:PORT_HTTPS;
+    conn->remote_port = PORT_HTTPS;
+    conn->protocol |= PROT_HTTP|PROT_HTTPS|PROT_SSL;
+
+    conn->curl_do = Curl_http;
+    conn->curl_done = Curl_http_done;
+    conn->curl_connect = Curl_http_connect;
+
+#else /* USE_SSLEAY */
+    failf(data, LIBCURL_NAME
+          " was built with SSL disabled, https: not supported!");
+    return CURLE_UNSUPPORTED_PROTOCOL;
+#endif /* !USE_SSLEAY */
+  }
+  else if (strequal(conn->protostr, "GOPHER")) {
+    conn->port = (data->set.use_port && data->state.allow_port)?
+      data->set.use_port:PORT_GOPHER;
+    conn->remote_port = PORT_GOPHER;
+    /* Skip /<item-type>/ in path if present */
+    if (isdigit((int)conn->path[1])) {
+      conn->ppath = strchr(&conn->path[1], '/');
+      if (conn->ppath == NULL)
+        conn->ppath = conn->path;
+      }
+    conn->protocol |= PROT_GOPHER;
+    conn->curl_do = Curl_http;
+    conn->curl_done = Curl_http_done;
+  }
+  else if(strequal(conn->protostr, "FTP") ||
+          strequal(conn->protostr, "FTPS")) {
+    char *type;
+
+    if(strequal(conn->protostr, "FTPS")) {
+#ifdef USE_SSLEAY
+      conn->protocol |= PROT_FTPS|PROT_SSL;
+#else
+      failf(data, LIBCURL_NAME
+            " was built with SSL disabled, ftps: not supported!");
+      return CURLE_UNSUPPORTED_PROTOCOL;
+#endif /* !USE_SSLEAY */
+    }
+
+    conn->port = (data->set.use_port && data->state.allow_port)?
+      data->set.use_port:PORT_FTP;
+    conn->remote_port = PORT_FTP;
+    conn->protocol |= PROT_FTP;
+
+    if(data->change.proxy &&
+       !data->set.tunnel_thru_httpproxy) {
+      /* Unless we have asked to tunnel ftp operations through the proxy, we
+         switch and use HTTP operations only */
+      if(conn->protocol & PROT_FTPS) {
+        /* FTPS is a hacked protocol and does not work through your
+           ordinary http proxy! */
+        failf(data, "ftps does not work through http proxy!");
+        return CURLE_UNSUPPORTED_PROTOCOL;
+      }
+      conn->curl_do = Curl_http;
+      conn->curl_done = Curl_http_done;
+    }
+    else {
+      conn->curl_do = Curl_ftp;
+      conn->curl_done = Curl_ftp_done;
+      conn->curl_connect = Curl_ftp_connect;
+      conn->curl_disconnect = Curl_ftp_disconnect;
+    }
+
+    conn->ppath++; /* don't include the initial slash */
+
+    /* FTP URLs support an extension like ";type=<typecode>" that
+     * we'll try to get now! */
+    type=strstr(conn->ppath, ";type=");
+    if(!type) {
+      type=strstr(conn->gname, ";type=");
+    }
+    if(type) {
+      char command;
+      *type=0;
+      command = toupper(type[6]);
+      switch(command) {
+      case 'A': /* ASCII mode */
+        data->set.ftp_ascii = 1;
+        break;
+      case 'D': /* directory mode */
+        data->set.ftp_list_only = 1;
+        break;
+      case 'I': /* binary mode */
+      default:
+        /* switch off ASCII */
+        data->set.ftp_ascii = 0;
+        break;
+      }
+    }
+  }
+  else if(strequal(conn->protostr, "TELNET")) {
+    /* telnet testing factory */
+    conn->protocol |= PROT_TELNET;
+
+    conn->port = (data->set.use_port && data->state.allow_port)?
+      data->set.use_port: PORT_TELNET;
+    conn->remote_port = PORT_TELNET;
+    conn->curl_do = Curl_telnet;
+    conn->curl_done = Curl_telnet_done;
+  }
+  else if (strequal(conn->protostr, "DICT")) {
+    conn->protocol |= PROT_DICT;
+    conn->port = (data->set.use_port && data->state.allow_port)?
+      data->set.use_port:PORT_DICT;
+    conn->remote_port = PORT_DICT;
+    conn->curl_do = Curl_dict;
+    conn->curl_done = NULL; /* no DICT-specific done */
+  }
+  else if (strequal(conn->protostr, "LDAP")) {
+    conn->protocol |= PROT_LDAP;
+    conn->port = (data->set.use_port && data->state.allow_port)?
+      data->set.use_port:PORT_LDAP;
+    conn->remote_port = PORT_LDAP;
+    conn->curl_do = Curl_ldap;
+    conn->curl_done = NULL; /* no LDAP-specific done */
+  }
+  else if (strequal(conn->protostr, "FILE")) {
+    conn->protocol |= PROT_FILE;
+
+    conn->curl_do = Curl_file;
+    /* no done() function */
+
+    /* anyway, this is supposed to be the connect function so we better
+       at least check that the file is present here! */
+    result = Curl_file_connect(conn);
+
+    /* Setup a "faked" transfer that'll do nothing */
+    if(CURLE_OK == result) {
+      result = Curl_Transfer(conn, -1, -1, FALSE, NULL, /* no download */
+                             -1, NULL); /* no upload */
+    }
+
+    return result;
+  }
+  else {
+    /* We fell through all checks and thus we don't support the specified
+       protocol */
+    failf(data, "Unsupported protocol: %s", conn->protostr);
+    return CURLE_UNSUPPORTED_PROTOCOL;
+  }
+
+  /*************************************************************
+   * .netrc scanning coming up
+   *************************************************************/
+  if(data->set.use_netrc) {
+    if(Curl_parsenetrc(conn->hostname,
+                       data->state.user,
+                       data->state.passwd)) {
+      infof(data, "Couldn't find host %s in the .netrc file, using defaults",
+            conn->hostname);
+    }
+    else
+      conn->bits.user_passwd = 1; /* enable user+password */
+
+    /* weather we failed or not, we don't know which fields that were filled
+       in anyway */
+    if(!data->state.user[0])
+      strcpy(data->state.user, CURL_DEFAULT_USER);
+    if(!data->state.passwd[0])
+      strcpy(data->state.passwd, CURL_DEFAULT_PASSWORD);
+  }
+  else if(!(conn->bits.user_passwd) &&
+          (conn->protocol & (PROT_FTP|PROT_HTTP)) ) {
+    /* This is a FTP or HTTP URL, and we haven't got the user+password in
+     * the extra parameter, we will now try to extract the possible
+     * user+password pair in a string like:
+     * ftp://user:[email protected]:8021/README */
+    char *ptr=NULL; /* assign to remove possible warnings */
+    if((ptr=strchr(conn->name, '@'))) {
+      /* there's a user+password given here, to the left of the @ */
+
+      data->state.user[0] =0;
+      data->state.passwd[0]=0;
+
+      if(*conn->name != ':') {
+        /* the name is given, get user+password */
+        sscanf(conn->name, "%127[^:@]:%127[^@]",
+               data->state.user, data->state.passwd);
+      }
+      else
+        /* no name given, get the password only */
+        sscanf(conn->name+1, "%127[^@]", data->state.passwd);
+
+      if(data->state.user[0]) {
+        char *newname=curl_unescape(data->state.user, 0);
+        if(strlen(newname) < sizeof(data->state.user)) {
+          strcpy(data->state.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);
+      }
+
+      /* check for password, if no ask for one */
+      if( !data->state.passwd[0] ) {
+        if(!data->set.fpasswd ||
+           data->set.fpasswd(data->set.passwd_client,
+                             "password:", data->state.passwd,
+                             sizeof(data->state.passwd)))
+          return CURLE_BAD_PASSWORD_ENTERED;
+      }
+      else {
+        /* we have a password found in the URL, decode it! */
+        char *newpasswd=curl_unescape(data->state.passwd, 0);
+        if(strlen(newpasswd) < sizeof(data->state.passwd)) {
+          strcpy(data->state.passwd, newpasswd);
+        }
+        free(newpasswd);
+      }
+
+      conn->name = ++ptr;
+      conn->bits.user_passwd=TRUE; /* enable user+password */
+    }
+    else {
+      strcpy(data->state.user, CURL_DEFAULT_USER);
+      strcpy(data->state.passwd, CURL_DEFAULT_PASSWORD);
+    }
+  }
+
+  /*************************************************************
+   * Figure out the remote port number
+   *
+   * 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)
+   *************************************************************/
+
+  if((1 == sscanf(conn->name, "[%*39[0-9a-fA-F:.]%c", &endbracket)) &&
+     (']' == endbracket)) {
+    /* This is a (IPv6-style) specified IP-address. We support _any_
+       IP within brackets to be really generic. */
+    conn->name++; /* pass the starting bracket */
+
+    tmp = strchr(conn->name, ']');
+    *tmp = 0; /* zero terminate */
+
+    tmp++; /* pass the ending bracket */
+    if(':' != *tmp)
+      tmp = NULL; /* no port number available */
+  }
+  else {
+    /* traditional IPv4-style port-extracting */
+    tmp = strchr(conn->name, ':');
+  }
+
+  if (tmp) {
+    *tmp++ = '\0'; /* cut off the name there */
+    conn->remote_port = atoi(tmp);
+  }
+
+  if(data->change.proxy) {
+    /* If this is supposed to use a proxy, we need to figure out the proxy
+       host name name, so that we can re-use an existing connection
+       that may exist registered to the same proxy host. */
+
+    char *prox_portno;
+    char *endofprot;
+
+    /* We need to make a duplicate of the proxy so that we can modify the
+       string safely. */
+    char *proxydup=strdup(data->change.proxy);
+
+    /* We use 'proxyptr' to point to the proxy name from now on... */
+    char *proxyptr=proxydup;
+
+    if(NULL == proxydup) {
+      failf(data, "memory shortage");
+      return CURLE_OUT_OF_MEMORY;
+    }
+
+    /* Daniel Dec 10, 1998:
+       We do the proxy host string parsing here. We want the host name and the
+       port name. Accept a protocol:// prefix, even though it should just be
+       ignored. */
+
+    /* 1. skip the protocol part if present */
+    endofprot=strstr(proxyptr, "://");
+    if(endofprot) {
+      proxyptr = endofprot+3;
+    }
+
+    /* allow user to specify proxy.server.com:1080 if desired */
+    prox_portno = strchr (proxyptr, ':');
+    if (prox_portno) {
+      *prox_portno = 0x0; /* cut off number from host name */
+      prox_portno ++;
+      /* now set the local port number */
+      conn->port = atoi(prox_portno);
+    }
+    else 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->proxyhost = strdup(proxyptr);
+
+    free(proxydup); /* free the duplicate pointer and not the modified */
+  }
+
+  /*************************************************************
+   * 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 set TRUE if we are told to use a fresh connection
+     by force */
+  if(!data->set.reuse_fresh &&
+     ConnectionExists(data, conn, &conn_temp)) {
+    /*
+     * 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.
+     */
+    struct connectdata *old_conn = conn;
+    char *path = old_conn->path; /* setup the current path pointer properly */
+    char *ppath = old_conn->ppath; /* this is the modified path pointer */
+    if(old_conn->proxyhost)
+      free(old_conn->proxyhost);
+    conn = conn_temp;        /* use this connection from now on */
+
+    /* we need these pointers if we speak over a proxy */
+    conn->hostname = conn->gname;
+    conn->name = &conn->gname[old_conn->name - old_conn->gname];
+
+    free(conn->path);    /* free the previously allocated path pointer */
+
+    /* 'path' points to the allocated data, 'ppath' may have been advanced
+       to point somewhere within the 'path' area. */
+    conn->path = path; 
+    conn->ppath = ppath;
+
+    /* re-use init */
+    conn->bits.reuse = TRUE; /* yes, we're re-using here */
+    conn->bits.chunk = FALSE; /* always assume not chunked unless told
+                                 otherwise */
+    conn->maxdownload = -1;  /* might have been used previously! */
+
+    free(old_conn);          /* we don't need this anymore */
+
+    /*
+     * If we're doing a resumed transfer, we need to setup our stuff
+     * properly.
+     */
+    conn->resume_from = data->set.set_resume_from;
+    if (conn->resume_from) {
+        snprintf(resumerange, sizeof(resumerange), "%d-", conn->resume_from);
+        if (conn->bits.rangestringalloc == TRUE) 
+            free(conn->range);
+        
+        /* tell ourselves to fetch this range */
+        conn->range = strdup(resumerange);
+        conn->bits.use_range = TRUE;        /* enable range download */
+        conn->bits.rangestringalloc = TRUE; /* mark range string allocated */
+    }
+    else if (data->set.set_range) {
+      /* There is a range, but is not a resume, useful for random ftp access */
+      conn->range = strdup(data->set.set_range);
+      conn->bits.rangestringalloc = TRUE; /* mark range string allocated */
+      conn->bits.use_range = TRUE;        /* enable range download */
+    }
+    
+    *in_connect = conn;      /* return this instead! */
+
+    infof(data, "Re-using existing connection! (#%d)\n", conn->connectindex);
+  }
+  else {
+    /*
+     * This is a brand new connection, so let's store it in the connection
+     * cache of ours!
+     */
+    ConnectionStore(data, conn);
+  }
+
+  /*************************************************************
+   * Set timeout if that is being used
+   *************************************************************/
+  if(data->set.timeout || data->set.connecttimeout) {
+    /*************************************************************
+     * Set signal handler to catch SIGALRM
+     * Store the old value to be able to set it back later!
+     *************************************************************/
+
+#ifdef HAVE_SIGACTION
+    struct sigaction sigact;
+    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
+    /* no sigaction(), revert to the much lamer signal() */
+#ifdef HAVE_SIGNAL
+    keep_sigact = signal(SIGALRM, alarmfunc);
+#endif
+#endif
+
+    /* We set the timeout on the name resolving phase first, separately from
+     * the download/upload part to allow a maximum time on everything. This is
+     * a signal-based timeout, why it won't work and shouldn't be used in
+     * multi-threaded environments. */
+
+#ifdef HAVE_ALARM
+    /* alarm() makes a signal get sent when the timeout fires off, and that
+       will abort system calls */
+    prev_alarm = alarm(data->set.connecttimeout?
+                       data->set.connecttimeout:
+                       data->set.timeout);
+    /* We can expect the conn->created time to be "now", as that was just
+       recently set in the beginning of this function and nothing slow
+       has been done since then until now. */
+#endif
+  }
+
+  /*************************************************************
+   * Resolve the name of the server or proxy
+   *************************************************************/
+  if(!data->change.proxy) {
+    /* 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 */
+    if(!conn->hostaddr) {
+      /* it might already be set if reusing a connection */
+      conn->hostaddr = Curl_resolv(data, conn->name, conn->port,
+                                   &conn->hostent_buf);
+    }
+    if(!conn->hostaddr) {
+      failf(data, "Couldn't resolve host '%s'", conn->name);
+      result =  CURLE_COULDNT_RESOLVE_HOST;
+      /* don't return yet, we need to clean up the timeout first */
+    }
+  }
+  else if(!conn->hostaddr) {
+    /* This is a proxy that hasn't been resolved yet. It may be resolved
+       if we're reusing an existing connection. */
+
+    /* resolve proxy */
+    /* it might already be set if reusing a connection */
+    conn->hostaddr = Curl_resolv(data, conn->proxyhost, conn->port,
+                                 &conn->hostent_buf);
+
+    if(!conn->hostaddr) {
+      failf(data, "Couldn't resolve proxy '%s'", conn->proxyhost);
+      result = CURLE_COULDNT_RESOLVE_PROXY;
+      /* don't return yet, we need to clean up the timeout first */
+    }
+  }
+  Curl_pgrsTime(data, TIMER_NAMELOOKUP);
+#ifdef HAVE_ALARM
+  if(data->set.timeout || data->set.connecttimeout) {
+#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
+    /* 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 */
+      long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);
+      long alarm_set;
+
+      /* the alarm period is counted in even number of seconds */
+      alarm_set = prev_alarm - elapsed_ms/1000;
+
+      if(alarm_set<=0) {
+        /* if it turned negative, 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);
+        result = CURLE_OPERATION_TIMEOUTED;
+        failf(data, "Previous alarm fired off!");
+      }
+      else
+        alarm(alarm_set);
+    }
+    else
+      alarm(0); /* just shut it off */
+  }
+#endif
+  if(result)
+    return result;
+
+  /*************************************************************
+   * Proxy authentication
+   *************************************************************/
+  if(conn->bits.proxy_user_passwd) {
+    char *authorization;
+    snprintf(data->state.buffer, BUFSIZE, "%s:%s",
+             data->state.proxyuser, data->state.proxypasswd);
+    if(Curl_base64_encode(data->state.buffer, strlen(data->state.buffer),
+                          &authorization) >= 0) {
+      if(conn->allocptr.proxyuserpwd)
+        free(conn->allocptr.proxyuserpwd);
+      conn->allocptr.proxyuserpwd =
+        aprintf("Proxy-authorization: Basic %s\015\012", authorization);
+      free(authorization);
+    }
+  }
+
+  /*************************************************************
+   * Send user-agent to HTTP proxies even if the target protocol
+   * isn't HTTP.
+   *************************************************************/
+  if((conn->protocol&PROT_HTTP) || data->change.proxy) {
+    if(data->set.useragent) {
+      if(conn->allocptr.uagent)
+        free(conn->allocptr.uagent);
+      conn->allocptr.uagent =
+        aprintf("User-Agent: %s\015\012", data->set.useragent);
+    }
+  }
+
+  if(-1 == conn->firstsocket) {
+    /* Connect only if not already connected! */
+    result = ConnectPlease(conn);
+    if(CURLE_OK != result)
+      return result;
+
+    if(conn->curl_connect) {
+      /* is there a connect() procedure? */
+
+      /* set start time here for timeout purposes in the
+       * connect procedure, it is later set again for the
+       * progress meter purpose */
+      conn->now = Curl_tvnow();
+
+      /* Call the protocol-specific connect function */
+      result = conn->curl_connect(conn);
+      if(result != CURLE_OK)
+        return result; /* pass back errors */
+    }
+  }
+
+  Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected */
+
+  conn->now = Curl_tvnow(); /* time this *after* the connect is done */
+  conn->bytecount = 0;
+  conn->headerbytecount = 0;
+  
+  /* Figure out the ip-number and display the first host name it shows: */
+#ifdef ENABLE_IPV6
+  {
+    char hbuf[NI_MAXHOST];
+#ifdef NI_WITHSCOPEID
+    const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID;
+#else
+    const int niflags = NI_NUMERICHOST;
+#endif
+    struct addrinfo *ai = conn->serv_addr;
+
+    if (getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf), NULL, 0,
+        niflags)) {
+      snprintf(hbuf, sizeof(hbuf), "?");
+    }
+    if (ai->ai_canonname) {
+      infof(data, "Connected to %s (%s) port %d\n", ai->ai_canonname, hbuf,
+            conn->port);
+    } else {
+      infof(data, "Connected to %s port %d\n", hbuf, conn->port);
+    }
+  }
+#else
+  {
+    struct in_addr in;
+    (void) memcpy(&in.s_addr, &conn->serv_addr.sin_addr, sizeof (in.s_addr));
+    infof(data, "Connected to %s (%s)\n", conn->hostaddr->h_name,
+#if defined(HAVE_INET_NTOA_R)
+          inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf))
+#else
+          inet_ntoa(in)
+#endif
+          );
+  }
+#endif
+
+#ifdef __EMX__
+  /* 20000330 mgs
+   * the 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 CURLE_OK;
+}
+
+CURLcode Curl_connect(struct SessionHandle *data,
+                      struct connectdata **in_connect)
+{
+  CURLcode code;
+  struct connectdata *conn;
+
+  /* call the stuff that needs to be called */
+  code = CreateConnection(data, in_connect);
+
+  if(CURLE_OK != code) {
+    /* We're not allowed to return failure with memory left allocated
+       in the connectdata struct, free those here */
+    conn = (struct connectdata *)*in_connect;
+    if(conn) {
+      Curl_disconnect(conn);      /* close the connection */
+      *in_connect = NULL;         /* return a NULL */
+    }
+  }
+  return code;
+}
+
+
+CURLcode Curl_done(struct connectdata *conn)
+{
+  struct SessionHandle *data=conn->data;
+  CURLcode result;
+
+  /* cleanups done even if the connection is re-used */
+
+  if(conn->bits.rangestringalloc) {
+    free(conn->range);
+    conn->bits.rangestringalloc = FALSE;
+  }
+
+  /* Cleanup possible redirect junk */
+  if(conn->newurl) {
+    free(conn->newurl);
+    conn->newurl = NULL;
+  }
+ 
+  /* this calls the protocol-specific function pointer previously set */
+  if(conn->curl_done)
+    result = conn->curl_done(conn);
+  else
+    result = CURLE_OK;
+
+  Curl_pgrsDone(conn); /* done with the operation */
+
+  /* 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(data->set.reuse_forbid ||
+     ((CURLE_OK == result) && conn->bits.close))
+    result = Curl_disconnect(conn); /* close the connection */
+  else
+    infof(data, "Connection #%d left intact\n", conn->connectindex);
+
+  return result;
+}
+
+CURLcode Curl_do(struct connectdata **connp)
+{
+  CURLcode result=CURLE_OK;
+  struct connectdata *conn = *connp;
+  struct SessionHandle *data=conn->data;
+
+  if(conn->curl_do) {
+    /* generic protocol-specific function pointer set in curl_connect() */
+    result = conn->curl_do(conn);
+
+    /* This was formerly done in transfer.c, but we better do it here */
+    
+    if((CURLE_WRITE_ERROR == result) && conn->bits.reuse) {
+      /* 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 connetion */
+      result = Curl_done(conn);   /* we are so done with this */
+      if(CURLE_OK == result) {
+        /* Now, redo the connect and get a new connection */
+        result = Curl_connect(data, connp);
+        if(CURLE_OK == result)
+          /* ... finally back to actually retry the DO phase */
+          result = conn->curl_do(*connp);
+      }
+    }
+  }
+  return result;
+}
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */

+ 38 - 0
Source/CTest/Curl/url.h

@@ -0,0 +1,38 @@
+#ifndef __URL_H
+#define __URL_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2001, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+/*
+ * Prototypes for library-wide functions provided by url.c
+ */
+
+CURLcode Curl_open(struct SessionHandle **curl);
+CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...);
+CURLcode Curl_close(struct SessionHandle *data); /* opposite of curl_open() */
+CURLcode Curl_connect(struct SessionHandle *, struct connectdata **);
+CURLcode Curl_do(struct connectdata **);
+CURLcode Curl_done(struct connectdata *);
+CURLcode Curl_disconnect(struct connectdata *);
+
+#endif

+ 687 - 0
Source/CTest/Curl/urldata.h

@@ -0,0 +1,687 @@
+#ifndef __URLDATA_H
+#define __URLDATA_H
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2001, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+/* This file is for lib internal stuff */
+
+#include "setup.h"
+#include "hostip.h"
+#include "hash.h"
+
+#define PORT_FTP 21
+#define PORT_TELNET 23
+#define PORT_GOPHER 70
+#define PORT_HTTP 80
+#define PORT_HTTPS 443
+#define PORT_DICT 2628
+#define PORT_LDAP 389
+
+#define DICT_MATCH "/MATCH:"
+#define DICT_MATCH2 "/M:"
+#define DICT_MATCH3 "/FIND:"
+#define DICT_DEFINE "/DEFINE:"
+#define DICT_DEFINE2 "/D:"
+#define DICT_DEFINE3 "/LOOKUP:"
+
+#define CURL_DEFAULT_USER "anonymous"
+#define CURL_DEFAULT_PASSWORD "[email protected]"
+
+#include "cookie.h"
+#include "formdata.h"
+    
+#ifdef USE_SSLEAY
+/* SSLeay stuff usually in /usr/local/ssl/include */
+#ifdef USE_OPENSSL
+#include "openssl/rsa.h"
+#include "openssl/crypto.h"
+#include "openssl/x509.h"
+#include "openssl/pem.h"
+#include "openssl/ssl.h"
+#include "openssl/err.h"
+#ifdef HAVE_OPENSSL_ENGINE_H
+#include <openssl/engine.h>
+#endif
+#else
+#include "rsa.h"
+#include "crypto.h"
+#include "x509.h"
+#include "pem.h"
+#include "ssl.h"
+#include "err.h"
+#endif
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#include "timeval.h"
+
+#include <curl/curl.h>
+
+#include "http_chunks.h" /* for the structs and enum stuff */
+
+/* Download buffer size, keep it fairly big for speed reasons */
+#define BUFSIZE (1024*20)
+
+/* Initial size of the buffer to store headers in, it'll be enlarged in case
+   of need. */
+#define HEADERSIZE 256
+
+/* Just a convenience macro to get the larger value out of two given */
+#ifndef MAX
+#define MAX(x,y) ((x)>(y)?(x):(y))
+#endif
+
+#ifdef KRB4
+/* Types needed for krb4-ftp connections */
+struct krb4buffer {
+  void *data;
+  size_t size;
+  size_t index;
+  int eof_flag;
+};
+enum protection_level { 
+    prot_clear, 
+    prot_safe, 
+    prot_confidential, 
+    prot_private 
+};
+#endif
+
+#ifndef HAVE_OPENSSL_ENGINE_H
+typedef void ENGINE;
+#endif
+/* struct for data related to SSL and SSL connections */
+struct ssl_connect_data {
+  bool use;              /* use ssl encrypted communications TRUE/FALSE */
+#ifdef USE_SSLEAY
+  /* these ones requires specific SSL-types */
+  SSL_CTX* ctx;
+  SSL*     handle;
+  X509*    server_cert;
+#endif /* USE_SSLEAY */
+};
+
+/* information about one single SSL session */
+struct curl_ssl_session {
+  char *name;       /* host name for which this ID was used */
+  void *sessionid;  /* as returned from the SSL layer */
+  long age;         /* just a number, the higher the more recent */
+  unsigned short remote_port; /* remote port to connect to */
+};
+
+struct ssl_config_data {
+  long version;          /* what version the client wants to use */
+  long certverifyresult; /* result from the certificate verification */
+  long verifypeer;       /* set TRUE if this is desired */
+  long verifyhost;       /* 0: no verif, 1: check that CN exists, 2: CN must match hostname */
+  char *CApath;          /* DOES NOT WORK ON WINDOWS */
+  char *CAfile;          /* cerficate to verify peer against */
+  char *random_file;     /* path to file containing "random" data */
+  char *egdsocket;       /* path to file containing the EGD daemon socket */
+  char *cipher_list;     /* list of ciphers to use */
+
+  long numsessions;                 /* SSL session id cache size */
+};
+
+/****************************************************************************
+ * HTTP unique setup
+ ***************************************************************************/
+struct HTTP {
+  struct FormData *sendit;
+  int postsize;
+  const char *p_pragma;      /* Pragma: string */
+  const char *p_accept;      /* Accept: string */
+  long readbytecount; 
+  long writebytecount;
+
+  /* For FORM posting */
+  struct Form form;
+  curl_read_callback storefread;
+  FILE *in;
+
+  struct Curl_chunker chunk;
+};
+
+/****************************************************************************
+ * FTP unique setup
+ ***************************************************************************/
+struct FTP {
+  long *bytecountp;
+  char *user;    /* user name string */
+  char *passwd;  /* password string */
+  char *urlpath; /* the originally given path part of the URL */
+  char *dir;     /* decoded directory */
+  char *file;    /* decoded file */
+
+  char *entrypath; /* the PWD reply when we logged on */
+
+  char *cache;       /* data cache between getresponse()-calls */
+  size_t cache_size; /* size of cache in bytes */                    
+};
+
+/****************************************************************************
+ * FILE unique setup
+ ***************************************************************************/
+struct FILE {
+  int fd; /* open file descriptor to read from! */
+};
+
+/*
+ * Boolean values that concerns this connection.
+ */
+struct ConnectBits {
+  bool close; /* if set, we close the connection after this request */
+  bool reuse; /* if set, this is a re-used connection */
+  bool chunk; /* if set, this is a chunked transfer-encoding */
+  bool httpproxy;    /* if set, this transfer is done through a http proxy */
+  bool user_passwd;    /* do we use user+password for this connection? */
+  bool proxy_user_passwd; /* user+password for the proxy? */
+
+  bool use_range;
+  bool rangestringalloc; /* the range string is malloc()'ed */
+
+  bool resume_done; /* nothing was transfered, resumed transfer already
+                       complete */
+};
+
+/*
+ * This struct is all the previously local variables from Curl_perform() moved
+ * to struct to allow the function to return and get re-invoked better without
+ * losing state.
+ */
+
+struct Curl_transfer_keeper {
+  int bytecount;                /* total number of bytes read */
+  int writebytecount;           /* number of bytes written */
+  long contentlength;           /* size of incoming data */
+  struct timeval start;         /* transfer started at this time */
+  struct timeval now;           /* current time */
+  bool header;                  /* incoming data has HTTP header */
+  int headerline;               /* counts header lines to better track the
+                                   first one */
+  char *hbufp;                  /* points at *end* of header line */
+  int hbuflen;
+  char *str;                    /* within buf */
+  char *str_start;              /* within buf */
+  char *end_ptr;                /* within buf */
+  char *p;                      /* within headerbuff */
+  bool content_range;           /* set TRUE if Content-Range: was found */
+  int offset;                   /* possible resume offset read from the
+                                   Content-Range: header */
+  int httpcode;                 /* error code from the 'HTTP/1.? XXX' line */
+  int httpversion;              /* the HTTP version*10 */
+  bool write_after_100_header;  /* should we enable the write after
+                                   we received a 100-continue/timeout
+                                   or directly */
+
+  /* for the low speed checks: */
+  time_t timeofdoc;
+  long bodywrites;
+  int writetype;
+
+  /* the highest fd we use + 1 */
+  struct SessionHandle *data;
+  struct connectdata *conn;
+  char *buf;
+  char *uploadbuf;
+  int maxfd;
+
+  /* the file descriptors to play with */
+  fd_set readfd;
+  fd_set writefd;
+  fd_set rkeepfd;
+  fd_set wkeepfd;
+  int keepon;
+
+};
+
+
+/*
+ * The connectdata struct contains all fields and variables that should be
+ * unique for an entire connection.
+ */
+struct connectdata {
+  /**** Fields set when inited and not modified again */
+  struct SessionHandle *data; /* link to the root CURL struct */
+  int connectindex; /* what index in the connects index this particular
+                       struct has */
+
+  long protocol; /* PROT_* flags concerning the protocol set */
+#define PROT_MISSING (1<<0)
+#define PROT_GOPHER  (1<<1)
+#define PROT_HTTP    (1<<2)
+#define PROT_HTTPS   (1<<3)
+#define PROT_FTP     (1<<4)
+#define PROT_TELNET  (1<<5)
+#define PROT_DICT    (1<<6)
+#define PROT_LDAP    (1<<7)
+#define PROT_FILE    (1<<8)
+#define PROT_FTPS    (1<<9)
+#define PROT_SSL     (1<<10) /* protocol requires SSL */
+
+  Curl_addrinfo *hostaddr; /* IP-protocol independent host info pointer list */
+  char *hostent_buf; /* pointer to allocated memory for name info */
+
+#ifdef ENABLE_IPV6
+  struct addrinfo *serv_addr;   /* the particular host we use */
+#else
+  struct sockaddr_in serv_addr;
+#endif
+  char protostr[64];  /* store the protocol string in this buffer */
+  char gname[513]; /* store the hostname in this buffer */
+  char *name;      /* host name pointer to fool around with */
+  char *path;      /* allocated buffer to store the URL's path part in */
+  char *hostname;  /* hostname to connect, as parsed from url */
+  long port;       /* which port to use locally */
+  unsigned short remote_port; /* what remote port to connect to,
+                                 not the proxy port! */
+  char *ppath;
+  long bytecount;
+  long headerbytecount;  /* only count received headers */
+
+  char *range; /* range, if used. See README for detailed specification on
+                  this syntax. */
+  ssize_t resume_from; /* continue [ftp] transfer from here */
+
+  char *proxyhost; /* name of the http proxy host */
+
+  struct timeval now;     /* "current" time */
+  struct timeval created; /* creation time */
+  int firstsocket;     /* the main socket to use */
+  int secondarysocket; /* for i.e ftp transfers */
+  long maxdownload; /* in bytes, the maximum amount of data to fetch, 0
+                       means unlimited */
+  
+  struct ssl_connect_data ssl; /* this is for ssl-stuff */
+
+  struct ConnectBits bits;    /* various state-flags for this connection */
+
+  /* These two functions MUST be set by the curl_connect() function to be
+     be protocol dependent */
+  CURLcode (*curl_do)(struct connectdata *connect);
+  CURLcode (*curl_done)(struct connectdata *connect);
+
+  /* This function *MAY* be set to a protocol-dependent function that is run
+   * after the connect() and everything is done, as a step in the connection.
+   */ 
+  CURLcode (*curl_connect)(struct connectdata *connect);
+
+  /* This function *MAY* be set to a protocol-dependent function that is run
+   * by the curl_disconnect(), as a step in the disconnection.
+   */ 
+  CURLcode (*curl_disconnect)(struct connectdata *connect);
+
+  /* This function *MAY* be set to a protocol-dependent function that is run
+   * in the curl_close() function if protocol-specific cleanups are required.
+   */ 
+  CURLcode (*curl_close)(struct connectdata *connect);
+
+  /**** curl_get() phase fields */
+
+  /* READ stuff */
+  int sockfd;            /* socket to read from or -1 */
+  int size;              /* -1 if unknown at this point */
+  bool getheader;        /* TRUE if header parsing is wanted */
+  long *bytecountp;      /* return number of bytes read or NULL */
+          
+  /* WRITE stuff */
+  int writesockfd;       /* socket to write to, it may very well be
+                            the same we read from. -1 disables */
+  long *writebytecountp; /* return number of bytes written or NULL */
+
+  /** Dynamicly allocated strings, may need to be freed before this **/
+  /** struct is killed.                                             **/
+  struct dynamically_allocated_data {
+    char *proxyuserpwd; /* free later if not NULL! */
+    char *uagent; /* free later if not NULL! */
+    char *userpwd; /* free later if not NULL! */
+    char *rangeline; /* free later if not NULL! */
+    char *ref; /* free later if not NULL! */
+    char *cookie; /* free later if not NULL! */
+    char *host; /* free later if not NULL */
+  } allocptr;
+
+  char *newurl; /* This can only be set if a Location: was in the
+                   document headers */
+
+#ifdef KRB4
+  enum protection_level command_prot;
+  enum protection_level data_prot;
+  enum protection_level request_data_prot;
+
+  size_t buffer_size;
+
+  struct krb4buffer in_buffer, out_buffer;
+  int sec_complete;
+  void *app_data;
+
+  struct Curl_sec_client_mech *mech;
+  struct sockaddr_in local_addr;
+
+#endif
+
+  /*************** Request - specific items ************/
+  /* previously this was in the urldata struct */
+  union {
+    struct HTTP *http;
+    struct HTTP *gopher; /* alias, just for the sake of being more readable */
+    struct HTTP *https;  /* alias, just for the sake of being more readable */
+    struct FTP *ftp;
+    struct FILE *file;
+    void *telnet;        /* private for telnet.c-eyes only */
+#if 0 /* no need for special ones for these: */
+    struct LDAP *ldap;
+    struct DICT *dict;
+#endif
+    void *generic;
+  } proto;
+
+  /* This struct is inited when needed */
+  struct Curl_transfer_keeper keep;
+
+  /* 'upload_present' is used to keep a byte counter of how much data there is
+     still left in the buffer, aimed for upload. */
+  int upload_present;
+
+   /* 'upload_fromhere' is used as a read-pointer when we uploaded parts of a
+      buffer, so the next read should read from where this pointer points to,
+      and the 'upload_present' contains the number of bytes available at this
+      position */
+  char *upload_fromhere;                                   
+};
+
+/*
+ * Struct to keep statistical and informational data.
+ */
+struct PureInfo {
+  int httpcode;
+  int httpversion;
+  long filetime; /* If requested, this is might get set. Set to -1 if
+                    the time was unretrievable */
+  long header_size;  /* size of read header(s) in bytes */
+  long request_size; /* the amount of bytes sent in the request(s) */
+
+  char *contenttype; /* the content type of the object */
+};
+
+
+struct Progress {
+  long lastshow; /* time() of the last displayed progress meter or NULL to
+                    force redraw at next call */
+  double size_dl;
+  double size_ul;
+  double downloaded;
+  double uploaded;
+
+  double current_speed; /* uses the currently fastest transfer */
+
+  bool callback;  /* set when progress callback is used */
+  int width; /* screen width at download start */
+  int flags; /* see progress.h */
+
+  double timespent;
+
+  double dlspeed;
+  double ulspeed;
+
+  double t_nslookup;
+  double t_connect;
+  double t_pretransfer;
+  double t_starttransfer;
+
+  struct timeval start;
+  struct timeval t_startsingle;
+#define CURR_TIME (5+1) /* 6 entries for 5 seconds */
+
+  double speeder[ CURR_TIME ];
+  struct timeval speeder_time[ CURR_TIME ];
+  int speeder_c;
+};
+
+typedef enum {
+  HTTPREQ_NONE, /* first in list */
+  HTTPREQ_GET,
+  HTTPREQ_POST,
+  HTTPREQ_POST_FORM, /* we make a difference internally */
+  HTTPREQ_PUT,
+  HTTPREQ_CUSTOM,
+  HTTPREQ_LAST /* last in list */
+} Curl_HttpReq;
+
+/*
+ * Values that are generated, temporary or calculated internally for a
+ * "session handle" must be defined within the 'struct urlstate'.  This struct
+ * will be used within the SessionHandle struct. When the 'SessionHandle'
+ * struct is cloned, this data MUST NOT be copied.
+ *
+ * Remember that any "state" information goes globally for the curl handle.
+ * Session-data MUST be put in the connectdata struct and here.  */
+#define MAX_CURL_USER_LENGTH 256
+#define MAX_CURL_PASSWORD_LENGTH 256
+
+struct UrlState {
+  /* buffers to store authentication data in, as parsed from input options */
+  char user[MAX_CURL_USER_LENGTH];
+  char passwd[MAX_CURL_PASSWORD_LENGTH];
+  char proxyuser[MAX_CURL_USER_LENGTH];
+  char proxypasswd[MAX_CURL_PASSWORD_LENGTH];
+
+  struct timeval keeps_speed; /* for the progress meter really */
+
+  /* 'connects' will be an allocated array with pointers. If the pointer is
+     set, it holds an allocated connection. */
+  struct connectdata **connects;
+  long numconnects; /* size of the 'connects' array */
+
+  char *headerbuff; /* allocated buffer to store headers in */
+  int headersize;   /* size of the allocation */
+
+  char buffer[BUFSIZE+1]; /* download buffer */
+  char uploadbuffer[BUFSIZE+1]; /* upload buffer */
+  double current_speed;  /* the ProgressShow() funcion sets this */
+
+  bool this_is_a_follow; /* this is a followed Location: request */
+
+  char *auth_host; /* if set, this should be the host name that we will
+                      sent authorization to, no else. Used to make Location:
+                      following not keep sending user+password... This is
+                      strdup() data.
+                    */
+
+  struct curl_ssl_session *session; /* array of 'numsessions' size */
+  long sessionage;                  /* number of the most recent session */
+
+  char scratch[BUFSIZE*2]; /* huge buffer when doing upload CRLF replacing */
+  bool errorbuf; /* Set to TRUE if the error buffer is already filled in.
+                    This must be set to FALSE every time _easy_perform() is
+                    called. */
+
+#ifdef HAVE_SIGNAL
+  /* storage for the previous bag^H^H^HSIGPIPE signal handler :-) */
+  void (*prev_signal)(int sig);
+#endif
+  bool allow_port; /* Is set.use_port allowed to take effect or not. This
+                      is always set TRUE when curl_easy_perform() is called. */
+};
+
+
+/*
+ * This 'DynamicStatic' struct defines dynamic states that actually change
+ * values in the 'UserDefined' area, which MUST be taken into consideration
+ * if the UserDefined struct is cloned or similar. You can probably just
+ * copy these, but each one indicate a special action on other data.
+ */
+
+struct DynamicStatic {
+  char *url;        /* work URL, copied from UserDefined */
+  bool url_alloc;   /* URL string is malloc()'ed */
+  char *proxy;      /* work proxy, copied from UserDefined */
+  bool proxy_alloc; /* http proxy string is malloc()'ed */
+  char *referer;    /* referer string */
+  bool referer_alloc; /* referer sting is malloc()ed */
+};
+
+/*
+ * This 'UserDefined' struct must only contain data that is set once to go
+ * for many (perhaps) independent connections. Values that are generated or
+ * calculated internally for the "session handle" MUST be defined within the
+ * 'struct urlstate' instead. The only exceptions MUST note the changes in
+ * the 'DynamicStatic' struct.
+ */
+
+struct UserDefined {
+  FILE *err;    /* the stderr writes goes here */
+  char *errorbuffer; /* store failure messages in here */
+  char *proxyuserpwd;  /* Proxy <user:password>, if used */
+  long proxyport; /* If non-zero, use this port number by default. If the
+                     proxy string features a ":[port]" that one will override
+                     this. */  
+  void *out;         /* the fetched file goes here */
+  void *in;          /* the uploaded file is read from here */
+  void *writeheader; /* write the header to this is non-NULL */
+  char *set_url;     /* what original URL to work on */
+  char *set_proxy;   /* proxy to use */
+  long use_port;     /* which port to use (when not using default) */
+  char *userpwd;     /* <user:password>, if used */
+  char *set_range;   /* range, if used. See README for detailed specification
+                        on this syntax. */
+  long followlocation; /* as in HTTP Location: */
+  long maxredirs;    /* maximum no. of http(s) redirects to follow */
+  char *set_referer; /* custom string */
+  bool free_referer; /* set TRUE if 'referer' points to a string we
+                        allocated */
+  char *useragent;   /* User-Agent string */
+  char *postfields;  /* if POST, set the fields' values here */
+  size_t postfieldsize; /* if POST, this might have a size to use instead of
+                           strlen(), and then the data *may* be binary (contain
+                           zero bytes) */
+  char *ftpport;     /* port to send with the FTP PORT command */
+  char *device;      /* network interface to use */
+  curl_write_callback fwrite;        /* function that stores the output */
+  curl_write_callback fwrite_header; /* function that stores headers */
+  curl_read_callback fread;          /* function that reads the input */
+  curl_progress_callback fprogress;  /* function for progress information */
+  void *progress_client; /* pointer to pass to the progress callback */
+  curl_passwd_callback fpasswd;      /* call for password */
+  void *passwd_client;               /* pass to the passwd callback */
+  long timeout;         /* in seconds, 0 means no timeout */
+  long connecttimeout;  /* in seconds, 0 means no timeout */
+  long infilesize;      /* size of file to upload, -1 means unknown */
+  long low_speed_limit; /* bytes/second */
+  long low_speed_time;  /* number of seconds */
+  int set_resume_from;  /* continue [ftp] transfer from here */
+  char *cookie;         /* HTTP cookie string to send */
+  struct curl_slist *headers; /* linked list of extra headers */
+  struct HttpPost *httppost;  /* linked list of POST data */
+  char *cert;           /* certificate */
+  char *cert_type;      /* format for certificate (default: PEM) */
+  char *key;            /* private key */
+  char *key_type;       /* format for private key (default: PEM) */
+  char *key_passwd;     /* plain text private key password */
+  char *crypto_engine;  /* name of the crypto engine to use */
+  char *cookiejar;      /* dump all cookies to this file */
+  bool crlf;            /* convert crlf on ftp upload(?) */
+  struct curl_slist *quote;     /* after connection is established */
+  struct curl_slist *postquote; /* after the transfer */
+  struct curl_slist *prequote; /* before the transfer, after type (Wesley Laxton)*/
+  struct curl_slist *telnet_options; /* linked list of telnet options */
+  curl_TimeCond timecondition; /* kind of time/date comparison */
+  time_t timevalue;       /* what time to compare with */
+  curl_closepolicy closepolicy; /* connection cache close concept */
+  Curl_HttpReq httpreq;   /* what kind of HTTP request (if any) is this */
+  char *customrequest;    /* HTTP/FTP request to use */
+  long httpversion; /* when non-zero, a specific HTTP version requested to
+                       be used in the library's request(s) */
+  char *auth_host; /* if set, this is the allocated string to the host name
+                    * to which to send the authorization data to, and no other
+                    * host (which location-following otherwise could lead to)
+                    */
+  char *krb4_level; /* what security level */
+  struct ssl_config_data ssl;  /* user defined SSL stuff */
+
+  int dns_cache_timeout; /* DNS cache timeout */
+  
+/* Here follows boolean settings that define how to behave during
+   this session. They are STATIC, set by libcurl users or at least initially
+   and they don't change during operations. */
+
+  bool get_filetime;
+  bool tunnel_thru_httpproxy;
+  bool ftp_append;
+  bool ftp_ascii;
+  bool ftp_list_only;
+  bool ftp_use_port;
+  bool hide_progress;
+  bool http_fail_on_error;
+  bool http_follow_location;
+  bool include_header;
+#define http_include_header include_header /* former name */
+
+  bool http_set_referer;
+  bool http_auto_referer; /* set "correct" referer when following location: */
+  bool no_body;
+  bool set_port;
+  bool upload;
+  bool use_netrc;
+  bool verbose;
+  bool krb4;             /* kerberos4 connection requested */
+  bool reuse_forbid;     /* forbidden to be reused, close after use */
+  bool reuse_fresh;      /* do not re-use an existing connection  */
+  bool expect100header;  /* TRUE if we added Expect: 100-continue */
+  bool ftp_use_epsv;     /* if EPSV is to be attempted or not */
+
+  bool global_dns_cache;
+};
+
+/*
+ * In August 2001, this struct was redesigned and is since stricter than
+ * before. The 'connectdata' struct MUST have all the connection oriented
+ * stuff as we may now have several simultaneous connections and connection
+ * structs in memory.
+ *
+ * From now on, the 'SessionHandle' must only contain data that is set once to
+ * go for many (perhaps) independent connections. Values that are generated or
+ * calculated internally for the "session handle" must be defined within the
+ * 'struct urlstate' instead.  */
+
+struct SessionHandle {
+  curl_hash          *hostcache;
+  struct UserDefined set;      /* values set by the libcurl user */
+  struct DynamicStatic change; /* possibly modified userdefined data */
+
+  struct CookieInfo *cookies;  /* the cookies, read from files and servers */
+  struct Progress progress;    /* for all the progress meter data */
+  struct UrlState state;       /* struct for fields used for state info and
+                                  other dynamic purposes */
+  struct PureInfo info;        /* stats, reports and info data */
+#ifdef USE_SSLEAY
+  ENGINE*  engine;
+#endif /* USE_SSLEAY */
+};
+
+#define LIBCURL_NAME "libcurl"
+
+#endif

+ 121 - 0
Source/CTest/Curl/version.c

@@ -0,0 +1,121 @@
+/*****************************************************************************
+ *                                  _   _ ____  _     
+ *  Project                     ___| | | |  _ \| |    
+ *                             / __| | | | |_) | |    
+ *                            | (__| |_| |  _ <| |___ 
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2002, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * In order to be useful for every potential user, curl and libcurl are
+ * dual-licensed under the MPL and the MIT/X-derivate licenses.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the MPL or the MIT/X-derivate
+ * licenses. You may pick one of these licenses.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ *****************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+#include <stdio.h>
+
+#include <curl/curl.h>
+#include "urldata.h"
+
+char *curl_version(void)
+{
+  static char version[200];
+  char *ptr;
+  strcpy(version, LIBCURL_NAME " " LIBCURL_VERSION );
+  ptr=strchr(version, '\0');
+
+#ifdef USE_SSLEAY
+
+#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]=((ssleay_value>>4)&0xff) + 'a' -1;
+      }
+      else
+        sub[0]='\0';
+    }
+
+    sprintf(ptr, " (OpenSSL %lx.%lx.%lx%s)",
+            (ssleay_value>>28)&0xf,
+            (ssleay_value>>20)&0xff,
+            (ssleay_value>>12)&0xff,
+            sub);
+  }
+
+#else
+#if (SSLEAY_VERSION_NUMBER >= 0x900000)
+  sprintf(ptr, " (SSL %lx.%lx.%lx)",
+          (SSLEAY_VERSION_NUMBER>>28)&0xff,
+          (SSLEAY_VERSION_NUMBER>>20)&0xff,
+          (SSLEAY_VERSION_NUMBER>>12)&0xf);
+#else
+  {
+    char sub[2];
+    sub[1]='\0';
+    if(SSLEAY_VERSION_NUMBER&0x0f) {
+      sub[0]=(SSLEAY_VERSION_NUMBER&0x0f) + 'a' -1;
+    }
+    else
+      sub[0]='\0';
+
+    sprintf(ptr, " (SSL %x.%x.%x%s)",
+            (SSLEAY_VERSION_NUMBER>>12)&0xff,
+            (SSLEAY_VERSION_NUMBER>>8)&0xf,
+            (SSLEAY_VERSION_NUMBER>>4)&0xf, sub);
+  }
+#endif
+#endif
+  ptr=strchr(ptr, '\0');
+#endif
+
+#if defined(KRB4) || defined(ENABLE_IPV6)
+  strcat(ptr, " (");
+  ptr+=2;
+#ifdef KRB4
+  sprintf(ptr, "krb4 ");
+  ptr += strlen(ptr);
+#endif
+#ifdef ENABLE_IPV6
+  sprintf(ptr, "ipv6 ");
+  ptr += strlen(ptr);
+#endif
+  sprintf(ptr, "enabled)");
+  ptr += strlen(ptr);
+#endif
+
+#ifdef USE_ZLIB
+  sprintf(ptr, " (zlib %s)", zlibVersion());
+  ptr += strlen(ptr);
+#endif
+
+  return version;
+}
+
+/*
+ * local variables:
+ * eval: (load-file "../curl-mode.el")
+ * end:
+ * vim600: fdm=marker
+ * vim: et sw=2 ts=2 sts=2 tw=78
+ */